From 44f9576c0cfcc81f68fc7e7e456264d049ab6581 Mon Sep 17 00:00:00 2001
From: Thomas Feuvrier <thomas.feuvrier@c-s.fr>
Date: Mon, 22 May 2006 16:20:22 +0000
Subject: [PATCH] Ajout GDAL Nouvelle version

---
 Utilities/GDAL/frmts/bmp/GNUmakefile          |   13 +
 Utilities/GDAL/frmts/bmp/bmpdataset.cpp       | 1435 +++++
 Utilities/GDAL/frmts/bmp/frmt_bmp.html        |   41 +
 Utilities/GDAL/frmts/bmp/makefile.vc          |   13 +
 Utilities/GDAL/frmts/bsb/GNUmakefile          |   37 +
 Utilities/GDAL/frmts/bsb/Makefile.dist        |    2 +
 Utilities/GDAL/frmts/bsb/README.dist          |   48 +
 Utilities/GDAL/frmts/bsb/bsb2raw.c            |  118 +
 Utilities/GDAL/frmts/bsb/bsb_read.c           |  794 +++
 Utilities/GDAL/frmts/bsb/bsb_read.h           |   93 +
 Utilities/GDAL/frmts/bsb/bsbdataset.cpp       |  821 +++
 Utilities/GDAL/frmts/bsb/makefile.vc          |   13 +
 Utilities/GDAL/frmts/ceos2/GNUmakefile        |   16 +
 Utilities/GDAL/frmts/ceos2/ceos.c             |  425 ++
 Utilities/GDAL/frmts/ceos2/ceos.h             |  359 ++
 Utilities/GDAL/frmts/ceos2/ceosrecipe.c       |  816 +++
 Utilities/GDAL/frmts/ceos2/ceossar.c          |  144 +
 Utilities/GDAL/frmts/ceos2/link.c             |  106 +
 Utilities/GDAL/frmts/ceos2/makefile.vc        |   16 +
 .../GDAL/frmts/ceos2/sar_ceosdataset.cpp      | 2106 +++++++
 Utilities/GDAL/frmts/dods/GNUmakefile         |   35 +
 Utilities/GDAL/frmts/dods/dodsdataset2.cpp    | 1685 +++++
 Utilities/GDAL/frmts/dods/frmt_dods.html      |  185 +
 Utilities/GDAL/frmts/ecw/GNUmakefile          |   13 +
 Utilities/GDAL/frmts/ecw/ecwcreatecopy.cpp    | 1652 +++++
 Utilities/GDAL/frmts/ecw/ecwdataset.cpp       | 1784 ++++++
 Utilities/GDAL/frmts/ecw/frmt_ecw.html        |   66 +
 Utilities/GDAL/frmts/ecw/frmt_jp2ecw.html     |   85 +
 Utilities/GDAL/frmts/ecw/jp2userbox.cpp       |  136 +
 Utilities/GDAL/frmts/ecw/jp2userbox.h         |   67 +
 Utilities/GDAL/frmts/ecw/lookup.py            |  196 +
 Utilities/GDAL/frmts/ecw/makefile.vc          |   27 +
 Utilities/GDAL/frmts/ecw/vsiiostream.h        |  219 +
 Utilities/GDAL/frmts/elas/GNUmakefile         |   14 +
 Utilities/GDAL/frmts/elas/elasdataset.cpp     |  684 +++
 Utilities/GDAL/frmts/elas/makefile.vc         |   13 +
 Utilities/GDAL/frmts/envisat/EnvisatFile.c    | 1925 ++++++
 Utilities/GDAL/frmts/envisat/EnvisatFile.h    |  137 +
 Utilities/GDAL/frmts/envisat/GNUmakefile      |   24 +
 Utilities/GDAL/frmts/envisat/dumpgeo.c        |  171 +
 Utilities/GDAL/frmts/envisat/envisat_dump.c   |  118 +
 .../GDAL/frmts/envisat/envisatdataset.cpp     |  840 +++
 Utilities/GDAL/frmts/envisat/makefile.vc      |   15 +
 Utilities/GDAL/frmts/fit/GNUmakefile          |   15 +
 Utilities/GDAL/frmts/fit/fit.cpp              |  210 +
 Utilities/GDAL/frmts/fit/fit.h                |  122 +
 Utilities/GDAL/frmts/fit/fitdataset.cpp       | 1445 +++++
 Utilities/GDAL/frmts/fit/gstEndian.h          |  140 +
 Utilities/GDAL/frmts/fit/gstTypes.h           |   57 +
 Utilities/GDAL/frmts/fit/makefile.vc          |   14 +
 Utilities/GDAL/frmts/fits/GNUmakefile         |   15 +
 Utilities/GDAL/frmts/fits/fitsdataset.cpp     |  684 +++
 Utilities/GDAL/frmts/fits/makefile.vc         |   15 +
 Utilities/GDAL/frmts/frmt_various.html        |  725 +++
 Utilities/GDAL/frmts/gdalallregister.cpp      |  384 ++
 Utilities/GDAL/frmts/gif/GNUmakefile          |   26 +
 Utilities/GDAL/frmts/gif/frmt_gif.html        |   52 +
 Utilities/GDAL/frmts/gif/gifdataset.cpp       |  795 +++
 Utilities/GDAL/frmts/gif/libungif/README      |   14 +
 Utilities/GDAL/frmts/gif/libungif/dgif_lib.c  | 1001 +++
 Utilities/GDAL/frmts/gif/libungif/egif_lib.c  |  824 +++
 Utilities/GDAL/frmts/gif/libungif/gif_err.c   |  133 +
 Utilities/GDAL/frmts/gif/libungif/gif_lib.h   |  301 +
 .../GDAL/frmts/gif/libungif/gif_lib_private.h |   50 +
 Utilities/GDAL/frmts/gif/libungif/gifalloc.c  |  345 ++
 Utilities/GDAL/frmts/gif/libungif/makefile.vc |   14 +
 Utilities/GDAL/frmts/gif/makefile.vc          |   23 +
 Utilities/GDAL/frmts/grass/GNUmakefile        |   28 +
 Utilities/GDAL/frmts/grass/frmt_grass.html    |   76 +
 Utilities/GDAL/frmts/grass/grass57dataset.cpp |  994 +++
 Utilities/GDAL/frmts/grass/grassdataset.cpp   |  647 ++
 Utilities/GDAL/frmts/grass/pkg/Makefile.in    |   49 +
 Utilities/GDAL/frmts/grass/pkg/README         |   63 +
 Utilities/GDAL/frmts/grass/pkg/aclocal.m4     |  202 +
 Utilities/GDAL/frmts/grass/pkg/configure      | 3901 ++++++++++++
 Utilities/GDAL/frmts/grass/pkg/configure.in   |  147 +
 Utilities/GDAL/frmts/gtiff/GNUmakefile        |   48 +
 Utilities/GDAL/frmts/gtiff/epsg_to_wkt.cpp    |  354 ++
 Utilities/GDAL/frmts/gtiff/frmt_gtiff.html    |  113 +
 Utilities/GDAL/frmts/gtiff/geotiff.cpp        | 4752 +++++++++++++++
 Utilities/GDAL/frmts/gtiff/gt_overview.cpp    |  403 ++
 Utilities/GDAL/frmts/gtiff/gt_wkt_srs.cpp     | 1884 ++++++
 .../GDAL/frmts/gtiff/libgeotiff/GNUmakefile   |   61 +
 .../GDAL/frmts/gtiff/libgeotiff/cpl_serv.h    |   41 +
 .../frmts/gtiff/libgeotiff/epsg_datum.inc     |  174 +
 .../frmts/gtiff/libgeotiff/epsg_ellipse.inc   |   48 +
 .../GDAL/frmts/gtiff/libgeotiff/epsg_gcs.inc  |  193 +
 .../GDAL/frmts/gtiff/libgeotiff/epsg_pcs.inc  | 1012 +++
 .../GDAL/frmts/gtiff/libgeotiff/epsg_pm.inc   |   22 +
 .../GDAL/frmts/gtiff/libgeotiff/epsg_proj.inc |  443 ++
 .../frmts/gtiff/libgeotiff/epsg_units.inc     |   35 +
 .../frmts/gtiff/libgeotiff/epsg_vertcs.inc    |   46 +
 .../GDAL/frmts/gtiff/libgeotiff/geo_config.h  |    7 +
 .../frmts/gtiff/libgeotiff/geo_ctrans.inc     |   91 +
 .../GDAL/frmts/gtiff/libgeotiff/geo_extra.c   |  747 +++
 .../GDAL/frmts/gtiff/libgeotiff/geo_free.c    |   62 +
 .../GDAL/frmts/gtiff/libgeotiff/geo_get.c     |  176 +
 .../GDAL/frmts/gtiff/libgeotiff/geo_keyp.h    |   98 +
 .../GDAL/frmts/gtiff/libgeotiff/geo_names.c   |  175 +
 .../GDAL/frmts/gtiff/libgeotiff/geo_new.c     |  242 +
 .../frmts/gtiff/libgeotiff/geo_normalize.c    | 2402 ++++++++
 .../frmts/gtiff/libgeotiff/geo_normalize.h    |  238 +
 .../GDAL/frmts/gtiff/libgeotiff/geo_print.c   |  517 ++
 .../GDAL/frmts/gtiff/libgeotiff/geo_set.c     |  262 +
 .../GDAL/frmts/gtiff/libgeotiff/geo_tiffp.c   |  140 +
 .../GDAL/frmts/gtiff/libgeotiff/geo_tiffp.h   |  114 +
 .../GDAL/frmts/gtiff/libgeotiff/geo_trans.c   |  334 +
 .../GDAL/frmts/gtiff/libgeotiff/geo_write.c   |  193 +
 .../GDAL/frmts/gtiff/libgeotiff/geokeys.h     |   54 +
 .../GDAL/frmts/gtiff/libgeotiff/geokeys.inc   |   76 +
 .../GDAL/frmts/gtiff/libgeotiff/geonames.h    |  146 +
 .../GDAL/frmts/gtiff/libgeotiff/geotiff.h     |  117 +
 .../frmts/gtiff/libgeotiff/geotiff_proj4.c    |  716 +++
 .../GDAL/frmts/gtiff/libgeotiff/geotiffio.h   |   16 +
 .../GDAL/frmts/gtiff/libgeotiff/geovalues.h   |  120 +
 .../GDAL/frmts/gtiff/libgeotiff/makefile.vc   |   27 +
 Utilities/GDAL/frmts/gtiff/libgeotiff/xtiff.c |  199 +
 .../GDAL/frmts/gtiff/libgeotiff/xtiffio.h     |   83 +
 .../GDAL/frmts/gtiff/libtiff/GNUmakefile      |   79 +
 .../GDAL/frmts/gtiff/libtiff/makefile.vc      |   60 +
 Utilities/GDAL/frmts/gtiff/libtiff/t4.h       |  285 +
 Utilities/GDAL/frmts/gtiff/libtiff/tif_aux.c  |  267 +
 .../GDAL/frmts/gtiff/libtiff/tif_close.c      |  119 +
 .../GDAL/frmts/gtiff/libtiff/tif_codec.c      |  150 +
 .../GDAL/frmts/gtiff/libtiff/tif_color.c      |  275 +
 .../GDAL/frmts/gtiff/libtiff/tif_compress.c   |  286 +
 .../GDAL/frmts/gtiff/libtiff/tif_config.h     |   41 +
 Utilities/GDAL/frmts/gtiff/libtiff/tif_dir.c  | 1350 ++++
 Utilities/GDAL/frmts/gtiff/libtiff/tif_dir.h  |  202 +
 .../GDAL/frmts/gtiff/libtiff/tif_dirinfo.c    |  846 +++
 .../GDAL/frmts/gtiff/libtiff/tif_dirread.c    | 1898 ++++++
 .../GDAL/frmts/gtiff/libtiff/tif_dirwrite.c   | 1242 ++++
 .../GDAL/frmts/gtiff/libtiff/tif_dumpmode.c   |  117 +
 .../GDAL/frmts/gtiff/libtiff/tif_error.c      |   73 +
 .../GDAL/frmts/gtiff/libtiff/tif_extension.c  |  111 +
 Utilities/GDAL/frmts/gtiff/libtiff/tif_fax3.c | 1566 +++++
 Utilities/GDAL/frmts/gtiff/libtiff/tif_fax3.h |  525 ++
 .../GDAL/frmts/gtiff/libtiff/tif_fax3sm.c     | 1253 ++++
 .../GDAL/frmts/gtiff/libtiff/tif_flush.c      |   67 +
 .../GDAL/frmts/gtiff/libtiff/tif_getimage.c   | 2741 +++++++++
 Utilities/GDAL/frmts/gtiff/libtiff/tif_jpeg.c | 1970 ++++++
 Utilities/GDAL/frmts/gtiff/libtiff/tif_luv.c  | 1606 +++++
 Utilities/GDAL/frmts/gtiff/libtiff/tif_lzw.c  | 1084 ++++
 Utilities/GDAL/frmts/gtiff/libtiff/tif_next.c |  144 +
 Utilities/GDAL/frmts/gtiff/libtiff/tif_open.c |  683 +++
 .../GDAL/frmts/gtiff/libtiff/tif_packbits.c   |  293 +
 .../GDAL/frmts/gtiff/libtiff/tif_pixarlog.c   | 1342 ++++
 .../GDAL/frmts/gtiff/libtiff/tif_predict.c    |  626 ++
 .../GDAL/frmts/gtiff/libtiff/tif_predict.h    |   64 +
 .../GDAL/frmts/gtiff/libtiff/tif_print.c      |  639 ++
 Utilities/GDAL/frmts/gtiff/libtiff/tif_read.c |  685 +++
 .../GDAL/frmts/gtiff/libtiff/tif_strip.c      |  363 ++
 Utilities/GDAL/frmts/gtiff/libtiff/tif_swab.c |  235 +
 .../GDAL/frmts/gtiff/libtiff/tif_thunder.c    |  158 +
 Utilities/GDAL/frmts/gtiff/libtiff/tif_tile.c |  273 +
 .../GDAL/frmts/gtiff/libtiff/tif_version.c    |   33 +
 Utilities/GDAL/frmts/gtiff/libtiff/tif_vsi.c  |  235 +
 .../GDAL/frmts/gtiff/libtiff/tif_warning.c    |   74 +
 .../GDAL/frmts/gtiff/libtiff/tif_write.c      |  725 +++
 Utilities/GDAL/frmts/gtiff/libtiff/tif_zip.c  |  378 ++
 Utilities/GDAL/frmts/gtiff/libtiff/tiff.h     |  647 ++
 Utilities/GDAL/frmts/gtiff/libtiff/tiffconf.h |    3 +
 Utilities/GDAL/frmts/gtiff/libtiff/tiffio.h   |  520 ++
 Utilities/GDAL/frmts/gtiff/libtiff/tiffiop.h  |  325 +
 Utilities/GDAL/frmts/gtiff/libtiff/tiffvers.h |    9 +
 Utilities/GDAL/frmts/gtiff/libtiff/uvcode.h   |  173 +
 Utilities/GDAL/frmts/gtiff/makefile.vc        |   43 +
 Utilities/GDAL/frmts/gtiff/tif_float.c        |  199 +
 Utilities/GDAL/frmts/gtiff/tif_memio.c        |  255 +
 Utilities/GDAL/frmts/gtiff/tif_memio.h        |   73 +
 Utilities/GDAL/frmts/gtiff/tif_overview.c     |  644 ++
 Utilities/GDAL/frmts/gtiff/tif_ovrcache.c     |  273 +
 Utilities/GDAL/frmts/gtiff/tif_ovrcache.h     |   93 +
 Utilities/GDAL/frmts/gtiff/tifvsi.cpp         |  169 +
 Utilities/GDAL/frmts/gxf/.cvsignore           |    7 +
 Utilities/GDAL/frmts/gxf/Doxyfile             |  255 +
 Utilities/GDAL/frmts/gxf/GNUmakefile          |   64 +
 Utilities/GDAL/frmts/gxf/Makefile.in          |   34 +
 Utilities/GDAL/frmts/gxf/README               |   65 +
 Utilities/GDAL/frmts/gxf/configure.in         |   16 +
 Utilities/GDAL/frmts/gxf/gxf.dox              |   91 +
 Utilities/GDAL/frmts/gxf/gxf_ogcwkt.c         |  637 ++
 Utilities/GDAL/frmts/gxf/gxf_proj4.c          |  630 ++
 Utilities/GDAL/frmts/gxf/gxfdataset.cpp       |  388 ++
 Utilities/GDAL/frmts/gxf/gxfopen.c            | 1012 +++
 Utilities/GDAL/frmts/gxf/gxfopen.h            |  154 +
 Utilities/GDAL/frmts/gxf/makefile.vc          |   15 +
 Utilities/GDAL/frmts/gxf/makefile.vc.dist     |   14 +
 Utilities/GDAL/frmts/hdf5/GNUmakefile         |   16 +
 Utilities/GDAL/frmts/hdf5/frmt_hdf5.html      |  231 +
 Utilities/GDAL/frmts/hdf5/hdf5dataset.cpp     |  919 +++
 Utilities/GDAL/frmts/hdf5/hdf5dataset.h       |  124 +
 .../GDAL/frmts/hdf5/hdf5imagedataset.cpp      |  683 +++
 Utilities/GDAL/frmts/hdf5/makefile.vc         |   14 +
 Utilities/GDAL/frmts/ilwis/GNUmakefile        |   13 +
 Utilities/GDAL/frmts/ilwis/frmt_ilwis.html    |  322 +
 .../frmts/ilwis/ilwiscoordinatesystem.cpp     | 1197 ++++
 Utilities/GDAL/frmts/ilwis/ilwisdataset.cpp   | 1907 ++++++
 Utilities/GDAL/frmts/ilwis/ilwisdataset.h     |  229 +
 Utilities/GDAL/frmts/ilwis/makefile.vc        |   15 +
 Utilities/GDAL/frmts/iso8211/.cvsignore       |    5 +
 Utilities/GDAL/frmts/iso8211/8211dump.cpp     |  134 +
 Utilities/GDAL/frmts/iso8211/8211view.cpp     |  277 +
 Utilities/GDAL/frmts/iso8211/Doxyfile         |  255 +
 Utilities/GDAL/frmts/iso8211/GNUmakefile      |   91 +
 Utilities/GDAL/frmts/iso8211/Makefile.in      |   72 +
 Utilities/GDAL/frmts/iso8211/aclocal.m4       |   15 +
 Utilities/GDAL/frmts/iso8211/configure.in     |   18 +
 Utilities/GDAL/frmts/iso8211/ddffield.cpp     |  387 ++
 Utilities/GDAL/frmts/iso8211/ddffielddefn.cpp |  953 +++
 Utilities/GDAL/frmts/iso8211/ddfmodule.cpp    |  759 +++
 Utilities/GDAL/frmts/iso8211/ddfrecord.cpp    | 1941 ++++++
 .../GDAL/frmts/iso8211/ddfsubfielddefn.cpp    |  994 +++
 Utilities/GDAL/frmts/iso8211/ddfutils.cpp     |  125 +
 Utilities/GDAL/frmts/iso8211/intro.dox        |  219 +
 Utilities/GDAL/frmts/iso8211/iso8211.h        |  582 ++
 Utilities/GDAL/frmts/iso8211/makefile.vc      |   29 +
 Utilities/GDAL/frmts/iso8211/mkcatalog.cpp    |  438 ++
 Utilities/GDAL/frmts/iso8211/teststream.out   | 2986 +++++++++
 Utilities/GDAL/frmts/iso8211/teststream.sh    |   16 +
 Utilities/GDAL/frmts/iso8211/timetest.cpp     |  198 +
 Utilities/GDAL/frmts/jdem/GNUmakefile         |   13 +
 Utilities/GDAL/frmts/jdem/jdemdataset.cpp     |  346 ++
 Utilities/GDAL/frmts/jdem/makefile.vc         |   15 +
 Utilities/GDAL/frmts/jpeg/GNUmakefile         |   34 +
 Utilities/GDAL/frmts/jpeg/frmt_jpeg.html      |   55 +
 Utilities/GDAL/frmts/jpeg/gdalexif.h          |  306 +
 Utilities/GDAL/frmts/jpeg/jpgdataset.cpp      | 1448 +++++
 Utilities/GDAL/frmts/jpeg/libjpeg/README      |  385 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jcapimin.c  |  280 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jcapistd.c  |  161 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jccoefct.c  |  449 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jccolor.c   |  459 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jcdctmgr.c  |  387 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jchuff.c    |  909 +++
 Utilities/GDAL/frmts/jpeg/libjpeg/jchuff.h    |   47 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jcinit.c    |   72 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jcmainct.c  |  293 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jcmarker.c  |  664 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jcmaster.c  |  590 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jcomapi.c   |  106 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jconfig.h   |   45 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jcparam.c   |  610 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jcphuff.c   |  833 +++
 Utilities/GDAL/frmts/jpeg/libjpeg/jcprepct.c  |  354 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jcsample.c  |  519 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jctrans.c   |  388 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jdapimin.c  |  395 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jdapistd.c  |  275 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jdatadst.c  |  151 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jdatasrc.c  |  212 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jdcoefct.c  |  736 +++
 Utilities/GDAL/frmts/jpeg/libjpeg/jdcolor.c   |  396 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jdct.h      |  176 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jddctmgr.c  |  269 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jdhuff.c    |  651 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jdhuff.h    |  201 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jdinput.c   |  381 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jdmainct.c  |  512 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jdmarker.c  | 1360 +++++
 Utilities/GDAL/frmts/jpeg/libjpeg/jdmaster.c  |  557 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jdmerge.c   |  400 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jdphuff.c   |  668 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jdpostct.c  |  290 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jdsample.c  |  478 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jdtrans.c   |  143 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jerror.c    |  252 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jerror.h    |  291 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jfdctflt.c  |  168 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jfdctfst.c  |  224 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jfdctint.c  |  283 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jidctflt.c  |  242 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jidctfst.c  |  368 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jidctint.c  |  389 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jidctred.c  |  398 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jinclude.h  |   91 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jmemansi.c  |  167 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jmemmgr.c   | 1118 ++++
 Utilities/GDAL/frmts/jpeg/libjpeg/jmemsys.h   |  198 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jmorecfg.h  |  371 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jpegint.h   |  392 ++
 Utilities/GDAL/frmts/jpeg/libjpeg/jpeglib.h   | 1096 ++++
 Utilities/GDAL/frmts/jpeg/libjpeg/jquant1.c   |  856 +++
 Utilities/GDAL/frmts/jpeg/libjpeg/jquant2.c   | 1310 ++++
 Utilities/GDAL/frmts/jpeg/libjpeg/jutils.c    |  179 +
 Utilities/GDAL/frmts/jpeg/libjpeg/jversion.h  |   14 +
 Utilities/GDAL/frmts/jpeg/libjpeg/makefile.vc |   24 +
 Utilities/GDAL/frmts/jpeg/makefile.vc         |   28 +
 Utilities/GDAL/frmts/jpeg/vsidataio.cpp       |  374 ++
 Utilities/GDAL/frmts/jpeg2000/GNUmakefile     |   14 +
 .../GDAL/frmts/jpeg2000/frmt_jpeg2000.html    |  258 +
 .../GDAL/frmts/jpeg2000/jpeg2000dataset.cpp   | 1172 ++++
 Utilities/GDAL/frmts/jpeg2000/makefile.vc     |   14 +
 Utilities/GDAL/frmts/l1b/GNUmakefile          |   13 +
 Utilities/GDAL/frmts/l1b/frmt_l1b.html        |  107 +
 Utilities/GDAL/frmts/l1b/l1bdataset.cpp       | 1401 +++++
 Utilities/GDAL/frmts/l1b/makefile.vc          |   13 +
 Utilities/GDAL/frmts/mem/GNUmakefile          |   17 +
 Utilities/GDAL/frmts/mem/frmt_mem.html        |   56 +
 Utilities/GDAL/frmts/mem/makefile.vc          |   13 +
 Utilities/GDAL/frmts/mem/memdataset.cpp       |  814 +++
 Utilities/GDAL/frmts/mem/memdataset.h         |  176 +
 Utilities/GDAL/frmts/mrsid/GNUmakefile        |   17 +
 Utilities/GDAL/frmts/mrsid/frmt_jp2mrsid.html |   80 +
 Utilities/GDAL/frmts/mrsid/frmt_mrsid.html    |   84 +
 Utilities/GDAL/frmts/mrsid/makefile.vc        |   24 +
 Utilities/GDAL/frmts/mrsid/mrsidcommon.h      | 1302 ++++
 Utilities/GDAL/frmts/mrsid/mrsiddataset.cpp   | 1920 ++++++
 Utilities/GDAL/frmts/msg/GNUmakefile          |   20 +
 .../frmts/msg/PublicDecompWTMakefiles.zip     |  Bin 0 -> 1025 bytes
 Utilities/GDAL/frmts/msg/frmt_msg.html        |  141 +
 Utilities/GDAL/frmts/msg/makefile.vc          |   26 +
 Utilities/GDAL/frmts/msg/msgcommand.cpp       |  463 ++
 Utilities/GDAL/frmts/msg/msgcommand.h         |   73 +
 Utilities/GDAL/frmts/msg/msgdataset.cpp       |  717 +++
 Utilities/GDAL/frmts/msg/msgdataset.h         |   94 +
 Utilities/GDAL/frmts/msg/prologue.cpp         |  237 +
 Utilities/GDAL/frmts/msg/prologue.h           |  128 +
 .../GDAL/frmts/msg/reflectancecalculator.cpp  |  163 +
 .../GDAL/frmts/msg/reflectancecalculator.h    |   61 +
 Utilities/GDAL/frmts/msg/xritheaderparser.cpp |  415 ++
 Utilities/GDAL/frmts/msg/xritheaderparser.h   |  113 +
 Utilities/GDAL/frmts/msgn/GNUmakefile         |   13 +
 Utilities/GDAL/frmts/msgn/frmt_msgn.html      |   24 +
 Utilities/GDAL/frmts/msgn/makefile.vc         |   15 +
 Utilities/GDAL/frmts/msgn/msg_basic_types.cpp |  231 +
 Utilities/GDAL/frmts/msgn/msg_basic_types.h   |  254 +
 Utilities/GDAL/frmts/msgn/msg_reader_core.cpp |  279 +
 Utilities/GDAL/frmts/msgn/msg_reader_core.h   |  150 +
 Utilities/GDAL/frmts/msgn/msgndataset.cpp     |  407 ++
 Utilities/GDAL/frmts/o/.cvsignore             |    1 +
 Utilities/GDAL/frmts/o/README.TXT             |    7 +
 Utilities/GDAL/frmts/ogdi/GNUmakefile         |   13 +
 Utilities/GDAL/frmts/ogdi/frmt_ogdi.html      |   83 +
 Utilities/GDAL/frmts/ogdi/makefile.vc         |   16 +
 Utilities/GDAL/frmts/ogdi/ogdidataset.cpp     | 1029 ++++
 Utilities/GDAL/frmts/pcidsk/GNUmakefile       |   13 +
 Utilities/GDAL/frmts/pcidsk/frmt_pcidsk.html  |   89 +
 Utilities/GDAL/frmts/pcidsk/gdal_pcidsk.h     |  215 +
 Utilities/GDAL/frmts/pcidsk/makefile.vc       |   15 +
 Utilities/GDAL/frmts/pcidsk/notes.txt         |   46 +
 Utilities/GDAL/frmts/pcidsk/pcidskdataset.cpp | 1599 +++++
 .../frmts/pcidsk/pcidsktiledrasterband.cpp    |  311 +
 Utilities/GDAL/frmts/pcraster/.cvsignore      |    2 +
 Utilities/GDAL/frmts/pcraster/GNUmakefile     |   28 +
 Utilities/GDAL/frmts/pcraster/doxygen.cfg     | 1161 ++++
 Utilities/GDAL/frmts/pcraster/libcsf/AUTHORS  |   10 +
 Utilities/GDAL/frmts/pcraster/libcsf/COPYING  |   30 +
 Utilities/GDAL/frmts/pcraster/libcsf/README   |   11 +
 .../GDAL/frmts/pcraster/libcsf/_getcell.c     |   29 +
 .../GDAL/frmts/pcraster/libcsf/_getrow.c      |   28 +
 .../GDAL/frmts/pcraster/libcsf/_gsomece.c     |   41 +
 .../GDAL/frmts/pcraster/libcsf/_putcell.c     |   31 +
 .../GDAL/frmts/pcraster/libcsf/_rputrow.c     |   31 +
 Utilities/GDAL/frmts/pcraster/libcsf/angle.c  |   60 +
 .../GDAL/frmts/pcraster/libcsf/attravai.c     |   26 +
 .../GDAL/frmts/pcraster/libcsf/attrsize.c     |   21 +
 .../GDAL/frmts/pcraster/libcsf/cellsize.c     |  111 +
 .../GDAL/frmts/pcraster/libcsf/create2.c      |  222 +
 Utilities/GDAL/frmts/pcraster/libcsf/csf.h    |  361 ++
 .../GDAL/frmts/pcraster/libcsf/csfattr.h      |   29 +
 .../GDAL/frmts/pcraster/libcsf/csfglob.c      |   12 +
 .../GDAL/frmts/pcraster/libcsf/csfimpl.h      |  238 +
 Utilities/GDAL/frmts/pcraster/libcsf/csfsup.c |   55 +
 .../GDAL/frmts/pcraster/libcsf/csftypes.h     |  416 ++
 .../GDAL/frmts/pcraster/libcsf/delattr.c      |   75 +
 .../GDAL/frmts/pcraster/libcsf/dumconv.c      |   70 +
 Utilities/GDAL/frmts/pcraster/libcsf/endian.c |   20 +
 .../GDAL/frmts/pcraster/libcsf/filename.c     |   41 +
 .../GDAL/frmts/pcraster/libcsf/gattrblk.c     |   89 +
 .../GDAL/frmts/pcraster/libcsf/gattridx.c     |   51 +
 .../GDAL/frmts/pcraster/libcsf/gcellrep.c     |   23 +
 .../GDAL/frmts/pcraster/libcsf/gdattype.c     |   48 +
 .../GDAL/frmts/pcraster/libcsf/getattr.c      |   81 +
 Utilities/GDAL/frmts/pcraster/libcsf/getx0.c  |   43 +
 Utilities/GDAL/frmts/pcraster/libcsf/gety0.c  |   45 +
 .../GDAL/frmts/pcraster/libcsf/ggisfid.c      |   47 +
 .../GDAL/frmts/pcraster/libcsf/gmaxval.c      |   37 +
 .../GDAL/frmts/pcraster/libcsf/gminval.c      |   38 +
 .../GDAL/frmts/pcraster/libcsf/gnrcols.c      |   45 +
 .../GDAL/frmts/pcraster/libcsf/gnrrows.c      |   45 +
 Utilities/GDAL/frmts/pcraster/libcsf/gproj.c  |   46 +
 .../GDAL/frmts/pcraster/libcsf/gputproj.c     |   62 +
 .../GDAL/frmts/pcraster/libcsf/gvalscal.c     |   44 +
 .../GDAL/frmts/pcraster/libcsf/gvartype.c     |   50 +
 .../GDAL/frmts/pcraster/libcsf/gversion.c     |   42 +
 Utilities/GDAL/frmts/pcraster/libcsf/ismv.c   |   87 +
 .../GDAL/frmts/pcraster/libcsf/kernlcsf.c     |  147 +
 Utilities/GDAL/frmts/pcraster/libcsf/legend.c |  134 +
 .../GDAL/frmts/pcraster/libcsf/makefile.vc    |   29 +
 Utilities/GDAL/frmts/pcraster/libcsf/mclose.c |   96 +
 Utilities/GDAL/frmts/pcraster/libcsf/mopen.c  |  202 +
 .../GDAL/frmts/pcraster/libcsf/moreattr.c     |  182 +
 .../GDAL/frmts/pcraster/libcsf/mperror.c      |   84 +
 .../GDAL/frmts/pcraster/libcsf/pcrtypes.h     |  246 +
 .../GDAL/frmts/pcraster/libcsf/pgisfid.c      |   56 +
 .../GDAL/frmts/pcraster/libcsf/pmaxval.c      |   75 +
 .../GDAL/frmts/pcraster/libcsf/pminval.c      |   76 +
 .../GDAL/frmts/pcraster/libcsf/putallmv.c     |   56 +
 .../GDAL/frmts/pcraster/libcsf/putattr.c      |  214 +
 .../GDAL/frmts/pcraster/libcsf/putsomec.c     |  265 +
 Utilities/GDAL/frmts/pcraster/libcsf/putx0.c  |   56 +
 Utilities/GDAL/frmts/pcraster/libcsf/puty0.c  |   57 +
 .../GDAL/frmts/pcraster/libcsf/pvalscal.c     |   64 +
 .../GDAL/frmts/pcraster/libcsf/rattrblk.c     |   53 +
 Utilities/GDAL/frmts/pcraster/libcsf/rcomp.c  |   81 +
 .../GDAL/frmts/pcraster/libcsf/rcoords.c      |  104 +
 Utilities/GDAL/frmts/pcraster/libcsf/rdup2.c  |   52 +
 .../GDAL/frmts/pcraster/libcsf/reseterr.c     |   43 +
 .../GDAL/frmts/pcraster/libcsf/rextend.c      |  104 +
 .../GDAL/frmts/pcraster/libcsf/rmalloc.c      |   34 +
 .../GDAL/frmts/pcraster/libcsf/rrowcol.c      |  142 +
 Utilities/GDAL/frmts/pcraster/libcsf/ruseas.c |  517 ++
 .../GDAL/frmts/pcraster/libcsf/setangle.c     |   79 +
 Utilities/GDAL/frmts/pcraster/libcsf/setmv.c  |   85 +
 .../GDAL/frmts/pcraster/libcsf/setvtmv.c      |   51 +
 .../GDAL/frmts/pcraster/libcsf/strconst.c     |   97 +
 Utilities/GDAL/frmts/pcraster/libcsf/strpad.c |   37 +
 Utilities/GDAL/frmts/pcraster/libcsf/swapio.c |  121 +
 .../GDAL/frmts/pcraster/libcsf/trackmm.c      |   71 +
 Utilities/GDAL/frmts/pcraster/libcsf/vs2.c    |   49 +
 Utilities/GDAL/frmts/pcraster/libcsf/vsdef.c  |   85 +
 Utilities/GDAL/frmts/pcraster/libcsf/vsis.c   |  102 +
 Utilities/GDAL/frmts/pcraster/libcsf/vsvers.c |   57 +
 .../GDAL/frmts/pcraster/libcsf/wattrblk.c     |   59 +
 Utilities/GDAL/frmts/pcraster/makefile.vc     |   30 +
 .../GDAL/frmts/pcraster/pcrasterdataset.cpp   |  452 ++
 .../GDAL/frmts/pcraster/pcrasterdataset.h     |  138 +
 .../GDAL/frmts/pcraster/pcrastermisc.cpp      |   47 +
 .../frmts/pcraster/pcrasterrasterband.cpp     |  194 +
 .../GDAL/frmts/pcraster/pcrasterrasterband.h  |  100 +
 .../GDAL/frmts/pcraster/pcrasterutil.cpp      |  477 ++
 Utilities/GDAL/frmts/pcraster/pcrasterutil.h  |   64 +
 Utilities/GDAL/frmts/pgchip/GNUmakefile       |   24 +
 Utilities/GDAL/frmts/pgchip/INSTALL           |   37 +
 Utilities/GDAL/frmts/pgchip/README            |   29 +
 Utilities/GDAL/frmts/pgchip/makefile.vc       |   15 +
 Utilities/GDAL/frmts/pgchip/pgchip.h          |  123 +
 Utilities/GDAL/frmts/pgchip/pgchipdataset.cpp | 1074 ++++
 .../GDAL/frmts/pgchip/pgchiprasterband.cpp    |  218 +
 .../GDAL/frmts/pgchip/pgchiputilities.cpp     |  290 +
 Utilities/GDAL/frmts/pgchip/todo              |    9 +
 Utilities/GDAL/frmts/png/GNUmakefile          |   30 +
 Utilities/GDAL/frmts/png/libpng/LICENSE       |  109 +
 Utilities/GDAL/frmts/png/libpng/makefile.vc   |   22 +
 Utilities/GDAL/frmts/png/libpng/png.c         |  828 +++
 Utilities/GDAL/frmts/png/libpng/png.h         | 3419 +++++++++++
 Utilities/GDAL/frmts/png/libpng/pngconf.h     | 1437 +++++
 Utilities/GDAL/frmts/png/libpng/pngerror.c    |  295 +
 Utilities/GDAL/frmts/png/libpng/pnggccrd.c    | 5408 +++++++++++++++++
 Utilities/GDAL/frmts/png/libpng/pngget.c      |  934 +++
 Utilities/GDAL/frmts/png/libpng/pngmem.c      |  595 ++
 Utilities/GDAL/frmts/png/libpng/pngpread.c    | 1573 +++++
 Utilities/GDAL/frmts/png/libpng/pngread.c     | 1456 +++++
 Utilities/GDAL/frmts/png/libpng/pngrio.c      |  161 +
 Utilities/GDAL/frmts/png/libpng/pngrtran.c    | 4177 +++++++++++++
 Utilities/GDAL/frmts/png/libpng/pngrutil.c    | 3124 ++++++++++
 Utilities/GDAL/frmts/png/libpng/pngset.c      | 1219 ++++
 Utilities/GDAL/frmts/png/libpng/pngtrans.c    |  650 ++
 Utilities/GDAL/frmts/png/libpng/pngvcrd.c     | 3903 ++++++++++++
 Utilities/GDAL/frmts/png/libpng/pngwio.c      |  228 +
 Utilities/GDAL/frmts/png/libpng/pngwrite.c    | 1464 +++++
 Utilities/GDAL/frmts/png/libpng/pngwtran.c    |  563 ++
 Utilities/GDAL/frmts/png/libpng/pngwutil.c    | 2730 +++++++++
 Utilities/GDAL/frmts/png/makefile.vc          |   28 +
 Utilities/GDAL/frmts/png/pngdataset.cpp       | 1231 ++++
 Utilities/GDAL/frmts/rik/GNUmakefile          |   19 +
 Utilities/GDAL/frmts/rik/frmt_rik.html        |   20 +
 Utilities/GDAL/frmts/rik/makefile.vc          |   15 +
 Utilities/GDAL/frmts/rik/rikdataset.cpp       | 1205 ++++
 Utilities/GDAL/frmts/rmf/GNUmakefile          |   13 +
 Utilities/GDAL/frmts/rmf/frmt_rmf.html        |   41 +
 Utilities/GDAL/frmts/rmf/makefile.vc          |   13 +
 Utilities/GDAL/frmts/rmf/rmfdataset.cpp       | 1468 +++++
 Utilities/GDAL/frmts/rs2/GNUmakefile          |   13 +
 Utilities/GDAL/frmts/rs2/frmt_rs2.html        |   38 +
 Utilities/GDAL/frmts/rs2/makefile.vc          |   13 +
 Utilities/GDAL/frmts/rs2/rs2dataset.cpp       |  521 ++
 Utilities/GDAL/frmts/sgi/GNUmakefile          |   18 +
 Utilities/GDAL/frmts/sgi/makefile.vc          |   14 +
 Utilities/GDAL/frmts/sgi/sgidataset.cpp       |  571 ++
 Utilities/GDAL/frmts/terragen/GNUmakefile     |   13 +
 .../GDAL/frmts/terragen/frmt_terragen.html    |   82 +
 Utilities/GDAL/frmts/terragen/makefile.vc     |   13 +
 Utilities/GDAL/frmts/terragen/readme.txt      |   14 +
 .../GDAL/frmts/terragen/terragendataset.cpp   | 1101 ++++
 Utilities/GDAL/frmts/vrt/GNUmakefile          |   21 +
 Utilities/GDAL/frmts/vrt/makefile.vc          |   17 +
 Utilities/GDAL/frmts/vrt/vrt_tutorial.dox     |  613 ++
 Utilities/GDAL/frmts/vrt/vrtdataset.cpp       |  946 +++
 Utilities/GDAL/frmts/vrt/vrtdataset.h         |  666 ++
 .../GDAL/frmts/vrt/vrtderivedrasterband.cpp   |  453 ++
 Utilities/GDAL/frmts/vrt/vrtdriver.cpp        |  309 +
 Utilities/GDAL/frmts/vrt/vrtfilters.cpp       |  629 ++
 Utilities/GDAL/frmts/vrt/vrtrasterband.cpp    |  811 +++
 Utilities/GDAL/frmts/vrt/vrtrawrasterband.cpp |  414 ++
 .../GDAL/frmts/vrt/vrtsourcedrasterband.cpp   |  596 ++
 Utilities/GDAL/frmts/vrt/vrtsources.cpp       |  932 +++
 Utilities/GDAL/frmts/vrt/vrtwarped.cpp        | 1039 ++++
 Utilities/GDAL/frmts/xpm/GNUmakefile          |   15 +
 Utilities/GDAL/frmts/xpm/makefile.vc          |   15 +
 Utilities/GDAL/frmts/xpm/xpmdataset.cpp       |  671 ++
 502 files changed, 198343 insertions(+)
 create mode 100644 Utilities/GDAL/frmts/bmp/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/bmp/bmpdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/bmp/frmt_bmp.html
 create mode 100644 Utilities/GDAL/frmts/bmp/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/bsb/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/bsb/Makefile.dist
 create mode 100644 Utilities/GDAL/frmts/bsb/README.dist
 create mode 100644 Utilities/GDAL/frmts/bsb/bsb2raw.c
 create mode 100644 Utilities/GDAL/frmts/bsb/bsb_read.c
 create mode 100644 Utilities/GDAL/frmts/bsb/bsb_read.h
 create mode 100644 Utilities/GDAL/frmts/bsb/bsbdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/bsb/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/ceos2/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/ceos2/ceos.c
 create mode 100644 Utilities/GDAL/frmts/ceos2/ceos.h
 create mode 100644 Utilities/GDAL/frmts/ceos2/ceosrecipe.c
 create mode 100644 Utilities/GDAL/frmts/ceos2/ceossar.c
 create mode 100644 Utilities/GDAL/frmts/ceos2/link.c
 create mode 100644 Utilities/GDAL/frmts/ceos2/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/ceos2/sar_ceosdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/dods/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/dods/dodsdataset2.cpp
 create mode 100644 Utilities/GDAL/frmts/dods/frmt_dods.html
 create mode 100644 Utilities/GDAL/frmts/ecw/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/ecw/ecwcreatecopy.cpp
 create mode 100644 Utilities/GDAL/frmts/ecw/ecwdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/ecw/frmt_ecw.html
 create mode 100644 Utilities/GDAL/frmts/ecw/frmt_jp2ecw.html
 create mode 100644 Utilities/GDAL/frmts/ecw/jp2userbox.cpp
 create mode 100644 Utilities/GDAL/frmts/ecw/jp2userbox.h
 create mode 100644 Utilities/GDAL/frmts/ecw/lookup.py
 create mode 100644 Utilities/GDAL/frmts/ecw/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/ecw/vsiiostream.h
 create mode 100644 Utilities/GDAL/frmts/elas/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/elas/elasdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/elas/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/envisat/EnvisatFile.c
 create mode 100644 Utilities/GDAL/frmts/envisat/EnvisatFile.h
 create mode 100644 Utilities/GDAL/frmts/envisat/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/envisat/dumpgeo.c
 create mode 100644 Utilities/GDAL/frmts/envisat/envisat_dump.c
 create mode 100644 Utilities/GDAL/frmts/envisat/envisatdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/envisat/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/fit/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/fit/fit.cpp
 create mode 100644 Utilities/GDAL/frmts/fit/fit.h
 create mode 100644 Utilities/GDAL/frmts/fit/fitdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/fit/gstEndian.h
 create mode 100644 Utilities/GDAL/frmts/fit/gstTypes.h
 create mode 100644 Utilities/GDAL/frmts/fit/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/fits/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/fits/fitsdataset.cpp
 create mode 100755 Utilities/GDAL/frmts/fits/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/frmt_various.html
 create mode 100644 Utilities/GDAL/frmts/gdalallregister.cpp
 create mode 100644 Utilities/GDAL/frmts/gif/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/gif/frmt_gif.html
 create mode 100644 Utilities/GDAL/frmts/gif/gifdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/gif/libungif/README
 create mode 100644 Utilities/GDAL/frmts/gif/libungif/dgif_lib.c
 create mode 100644 Utilities/GDAL/frmts/gif/libungif/egif_lib.c
 create mode 100644 Utilities/GDAL/frmts/gif/libungif/gif_err.c
 create mode 100644 Utilities/GDAL/frmts/gif/libungif/gif_lib.h
 create mode 100644 Utilities/GDAL/frmts/gif/libungif/gif_lib_private.h
 create mode 100644 Utilities/GDAL/frmts/gif/libungif/gifalloc.c
 create mode 100644 Utilities/GDAL/frmts/gif/libungif/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/gif/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/grass/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/grass/frmt_grass.html
 create mode 100644 Utilities/GDAL/frmts/grass/grass57dataset.cpp
 create mode 100644 Utilities/GDAL/frmts/grass/grassdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/grass/pkg/Makefile.in
 create mode 100644 Utilities/GDAL/frmts/grass/pkg/README
 create mode 100644 Utilities/GDAL/frmts/grass/pkg/aclocal.m4
 create mode 100755 Utilities/GDAL/frmts/grass/pkg/configure
 create mode 100644 Utilities/GDAL/frmts/grass/pkg/configure.in
 create mode 100644 Utilities/GDAL/frmts/gtiff/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/gtiff/epsg_to_wkt.cpp
 create mode 100644 Utilities/GDAL/frmts/gtiff/frmt_gtiff.html
 create mode 100644 Utilities/GDAL/frmts/gtiff/geotiff.cpp
 create mode 100644 Utilities/GDAL/frmts/gtiff/gt_overview.cpp
 create mode 100644 Utilities/GDAL/frmts/gtiff/gt_wkt_srs.cpp
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/cpl_serv.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_datum.inc
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_ellipse.inc
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_gcs.inc
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_pcs.inc
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_pm.inc
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_proj.inc
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_units.inc
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_vertcs.inc
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_config.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_ctrans.inc
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_extra.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_free.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_get.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_keyp.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_names.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_new.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_normalize.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_normalize.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_print.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_set.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_tiffp.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_tiffp.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_trans.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geo_write.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geokeys.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geokeys.inc
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geonames.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geotiff.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geotiff_proj4.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geotiffio.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/geovalues.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/xtiff.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libgeotiff/xtiffio.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/t4.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_aux.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_close.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_codec.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_color.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_compress.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_config.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_dir.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_dir.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_dirinfo.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_dirread.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_dirwrite.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_dumpmode.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_error.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_extension.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_fax3.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_fax3.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_fax3sm.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_flush.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_getimage.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_jpeg.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_luv.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_lzw.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_next.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_open.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_packbits.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_pixarlog.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_predict.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_predict.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_print.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_read.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_strip.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_swab.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_thunder.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_tile.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_version.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_vsi.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_warning.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_write.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tif_zip.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tiff.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tiffconf.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tiffio.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tiffiop.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/tiffvers.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/libtiff/uvcode.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/gtiff/tif_float.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/tif_memio.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/tif_memio.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/tif_overview.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/tif_ovrcache.c
 create mode 100644 Utilities/GDAL/frmts/gtiff/tif_ovrcache.h
 create mode 100644 Utilities/GDAL/frmts/gtiff/tifvsi.cpp
 create mode 100644 Utilities/GDAL/frmts/gxf/.cvsignore
 create mode 100644 Utilities/GDAL/frmts/gxf/Doxyfile
 create mode 100644 Utilities/GDAL/frmts/gxf/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/gxf/Makefile.in
 create mode 100644 Utilities/GDAL/frmts/gxf/README
 create mode 100644 Utilities/GDAL/frmts/gxf/configure.in
 create mode 100644 Utilities/GDAL/frmts/gxf/gxf.dox
 create mode 100644 Utilities/GDAL/frmts/gxf/gxf_ogcwkt.c
 create mode 100644 Utilities/GDAL/frmts/gxf/gxf_proj4.c
 create mode 100644 Utilities/GDAL/frmts/gxf/gxfdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/gxf/gxfopen.c
 create mode 100644 Utilities/GDAL/frmts/gxf/gxfopen.h
 create mode 100644 Utilities/GDAL/frmts/gxf/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/gxf/makefile.vc.dist
 create mode 100644 Utilities/GDAL/frmts/hdf5/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/hdf5/frmt_hdf5.html
 create mode 100644 Utilities/GDAL/frmts/hdf5/hdf5dataset.cpp
 create mode 100644 Utilities/GDAL/frmts/hdf5/hdf5dataset.h
 create mode 100644 Utilities/GDAL/frmts/hdf5/hdf5imagedataset.cpp
 create mode 100644 Utilities/GDAL/frmts/hdf5/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/ilwis/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/ilwis/frmt_ilwis.html
 create mode 100644 Utilities/GDAL/frmts/ilwis/ilwiscoordinatesystem.cpp
 create mode 100644 Utilities/GDAL/frmts/ilwis/ilwisdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/ilwis/ilwisdataset.h
 create mode 100644 Utilities/GDAL/frmts/ilwis/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/iso8211/.cvsignore
 create mode 100644 Utilities/GDAL/frmts/iso8211/8211dump.cpp
 create mode 100644 Utilities/GDAL/frmts/iso8211/8211view.cpp
 create mode 100644 Utilities/GDAL/frmts/iso8211/Doxyfile
 create mode 100644 Utilities/GDAL/frmts/iso8211/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/iso8211/Makefile.in
 create mode 100644 Utilities/GDAL/frmts/iso8211/aclocal.m4
 create mode 100644 Utilities/GDAL/frmts/iso8211/configure.in
 create mode 100644 Utilities/GDAL/frmts/iso8211/ddffield.cpp
 create mode 100644 Utilities/GDAL/frmts/iso8211/ddffielddefn.cpp
 create mode 100644 Utilities/GDAL/frmts/iso8211/ddfmodule.cpp
 create mode 100644 Utilities/GDAL/frmts/iso8211/ddfrecord.cpp
 create mode 100644 Utilities/GDAL/frmts/iso8211/ddfsubfielddefn.cpp
 create mode 100644 Utilities/GDAL/frmts/iso8211/ddfutils.cpp
 create mode 100644 Utilities/GDAL/frmts/iso8211/intro.dox
 create mode 100644 Utilities/GDAL/frmts/iso8211/iso8211.h
 create mode 100644 Utilities/GDAL/frmts/iso8211/makefile.vc
 create mode 100755 Utilities/GDAL/frmts/iso8211/mkcatalog.cpp
 create mode 100644 Utilities/GDAL/frmts/iso8211/teststream.out
 create mode 100755 Utilities/GDAL/frmts/iso8211/teststream.sh
 create mode 100644 Utilities/GDAL/frmts/iso8211/timetest.cpp
 create mode 100644 Utilities/GDAL/frmts/jdem/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/jdem/jdemdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/jdem/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/jpeg/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/jpeg/frmt_jpeg.html
 create mode 100755 Utilities/GDAL/frmts/jpeg/gdalexif.h
 create mode 100644 Utilities/GDAL/frmts/jpeg/jpgdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/README
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jcapimin.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jcapistd.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jccoefct.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jccolor.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jcdctmgr.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jchuff.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jchuff.h
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jcinit.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jcmainct.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jcmarker.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jcmaster.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jcomapi.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jconfig.h
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jcparam.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jcphuff.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jcprepct.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jcsample.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jctrans.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdapimin.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdapistd.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdatadst.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdatasrc.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdcoefct.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdcolor.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdct.h
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jddctmgr.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdhuff.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdhuff.h
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdinput.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdmainct.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdmarker.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdmaster.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdmerge.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdphuff.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdpostct.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdsample.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jdtrans.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jerror.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jerror.h
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jfdctflt.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jfdctfst.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jfdctint.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jidctflt.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jidctfst.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jidctint.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jidctred.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jinclude.h
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jmemansi.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jmemmgr.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jmemsys.h
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jmorecfg.h
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jpegint.h
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jpeglib.h
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jquant1.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jquant2.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jutils.c
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/jversion.h
 create mode 100644 Utilities/GDAL/frmts/jpeg/libjpeg/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/jpeg/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/jpeg/vsidataio.cpp
 create mode 100644 Utilities/GDAL/frmts/jpeg2000/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/jpeg2000/frmt_jpeg2000.html
 create mode 100644 Utilities/GDAL/frmts/jpeg2000/jpeg2000dataset.cpp
 create mode 100644 Utilities/GDAL/frmts/jpeg2000/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/l1b/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/l1b/frmt_l1b.html
 create mode 100644 Utilities/GDAL/frmts/l1b/l1bdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/l1b/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/mem/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/mem/frmt_mem.html
 create mode 100644 Utilities/GDAL/frmts/mem/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/mem/memdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/mem/memdataset.h
 create mode 100644 Utilities/GDAL/frmts/mrsid/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/mrsid/frmt_jp2mrsid.html
 create mode 100644 Utilities/GDAL/frmts/mrsid/frmt_mrsid.html
 create mode 100644 Utilities/GDAL/frmts/mrsid/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/mrsid/mrsidcommon.h
 create mode 100644 Utilities/GDAL/frmts/mrsid/mrsiddataset.cpp
 create mode 100644 Utilities/GDAL/frmts/msg/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/msg/PublicDecompWTMakefiles.zip
 create mode 100644 Utilities/GDAL/frmts/msg/frmt_msg.html
 create mode 100644 Utilities/GDAL/frmts/msg/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/msg/msgcommand.cpp
 create mode 100644 Utilities/GDAL/frmts/msg/msgcommand.h
 create mode 100644 Utilities/GDAL/frmts/msg/msgdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/msg/msgdataset.h
 create mode 100644 Utilities/GDAL/frmts/msg/prologue.cpp
 create mode 100644 Utilities/GDAL/frmts/msg/prologue.h
 create mode 100644 Utilities/GDAL/frmts/msg/reflectancecalculator.cpp
 create mode 100644 Utilities/GDAL/frmts/msg/reflectancecalculator.h
 create mode 100644 Utilities/GDAL/frmts/msg/xritheaderparser.cpp
 create mode 100644 Utilities/GDAL/frmts/msg/xritheaderparser.h
 create mode 100644 Utilities/GDAL/frmts/msgn/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/msgn/frmt_msgn.html
 create mode 100644 Utilities/GDAL/frmts/msgn/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/msgn/msg_basic_types.cpp
 create mode 100644 Utilities/GDAL/frmts/msgn/msg_basic_types.h
 create mode 100644 Utilities/GDAL/frmts/msgn/msg_reader_core.cpp
 create mode 100644 Utilities/GDAL/frmts/msgn/msg_reader_core.h
 create mode 100644 Utilities/GDAL/frmts/msgn/msgndataset.cpp
 create mode 100644 Utilities/GDAL/frmts/o/.cvsignore
 create mode 100644 Utilities/GDAL/frmts/o/README.TXT
 create mode 100644 Utilities/GDAL/frmts/ogdi/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/ogdi/frmt_ogdi.html
 create mode 100644 Utilities/GDAL/frmts/ogdi/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/ogdi/ogdidataset.cpp
 create mode 100644 Utilities/GDAL/frmts/pcidsk/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/pcidsk/frmt_pcidsk.html
 create mode 100644 Utilities/GDAL/frmts/pcidsk/gdal_pcidsk.h
 create mode 100644 Utilities/GDAL/frmts/pcidsk/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/pcidsk/notes.txt
 create mode 100644 Utilities/GDAL/frmts/pcidsk/pcidskdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/pcidsk/pcidsktiledrasterband.cpp
 create mode 100644 Utilities/GDAL/frmts/pcraster/.cvsignore
 create mode 100644 Utilities/GDAL/frmts/pcraster/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/pcraster/doxygen.cfg
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/AUTHORS
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/COPYING
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/README
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/_getcell.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/_getrow.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/_gsomece.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/_putcell.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/_rputrow.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/angle.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/attravai.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/attrsize.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/cellsize.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/create2.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/csf.h
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/csfattr.h
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/csfglob.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/csfimpl.h
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/csfsup.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/csftypes.h
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/delattr.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/dumconv.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/endian.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/filename.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/gattrblk.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/gattridx.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/gcellrep.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/gdattype.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/getattr.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/getx0.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/gety0.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/ggisfid.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/gmaxval.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/gminval.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/gnrcols.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/gnrrows.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/gproj.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/gputproj.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/gvalscal.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/gvartype.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/gversion.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/ismv.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/kernlcsf.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/legend.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/mclose.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/mopen.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/moreattr.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/mperror.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/pcrtypes.h
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/pgisfid.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/pmaxval.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/pminval.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/putallmv.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/putattr.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/putsomec.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/putx0.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/puty0.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/pvalscal.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/rattrblk.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/rcomp.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/rcoords.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/rdup2.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/reseterr.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/rextend.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/rmalloc.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/rrowcol.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/ruseas.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/setangle.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/setmv.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/setvtmv.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/strconst.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/strpad.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/swapio.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/trackmm.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/vs2.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/vsdef.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/vsis.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/vsvers.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/libcsf/wattrblk.c
 create mode 100644 Utilities/GDAL/frmts/pcraster/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/pcraster/pcrasterdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/pcraster/pcrasterdataset.h
 create mode 100644 Utilities/GDAL/frmts/pcraster/pcrastermisc.cpp
 create mode 100644 Utilities/GDAL/frmts/pcraster/pcrasterrasterband.cpp
 create mode 100644 Utilities/GDAL/frmts/pcraster/pcrasterrasterband.h
 create mode 100644 Utilities/GDAL/frmts/pcraster/pcrasterutil.cpp
 create mode 100644 Utilities/GDAL/frmts/pcraster/pcrasterutil.h
 create mode 100644 Utilities/GDAL/frmts/pgchip/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/pgchip/INSTALL
 create mode 100644 Utilities/GDAL/frmts/pgchip/README
 create mode 100644 Utilities/GDAL/frmts/pgchip/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/pgchip/pgchip.h
 create mode 100644 Utilities/GDAL/frmts/pgchip/pgchipdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/pgchip/pgchiprasterband.cpp
 create mode 100644 Utilities/GDAL/frmts/pgchip/pgchiputilities.cpp
 create mode 100644 Utilities/GDAL/frmts/pgchip/todo
 create mode 100644 Utilities/GDAL/frmts/png/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/png/libpng/LICENSE
 create mode 100644 Utilities/GDAL/frmts/png/libpng/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/png/libpng/png.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/png.h
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngconf.h
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngerror.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pnggccrd.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngget.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngmem.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngpread.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngread.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngrio.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngrtran.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngrutil.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngset.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngtrans.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngvcrd.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngwio.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngwrite.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngwtran.c
 create mode 100644 Utilities/GDAL/frmts/png/libpng/pngwutil.c
 create mode 100644 Utilities/GDAL/frmts/png/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/png/pngdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/rik/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/rik/frmt_rik.html
 create mode 100644 Utilities/GDAL/frmts/rik/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/rik/rikdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/rmf/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/rmf/frmt_rmf.html
 create mode 100644 Utilities/GDAL/frmts/rmf/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/rmf/rmfdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/rs2/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/rs2/frmt_rs2.html
 create mode 100644 Utilities/GDAL/frmts/rs2/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/rs2/rs2dataset.cpp
 create mode 100644 Utilities/GDAL/frmts/sgi/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/sgi/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/sgi/sgidataset.cpp
 create mode 100644 Utilities/GDAL/frmts/terragen/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/terragen/frmt_terragen.html
 create mode 100644 Utilities/GDAL/frmts/terragen/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/terragen/readme.txt
 create mode 100644 Utilities/GDAL/frmts/terragen/terragendataset.cpp
 create mode 100644 Utilities/GDAL/frmts/vrt/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/vrt/makefile.vc
 create mode 100755 Utilities/GDAL/frmts/vrt/vrt_tutorial.dox
 create mode 100644 Utilities/GDAL/frmts/vrt/vrtdataset.cpp
 create mode 100644 Utilities/GDAL/frmts/vrt/vrtdataset.h
 create mode 100644 Utilities/GDAL/frmts/vrt/vrtderivedrasterband.cpp
 create mode 100644 Utilities/GDAL/frmts/vrt/vrtdriver.cpp
 create mode 100644 Utilities/GDAL/frmts/vrt/vrtfilters.cpp
 create mode 100644 Utilities/GDAL/frmts/vrt/vrtrasterband.cpp
 create mode 100644 Utilities/GDAL/frmts/vrt/vrtrawrasterband.cpp
 create mode 100644 Utilities/GDAL/frmts/vrt/vrtsourcedrasterband.cpp
 create mode 100644 Utilities/GDAL/frmts/vrt/vrtsources.cpp
 create mode 100644 Utilities/GDAL/frmts/vrt/vrtwarped.cpp
 create mode 100644 Utilities/GDAL/frmts/xpm/GNUmakefile
 create mode 100644 Utilities/GDAL/frmts/xpm/makefile.vc
 create mode 100644 Utilities/GDAL/frmts/xpm/xpmdataset.cpp

diff --git a/Utilities/GDAL/frmts/bmp/GNUmakefile b/Utilities/GDAL/frmts/bmp/GNUmakefile
new file mode 100644
index 0000000000..2d71d6c079
--- /dev/null
+++ b/Utilities/GDAL/frmts/bmp/GNUmakefile
@@ -0,0 +1,13 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	bmpdataset.o
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/bmp/bmpdataset.cpp b/Utilities/GDAL/frmts/bmp/bmpdataset.cpp
new file mode 100644
index 0000000000..300e5ade7c
--- /dev/null
+++ b/Utilities/GDAL/frmts/bmp/bmpdataset.cpp
@@ -0,0 +1,1435 @@
+/******************************************************************************
+ * $Id: bmpdataset.cpp,v 1.40 2006/03/18 18:43:03 dron Exp $
+ *
+ * Project:  Microsoft Windows Bitmap
+ * Purpose:  Read/write MS Windows Device Independent Bitmap (DIB) files
+ *           and OS/2 Presentation Manager bitmaps v. 1.x and v. 2.x
+ * Author:   Andrey Kiselev, dron@remotesensing.org
+ *
+ ******************************************************************************
+ * Copyright (c) 2002, Andrey Kiselev <dron@remotesensing.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: bmpdataset.cpp,v $
+ * Revision 1.40  2006/03/18 18:43:03  dron
+ * Fixed absolute mode decoding of RLE comressed images.
+ *
+ * Revision 1.39  2006/03/02 12:12:15  dron
+ * Avoid warnings.
+ *
+ * Revision 1.38  2006/02/22 01:37:57  fwarmerdam
+ * added support for 32bit images with bitfields compression (pgao)
+ *
+ * Revision 1.37  2005/10/31 13:03:42  dron
+ * Check for integer overflow conditions when calculating nScanSize.
+ *
+ * Revision 1.36  2005/10/11 23:34:02  fwarmerdam
+ * fixed up handling of pszFilename
+ *
+ * Revision 1.35  2005/07/05 18:00:10  dron
+ * Add support for reading external overviews.
+ *
+ * Revision 1.34  2005/05/17 15:12:24  fwarmerdam
+ * Cleanup out residual flushcache and projections support for
+ * better pam support.
+ *
+ * Revision 1.33  2005/05/05 14:01:36  fwarmerdam
+ * PAM Enable
+ *
+ * Revision 1.32  2004/09/28 14:18:06  fwarmerdam
+ * A few more additions to improve error handling.  The "create" write
+ * error checks don't work (at least on Linux) because of the buffering.
+ *
+ * Revision 1.31  2004/09/28 14:05:21  fwarmerdam
+ * Try to strategically check the success of VSIFWriteL() calls in
+ * the Create() method as per:
+ * http://bugzilla.remotesensing.org/show_bug.cgi?id=619
+ *
+ * Revision 1.30  2004/06/08 15:53:08  dron
+ * Even more fixes.
+ *
+ * Revision 1.29  2004/06/08 13:38:16  dron
+ * Few minor fixes.
+ *
+ * Revision 1.28  2004/01/06 10:56:54  dron
+ * Few optimizations in IReadBlock().
+ *
+ * Revision 1.27  2003/11/07 13:12:38  dron
+ * Handle 32-bit BMPs properly.
+ *
+ * Revision 1.26  2003/10/24 20:31:53  warmerda
+ * Added extension metadata.
+ *
+ * Revision 1.25  2003/09/22 17:17:50  warmerda
+ * added support for .bpw and .bmpw worldfiles
+ *
+ * Revision 1.24  2003/09/11 19:55:15  warmerda
+ * avoid warnings
+ *
+ * Revision 1.23  2003/06/17 17:59:03  dron
+ * Do not close GDALOpenInfo::fp handler before reopening.
+ *
+ * Revision 1.22  2003/05/30 05:26:56  dron
+ * Fixed problem with scanline numbering.
+ *
+ * Revision 1.21  2003/05/29 14:58:01  dron
+ * Workaround for files with extra bytes at the end of data stream.
+ *
+ * Revision 1.20  2003/05/20 08:59:10  dron
+ * BMPDataset::IRasterIO() method added.
+ *
+ * Revision 1.19  2003/05/01 17:40:43  dron
+ * Report colour interpretation GCI_PaletteIndex for 1-bit images.
+ *
+ * Revision 1.18  2003/03/27 15:51:06  dron
+ * Improvements in update state handling in IReadBlock().
+ *
+ * Revision 1.17  2003/03/27 13:24:53  dron
+ * Fixes for large file support.
+ *
+ * Revision 1.16  2003/02/15 14:23:09  dron
+ * Fix in GetGeoTransform().
+ *
+ * Revision 1.15  2003/02/03 18:40:15  dron
+ * Type of input data checked in Create() method now.
+ *
+ * Revision 1.14  2002/12/15 15:24:41  dron
+ * Typos fixed.
+ *
+ * Revision 1.13  2002/12/15 15:13:33  dron
+ * BMP structures extended.
+ *
+ * Revision 1.12  2002/12/13 14:15:50  dron
+ * IWriteBlock() fixed, CreateCopy() removed, added RLE4 decoding and OS/2 BMP
+ * support.
+ */
+
+#include "gdal_pam.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: bmpdataset.cpp,v 1.40 2006/03/18 18:43:03 dron Exp $");
+
+CPL_C_START
+void    GDALRegister_BMP(void);
+CPL_C_END
+
+enum BMPType
+{
+    BMPT_WIN4,      // BMP used in Windows 3.0/NT 3.51/95
+    BMPT_WIN5,      // BMP used in Windows NT 4.0/98/Me/2000/XP
+    BMPT_OS21,      // BMP used in OS/2 PM 1.x
+    BMPT_OS22       // BMP used in OS/2 PM 2.x
+};
+
+// Bitmap file consists of a BMPFileHeader structure followed by a
+// BMPInfoHeader structure. An array of BMPColorEntry structures (also called
+// a colour table) follows the bitmap information header structure. The colour
+// table is followed by a second array of indexes into the colour table (the
+// actual bitmap data). Data may be comressed, for 4-bpp and 8-bpp used RLE
+// compression.
+//
+// +---------------------+
+// | BMPFileHeader       |
+// +---------------------+
+// | BMPInfoHeader       |
+// +---------------------+
+// | BMPColorEntry array |
+// +---------------------+
+// | Colour-index array  |
+// +---------------------+
+//
+// All numbers stored in Intel order with least significant byte first.
+
+enum BMPComprMethod
+{
+    BMPC_RGB = 0L,              // Uncompressed
+    BMPC_RLE8 = 1L,             // RLE for 8 bpp images
+    BMPC_RLE4 = 2L,             // RLE for 4 bpp images
+    BMPC_BITFIELDS = 3L,        // Bitmap is not compressed and the colour table
+                                // consists of three DWORD color masks that specify
+                                // the red, green, and blue components of each pixel.
+                                // This is valid when used with 16- and 32-bpp bitmaps.
+    BMPC_JPEG = 4L,             // Indicates that the image is a JPEG image.
+    BMPC_PNG = 5L               // Indicates that the image is a PNG image.
+};
+
+enum BMPLCSType                 // Type of logical color space.
+{
+    BMPLT_CALIBRATED_RGB = 0,   // This value indicates that endpoints and gamma
+                                // values are given in the appropriate fields.
+    BMPLT_DEVICE_RGB = 1,
+    BMPLT_DEVICE_CMYK = 2,
+};
+
+typedef struct
+{
+    GInt32      iCIEX;
+    GInt32      iCIEY;
+    GInt32      iCIEZ;
+} BMPCIEXYZ;
+
+typedef struct                  // This structure contains the x, y, and z
+{                               // coordinates of the three colors that correspond
+    BMPCIEXYZ   iCIERed;        // to the red, green, and blue endpoints for
+    BMPCIEXYZ   iCIEGreen;      // a specified logical color space.
+    BMPCIEXYZ   iCIEBlue;
+} BMPCIEXYZTriple;
+
+typedef struct
+{
+    GByte       bType[2];       // Signature "BM"
+    GUInt32     iSize;          // Size in bytes of the bitmap file. Should always
+                                // be ignored while reading because of error
+                                // in Windows 3.0 SDK's description of this field
+    GUInt16     iReserved1;     // Reserved, set as 0
+    GUInt16     iReserved2;     // Reserved, set as 0
+    GUInt32     iOffBits;       // Offset of the image from file start in bytes
+} BMPFileHeader;
+
+// File header size in bytes:
+const int       BFH_SIZE = 14;
+
+typedef struct
+{
+    GUInt32     iSize;          // Size of BMPInfoHeader structure in bytes.
+                                // Should be used to determine start of the
+                                // colour table
+    GInt32      iWidth;         // Image width
+    GInt32      iHeight;        // Image height. If positive, image has bottom left
+                                // origin, if negative --- top left.
+    GUInt16     iPlanes;        // Number of image planes (must be set to 1)
+    GUInt16     iBitCount;      // Number of bits per pixel (1, 4, 8, 16, 24 or 32).
+                                // If 0 then the number of bits per pixel is
+                                // specified or is implied by the JPEG or PNG format.
+    BMPComprMethod iCompression; // Compression method
+    GUInt32     iSizeImage;     // Size of uncomressed image in bytes. May be 0
+                                // for BMPC_RGB bitmaps. If iCompression is BI_JPEG
+                                // or BI_PNG, iSizeImage indicates the size
+                                // of the JPEG or PNG image buffer.
+    GInt32      iXPelsPerMeter; // X resolution, pixels per meter (0 if not used)
+    GInt32      iYPelsPerMeter; // Y resolution, pixels per meter (0 if not used)
+    GUInt32     iClrUsed;       // Size of colour table. If 0, iBitCount should
+                                // be used to calculate this value (1<<iBitCount)
+    GUInt32     iClrImportant;  // Number of important colours. If 0, all
+                                // colours are required
+
+    // Fields above should be used for bitmaps, compatible with Windows NT 3.51
+    // and earlier. Windows 98/Me, Windows 2000/XP introduces additional fields:
+
+    GUInt32     iRedMask;       // Colour mask that specifies the red component
+                                // of each pixel, valid only if iCompression
+                                // is set to BI_BITFIELDS.
+    GUInt32     iGreenMask;     // The same for green component
+    GUInt32     iBlueMask;      // The same for blue component
+    GUInt32     iAlphaMask;     // Colour mask that specifies the alpha
+                                // component of each pixel.
+    BMPLCSType  iCSType;        // Colour space of the DIB.
+    BMPCIEXYZTriple sEndpoints; // This member is ignored unless the iCSType member
+                                // specifies BMPLT_CALIBRATED_RGB.
+    GUInt32     iGammaRed;      // Toned response curve for red. This member
+                                // is ignored unless color values are calibrated
+                                // RGB values and iCSType is set to
+                                // BMPLT_CALIBRATED_RGB. Specified in 16^16 format.
+    GUInt32     iGammaGreen;    // Toned response curve for green.
+    GUInt32     iGammaBlue;     // Toned response curve for blue.
+} BMPInfoHeader;
+
+// Info header size in bytes:
+const unsigned int  BIH_WIN4SIZE = 40; // for BMPT_WIN4
+const unsigned int  BIH_WIN5SIZE = 57; // for BMPT_WIN5
+const unsigned int  BIH_OS21SIZE = 12; // for BMPT_OS21
+const unsigned int  BIH_OS22SIZE = 64; // for BMPT_OS22
+
+// We will use plain byte array instead of this structure, but declaration
+// provided for reference
+typedef struct
+{
+    GByte       bBlue;
+    GByte       bGreen;
+    GByte       bRed;
+    GByte       bReserved;      // Must be 0
+} BMPColorEntry;
+
+/************************************************************************/
+/* ==================================================================== */
+/*                              BMPDataset                              */
+/* ==================================================================== */
+/************************************************************************/
+
+class BMPDataset : public GDALPamDataset
+{
+    friend class BMPRasterBand;
+    friend class BMPComprRasterBand;
+
+    BMPFileHeader       sFileHeader;
+    BMPInfoHeader       sInfoHeader;
+    int                 nColorTableSize, nColorElems;
+    GByte               *pabyColorTable;
+    GDALColorTable      *poColorTable;
+    double              adfGeoTransform[6];
+    int                 bGeoTransformValid;
+
+    char                *pszFilename;
+    FILE                *fp;
+
+  protected:
+    virtual CPLErr      IRasterIO( GDALRWFlag, int, int, int, int,
+                                   void *, int, int, GDALDataType,
+                                   int, int *, int, int, int );
+
+  public:
+                BMPDataset();
+                ~BMPDataset();
+
+    static GDALDataset  *Open( GDALOpenInfo * );
+    static GDALDataset  *Create( const char * pszFilename,
+                                int nXSize, int nYSize, int nBands,
+                                GDALDataType eType, char ** papszParmList );
+
+    CPLErr              GetGeoTransform( double * padfTransform );
+    virtual CPLErr      SetGeoTransform( double * );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            BMPRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class BMPRasterBand : public GDALPamRasterBand
+{
+    friend class BMPDataset;
+
+  protected:
+
+    GUInt32         nScanSize;
+    unsigned int    iBytesPerPixel;
+    GByte           *pabyScan;
+
+  public:
+
+                BMPRasterBand( BMPDataset *, int );
+                ~BMPRasterBand();
+
+    virtual CPLErr          IReadBlock( int, int, void * );
+    virtual CPLErr          IWriteBlock( int, int, void * );
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual GDALColorTable  *GetColorTable();
+    CPLErr                  SetColorTable( GDALColorTable * );
+};
+
+/************************************************************************/
+/*                           BMPRasterBand()                            */
+/************************************************************************/
+
+BMPRasterBand::BMPRasterBand( BMPDataset *poDS, int nBand )
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+    eDataType = GDT_Byte;
+    iBytesPerPixel = poDS->sInfoHeader.iBitCount / 8;
+
+    // We will read one scanline per time. Scanlines in BMP aligned at 4-byte
+    // boundary
+    nBlockXSize = poDS->GetRasterXSize();
+    nScanSize =
+        ((poDS->GetRasterXSize() * poDS->sInfoHeader.iBitCount + 31) & ~31) / 8;
+    nBlockYSize = 1;
+
+#ifdef DEBUG
+    CPLDebug( "BMP",
+              "Band %d: set nBlockXSize=%d, nBlockYSize=%d, nScanSize=%d",
+              nBand, nBlockXSize, nBlockYSize, nScanSize );
+#endif
+
+    pabyScan = (GByte *) CPLMalloc( nScanSize );
+}
+
+/************************************************************************/
+/*                           ~BMPRasterBand()                           */
+/************************************************************************/
+
+BMPRasterBand::~BMPRasterBand()
+{
+    CPLFree( pabyScan );
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr BMPRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+{
+    BMPDataset  *poGDS = (BMPDataset *) poDS;
+    GUInt32     iScanOffset;
+    int         i;
+
+    if ( poGDS->sInfoHeader.iHeight > 0 )
+        iScanOffset = poGDS->sFileHeader.iOffBits +
+            ( poGDS->GetRasterYSize() - nBlockYOff - 1 ) * nScanSize;
+    else
+        iScanOffset = poGDS->sFileHeader.iOffBits + nBlockYOff * nScanSize;
+
+    if ( VSIFSeekL( poGDS->fp, iScanOffset, SEEK_SET ) < 0 )
+    {
+        // XXX: We will not report error here, because file just may be
+	// in update state and data for this block will be available later
+        if( poGDS->eAccess == GA_Update )
+        {
+            memset( pImage, 0, nBlockXSize );
+            return CE_None;
+        }
+        else
+        {
+            CPLError( CE_Failure, CPLE_FileIO,
+                      "Can't seek to offset %ld in input file to read data.",
+                      iScanOffset );
+            return CE_Failure;
+        }
+    }
+    if ( VSIFReadL( pabyScan, 1, nScanSize, poGDS->fp ) < nScanSize )
+    {
+        // XXX
+        if( poGDS->eAccess == GA_Update )
+        {
+            memset( pImage, 0, nBlockXSize );
+            return CE_None;
+        }
+        else
+        {
+            CPLError( CE_Failure, CPLE_FileIO,
+                      "Can't read from offset %ld in input file.", iScanOffset );
+            return CE_Failure;
+        }
+    }
+
+    if ( poGDS->sInfoHeader.iBitCount == 24 ||
+         poGDS->sInfoHeader.iBitCount == 32 )
+    {
+        GByte *pabyTemp = pabyScan + 3 - nBand;
+
+        for ( i = 0; i < nBlockXSize; i++ )
+        {
+            // Colour triplets in BMP file organized in reverse order:
+            // blue, green, red. When we have 32-bit BMP the forth byte
+            // in quadriplet should be discarded as it has no meaning.
+            // That is why we always use 3 byte count in the following
+            // pabyTemp index.
+            ((GByte *) pImage)[i] = *pabyTemp;
+            pabyTemp += iBytesPerPixel;
+        }
+    }
+    else if ( poGDS->sInfoHeader.iBitCount == 8 )
+    {
+        memcpy( pImage, pabyScan, nBlockXSize );
+    }
+    else if ( poGDS->sInfoHeader.iBitCount == 16 )
+    {
+        for ( i = 0; i < nBlockXSize; i++ )
+        {
+            switch ( nBand )
+            {
+                case 1:
+                ((GByte *) pImage)[i] = pabyScan[i + 1] & 0x1F;
+                break;
+                case 2:
+                ((GByte *) pImage)[i] = ((pabyScan[i] & 0x03) << 3) |
+                                        ((pabyScan[i + 1] & 0xE0) >> 5);
+                break;
+                case 3:
+                ((GByte *) pImage)[i] = (pabyScan[i] & 0x7c) >> 2;
+                break;
+                default:
+                break;
+            }
+        }
+    }
+    else if ( poGDS->sInfoHeader.iBitCount == 4 )
+    {
+        GByte *pabyTemp = pabyScan;
+
+        for ( i = 0; i < nBlockXSize; i++ )
+        {
+            // Most significant part of the byte represents leftmost pixel
+            if ( i & 0x01 )
+                ((GByte *) pImage)[i] = *pabyTemp++ & 0x0F;
+            else
+                ((GByte *) pImage)[i] = (*pabyTemp & 0xF0) >> 4;
+        }
+    }
+    else if ( poGDS->sInfoHeader.iBitCount == 1 )
+    {
+        GByte *pabyTemp = pabyScan;
+
+        for ( i = 0; i < nBlockXSize; i++ )
+        {
+            switch ( i & 0x7 )
+            {
+                case 0:
+                ((GByte *) pImage)[i] = (*pabyTemp & 0x80) >> 7;
+                break;
+                case 1:
+                ((GByte *) pImage)[i] = (*pabyTemp & 0x40) >> 6;
+                break;
+                case 2:
+                ((GByte *) pImage)[i] = (*pabyTemp & 0x20) >> 5;
+                break;
+                case 3:
+                ((GByte *) pImage)[i] = (*pabyTemp & 0x10) >> 4;
+                break;
+                case 4:
+                ((GByte *) pImage)[i] = (*pabyTemp & 0x08) >> 3;
+                break;
+                case 5:
+                ((GByte *) pImage)[i] = (*pabyTemp & 0x04) >> 2;
+                break;
+                case 6:
+                ((GByte *) pImage)[i] = (*pabyTemp & 0x02) >> 1;
+                break;
+                case 7:
+                ((GByte *) pImage)[i] = *pabyTemp++ & 0x01;
+                break;
+                default:
+                break;
+            }
+        }
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                            IWriteBlock()                             */
+/************************************************************************/
+
+CPLErr BMPRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
+                                   void * pImage )
+{
+    BMPDataset  *poGDS = (BMPDataset *)poDS;
+    int         iInPixel, iOutPixel;
+    GUInt32     iScanOffset;
+
+    CPLAssert( poGDS != NULL
+               && nBlockXOff >= 0
+               && nBlockYOff >= 0
+               && pImage != NULL );
+
+    iScanOffset = poGDS->sFileHeader.iOffBits +
+            ( poGDS->GetRasterYSize() - nBlockYOff - 1 ) * nScanSize;
+    if ( VSIFSeekL( poGDS->fp, iScanOffset, SEEK_SET ) < 0 )
+    {
+        CPLError( CE_Failure, CPLE_FileIO,
+                  "Can't seek to offset %ld in output file to write data.\n%s",
+                  iScanOffset, VSIStrerror( errno ) );
+        return CE_Failure;
+    }
+
+    if( poGDS->nBands != 1 )
+    {
+        memset( pabyScan, 0, nScanSize );
+        VSIFReadL( pabyScan, 1, nScanSize, poGDS->fp );
+        VSIFSeekL( poGDS->fp, iScanOffset, SEEK_SET );
+    }
+
+    for ( iInPixel = 0, iOutPixel = iBytesPerPixel - nBand;
+          iInPixel < nBlockXSize; iInPixel++, iOutPixel += poGDS->nBands )
+    {
+        pabyScan[iOutPixel] = ((GByte *) pImage)[iInPixel];
+    }
+
+    if ( VSIFWriteL( pabyScan, 1, nScanSize, poGDS->fp ) < nScanSize )
+    {
+        CPLError( CE_Failure, CPLE_FileIO,
+                  "Can't write block with X offset %d and Y offset %d.\n%s",
+                  nBlockXOff, nBlockYOff,
+                  VSIStrerror( errno ) );
+        return CE_Failure;
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *BMPRasterBand::GetColorTable()
+{
+    BMPDataset   *poGDS = (BMPDataset *) poDS;
+
+    return poGDS->poColorTable;
+}
+
+/************************************************************************/
+/*                           SetColorTable()                            */
+/************************************************************************/
+
+CPLErr BMPRasterBand::SetColorTable( GDALColorTable *poColorTable )
+{
+    BMPDataset  *poGDS = (BMPDataset *) poDS;
+
+    if ( poColorTable )
+    {
+        GDALColorEntry  oEntry;
+        GUInt32         iULong;
+        unsigned int    i;
+
+        poGDS->sInfoHeader.iClrUsed = poColorTable->GetColorEntryCount();
+        if ( poGDS->sInfoHeader.iClrUsed < 1 ||
+             poGDS->sInfoHeader.iClrUsed > (1U << poGDS->sInfoHeader.iBitCount) )
+            return CE_Failure;
+
+        VSIFSeekL( poGDS->fp, BFH_SIZE + 32, SEEK_SET );
+
+        iULong = CPL_LSBWORD32( poGDS->sInfoHeader.iClrUsed );
+        VSIFWriteL( &iULong, 4, 1, poGDS->fp );
+        poGDS->pabyColorTable = (GByte *) CPLRealloc( poGDS->pabyColorTable,
+                        poGDS->nColorElems * poGDS->sInfoHeader.iClrUsed );
+        if ( !poGDS->pabyColorTable )
+            return CE_Failure;
+
+        for( i = 0; i < poGDS->sInfoHeader.iClrUsed; i++ )
+        {
+            poColorTable->GetColorEntryAsRGB( i, &oEntry );
+            poGDS->pabyColorTable[i * poGDS->nColorElems + 3] = 0;
+            poGDS->pabyColorTable[i * poGDS->nColorElems + 2] = 
+                (GByte) oEntry.c1; // Red
+            poGDS->pabyColorTable[i * poGDS->nColorElems + 1] = 
+                (GByte) oEntry.c2; // Green
+            poGDS->pabyColorTable[i * poGDS->nColorElems] = 
+                (GByte) oEntry.c3;     // Blue
+        }
+
+        VSIFSeekL( poGDS->fp, BFH_SIZE + poGDS->sInfoHeader.iSize, SEEK_SET );
+        if ( VSIFWriteL( poGDS->pabyColorTable, 1,
+                poGDS->nColorElems * poGDS->sInfoHeader.iClrUsed, poGDS->fp ) <
+             poGDS->nColorElems * (GUInt32) poGDS->sInfoHeader.iClrUsed )
+        {
+            return CE_Failure;
+        }
+    }
+    else
+        return CE_Failure;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp BMPRasterBand::GetColorInterpretation()
+{
+    BMPDataset      *poGDS = (BMPDataset *) poDS;
+
+    if( poGDS->sInfoHeader.iBitCount == 24 ||
+        poGDS->sInfoHeader.iBitCount == 32 ||
+        poGDS->sInfoHeader.iBitCount == 16 )
+    {
+        if( nBand == 1 )
+            return GCI_RedBand;
+        else if( nBand == 2 )
+            return GCI_GreenBand;
+        else if( nBand == 3 )
+            return GCI_BlueBand;
+        else
+            return GCI_Undefined;
+    }
+    else
+    {
+        return GCI_PaletteIndex;
+    }
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                       BMPComprRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class BMPComprRasterBand : public BMPRasterBand
+{
+    friend class BMPDataset;
+
+    GByte           *pabyComprBuf;
+    GByte           *pabyUncomprBuf;
+
+  public:
+
+                BMPComprRasterBand( BMPDataset *, int );
+                ~BMPComprRasterBand();
+
+    virtual CPLErr          IReadBlock( int, int, void * );
+//    virtual CPLErr        IWriteBlock( int, int, void * );
+};
+
+/************************************************************************/
+/*                           BMPComprRasterBand()                       */
+/************************************************************************/
+
+BMPComprRasterBand::BMPComprRasterBand( BMPDataset *poDS, int nBand )
+    : BMPRasterBand( poDS, nBand )
+{
+    unsigned int    i, j, k, iLength;
+    GUInt32         iComprSize, iUncomprSize;
+
+    iComprSize = poDS->sFileHeader.iSize - poDS->sFileHeader.iOffBits;
+    iUncomprSize = poDS->GetRasterXSize() * poDS->GetRasterYSize();
+    pabyComprBuf = (GByte *) CPLMalloc( iComprSize );
+    pabyUncomprBuf = (GByte *) CPLMalloc( iUncomprSize );
+
+#ifdef DEBUG
+    CPLDebug( "BMP", "RLE compression detected." );
+    CPLDebug ( "BMP", "Size of compressed buffer %ld bytes,"
+               " size of uncompressed buffer %ld bytes.",
+               iComprSize, iUncomprSize );
+#endif
+
+    VSIFSeekL( poDS->fp, poDS->sFileHeader.iOffBits, SEEK_SET );
+    VSIFReadL( pabyComprBuf, 1, iComprSize, poDS->fp );
+    i = 0;
+    j = 0;
+    if ( poDS->sInfoHeader.iBitCount == 8 )         // RLE8
+    {
+        while( j < iUncomprSize && i < iComprSize )
+        {
+            if ( pabyComprBuf[i] )
+            {
+                iLength = pabyComprBuf[i++];
+                while( iLength > 0 && j < iUncomprSize && i < iComprSize )
+                {
+                    pabyUncomprBuf[j++] = pabyComprBuf[i];
+                    iLength--;
+                }
+                i++;
+            }
+            else
+            {
+                i++;
+                if ( pabyComprBuf[i] == 0 )         // Next scanline
+                {
+                    i++;
+                }
+                else if ( pabyComprBuf[i] == 1 )    // End of image
+                {
+                    break;
+                }
+                else if ( pabyComprBuf[i] == 2 )    // Move to...
+                {
+                    i++;
+                    if ( i < iComprSize - 1 )
+                    {
+                        j += pabyComprBuf[i] +
+                             pabyComprBuf[i+1] * poDS->GetRasterXSize();
+                        i += 2;
+                    }
+                    else
+                        break;
+                }
+                else                                // Absolute mode
+                {
+                    iLength = pabyComprBuf[i++];
+                    for ( k = 0; k < iLength && j < iUncomprSize && i < iComprSize; k++ )
+                        pabyUncomprBuf[j++] = pabyComprBuf[i++];
+                    if ( i & 0x01 )
+                        i++;
+                }
+            }
+        }
+    }
+    else                                            // RLE4
+    {
+        while( j < iUncomprSize && i < iComprSize )
+        {
+            if ( pabyComprBuf[i] )
+            {
+                iLength = pabyComprBuf[i++];
+                while( iLength > 0 && j < iUncomprSize && i < iComprSize )
+                {
+                    if ( iLength & 0x01 )
+                        pabyUncomprBuf[j++] = (pabyComprBuf[i] & 0xF0) >> 4;
+                    else
+                        pabyUncomprBuf[j++] = pabyComprBuf[i] & 0x0F;
+                    iLength--;
+                }
+                i++;
+            }
+            else
+            {
+                i++;
+                if ( pabyComprBuf[i] == 0 )         // Next scanline
+                {
+                    i++;
+                }
+                else if ( pabyComprBuf[i] == 1 )    // End of image
+                {
+                    break;
+                }
+                else if ( pabyComprBuf[i] == 2 )    // Move to...
+                {
+                    i++;
+                    if ( i < iComprSize - 1 )
+                    {
+                        j += pabyComprBuf[i] +
+                             pabyComprBuf[i+1] * poDS->GetRasterXSize();
+                        i += 2;
+                    }
+                    else
+                        break;
+                }
+                else                                // Absolute mode
+                {
+                    iLength = pabyComprBuf[i++];
+                    for ( k = 0; k < iLength && j < iUncomprSize && i < iComprSize; k++ )
+                    {
+                        if ( k & 0x01 )
+                            pabyUncomprBuf[j++] = pabyComprBuf[i++] & 0x0F;
+                        else
+                            pabyUncomprBuf[j++] = (pabyComprBuf[i] & 0xF0) >> 4;
+                    }
+                    if ( i & 0x01 )
+                        i++;
+                }
+            }
+        }
+    }
+}
+
+/************************************************************************/
+/*                           ~BMPComprRasterBand()                      */
+/************************************************************************/
+
+BMPComprRasterBand::~BMPComprRasterBand()
+{
+    if ( pabyComprBuf )
+        CPLFree( pabyComprBuf );
+    if ( pabyUncomprBuf )
+        CPLFree( pabyUncomprBuf );
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr BMPComprRasterBand::
+    IReadBlock( int nBlockXOff, int nBlockYOff, void * pImage )
+{
+    memcpy( pImage, pabyUncomprBuf +
+            (poDS->GetRasterYSize() - nBlockYOff - 1) * poDS->GetRasterXSize(),
+            nBlockXSize );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           BMPDataset()                               */
+/************************************************************************/
+
+BMPDataset::BMPDataset()
+{
+    pszFilename = NULL;
+    fp = NULL;
+    nBands = 0;
+    bGeoTransformValid = FALSE;
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+    pabyColorTable = NULL;
+    poColorTable = NULL;
+    memset( &sFileHeader, 0, sizeof(sFileHeader) );
+    memset( &sInfoHeader, 0, sizeof(sInfoHeader) );
+}
+
+/************************************************************************/
+/*                            ~BMPDataset()                             */
+/************************************************************************/
+
+BMPDataset::~BMPDataset()
+{
+    FlushCache();
+
+    if ( pabyColorTable )
+        CPLFree( pabyColorTable );
+    if ( poColorTable != NULL )
+        delete poColorTable;
+    if( fp != NULL )
+        VSIFCloseL( fp );
+    CPLFree( pszFilename );
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr BMPDataset::GetGeoTransform( double * padfTransform )
+{
+    memcpy( padfTransform, adfGeoTransform, sizeof(adfGeoTransform[0]) * 6 );
+
+    if( bGeoTransformValid )
+        return CE_None;
+    else
+        return GDALPamDataset::GetGeoTransform( padfTransform );
+}
+
+/************************************************************************/
+/*                          SetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr BMPDataset::SetGeoTransform( double * padfTransform )
+{
+    CPLErr              eErr = CE_None;
+
+    memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 );
+
+    if ( pszFilename && bGeoTransformValid )
+    {
+        if ( GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform )
+             == FALSE )
+        {
+            CPLError( CE_Failure, CPLE_FileIO, "Can't write world file." );
+            eErr = CE_Failure;
+        }
+    }
+
+    return eErr;
+}
+
+/************************************************************************/
+/*                             IRasterIO()                              */
+/*                                                                      */
+/*      Multi-band raster io handler.  We will use  block based         */
+/*      loading is used for multiband BMPs.  That is because they       */
+/*      are effectively pixel interleaved, so processing all bands      */
+/*      for a given block together avoid extra seeks.                   */
+/************************************************************************/
+
+CPLErr BMPDataset::IRasterIO( GDALRWFlag eRWFlag, 
+                              int nXOff, int nYOff, int nXSize, int nYSize,
+                              void *pData, int nBufXSize, int nBufYSize, 
+                              GDALDataType eBufType,
+                              int nBandCount, int *panBandMap, 
+                              int nPixelSpace, int nLineSpace, int nBandSpace )
+
+{
+    if( nBandCount > 1 )
+        return GDALDataset::BlockBasedRasterIO( 
+            eRWFlag, nXOff, nYOff, nXSize, nYSize,
+            pData, nBufXSize, nBufYSize, eBufType, 
+            nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace );
+    else
+        return 
+            GDALDataset::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
+                                    pData, nBufXSize, nBufYSize, eBufType, 
+                                    nBandCount, panBandMap, 
+                                    nPixelSpace, nLineSpace, nBandSpace );
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *BMPDataset::Open( GDALOpenInfo * poOpenInfo )
+{
+    if( poOpenInfo->fp == NULL )
+        return NULL;
+
+    if( !EQUALN((const char *) poOpenInfo->pabyHeader, "BM", 2) )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    BMPDataset      *poDS;
+    VSIStatBuf      sStat;
+
+    poDS = new BMPDataset();
+
+    if( poOpenInfo->eAccess == GA_ReadOnly )
+        poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
+    else
+        poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" );
+    if ( !poDS->fp )
+        return NULL;
+
+    CPLStat(poOpenInfo->pszFilename, &sStat);
+
+/* -------------------------------------------------------------------- */
+/*      Read the BMPFileHeader. We need iOffBits value only             */
+/* -------------------------------------------------------------------- */
+    VSIFSeekL( poDS->fp, 10, SEEK_SET );
+    VSIFReadL( &poDS->sFileHeader.iOffBits, 1, 4, poDS->fp );
+#ifdef CPL_MSB
+    CPL_SWAP32PTR( &poDS->sFileHeader.iOffBits );
+#endif
+    poDS->sFileHeader.iSize = sStat.st_size;
+
+#ifdef DEBUG
+    CPLDebug( "BMP", "File size %d bytes.", poDS->sFileHeader.iSize );
+    CPLDebug( "BMP", "Image offset 0x%x bytes from file start.",
+              poDS->sFileHeader.iOffBits );
+#endif
+
+/* -------------------------------------------------------------------- */
+/*      Read the BMPInfoHeader.                                         */
+/* -------------------------------------------------------------------- */
+    BMPType         eBMPType;
+
+    VSIFSeekL( poDS->fp, BFH_SIZE, SEEK_SET );
+    VSIFReadL( &poDS->sInfoHeader.iSize, 1, 4, poDS->fp );
+#ifdef CPL_MSB
+    CPL_SWAP32PTR( &poDS->sInfoHeader.iSize );
+#endif
+
+    if ( poDS->sInfoHeader.iSize == BIH_WIN4SIZE )
+        eBMPType = BMPT_WIN4;
+    else if ( poDS->sInfoHeader.iSize == BIH_OS21SIZE )
+        eBMPType = BMPT_OS21;
+    else if ( poDS->sInfoHeader.iSize == BIH_OS22SIZE ||
+              poDS->sInfoHeader.iSize == 16 )
+        eBMPType = BMPT_OS22;
+    else
+        eBMPType = BMPT_WIN5;
+
+    if ( eBMPType == BMPT_WIN4 || eBMPType == BMPT_WIN5 || eBMPType == BMPT_OS22 )
+    {
+        VSIFReadL( &poDS->sInfoHeader.iWidth, 1, 4, poDS->fp );
+        VSIFReadL( &poDS->sInfoHeader.iHeight, 1, 4, poDS->fp );
+        VSIFReadL( &poDS->sInfoHeader.iPlanes, 1, 2, poDS->fp );
+        VSIFReadL( &poDS->sInfoHeader.iBitCount, 1, 2, poDS->fp );
+        VSIFReadL( &poDS->sInfoHeader.iCompression, 1, 4, poDS->fp );
+        VSIFReadL( &poDS->sInfoHeader.iSizeImage, 1, 4, poDS->fp );
+        VSIFReadL( &poDS->sInfoHeader.iXPelsPerMeter, 1, 4, poDS->fp );
+        VSIFReadL( &poDS->sInfoHeader.iYPelsPerMeter, 1, 4, poDS->fp );
+        VSIFReadL( &poDS->sInfoHeader.iClrUsed, 1, 4, poDS->fp );
+        VSIFReadL( &poDS->sInfoHeader.iClrImportant, 1, 4, poDS->fp );
+#ifdef CPL_MSB
+        CPL_SWAP32PTR( &poDS->sInfoHeader.iWidth );
+        CPL_SWAP32PTR( &poDS->sInfoHeader.iHeight );
+        CPL_SWAP16PTR( &poDS->sInfoHeader.iPlanes );
+        CPL_SWAP16PTR( &poDS->sInfoHeader.iBitCount );
+        CPL_SWAP32PTR( &poDS->sInfoHeader.iCompression );
+        CPL_SWAP32PTR( &poDS->sInfoHeader.iSizeImage );
+        CPL_SWAP32PTR( &poDS->sInfoHeader.iXPelsPerMeter );
+        CPL_SWAP32PTR( &poDS->sInfoHeader.iYPelsPerMeter );
+        CPL_SWAP32PTR( &poDS->sInfoHeader.iClrUsed );
+        CPL_SWAP32PTR( &poDS->sInfoHeader.iClrImportant );
+#endif
+        poDS->nColorElems = 4;
+    }
+    
+    if ( eBMPType == BMPT_OS22 )
+    {
+        poDS->nColorElems = 3; // FIXME: different info in different documents regarding this!
+    }
+    
+    if ( eBMPType == BMPT_OS21 )
+    {
+        GInt16  iShort;
+
+        VSIFReadL( &iShort, 1, 2, poDS->fp );
+        poDS->sInfoHeader.iWidth = CPL_LSBWORD16( iShort );
+        VSIFReadL( &iShort, 1, 2, poDS->fp );
+        poDS->sInfoHeader.iHeight = CPL_LSBWORD16( iShort );
+        VSIFReadL( &iShort, 1, 2, poDS->fp );
+        poDS->sInfoHeader.iPlanes = CPL_LSBWORD16( iShort );
+        VSIFReadL( &iShort, 1, 2, poDS->fp );
+        poDS->sInfoHeader.iBitCount = CPL_LSBWORD16( iShort );
+        poDS->sInfoHeader.iCompression = BMPC_RGB;
+        poDS->nColorElems = 3;
+    }
+
+    if ( poDS->sInfoHeader.iBitCount != 1  &&
+         poDS->sInfoHeader.iBitCount != 4  &&
+         poDS->sInfoHeader.iBitCount != 8  &&
+         poDS->sInfoHeader.iBitCount != 16 &&
+         poDS->sInfoHeader.iBitCount != 24 &&
+         poDS->sInfoHeader.iBitCount != 32 )
+    {
+        delete poDS;
+        return NULL;
+    }
+
+#ifdef DEBUG
+    CPLDebug( "BMP", "Windows Device Independent Bitmap parameters:\n"
+              " info header size: %d bytes\n"
+              " width: %d\n height: %d\n planes: %d\n bpp: %d\n"
+              " compression: %d\n image size: %d bytes\n X resolution: %d\n"
+              " Y resolution: %d\n colours used: %d\n colours important: %d",
+              poDS->sInfoHeader.iSize,
+              poDS->sInfoHeader.iWidth, poDS->sInfoHeader.iHeight,
+              poDS->sInfoHeader.iPlanes, poDS->sInfoHeader.iBitCount,
+              poDS->sInfoHeader.iCompression, poDS->sInfoHeader.iSizeImage,
+              poDS->sInfoHeader.iXPelsPerMeter, poDS->sInfoHeader.iYPelsPerMeter,
+              poDS->sInfoHeader.iClrUsed, poDS->sInfoHeader.iClrImportant );
+#endif
+
+    poDS->nRasterXSize = poDS->sInfoHeader.iWidth;
+    poDS->nRasterYSize = (poDS->sInfoHeader.iHeight > 0)?
+        poDS->sInfoHeader.iHeight:-poDS->sInfoHeader.iHeight;
+    switch ( poDS->sInfoHeader.iBitCount )
+    {
+        case 1:
+        case 4:
+        case 8:
+        {
+            int     i;
+
+            poDS->nBands = 1;
+            // Allocate memory for colour table and read it
+            if ( poDS->sInfoHeader.iClrUsed )
+                poDS->nColorTableSize = poDS->sInfoHeader.iClrUsed;
+            else
+                poDS->nColorTableSize = 1 << poDS->sInfoHeader.iBitCount;
+            poDS->pabyColorTable =
+                (GByte *)CPLMalloc( poDS->nColorElems * poDS->nColorTableSize );
+            VSIFSeekL( poDS->fp, BFH_SIZE + poDS->sInfoHeader.iSize, SEEK_SET );
+            VSIFReadL( poDS->pabyColorTable, poDS->nColorElems,
+                      poDS->nColorTableSize, poDS->fp );
+
+            GDALColorEntry oEntry;
+            poDS->poColorTable = new GDALColorTable();
+            for( i = 0; i < poDS->nColorTableSize; i++ )
+            {
+                oEntry.c1 = poDS->pabyColorTable[i * poDS->nColorElems + 2]; // Red
+                oEntry.c2 = poDS->pabyColorTable[i * poDS->nColorElems + 1]; // Green
+                oEntry.c3 = poDS->pabyColorTable[i * poDS->nColorElems];     // Blue
+                oEntry.c4 = 255;
+
+                poDS->poColorTable->SetColorEntry( i, &oEntry );
+            }
+        }
+        break;
+        case 16:
+        case 24:
+        case 32:
+        poDS->nBands = 3;
+        break;
+        default:
+        delete poDS;
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    int             iBand;
+
+    if ( poDS->sInfoHeader.iCompression == BMPC_RGB
+    ||   poDS->sInfoHeader.iCompression == BMPC_BITFIELDS )
+    {
+        for( iBand = 1; iBand <= poDS->nBands; iBand++ )
+            poDS->SetBand( iBand, new BMPRasterBand( poDS, iBand ) );
+    }
+    else if ( poDS->sInfoHeader.iCompression == BMPC_RLE8
+              || poDS->sInfoHeader.iCompression == BMPC_RLE4 )
+    {
+        for( iBand = 1; iBand <= poDS->nBands; iBand++ )
+            poDS->SetBand( iBand, new BMPComprRasterBand( poDS, iBand ) );
+    }
+    else
+    {
+        delete poDS;
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for world file.                                           */
+/* -------------------------------------------------------------------- */
+    poDS->bGeoTransformValid =
+        GDALReadWorldFile( poOpenInfo->pszFilename, ".wld",
+                           poDS->adfGeoTransform );
+
+    if( !poDS->bGeoTransformValid )
+        poDS->bGeoTransformValid =
+            GDALReadWorldFile( poOpenInfo->pszFilename, ".bpw",
+                               poDS->adfGeoTransform );
+
+    if( !poDS->bGeoTransformValid )
+        poDS->bGeoTransformValid =
+            GDALReadWorldFile( poOpenInfo->pszFilename, ".bmpw",
+                               poDS->adfGeoTransform );
+
+/* -------------------------------------------------------------------- */
+/*      Check for overviews.                                            */
+/* -------------------------------------------------------------------- */
+    poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                               Create()                               */
+/************************************************************************/
+
+GDALDataset *BMPDataset::Create( const char * pszFilename,
+                                 int nXSize, int nYSize, int nBands,
+                                 GDALDataType eType, char **papszOptions )
+
+{
+    if( eType != GDT_Byte )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+              "Attempt to create BMP dataset with an illegal\n"
+              "data type (%s), only Byte supported by the format.\n",
+              GDALGetDataTypeName(eType) );
+
+        return NULL;
+    }
+
+    if( nBands != 1 && nBands != 3 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported,
+                  "BMP driver doesn't support %d bands. Must be 1 or 3.\n",
+                  nBands );
+
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create the dataset.                                             */
+/* -------------------------------------------------------------------- */
+    BMPDataset      *poDS;
+
+    poDS = new BMPDataset();
+
+    poDS->fp = VSIFOpenL( pszFilename, "wb+" );
+    if( poDS->fp == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Unable to create file %s.\n",
+                  pszFilename );
+        return NULL;
+    }
+
+    poDS->pszFilename = CPLStrdup(pszFilename);
+
+/* -------------------------------------------------------------------- */
+/*      Fill the BMPInfoHeader                                          */
+/* -------------------------------------------------------------------- */
+    GUInt32         nScanSize;
+
+    poDS->sInfoHeader.iSize = 40;
+    poDS->sInfoHeader.iWidth = nXSize;
+    poDS->sInfoHeader.iHeight = nYSize;
+    poDS->sInfoHeader.iPlanes = 1;
+    poDS->sInfoHeader.iBitCount = ( nBands == 3 )?24:8;
+    poDS->sInfoHeader.iCompression = BMPC_RGB;
+
+    /* XXX: Avoid integer overflow. We can calculate size in one
+     * step using
+     *
+     *   nScanSize = ((poDS->sInfoHeader.iWidth *
+     *            poDS->sInfoHeader.iBitCount + 31) & ~31) / 8
+     *
+     * formulae, but we should check for overflow conditions
+     * during calculation.
+     */
+    nScanSize =
+        (GUInt32)poDS->sInfoHeader.iWidth * poDS->sInfoHeader.iBitCount + 31;
+    if ( !poDS->sInfoHeader.iWidth
+         || !poDS->sInfoHeader.iBitCount
+         || (nScanSize - 31) / poDS->sInfoHeader.iBitCount
+                != (GUInt32)poDS->sInfoHeader.iWidth )
+    {
+        CPLError( CE_Failure, CPLE_FileIO,
+                  "Wrong image parameters; "
+                  "can't allocate space for scanline buffer" );
+        VSIFCloseL( poDS->fp );
+        delete poDS;
+
+        return NULL;
+    }
+    nScanSize = (nScanSize & ~31) / 8;
+
+    poDS->sInfoHeader.iSizeImage = nScanSize * poDS->sInfoHeader.iHeight;
+    poDS->sInfoHeader.iXPelsPerMeter = 0;
+    poDS->sInfoHeader.iYPelsPerMeter = 0;
+    poDS->nColorElems = 4;
+
+/* -------------------------------------------------------------------- */
+/*      Do we need colour table?                                        */
+/* -------------------------------------------------------------------- */
+    unsigned int    i;
+
+    if ( nBands == 1 )
+    {
+        poDS->sInfoHeader.iClrUsed = 1 << poDS->sInfoHeader.iBitCount;
+        poDS->pabyColorTable =
+            (GByte *) CPLMalloc( poDS->nColorElems * poDS->sInfoHeader.iClrUsed );
+        for ( i = 0; i < poDS->sInfoHeader.iClrUsed; i++ )
+        {
+            poDS->pabyColorTable[i * poDS->nColorElems] =
+                poDS->pabyColorTable[i * poDS->nColorElems + 1] =
+                poDS->pabyColorTable[i * poDS->nColorElems + 2] =
+                poDS->pabyColorTable[i * poDS->nColorElems + 3] = (GByte) i;
+        }
+    }
+    else
+    {
+        poDS->sInfoHeader.iClrUsed = 0;
+    }
+    poDS->sInfoHeader.iClrImportant = 0;
+
+/* -------------------------------------------------------------------- */
+/*      Fill the BMPFileHeader                                          */
+/* -------------------------------------------------------------------- */
+    poDS->sFileHeader.bType[0] = 'B';
+    poDS->sFileHeader.bType[1] = 'M';
+    poDS->sFileHeader.iSize = BFH_SIZE + poDS->sInfoHeader.iSize +
+                    poDS->sInfoHeader.iClrUsed * poDS->nColorElems +
+                    poDS->sInfoHeader.iSizeImage;
+    poDS->sFileHeader.iReserved1 = 0;
+    poDS->sFileHeader.iReserved2 = 0;
+    poDS->sFileHeader.iOffBits = BFH_SIZE + poDS->sInfoHeader.iSize +
+                    poDS->sInfoHeader.iClrUsed * poDS->nColorElems;
+
+/* -------------------------------------------------------------------- */
+/*      Write all structures to the file                                */
+/* -------------------------------------------------------------------- */
+    if( VSIFWriteL( &poDS->sFileHeader.bType, 1, 2, poDS->fp ) != 2 )
+    {
+        CPLError( CE_Failure, CPLE_FileIO, 
+                  "Write of first 2 bytes to BMP file %s failed.\n"
+                  "Is file system full?",
+                  pszFilename );
+        VSIFCloseL( poDS->fp );
+        delete poDS;
+
+        return NULL;
+    }
+
+    GInt32      iLong;
+    GUInt32     iULong;
+    GUInt16     iUShort;
+
+    iULong = CPL_LSBWORD32( poDS->sFileHeader.iSize );
+    VSIFWriteL( &iULong, 4, 1, poDS->fp );
+    iUShort = CPL_LSBWORD16( poDS->sFileHeader.iReserved1 );
+    VSIFWriteL( &iUShort, 2, 1, poDS->fp );
+    iUShort = CPL_LSBWORD16( poDS->sFileHeader.iReserved2 );
+    VSIFWriteL( &iUShort, 2, 1, poDS->fp );
+    iULong = CPL_LSBWORD32( poDS->sFileHeader.iOffBits );
+    VSIFWriteL( &iULong, 4, 1, poDS->fp );
+
+    iULong = CPL_LSBWORD32( poDS->sInfoHeader.iSize );
+    VSIFWriteL( &iULong, 4, 1, poDS->fp );
+    iLong = CPL_LSBWORD32( poDS->sInfoHeader.iWidth );
+    VSIFWriteL( &iLong, 4, 1, poDS->fp );
+    iLong = CPL_LSBWORD32( poDS->sInfoHeader.iHeight );
+    VSIFWriteL( &iLong, 4, 1, poDS->fp );
+    iUShort = CPL_LSBWORD16( poDS->sInfoHeader.iPlanes );
+    VSIFWriteL( &iUShort, 2, 1, poDS->fp );
+    iUShort = CPL_LSBWORD16( poDS->sInfoHeader.iBitCount );
+    VSIFWriteL( &iUShort, 2, 1, poDS->fp );
+    iULong = CPL_LSBWORD32( poDS->sInfoHeader.iCompression );
+    VSIFWriteL( &iULong, 4, 1, poDS->fp );
+    iULong = CPL_LSBWORD32( poDS->sInfoHeader.iSizeImage );
+    VSIFWriteL( &iULong, 4, 1, poDS->fp );
+    iLong = CPL_LSBWORD32( poDS->sInfoHeader.iXPelsPerMeter );
+    VSIFWriteL( &iLong, 4, 1, poDS->fp );
+    iLong = CPL_LSBWORD32( poDS->sInfoHeader.iYPelsPerMeter );
+    VSIFWriteL( &iLong, 4, 1, poDS->fp );
+    iULong = CPL_LSBWORD32( poDS->sInfoHeader.iClrUsed );
+    VSIFWriteL( &iULong, 4, 1, poDS->fp );
+    iULong = CPL_LSBWORD32( poDS->sInfoHeader.iClrImportant );
+    VSIFWriteL( &iULong, 4, 1, poDS->fp );
+
+    if ( poDS->sInfoHeader.iClrUsed )
+    {
+        if( VSIFWriteL( poDS->pabyColorTable, 1,
+                        poDS->nColorElems * poDS->sInfoHeader.iClrUsed, poDS->fp ) 
+            != poDS->nColorElems * poDS->sInfoHeader.iClrUsed )
+        {
+            CPLError( CE_Failure, CPLE_FileIO, 
+                      "Error writing color table.  Is disk full?" );
+            VSIFCloseL( poDS->fp );
+            delete poDS;
+
+            return NULL;
+        }
+    }
+
+    poDS->nRasterXSize = nXSize;
+    poDS->nRasterYSize = nYSize;
+    poDS->eAccess = GA_Update;
+    poDS->nBands = nBands;
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    int         iBand;
+
+    for( iBand = 1; iBand <= poDS->nBands; iBand++ )
+    {
+        poDS->SetBand( iBand, new BMPRasterBand( poDS, iBand ) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Do we need a world file?                                        */
+/* -------------------------------------------------------------------- */
+    if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) )
+        poDS->bGeoTransformValid = TRUE;
+
+    return (GDALDataset *) poDS;
+}
+
+/************************************************************************/
+/*                        GDALRegister_BMP()                            */
+/************************************************************************/
+
+void GDALRegister_BMP()
+
+{
+    GDALDriver  *poDriver;
+
+    if( GDALGetDriverByName( "BMP" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+
+        poDriver->SetDescription( "BMP" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
+                                   "MS Windows Device Independent Bitmap" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
+                                   "frmt_bmp.html" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "bmp" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
+"<CreationOptionList>"
+"   <Option name='WORLDFILE' type='boolean' description='Write out world file'/>"
+"</CreationOptionList>" );
+
+        poDriver->pfnOpen = BMPDataset::Open;
+        poDriver->pfnCreate = BMPDataset::Create;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/bmp/frmt_bmp.html b/Utilities/GDAL/frmts/bmp/frmt_bmp.html
new file mode 100644
index 0000000000..c72658958a
--- /dev/null
+++ b/Utilities/GDAL/frmts/bmp/frmt_bmp.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+	<title>BMP --- Microsoft Windows Device Independent Bitmap</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>BMP --- Microsoft Windows Device Independent Bitmap</h1>
+
+MS Windows Device Independent Bitmaps supported by the Windows kernel and
+mostly used for storing system decoration images. Due to the nature of the
+BMP format it has several restrictions and could not be used for general image
+storing. In particular, you can create only 1-bit monochrome,
+8-bit pseudocoloured and 24-bit RGB images only. Even grayscale images must
+be saved in pseudocolour form.<p>
+
+This driver supports reading almost any type of the BMP files and could write
+ones which should be supported on any Windows system. Only single- or three-
+band files could be saved in BMP file. Input values will be resampled to 8
+bit.<p>
+
+<h2>Creation Options</h2>
+<ul>
+	<li> <b>WORLDFILE=ON</b>: Force the generation of an associated
+	ESRI world file (.wld).<p>
+</ul>
+
+<h2>See Also:</h2>
+
+<ul>
+<li> Implemented as <tt>gdal/frmts/bmp/bmpdataset.cpp</tt>.<p>
+
+<li> <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_9qg5.asp">
+MSDN Bitmap Reference
+</a><p>
+
+</ul>
+
+</body>
+</html>
+
diff --git a/Utilities/GDAL/frmts/bmp/makefile.vc b/Utilities/GDAL/frmts/bmp/makefile.vc
new file mode 100644
index 0000000000..18476ee8e0
--- /dev/null
+++ b/Utilities/GDAL/frmts/bmp/makefile.vc
@@ -0,0 +1,13 @@
+
+OBJ	=	bmpdataset.obj
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/bsb/GNUmakefile b/Utilities/GDAL/frmts/bsb/GNUmakefile
new file mode 100644
index 0000000000..040ba998d9
--- /dev/null
+++ b/Utilities/GDAL/frmts/bsb/GNUmakefile
@@ -0,0 +1,37 @@
+
+
+VERSION =	1.3
+
+include ../../GDALmake.opt
+
+OBJ	=	bsb_read.o bsbdataset.o
+
+DISTDIR =	bsb-$(VERSION)
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o
+
+bsb2raw:	bsb2raw.o bsb_read.o
+	$(CXX) bsb2raw.o bsb_read.o $(GDAL_LIB) $(LIBS) -o bsb2raw
+
+
+install-obj:	$(O_OBJ)
+
+dist:	
+	rm -rf $(DISTDIR)
+	mkdir $(DISTDIR)
+	cp *.cpp *.c *.h $(DISTDIR)
+	rm $(DISTDIR)/bsbdataset.cpp
+	cp Makefile.dist $(DISTDIR)/Makefile
+	cp README.dist $(DISTDIR)/README
+	cp ../../port/{cpl_error*,cpl_port*,cpl_string*} $(DISTDIR)
+	cp ../../port/{cpl_vsisimple.cpp,cpl_config.h.in} $(DISTDIR)
+	cp ../../port/{cpl_vsi.h,cpl_conv.*,cpl_path.cpp} $(DISTDIR)
+	cp ../../port/cpl_config.h.in $(DISTDIR)/cpl_config.h
+	rm $(DISTDIR)/*.o
+	tar czf $(DISTDIR).tar.gz $(DISTDIR)
+	zip -r $(DISTDIR).zip $(DISTDIR)
diff --git a/Utilities/GDAL/frmts/bsb/Makefile.dist b/Utilities/GDAL/frmts/bsb/Makefile.dist
new file mode 100644
index 0000000000..05725a5a3e
--- /dev/null
+++ b/Utilities/GDAL/frmts/bsb/Makefile.dist
@@ -0,0 +1,2 @@
+bsb2raw:	
+	$(CXX) $(CFLAGS) *.c *.cpp -o bsb2raw
diff --git a/Utilities/GDAL/frmts/bsb/README.dist b/Utilities/GDAL/frmts/bsb/README.dist
new file mode 100644
index 0000000000..c230232a0d
--- /dev/null
+++ b/Utilities/GDAL/frmts/bsb/README.dist
@@ -0,0 +1,48 @@
+	Sample BSB Reader
+	=================
+
+
+Files
+-----
+
+ bsb2raw.c: Sample mainline program for converting BSB files to "raw" format.
+
+ bsb_read.c: Core BSB reading code ... you may use this in your application.
+ bsb_read.h: Declarations for functions and structures in bsb_read.c.  #include
+             this from your application.
+
+ cpl_*: Supporting low level code and include files.
+
+ Makefile: Simple Unix style makefile for building bsb2raw program.
+
+NOTE: It may be necessary to fool with the cpl_config.h include file for
+different platforms. In particular, change the #undef WORDS_BIGENDIAN to 
+a #define WORDS_BIGENDIAN for big endian systems (Sparc, SGI, etc).  Some
+changes may be needed in cpl_config.h for Windows.
+
+
+License
+-------
+
+MIT/X license ... see header of bsb_read.c. 
+
+On the Web
+----------
+
+http://gdal.velocet.ca/projects/bsb/
+
+GDAL
+----
+
+Note that this BSB code is normally incorporated into the larger GDAL 
+library.  To convert BSB to other common formats like GeoTIFF, PNG, etc
+consider using the GDAL utilities at:
+
+  http://www.remotesensing.org/gdal
+
+
+July 19, 2002
+Frank Warmerdam <warmerdam@pobox.com>
+
+
+
diff --git a/Utilities/GDAL/frmts/bsb/bsb2raw.c b/Utilities/GDAL/frmts/bsb/bsb2raw.c
new file mode 100644
index 0000000000..e511eeed8b
--- /dev/null
+++ b/Utilities/GDAL/frmts/bsb/bsb2raw.c
@@ -0,0 +1,118 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  BSB Reader
+ * Purpose:  Test program for dumping BSB to PPM raster format.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: bsb2raw.c,v $
+ * Revision 1.2  2002/07/19 20:32:41  warmerda
+ * report failed reads
+ *
+ * Revision 1.1  2001/12/08 04:35:16  warmerda
+ * New
+ *
+ */
+
+#include "cpl_error.h"
+#include "cpl_vsi.h"
+#include "cpl_conv.h"
+#include "bsb_read.h"
+
+CPL_CVSID("$Id$");
+
+/************************************************************************/
+/*                                main()                                */
+/************************************************************************/
+
+int main( int nArgc, char **papszArgv )
+
+{
+    BSBInfo	*psInfo;
+    int		iLine, i;
+    GByte	*pabyScanline;
+    FILE	*fp;
+    int         nError = 0;
+    
+    if( nArgc < 3 )
+    {
+        fprintf( stderr, "Usage: bsb2raw src_file dst_file\n" );
+        exit( 1 );
+    }
+
+    psInfo = BSBOpen( papszArgv[1] );
+    if( psInfo == NULL )
+        exit( 1 );
+
+    fp = VSIFOpen( papszArgv[2], "wb" );
+    if( fp == NULL )
+    {
+        perror( "open" );
+        exit( 1 );
+    }
+    
+    pabyScanline = (GByte *) CPLMalloc(psInfo->nXSize);
+    for( iLine = 0; iLine < psInfo->nYSize; iLine++ )
+    {
+        if( !BSBReadScanline( psInfo, iLine, pabyScanline ) )
+            nError++;
+
+        VSIFWrite( pabyScanline, 1, psInfo->nXSize, fp );
+    }
+
+    VSIFClose( fp );
+
+    if( nError > 0 )
+        fprintf( stderr, "Read failed for %d scanlines out of %d.\n",
+                 nError, psInfo->nYSize );
+
+/* -------------------------------------------------------------------- */
+/*      Write .aux file.                                                */
+/* -------------------------------------------------------------------- */
+    fp = VSIFOpen( CPLResetExtension( papszArgv[2], "aux" ), "wt" );
+
+    fprintf( fp, "AuxilaryTarget: %s\n", 
+             CPLGetFilename(papszArgv[2]) );
+
+    fprintf( fp, "RawDefinition: %d %d 1\n", 
+             psInfo->nXSize, psInfo->nYSize );
+
+    fprintf( fp, "ChanDefinition-1: 8U 0 1 %d Swapped\n", 
+             psInfo->nXSize );
+
+    for( i = 0; i < psInfo->nPCTSize; i++ )
+        fprintf( fp, "METADATA_IMG_1_Class_%d_Color: (RGB:%d %d %d)\n",
+                 i, 
+                 psInfo->pabyPCT[i*3 + 0],
+                 psInfo->pabyPCT[i*3 + 1],
+                 psInfo->pabyPCT[i*3 + 2] );
+    
+    VSIFClose( fp );
+    
+
+    exit( 0 );
+}
+
+
diff --git a/Utilities/GDAL/frmts/bsb/bsb_read.c b/Utilities/GDAL/frmts/bsb/bsb_read.c
new file mode 100644
index 0000000000..140571415a
--- /dev/null
+++ b/Utilities/GDAL/frmts/bsb/bsb_read.c
@@ -0,0 +1,794 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  BSB Reader
+ * Purpose:  Low level BSB Access API Implementation (non-GDAL).
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ * NOTE: This code is implemented on the basis of work by Mike Higgins.  The 
+ * BSB format is subject to US patent 5,727,090; however, that patent 
+ * apparently only covers *writing* BSB files, not reading them, so this code 
+ * should not be affected.
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: bsb_read.c,v $
+ * Revision 1.13  2003/10/15 15:51:17  warmerda
+ * Avoid C++ comments.
+ *
+ * Revision 1.12  2003/07/08 20:28:09  warmerda
+ * avoid warings
+ *
+ * Revision 1.11  2003/02/19 03:50:59  warmerda
+ * hacks to support two apparently corrupt BSB files supplied by optech
+ *
+ * Revision 1.10  2002/12/04 15:54:23  warmerda
+ * improved error reporting, remap 0 pixel values on write
+ *
+ * Revision 1.9  2002/12/04 14:52:54  warmerda
+ * Added binary flag writing.
+ *
+ * Revision 1.8  2002/11/04 04:26:45  warmerda
+ * preliminary work on write support
+ *
+ * Revision 1.7  2002/07/19 22:05:15  warmerda
+ * added support for NO1 (encrypted) files
+ *
+ * Revision 1.6  2002/07/19 20:57:32  warmerda
+ * nos files are VER/1 but have ver2 style line markers
+ *
+ * Revision 1.5  2002/07/19 20:33:55  warmerda
+ * Fixed for BSB 1.1 and 3.0 issues:
+ *  o Allow for longer lines.
+ *  o In BSB 1.1 the scanline marker is zero based.
+ *
+ * Revision 1.4  2002/03/19 20:58:24  warmerda
+ * Changes from Olle Soderholm that should allow for NOS files as well.
+ *
+ * Revision 1.3  2001/12/08 21:58:32  warmerda
+ * save header
+ *
+ * Revision 1.2  2001/12/08 15:58:45  warmerda
+ * fixed VSIFSeek() arguments as per Chris Schaefer
+ *
+ * Revision 1.1  2001/12/08 04:35:16  warmerda
+ * New
+ *
+ */
+
+#include "bsb_read.h"
+#include "cpl_conv.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id$");
+
+static const char *BSBReadHeaderLine( FILE *fp, int bNO1 );
+
+/************************************************************************
+
+Background:
+
+To: Frank Warmerdam <warmerda@home.com>
+From: Mike Higgins <higgins@monitor.net>
+Subject: Re: GISTrans: Maptech / NDI BSB Chart Format
+Mime-Version: 1.0
+Content-Type: text/plain; charset="us-ascii"; format=flowed
+
+         I did it! I just wrote a program that reads NOAA BSB chart files 
+and converts them to BMP files! BMP files are not the final goal of my 
+project, but it served as a proof-of-concept.  Next I will want to write 
+routines to extract pieces of the file at full resolution for printing, and 
+routines to filter pieces of the chart for display at lower resolution on 
+the screen.  (One of the terrible things about most chart display programs 
+is that they all sub-sample the charts instead of filtering it down). How 
+did I figure out how to read the BSB files?
+
+         If you recall, I have been trying to reverse engineer the file 
+formats of those nautical charts. When I am between projects I often do a 
+WEB search for the BSB file format to see if someone else has published a 
+hack for them. Monday I hit a NOAA project status report that mentioned 
+some guy named Marty Yellin who had recently completed writing a program to 
+convert BSB files to other file formats! I searched for him and found him 
+mentioned as a contact person for some NOAA program. I was composing a 
+letter to him in my head, or considering calling the NOAA phone number and 
+asking for his extension number, when I saw another NOAA status report 
+indicating that he had retired in 1998. His name showed up in a few more 
+reports, one of which said that he was the inventor of the BSB file format, 
+that it was patented (#5,727,090), and that the patent had been licensed to 
+Maptech (the evil company that will not allow anyone using their file 
+format to convert them to non-proprietary formats). Patents are readily 
+available on the WEB at the IBM patent server and this one is in the 
+dtabase!  I printed up a copy of the patent and of course it describes very 
+nicely (despite the usual typos and omissions of referenced items in the 
+figures) how to write one of these BSB files!
+
+         I was considering talking to a patent lawyer about the legality of 
+using information in the patent to read files without getting a license,
+when I noticed that the patent is only claiming programs that WRITE the 
+file format. I have noticed this before in RF patents where they describe 
+how to make a receiver and never bother to claim a transmitter. The logic 
+is that the transmitter is no good to anybody unless they license receivers 
+from the patent holder. But I think they did it backwards here! They should 
+have claimed a program that can READ the described file format. Now I can 
+read the files, build programs that read the files, and even sell them 
+without violating the claims in the patent! As long as I never try to write 
+one of the evil BSB files, I'm OK!!!
+
+         If you ever need to read these BSB chart programs, drop me a 
+note.  I would be happy to send you a copy of this conversion program.
+
+... later email ...
+
+         Well, here is my little proof of concept program. I hereby give 
+you permission to distribute it freely, modify for you own use, etc.
+I built it as a "WIN32 Console application" which means it runs in an MS 
+DOS box under Microsoft Windows. But the only Windows specific stuff in it 
+are the include files for the BMP file headers.  If you ripped out the BMP 
+code it should compile under UNIX or anyplace else.
+         I'd be overjoyed to have you announce it to GISTrans or anywhere 
+else.  I'm philosophically opposed to the proprietary treatment of the  BSB 
+file format and I want to break it open! Chart data for the People!
+
+ ************************************************************************/
+
+static int nSavedCharacter = -1000;
+
+/************************************************************************/
+/*                             BSBUngetc()                              */
+/************************************************************************/
+
+void BSBUngetc( int nCharacter )
+
+{
+    CPLAssert( nSavedCharacter == -1000 );
+    nSavedCharacter = nCharacter;
+}
+
+/************************************************************************/
+/*                              BSBGetc()                               */
+/************************************************************************/
+
+int BSBGetc( FILE * fp, int bNO1 )
+
+{
+    int nByte;
+
+    if( nSavedCharacter != -1000 )
+    {
+        nByte = nSavedCharacter;
+        nSavedCharacter = -1000;
+        return nByte;
+    }
+
+    nByte = VSIFGetc( fp );
+    
+    if( bNO1 )
+    {
+        nByte = nByte - 9;
+        if( nByte < 0 )
+            nByte = nByte + 256;
+    }
+
+    return nByte;
+}
+
+
+/************************************************************************/
+/*                              BSBOpen()                               */
+/*                                                                      */
+/*      Read BSB header, and return information.                        */
+/************************************************************************/
+
+BSBInfo *BSBOpen( const char *pszFilename )
+
+{
+    FILE	*fp;
+    char	achTestBlock[1000];
+    const char  *pszLine;
+    int         i, bNO1 = FALSE;
+    BSBInfo     *psInfo;
+
+/* -------------------------------------------------------------------- */
+/*      Open the file.                                                  */
+/* -------------------------------------------------------------------- */
+    fp = VSIFOpen( pszFilename, "rb" );
+    if( fp == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "File %s not found.", pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*	Read the first 1000 bytes, and verify that it contains the	*/
+/*	"BSB/" keyword"							*/
+/* -------------------------------------------------------------------- */
+    if( VSIFRead( achTestBlock, 1, sizeof(achTestBlock), fp ) 
+        != sizeof(achTestBlock) )
+    {
+        VSIFClose( fp );
+        CPLError( CE_Failure, CPLE_FileIO,
+                  "Could not read first %d bytes for header!", 
+                  sizeof(achTestBlock) );
+        return NULL;
+    }
+
+    for( i = 0; i < sizeof(achTestBlock) - 4; i++ )
+    {
+        /* Test for "BSB/" */
+        if( achTestBlock[i+0] == 'B' && achTestBlock[i+1] == 'S' 
+            && achTestBlock[i+2] == 'B' && achTestBlock[i+3] == '/' )
+            break;
+
+        /* Test for "NOS/" */
+        if( achTestBlock[i+0] == 'N' && achTestBlock[i+1] == 'O'
+            && achTestBlock[i+2] == 'S' && achTestBlock[i+3] == '/' )
+            break;
+
+        /* Test for "NOS/" offset by 9 in ASCII for NO1 files */
+        if( achTestBlock[i+0] == 'W' && achTestBlock[i+1] == 'X'
+            && achTestBlock[i+2] == '\\' && achTestBlock[i+3] == '8' )
+        {
+            bNO1 = TRUE;
+            break;
+        }
+    }
+
+    if( i == sizeof(achTestBlock) - 4 )
+    {
+        VSIFClose( fp );
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "This does not appear to be a BSB file, no BSB/ header." );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create info structure.                                          */
+/* -------------------------------------------------------------------- */
+    psInfo = (BSBInfo *) CPLCalloc(1,sizeof(BSBInfo));
+    psInfo->fp = fp;
+    psInfo->bNO1 = bNO1;
+
+/* -------------------------------------------------------------------- */
+/*      Rewind, and read line by line.                                  */
+/* -------------------------------------------------------------------- */
+    VSIFSeek( fp, 0, SEEK_SET );
+
+    while( (pszLine = BSBReadHeaderLine(fp, bNO1)) != NULL )
+    {
+        char	**papszTokens = NULL;
+        int      nCount = 0;
+
+        if( pszLine[3] == '/' )
+        {
+            psInfo->papszHeader = CSLAddString( psInfo->papszHeader, pszLine );
+            papszTokens = CSLTokenizeStringComplex( pszLine+4, ",=", 
+                                                    FALSE,FALSE);
+            nCount = CSLCount(papszTokens);
+        }
+
+        if( EQUALN(pszLine,"BSB/",4) )
+        {
+            int		nRAIndex;
+
+            nRAIndex = CSLFindString(papszTokens, "RA" );
+            if( nRAIndex < 0 || nRAIndex > nCount - 2 )
+            {
+                CSLDestroy( papszTokens );
+                CPLError( CE_Failure, CPLE_AppDefined, 
+                          "Failed to extract RA from BSB/ line." );
+                BSBClose( psInfo );
+                return NULL;
+            }
+            psInfo->nXSize = atoi(papszTokens[nRAIndex+1]);
+            psInfo->nYSize = atoi(papszTokens[nRAIndex+2]);
+        }
+        else if( EQUALN(pszLine,"NOS/",4) )
+        {
+            int  nRAIndex;
+            
+            nRAIndex = CSLFindString(papszTokens, "RA" );
+            if( nRAIndex < 0 || nRAIndex > nCount - 2 )
+            {
+                CSLDestroy( papszTokens );
+                CPLError( CE_Failure, CPLE_AppDefined,
+                          "Failed to extract RA from NOS/ line." );
+                BSBClose( psInfo );
+                return NULL;
+            }
+            psInfo->nXSize = atoi(papszTokens[nRAIndex+3]);
+            psInfo->nYSize = atoi(papszTokens[nRAIndex+4]);
+        }
+        else if( EQUALN(pszLine,"RGB/",4) && nCount >= 4 )
+        {
+            int	iPCT = atoi(papszTokens[0]);
+            if( iPCT > psInfo->nPCTSize-1 )
+            {
+                psInfo->pabyPCT = (unsigned char *) 
+                    CPLRealloc(psInfo->pabyPCT,(iPCT+1) * 3);
+                memset( psInfo->pabyPCT + psInfo->nPCTSize*3, 0, 
+                        (iPCT+1-psInfo->nPCTSize) * 3);
+                psInfo->nPCTSize = iPCT+1;
+            }
+
+            psInfo->pabyPCT[iPCT*3+0] = (unsigned char)atoi(papszTokens[1]);
+            psInfo->pabyPCT[iPCT*3+1] = (unsigned char)atoi(papszTokens[2]);
+            psInfo->pabyPCT[iPCT*3+2] = (unsigned char)atoi(papszTokens[3]);
+        }
+        else if( EQUALN(pszLine,"VER/",4) && nCount >= 1 )
+        {
+            psInfo->nVersion = (int) (100 * atof(papszTokens[0]) + 0.5);
+        }
+
+        CSLDestroy( papszTokens );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Verify we found required keywords.                              */
+/* -------------------------------------------------------------------- */
+    if( psInfo->nXSize == 0 || psInfo->nPCTSize == 0 )
+    {
+        BSBClose( psInfo );
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Failed to find required RGB/ or BSB/ keyword in header." );
+        
+        return NULL;
+    }
+
+    if( psInfo->nVersion == 0 )
+    {
+        CPLError( CE_Warning, CPLE_AppDefined,
+                  "VER (version) keyword not found, assuming 2.0." );
+        psInfo->nVersion = 200;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If all has gone well this far, we should be pointing at the     */
+/*      sequence "0x1A 0x00".  Read past to get to start of data.       */
+/*                                                                      */
+/*      We actually do some funny stuff here to be able to read past    */
+/*      some garbage to try and find the 0x1a 0x00 sequence since in    */
+/*      at least some files (ie. optech/World.kap) we find a few        */
+/*      bytes of extra junk in the way.                                 */
+/* -------------------------------------------------------------------- */
+/* from optech/World.kap 
+
+   11624: 30333237 34353938 2C302E30 35373836 03274598,0.05786
+   11640: 39303232 38332C31 332E3135 39363435 902283,13.159645
+   11656: 35390D0A 1A0D0A1A 00040190 C0510002 59~~~~~~~~~~~Q~~
+   11672: 90C05100 0390C051 000490C0 51000590 ~~Q~~~~Q~~~~Q~~~
+ */
+
+    {
+        int    nSkipped = 0;
+
+        while( nSkipped < 100 
+              && (BSBGetc( fp, bNO1 ) != 0x1A || BSBGetc( fp, bNO1 ) != 0x00) )
+            nSkipped++;
+
+        if( nSkipped == 100 )
+        {
+            BSBClose( psInfo );
+            CPLError( CE_Failure, CPLE_AppDefined, 
+                      "Failed to find compressed data segment of BSB file." );
+            return NULL;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read the number of bit size of color numbers.                   */
+/* -------------------------------------------------------------------- */
+    psInfo->nColorSize = BSBGetc( fp, bNO1 );
+    CPLAssert( psInfo->nColorSize > 0 && psInfo->nColorSize < 9 );
+
+/* -------------------------------------------------------------------- */
+/*      Initialize line offset list.                                    */
+/* -------------------------------------------------------------------- */
+    psInfo->panLineOffset = (int *) 
+        CPLMalloc(sizeof(int) * psInfo->nYSize);
+    for( i = 0; i < psInfo->nYSize; i++ )
+        psInfo->panLineOffset[i] = -1;
+
+    psInfo->panLineOffset[0] = VSIFTell( fp );
+
+    return psInfo;
+}
+
+/************************************************************************/
+/*                         BSBReadHeaderLine()                          */
+/*                                                                      */
+/*      Read one virtual line of text from the BSB header.  This        */
+/*      will end if a 0x1A (EOF) is encountered, indicating the data    */
+/*      is about to start.  It will also merge multiple physical        */
+/*      lines where appropriate.  The buffer is internal, and only      */
+/*      lasts till the next call.                                       */
+/************************************************************************/
+
+static const char *BSBReadHeaderLine( FILE * fp, int bNO1 )
+
+{
+    static char	szLine[1000];
+    char        chNext;
+    int	        nLineLen = 0;
+
+    while( !VSIFEof(fp) && nLineLen < sizeof(szLine)-1 )
+    {
+        chNext = (char) BSBGetc( fp, bNO1 );
+        if( chNext == 0x1A )
+        {
+            BSBUngetc( chNext );
+            return NULL;
+        }
+
+        /* each CR/LF (or LF/CR) as if just "CR" */
+        if( chNext == 10 || chNext == 13 )
+        {
+            char	chLF;
+
+            chLF = (char) BSBGetc( fp, bNO1 );
+            if( chLF != 10 && chLF != 13 )
+                BSBUngetc( chLF );
+            chNext = '\n';
+        }
+
+        /* If we are at the end-of-line, check for blank at start
+        ** of next line, to indicate need of continuation.
+        */
+        if( chNext == '\n' )
+        {
+            char chTest;
+
+            chTest = (char) BSBGetc(fp, bNO1);
+            /* Are we done? */
+            if( chTest != ' ' )
+            {
+                BSBUngetc( chTest );
+                szLine[nLineLen] = '\0';
+                return szLine;
+            }
+
+            /* eat pending spaces */
+            while( chTest == ' ' )
+                chTest = (char) BSBGetc(fp,bNO1);
+            BSBUngetc( chTest );
+
+            /* insert comma in data stream */
+            szLine[nLineLen++] = ',';
+        }
+        else
+        {
+            szLine[nLineLen++] = chNext;
+        }
+    }
+
+    return NULL;
+}
+
+/************************************************************************/
+/*                          BSBReadScanline()                           */
+/************************************************************************/
+
+int BSBReadScanline( BSBInfo *psInfo, int nScanline, 
+                     unsigned char *pabyScanlineBuf )
+
+{
+    int		nLineMarker = 0, nValueShift, iPixel = 0;
+    unsigned char byValueMask, byCountMask;
+    FILE	*fp = psInfo->fp;
+    int         byNext, i;
+
+/* -------------------------------------------------------------------- */
+/*      Do we know where the requested line is?  If not, read all       */
+/*      the preceeding ones to "find" our line.                         */
+/* -------------------------------------------------------------------- */
+    if( nScanline < 0 || nScanline >= psInfo->nYSize )
+    {
+        CPLError( CE_Failure, CPLE_FileIO, 
+                  "Scanline %d out of range.", 
+                   nScanline );
+        return FALSE;
+    }
+
+    if( psInfo->panLineOffset[nScanline] == -1 )
+    {
+        for( i = 0; i < nScanline; i++ )
+        {
+            if( psInfo->panLineOffset[i+1] == -1 )
+            {
+                if( !BSBReadScanline( psInfo, i, pabyScanlineBuf ) )
+                    return FALSE;
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Seek to requested scanline.                                     */
+/* -------------------------------------------------------------------- */
+    if( VSIFSeek( fp, psInfo->panLineOffset[nScanline], SEEK_SET ) != 0 )
+    {
+        CPLError( CE_Failure, CPLE_FileIO, 
+                  "Seek to offset %d for scanline %d failed.", 
+                  psInfo->panLineOffset[nScanline], nScanline );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read the line number.  Pre 2.0 BSB seemed to expect the line    */
+/*      numbers to be zero based, while 2.0 and later seemed to         */
+/*      expect it to be one based, and for a 0 to be some sort of       */
+/*      missing line marker.                                            */
+/* -------------------------------------------------------------------- */
+    do {
+        byNext = BSBGetc( fp, psInfo->bNO1 );
+
+        /* Special hack to skip over extra zeros in some files, such
+        ** as optech/sample1.kap.
+        */
+        while( nScanline != 0 && nLineMarker == 0 && byNext == 0 )
+            byNext = BSBGetc( fp, psInfo->bNO1 );
+
+        nLineMarker = nLineMarker * 128 + (byNext & 0x7f);
+    } while( (byNext & 0x80) != 0 );
+
+    if( nLineMarker != nScanline 
+        && nLineMarker != nScanline + 1 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Got scanline id %d when looking for %d @ offset %ld.", 
+                  nLineMarker, nScanline+1, VSIFTell( fp ) );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Setup masking values.                                           */
+/* -------------------------------------------------------------------- */
+    nValueShift = 7 - psInfo->nColorSize;
+    byValueMask = (unsigned char)
+        ((((1 << psInfo->nColorSize)) - 1) << nValueShift);
+    byCountMask = (unsigned char)
+        (1 << (7 - psInfo->nColorSize)) - 1;
+    
+/* -------------------------------------------------------------------- */
+/*      Read and expand runs.                                           */
+/* -------------------------------------------------------------------- */
+    while( (byNext = BSBGetc(fp,psInfo->bNO1)) != 0 )
+    {
+        int	nPixValue;
+        int     nRunCount, i;
+
+        nPixValue = (byNext & byValueMask) >> nValueShift;
+
+        nRunCount = byNext & byCountMask;
+
+        while( (byNext & 0x80) != 0 )
+        {
+            byNext = BSBGetc( fp, psInfo->bNO1 );
+            nRunCount = nRunCount * 128 + (byNext & 0x7f);
+        }
+
+        if( iPixel + nRunCount + 1 > psInfo->nXSize )
+            nRunCount = psInfo->nXSize - iPixel - 1;
+
+        for( i = 0; i < nRunCount+1; i++ )
+            pabyScanlineBuf[iPixel++] = (unsigned char) nPixValue;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      For reasons that are unclear, some scanlines are exactly one    */
+/*      pixel short (such as in the BSB 3.0 354704.KAP product from     */
+/*      NDI/CHS) but are otherwise OK.  Just add a zero if this         */
+/*      appear to have occured.                                         */
+/* -------------------------------------------------------------------- */
+    if( iPixel == psInfo->nXSize - 1 )
+        pabyScanlineBuf[iPixel++] = 0;
+
+/* -------------------------------------------------------------------- */
+/*      Remember the start of the next line.                            */
+/* -------------------------------------------------------------------- */
+    if( iPixel == psInfo->nXSize && nScanline < psInfo->nYSize-1 )
+        psInfo->panLineOffset[nScanline+1] = VSIFTell( fp );
+
+    if( iPixel != psInfo->nXSize )
+    {
+        CPLError( CE_Warning, CPLE_AppDefined, 
+                  "Got %d pixels when looking for %d pixels.",
+                  iPixel, psInfo->nXSize );
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                              BSBClose()                              */
+/************************************************************************/
+
+void BSBClose( BSBInfo *psInfo )
+
+{
+    if( psInfo->fp != NULL )
+        VSIFClose( psInfo->fp );
+
+    CSLDestroy( psInfo->papszHeader );
+    CPLFree( psInfo->panLineOffset );
+    CPLFree( psInfo->pabyPCT );
+    CPLFree( psInfo );
+}
+
+/************************************************************************/
+/*                             BSBCreate()                              */
+/************************************************************************/
+
+BSBInfo *BSBCreate( const char *pszFilename, int nCreationFlags, int nVersion, 
+                    int nXSize, int nYSize )
+
+{
+    FILE	*fp;
+    BSBInfo     *psInfo;
+
+/* -------------------------------------------------------------------- */
+/*      Open new KAP file.                                              */
+/* -------------------------------------------------------------------- */
+    fp = VSIFOpen( pszFilename, "wb" );
+    if( fp == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "Failed to open output file %s.", 
+                  pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Write out BSB line.                                             */
+/* -------------------------------------------------------------------- */
+    VSIFPrintf( fp, 
+                "!Copyright unknown\n" );
+    VSIFPrintf( fp, 
+                "VER/%.1f\n", nVersion / 100.0 );
+    VSIFPrintf( fp, 
+                "BSB/NA=UNKNOWN,NU=999502,RA=%d,%d,DU=254\n",
+                nXSize, nYSize );
+    VSIFPrintf( fp, 
+                "KNP/SC=25000,GD=WGS84,PR=Mercator\n" );
+    VSIFPrintf( fp, 
+                "    PP=31.500000,PI=0.033333,SP=,SK=0.000000,TA=90.000000\n");
+    VSIFPrintf( fp, 
+                "     UN=Metres,SD=HHWLT,DX=2.500000,DY=2.500000\n");
+
+
+/* -------------------------------------------------------------------- */
+/*      Create info structure.                                          */
+/* -------------------------------------------------------------------- */
+    psInfo = (BSBInfo *) CPLCalloc(1,sizeof(BSBInfo));
+    psInfo->fp = fp;
+    psInfo->bNO1 = FALSE;
+    psInfo->nVersion = nVersion;
+    psInfo->nXSize = nXSize;
+    psInfo->nYSize = nYSize;
+    psInfo->bNewFile = TRUE;
+    psInfo->nLastLineWritten = -1;
+
+    return psInfo;
+}
+
+/************************************************************************/
+/*                            BSBWritePCT()                             */
+/************************************************************************/
+
+int BSBWritePCT( BSBInfo *psInfo, int nPCTSize, unsigned char *pabyPCT )
+
+{
+    int        i;
+    
+/* -------------------------------------------------------------------- */
+/*      Verify the PCT not too large.                                   */
+/* -------------------------------------------------------------------- */
+    if( nPCTSize > 128 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Pseudo-color table too large (%d entries), at most 128\n"
+                  " entries allowed in BSB format.", nPCTSize );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Compute the number of bits required for the colors.             */
+/* -------------------------------------------------------------------- */
+    for( psInfo->nColorSize = 1; 
+         (1 << psInfo->nColorSize) < nPCTSize; 
+         psInfo->nColorSize++ ) {}
+
+/* -------------------------------------------------------------------- */
+/*      Write out the color table.  Note that color table entry zero    */
+/*      is ignored.  Zero is not a legal value.                         */
+/* -------------------------------------------------------------------- */
+    for( i = 1; i < nPCTSize; i++ )
+    {
+        VSIFPrintf( psInfo->fp, 
+                    "RGB/%d,%d,%d,%d\n", 
+                    i, pabyPCT[i*3+0], pabyPCT[i*3+1], pabyPCT[i*3+2] );
+    }
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                          BSBWriteScanline()                          */
+/************************************************************************/
+
+int BSBWriteScanline( BSBInfo *psInfo, unsigned char *pabyScanlineBuf )
+
+{
+    int   nValue, iX;
+
+    if( psInfo->nLastLineWritten == psInfo->nYSize - 1 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Attempt to write too many scanlines." );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If this is the first scanline writen out the EOF marker, and    */
+/*      the introductory info in the image segment.                     */
+/* -------------------------------------------------------------------- */
+    if( psInfo->nLastLineWritten == -1 )
+    {
+        VSIFPutc( 0x1A, psInfo->fp );
+        VSIFPutc( 0x00, psInfo->fp );
+        VSIFPutc( psInfo->nColorSize, psInfo->fp );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Write the line number.                                          */
+/* -------------------------------------------------------------------- */
+    nValue = ++psInfo->nLastLineWritten;
+
+    if( psInfo->nVersion >= 200 )
+        nValue++;
+
+    if( nValue >= 128*128 )
+        VSIFPutc( 0x80 | ((nValue & (0x7f<<14)) >> 14), psInfo->fp );
+    if( nValue >= 128 )
+        VSIFPutc( 0x80 | ((nValue & (0x7f<<7)) >> 7), psInfo->fp );
+    VSIFPutc( nValue & 0x7f, psInfo->fp );
+
+/* -------------------------------------------------------------------- */
+/*      Write out each pixel as a separate byte.  We don't try to       */
+/*      actually capture the runs since that radical and futuristic     */
+/*      concept is patented!                                            */
+/* -------------------------------------------------------------------- */
+    for( iX = 0; iX < psInfo->nXSize; iX++ )
+    {
+        if( pabyScanlineBuf[iX] == 0 )
+            VSIFPutc( 1 << (7-psInfo->nColorSize), 
+                      psInfo->fp );
+        else
+            VSIFPutc( pabyScanlineBuf[iX] << (7-psInfo->nColorSize), 
+                      psInfo->fp );
+    }
+
+    VSIFPutc( 0x00, psInfo->fp );
+
+    return TRUE;
+}
diff --git a/Utilities/GDAL/frmts/bsb/bsb_read.h b/Utilities/GDAL/frmts/bsb/bsb_read.h
new file mode 100644
index 0000000000..22329c6d75
--- /dev/null
+++ b/Utilities/GDAL/frmts/bsb/bsb_read.h
@@ -0,0 +1,93 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  BSB Reader
+ * Purpose:  non-GDAL BSB API Declarations
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: bsb_read.h,v $
+ * Revision 1.5  2002/11/04 04:26:45  warmerda
+ * preliminary work on write support
+ *
+ * Revision 1.4  2002/07/19 22:05:15  warmerda
+ * added support for NO1 (encrypted) files
+ *
+ * Revision 1.3  2002/07/19 20:32:57  warmerda
+ * added nVersion to structure.
+ *
+ * Revision 1.2  2001/12/08 21:58:32  warmerda
+ * save header
+ *
+ * Revision 1.1  2001/12/08 04:35:16  warmerda
+ * New
+ *
+ */
+
+#ifndef _BSBREAD_H_INCLUDED
+#define _BSBREAD_H_INCLUDED
+
+#include "cpl_port.h"
+
+CPL_C_START
+
+typedef struct {
+    FILE        *fp;
+
+    int		nXSize;
+    int         nYSize;
+
+    int         nPCTSize;
+    unsigned char *pabyPCT;
+
+    char        **papszHeader;
+
+    int		*panLineOffset;
+
+    int         nColorSize;
+
+    int		nVersion; /* times 100 */
+
+    int         bNO1;
+
+    int         bNewFile;
+    int         nLastLineWritten;
+} BSBInfo;
+
+BSBInfo CPL_DLL *BSBOpen( const char *pszFilename );
+int CPL_DLL BSBReadScanline( BSBInfo *psInfo, int nScanline, 
+                             unsigned char *pabyScanlineBuf );
+void CPL_DLL BSBClose( BSBInfo *psInfo );
+
+BSBInfo CPL_DLL *BSBCreate( const char *pszFilename, int nCreationFlags, 
+                            int nVersion, int nXSize, int nYSize );
+int CPL_DLL BSBWritePCT( BSBInfo *psInfo, 
+                         int nPCTSize, unsigned char *pabyPCT );
+int CPL_DLL BSBWriteScanline( BSBInfo *psInfo, 
+                              unsigned char *pabyScanlineBuf );
+                            
+CPL_C_END
+
+
+#endif /* ndef _BSBREAD_H_INCLUDED */
diff --git a/Utilities/GDAL/frmts/bsb/bsbdataset.cpp b/Utilities/GDAL/frmts/bsb/bsbdataset.cpp
new file mode 100644
index 0000000000..c16b6ac121
--- /dev/null
+++ b/Utilities/GDAL/frmts/bsb/bsbdataset.cpp
@@ -0,0 +1,821 @@
+/******************************************************************************
+ * $Id: bsbdataset.cpp,v 1.16 2005/10/15 19:51:37 fwarmerdam Exp $
+ *
+ * Project:  BSB Reader
+ * Purpose:  BSBDataset implementation for BSB format.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: bsbdataset.cpp,v $
+ * Revision 1.16  2005/10/15 19:51:37  fwarmerdam
+ * added NOS/GEO GCP support c/o Karl Palsson
+ *
+ * Revision 1.15  2005/05/05 14:01:36  fwarmerdam
+ * PAM Enable
+ *
+ * Revision 1.14  2004/04/02 16:38:38  warmerda
+ * Also disable creation type metadata.
+ *
+ * Revision 1.13  2004/04/02 15:17:49  warmerda
+ * Disable BSB creation.
+ *
+ * Revision 1.12  2003/10/20 17:26:01  warmerda
+ * Fixed memory leak when scanning for GCPs.
+ *
+ * Revision 1.11  2003/07/08 20:28:09  warmerda
+ * avoid warings
+ *
+ * Revision 1.10  2002/12/04 15:55:27  warmerda
+ * Remap BSB 1 based pixel values to 0 based when reading, and the corresponding
+ * shift up when writing.  Merge duplicate colors in color table when writing.
+ * Merge even more "close" colors on write if needed to get PCT size below 128.
+ *
+ * Revision 1.9  2002/11/23 18:54:17  warmerda
+ * added CREATIONDATATYPES metadata for drivers
+ *
+ * Revision 1.8  2002/11/04 04:26:45  warmerda
+ * preliminary work on write support
+ *
+ * Revision 1.7  2002/09/04 06:50:36  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.6  2002/07/19 22:05:15  warmerda
+ * added support for NO1 (encrypted) files
+ *
+ * Revision 1.5  2002/07/11 13:09:43  warmerda
+ * Added support for GCP to Geotransform conversion.
+ *
+ * Revision 1.4  2002/06/12 21:12:24  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.3  2002/05/07 13:59:08  warmerda
+ * added support for NOS files
+ *
+ * Revision 1.2  2001/12/08 21:58:27  warmerda
+ * added gcps
+ *
+ * Revision 1.1  2001/12/08 04:35:16  warmerda
+ * New
+ *
+ */
+
+#include "gdal_pam.h"
+#include "bsb_read.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: bsbdataset.cpp,v 1.16 2005/10/15 19:51:37 fwarmerdam Exp $");
+
+CPL_C_START
+void	GDALRegister_BSB(void);
+CPL_C_END
+
+// Define for BSB support, disable now since it doesn't really work.
+#undef BSB_CREATE
+
+/************************************************************************/
+/* ==================================================================== */
+/*				BSBDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class BSBRasterBand;
+
+class BSBDataset : public GDALPamDataset
+{
+    int         nGCPCount;
+    GDAL_GCP    *pasGCPList;
+    char        *pszGCPProjection;
+
+    double      adfGeoTransform[6];
+    int         bGeoTransformSet;
+
+    void        ScanForGCPs( bool isNos, const char *pszFilename );
+    void        ScanForGCPsNos( const char *pszFilename );
+    void        ScanForGCPsBSB();
+  public:
+                BSBDataset();
+		~BSBDataset();
+    
+    BSBInfo     *psInfo;
+
+    static GDALDataset *Open( GDALOpenInfo * );
+
+    virtual int    GetGCPCount();
+    virtual const char *GetGCPProjection();
+    virtual const GDAL_GCP *GetGCPs();
+
+    CPLErr 	GetGeoTransform( double * padfTransform );
+    const char *GetProjectionRef();
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            BSBRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class BSBRasterBand : public GDALPamRasterBand
+{
+    GDALColorTable	oCT;
+
+  public:
+    		BSBRasterBand( BSBDataset * );
+    
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual GDALColorTable *GetColorTable();
+    virtual GDALColorInterp GetColorInterpretation();
+};
+
+
+/************************************************************************/
+/*                           BSBRasterBand()                            */
+/************************************************************************/
+
+BSBRasterBand::BSBRasterBand( BSBDataset *poDS )
+
+{
+    this->poDS = poDS;
+    this->nBand = 1;
+
+    eDataType = GDT_Byte;
+
+    nBlockXSize = poDS->GetRasterXSize();
+    nBlockYSize = 1;
+
+    // Note that the first color table entry is dropped, everything is
+    // shifted down.
+    for( int i = 0; i < poDS->psInfo->nPCTSize-1; i++ )
+    {
+        GDALColorEntry  oColor;
+
+        oColor.c1 = poDS->psInfo->pabyPCT[i*3+0+3];
+        oColor.c2 = poDS->psInfo->pabyPCT[i*3+1+3];
+        oColor.c3 = poDS->psInfo->pabyPCT[i*3+2+3];
+        oColor.c4 = 255;
+
+        oCT.SetColorEntry( i, &oColor );
+    }
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr BSBRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                      void * pImage )
+
+{
+    BSBDataset *poGDS = (BSBDataset *) poDS;
+    GByte *pabyScanline = (GByte*) pImage;
+
+    if( BSBReadScanline( poGDS->psInfo, nBlockYOff, pabyScanline ) )
+    {
+        for( int i = 0; i < nBlockXSize; i++ )
+            pabyScanline[i] -= 1;
+
+        return CE_None;
+    }
+    else
+        return CE_Failure;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *BSBRasterBand::GetColorTable()
+
+{
+    return &oCT;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp BSBRasterBand::GetColorInterpretation()
+
+{
+    return GCI_PaletteIndex;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*				BSBDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                           BSBDataset()                               */
+/************************************************************************/
+
+BSBDataset::BSBDataset()
+
+{
+    psInfo = NULL;
+
+    bGeoTransformSet = FALSE;
+
+    nGCPCount = 0;
+    pasGCPList = NULL;
+    pszGCPProjection = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",7030]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",6326]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",4326]]";
+
+    adfGeoTransform[0] = 0.0;     /* X Origin (top left corner) */
+    adfGeoTransform[1] = 1.0;     /* X Pixel size */
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;     /* Y Origin (top left corner) */
+    adfGeoTransform[4] = 0.0;     
+    adfGeoTransform[5] = 1.0;     /* Y Pixel Size */
+
+}
+
+/************************************************************************/
+/*                            ~BSBDataset()                             */
+/************************************************************************/
+
+BSBDataset::~BSBDataset()
+
+{
+    FlushCache();
+
+    GDALDeinitGCPs( nGCPCount, pasGCPList );
+    CPLFree( pasGCPList );
+
+    if( psInfo != NULL )
+        BSBClose( psInfo );
+}
+
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr BSBDataset::GetGeoTransform( double * padfTransform )
+
+{
+    memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
+    
+    if( bGeoTransformSet )
+        return CE_None;
+    else
+        return CE_Failure;
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *BSBDataset::GetProjectionRef()
+
+{
+    if( bGeoTransformSet )
+        return pszGCPProjection;
+    else
+        return "";
+}
+
+/************************************************************************/
+/*                            ScanForGCPs( isNos, *pszFilename )        */
+/************************************************************************/
+
+void BSBDataset::ScanForGCPs( bool isNos, const char *pszFilename )
+
+{
+#define MAX_GCP		256
+
+    nGCPCount = 0;
+    pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),MAX_GCP);
+
+    if ( isNos )
+    {
+        ScanForGCPsNos(pszFilename);
+    } else {
+        ScanForGCPsBSB();
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Attempt to prepare a geotransform from the GCPs.                */
+/* -------------------------------------------------------------------- */
+    if( GDALGCPsToGeoTransform( nGCPCount, pasGCPList, adfGeoTransform, 
+                                FALSE ) )
+    {
+        bGeoTransformSet = TRUE;
+    }
+}
+
+/************************************************************************/
+/*                            ScanForGCPsNos( pszFilename )             */
+/* Nos files have an accompanying .geo file, that contains some of the  */
+/* information normally contained in the header section with BSB files. */
+/* we try and open a file with the same name, but a .geo extension, and */
+/* look for lines like...                                               */
+/*   PointX=long lat line pixel    (using the same naming system as BSB)*/
+/*   Point1=-22.0000 64.250000 197 744                                  */
+/************************************************************************/
+
+void BSBDataset::ScanForGCPsNos( const char *pszFilename )
+{
+    char **Tokens;
+    const char *geofile;
+    const char *extension;
+
+    extension = CPLGetExtension(pszFilename);
+
+    // pseudointelligently try and guess whether we want a .geo or a .GEO
+    if (extension[1] == 'O')
+    {
+        geofile = CPLResetExtension( pszFilename, "GEO");
+    } else {
+        geofile = CPLResetExtension( pszFilename, "geo");
+    }
+
+    FILE *gfp = VSIFOpen( geofile, "r" );  // Text files
+    if( gfp == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Couldn't find a matching .GEO file: %s", geofile );
+        return;
+    }
+
+    char *thisLine = (char *) CPLMalloc( 80 ); // FIXME
+    while (fgets(thisLine, 80, gfp))
+    {
+        if( EQUALN(thisLine, "Point", 5) )
+        {
+            // got a point line, turn it into a gcp
+            Tokens = CSLTokenizeStringComplex(thisLine, "=", FALSE, FALSE);
+            Tokens = CSLTokenizeStringComplex(Tokens[1], " ", FALSE, FALSE);
+
+            GDALInitGCPs( 1, pasGCPList + nGCPCount );
+            pasGCPList[nGCPCount].dfGCPX = atof(Tokens[0]);
+            pasGCPList[nGCPCount].dfGCPY = atof(Tokens[1]);
+            pasGCPList[nGCPCount].dfGCPPixel = atof(Tokens[3]);
+            pasGCPList[nGCPCount].dfGCPLine = atof(Tokens[2]);
+
+            CPLFree( pasGCPList[nGCPCount].pszId );
+            char	szName[50];
+            sprintf( szName, "GCP_%d", nGCPCount+1 );
+            pasGCPList[nGCPCount].pszId = CPLStrdup( szName );
+
+            nGCPCount++;
+        }
+    }
+
+    CPLFree(thisLine);
+    VSIFClose(gfp);
+}
+
+
+/************************************************************************/
+/*                            ScanForGCPsBSB()                          */
+/************************************************************************/
+
+void BSBDataset::ScanForGCPsBSB()
+{
+/* -------------------------------------------------------------------- */
+/*      Collect standalone GCPs.  They look like:                       */
+/*                                                                      */
+/*      REF/1,115,2727,32.346666666667,-60.881666666667			*/
+/*      REF/n,pixel,line,lat,long                                       */
+/* -------------------------------------------------------------------- */
+    int i;
+
+    for( i = 0; psInfo->papszHeader[i] != NULL; i++ )
+    {
+        char	**papszTokens;
+        char	szName[50];
+
+        if( !EQUALN(psInfo->papszHeader[i],"REF/",4) )
+            continue;
+
+        papszTokens = 
+            CSLTokenizeStringComplex( psInfo->papszHeader[i]+4, ",", 
+                                      FALSE, FALSE );
+
+        if( CSLCount(papszTokens) >= 4 )
+        {
+            GDALInitGCPs( 1, pasGCPList + nGCPCount );
+
+            pasGCPList[nGCPCount].dfGCPX = atof(papszTokens[4]);
+            pasGCPList[nGCPCount].dfGCPY = atof(papszTokens[3]);
+            pasGCPList[nGCPCount].dfGCPPixel = atof(papszTokens[1]);
+            pasGCPList[nGCPCount].dfGCPLine = atof(papszTokens[2]);
+
+            CPLFree( pasGCPList[nGCPCount].pszId );
+            if( CSLCount(papszTokens) > 5 )
+            {
+                pasGCPList[nGCPCount].pszId = papszTokens[5];
+            }
+            else
+            {
+                sprintf( szName, "GCP_%d", nGCPCount+1 );
+                pasGCPList[nGCPCount].pszId = CPLStrdup( szName );
+            }
+
+            nGCPCount++;
+        }
+        CSLDestroy( papszTokens );
+    }
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *BSBDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Check for BSB/ keyword.                                         */
+/* -------------------------------------------------------------------- */
+    int		i;
+    bool        isNos = false;
+
+    if( poOpenInfo->fp == NULL || poOpenInfo->nHeaderBytes < 1000 )
+        return NULL;
+
+    for( i = 0; i < poOpenInfo->nHeaderBytes - 4; i++ )
+    {
+        if( poOpenInfo->pabyHeader[i+0] == 'B' 
+            && poOpenInfo->pabyHeader[i+1] == 'S' 
+            && poOpenInfo->pabyHeader[i+2] == 'B' 
+            && poOpenInfo->pabyHeader[i+3] == '/' )
+            break;
+        if( poOpenInfo->pabyHeader[i+0] == 'N' 
+            && poOpenInfo->pabyHeader[i+1] == 'O' 
+            && poOpenInfo->pabyHeader[i+2] == 'S' 
+            && poOpenInfo->pabyHeader[i+3] == '/' )
+        {
+            isNos = true;
+            break;
+        }
+        if( poOpenInfo->pabyHeader[i+0] == 'W' 
+            && poOpenInfo->pabyHeader[i+1] == 'X' 
+            && poOpenInfo->pabyHeader[i+2] == '\\' 
+            && poOpenInfo->pabyHeader[i+3] == '8' )
+            break;
+    }
+
+    if( i == poOpenInfo->nHeaderBytes - 4 )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    BSBDataset 	*poDS;
+
+    poDS = new BSBDataset();
+
+/* -------------------------------------------------------------------- */
+/*      Open the file.                                                  */
+/* -------------------------------------------------------------------- */
+    poDS->psInfo = BSBOpen( poOpenInfo->pszFilename );
+    if( poDS->psInfo == NULL )
+    {
+        delete poDS;
+        return NULL;
+    }
+
+    poDS->nRasterXSize = poDS->psInfo->nXSize;
+    poDS->nRasterYSize = poDS->psInfo->nYSize;
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    poDS->SetBand( 1, new BSBRasterBand( poDS ));
+
+    poDS->ScanForGCPs( isNos, poOpenInfo->pszFilename );
+    poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                            GetGCPCount()                             */
+/************************************************************************/
+
+int BSBDataset::GetGCPCount()
+
+{
+    return nGCPCount;
+}
+
+/************************************************************************/
+/*                          GetGCPProjection()                          */
+/************************************************************************/
+
+const char *BSBDataset::GetGCPProjection()
+
+{
+    if( nGCPCount > 0 && pszGCPProjection != NULL )
+        return pszGCPProjection;
+    else
+        return "";
+}
+
+/************************************************************************/
+/*                               GetGCP()                               */
+/************************************************************************/
+
+const GDAL_GCP *BSBDataset::GetGCPs()
+
+{
+    return pasGCPList;
+}
+
+#ifdef BSB_CREATE
+/************************************************************************/
+/*                           BSBCreateCopy()                            */
+/************************************************************************/
+
+static GDALDataset *
+BSBCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, 
+               int bStrict, char ** papszOptions, 
+               GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    int  nBands = poSrcDS->GetRasterCount();
+    int  nXSize = poSrcDS->GetRasterXSize();
+    int  nYSize = poSrcDS->GetRasterYSize();
+
+/* -------------------------------------------------------------------- */
+/*      Some some rudimentary checks                                    */
+/* -------------------------------------------------------------------- */
+    if( nBands != 1 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "BSB driver only supports one band images.\n" );
+
+        return NULL;
+    }
+
+    if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte 
+        && bStrict )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "BSB driver doesn't support data type %s. "
+                  "Only eight bit bands supported.\n", 
+                  GDALGetDataTypeName( 
+                      poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
+
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Open the output file.                                           */
+/* -------------------------------------------------------------------- */
+    BSBInfo *psBSB;
+
+    psBSB = BSBCreate( pszFilename, 0, 200, nXSize, nYSize );
+    if( psBSB == NULL )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Prepare initial color table.colortable.                         */
+/* -------------------------------------------------------------------- */
+    GDALRasterBand	*poBand = poSrcDS->GetRasterBand(1);
+    int			iColor;
+    unsigned char       abyPCT[771];
+    int                 nPCTSize;
+    int                 anRemap[256];
+
+    abyPCT[0] = 0;
+    abyPCT[1] = 0;
+    abyPCT[2] = 0;
+
+    if( poBand->GetColorTable() == NULL )
+    {
+        /* map greyscale down to 63 grey levels. */
+        for( iColor = 0; iColor < 256; iColor++ )
+        {
+            int nOutValue = (int) (iColor / 4.1) + 1;
+
+            anRemap[iColor] = nOutValue;
+            abyPCT[nOutValue*3 + 0] = (unsigned char) iColor;
+            abyPCT[nOutValue*3 + 1] = (unsigned char) iColor;
+            abyPCT[nOutValue*3 + 2] = (unsigned char) iColor;
+        }
+        nPCTSize = 64;
+    }
+    else
+    {
+        GDALColorTable	*poCT = poBand->GetColorTable();
+
+        nPCTSize = poCT->GetColorEntryCount();
+        for( iColor = 0; iColor < nPCTSize; iColor++ )
+        {
+            GDALColorEntry	sEntry;
+
+            poCT->GetColorEntryAsRGB( iColor, &sEntry );
+            
+            anRemap[iColor] = (unsigned char) (iColor + 1);
+            abyPCT[(iColor+1)*3 + 0] = (unsigned char) sEntry.c1;
+            abyPCT[(iColor+1)*3 + 1] = (unsigned char) sEntry.c2;
+            abyPCT[(iColor+1)*3 + 2] = (unsigned char) sEntry.c3;
+        }
+
+        // Add entries for pixel values which apparently will not occur.
+        for( iColor = nPCTSize; iColor < 256; iColor++ )
+            anRemap[iColor] = 1;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Boil out all duplicate entries.                                 */
+/* -------------------------------------------------------------------- */
+    int  i;
+
+    for( i = 1; i < nPCTSize-1; i++ )
+    {
+        int  j;
+
+        for( j = i+1; j < nPCTSize; j++ )
+        {
+            if( abyPCT[i*3+0] == abyPCT[j*3+0] 
+                && abyPCT[i*3+1] == abyPCT[j*3+1] 
+                && abyPCT[i*3+2] == abyPCT[j*3+2] )
+            {
+                int   k;
+
+                nPCTSize--;
+                abyPCT[j*3+0] = abyPCT[nPCTSize*3+0];
+                abyPCT[j*3+1] = abyPCT[nPCTSize*3+1];
+                abyPCT[j*3+2] = abyPCT[nPCTSize*3+2];
+
+                for( k = 0; k < 256; k++ )
+                {
+                    // merge matching entries.
+                    if( anRemap[k] == j )
+                        anRemap[k] = i;
+
+                    // shift the last PCT entry into the new hole.
+                    if( anRemap[k] == nPCTSize )
+                        anRemap[k] = j;
+                }
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Boil out all duplicate entries.                                 */
+/* -------------------------------------------------------------------- */
+    if( nPCTSize > 128 )
+    {
+        CPLError( CE_Warning, CPLE_AppDefined,
+                  "Having to merge color table entries to reduce %d real\n"
+                  "color table entries down to 127 values.", 
+                  nPCTSize );
+    }
+
+    while( nPCTSize > 127 )
+    {
+        int nBestRange = 768;
+        int iBestMatch1=-1, iBestMatch2=-1;
+
+        // Find the closest pair of color table entries.
+
+        for( i = 1; i < nPCTSize-1; i++ )
+        {
+            int  j;
+            
+            for( j = i+1; j < nPCTSize; j++ )
+            {
+                int nRange = ABS(abyPCT[i*3+0] - abyPCT[j*3+0])
+                    + ABS(abyPCT[i*3+1] - abyPCT[j*3+1])
+                    + ABS(abyPCT[i*3+2] - abyPCT[j*3+2]);
+
+                if( nRange < nBestRange )
+                {
+                    iBestMatch1 = i;
+                    iBestMatch2 = j;
+                    nBestRange = nRange;
+                }
+            }
+        }
+
+        // Merge the second entry into the first. 
+        nPCTSize--;
+        abyPCT[iBestMatch2*3+0] = abyPCT[nPCTSize*3+0];
+        abyPCT[iBestMatch2*3+1] = abyPCT[nPCTSize*3+1];
+        abyPCT[iBestMatch2*3+2] = abyPCT[nPCTSize*3+2];
+
+        for( i = 0; i < 256; i++ )
+        {
+            // merge matching entries.
+            if( anRemap[i] == iBestMatch2 )
+                anRemap[i] = iBestMatch1;
+            
+            // shift the last PCT entry into the new hole.
+            if( anRemap[i] == nPCTSize )
+                anRemap[i] = iBestMatch2;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Write the PCT.                                                  */
+/* -------------------------------------------------------------------- */
+    if( !BSBWritePCT( psBSB, nPCTSize, abyPCT ) )
+    {
+        BSBClose( psBSB );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Loop over image, copying image data.                            */
+/* -------------------------------------------------------------------- */
+    GByte 	*pabyScanline;
+    CPLErr      eErr = CE_None;
+
+    pabyScanline = (GByte *) CPLMalloc( nXSize );
+
+    for( int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ )
+    {
+        eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, 
+                                 pabyScanline, nXSize, 1, GDT_Byte,
+                                 nBands, nBands * nXSize );
+        if( eErr == CE_None )
+        {
+            for( i = 0; i < nXSize; i++ )
+                pabyScanline[i] = (GByte) anRemap[pabyScanline[i]];
+
+            if( !BSBWriteScanline( psBSB, pabyScanline ) )
+                eErr = CE_Failure;
+        }
+    }
+
+    CPLFree( pabyScanline );
+
+/* -------------------------------------------------------------------- */
+/*      cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    BSBClose( psBSB );
+
+    if( eErr != CE_None )
+    {
+        VSIUnlink( pszFilename );
+        return NULL;
+    }
+    else
+        return (GDALDataset *) GDALOpen( pszFilename, GA_ReadOnly );
+}
+#endif
+
+/************************************************************************/
+/*                        GDALRegister_BSB()                            */
+/************************************************************************/
+
+void GDALRegister_BSB()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "BSB" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "BSB" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "Maptech BSB Nautical Charts" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_various.html#BSB" );
+#ifdef BSB_CREATE
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, 
+"<CreationOptionList>\n"
+"   <Option name='NA' type='string'/>\n"
+"</CreationOptionList>\n" );
+#endif
+        poDriver->pfnOpen = BSBDataset::Open;
+#ifdef BSB_CREATE
+        poDriver->pfnCreateCopy = BSBCreateCopy;
+#endif
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
diff --git a/Utilities/GDAL/frmts/bsb/makefile.vc b/Utilities/GDAL/frmts/bsb/makefile.vc
new file mode 100644
index 0000000000..ea5ed2aa13
--- /dev/null
+++ b/Utilities/GDAL/frmts/bsb/makefile.vc
@@ -0,0 +1,13 @@
+
+OBJ	=	bsbdataset.obj bsb_read.obj 
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/ceos2/GNUmakefile b/Utilities/GDAL/frmts/ceos2/GNUmakefile
new file mode 100644
index 0000000000..00085532f2
--- /dev/null
+++ b/Utilities/GDAL/frmts/ceos2/GNUmakefile
@@ -0,0 +1,16 @@
+
+
+OBJ	=	sar_ceosdataset.o \
+		ceosrecipe.o ceossar.o ceos.o link.o
+
+include ../../GDALmake.opt
+
+
+CPPFLAGS	:=	-I../raw $(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/ceos2/ceos.c b/Utilities/GDAL/frmts/ceos2/ceos.c
new file mode 100644
index 0000000000..9783d7c3c5
--- /dev/null
+++ b/Utilities/GDAL/frmts/ceos2/ceos.c
@@ -0,0 +1,425 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  ASI CEOS Translator
+ * Purpose:  Core CEOS functions.
+ * Author:   Paul Lahaie, pjlahaie@atlsci.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: ceos.c,v $
+ * Revision 1.6  2003/12/10 18:02:21  warmerda
+ * fixed some minor memory leaks
+ *
+ * Revision 1.5  2003/02/28 18:45:20  gpotts
+ * Prefixed CreateLink with ceos2 since there are multiple symbol conflict on static build under mac. Garrett Potts (gpotts@imagelinks.com)
+ *
+ * Revision 1.4  2001/10/18 17:03:21  warmerda
+ * use DetermineCeosRecordBodyLength internally
+ *
+ * Revision 1.3  2001/07/18 04:51:56  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.2  2000/03/31 13:33:34  warmerda
+ * ported to GDAL, replace headers
+ *
+ */
+
+#include "ceos.h"
+
+CPL_CVSID("$Id$");
+
+/* Function implementations of functions described in ceos.h */
+
+void CeosUpdateHeaderFromBuffer(CeosRecord_t *record);
+
+void InitEmptyCeosRecord(CeosRecord_t *record, int32 sequence, CeosTypeCode_t typecode, int32 length)
+{
+    if(record)
+    {
+	if((record->Buffer = HMalloc(length)) == NULL)
+	{
+	    return;
+	}
+	/* First we zero fill the buffer */
+	memset(record->Buffer,0,length);
+
+	/* Setup values inside the CeosRecord_t header */
+	record->Sequence = sequence;
+	record->Flavour = 0;
+	record->FileId = 0;
+	record->TypeCode = typecode;
+	record->Subsequence = 0;
+	record->Length = length;
+
+	/* Now we fill in the buffer portion as well */
+	NativeToCeos( record->Buffer+__SEQUENCE_OFF, &(record->Sequence), sizeof(record->Sequence), sizeof( record->Sequence ) );
+	memcpy(record->Buffer+__TYPE_OFF, &( record->TypeCode.Int32Code ), sizeof( record->TypeCode.Int32Code ) );
+	NativeToCeos( record->Buffer+__LENGTH_OFF, &length,  sizeof( length ), sizeof( length ) );
+    }
+}
+
+void InitCeosRecord(CeosRecord_t *record, uchar *buffer)
+{
+    if(record && buffer)
+    {
+	InitCeosRecordWithHeader(record, buffer, buffer+__CEOS_HEADER_LENGTH);
+    }
+}
+
+void InitCeosRecordWithHeader(CeosRecord_t *record, uchar *header, uchar *buffer)
+{
+    if(record && buffer && header)
+    {
+        if( record->Length != 0 )
+            record->Length = DetermineCeosRecordBodyLength( header );
+
+	if((record->Buffer = HMalloc(record->Length)) == NULL)
+	{
+	    record->Length = 0;
+	    return;
+	}
+
+	/* First copy the header then the buffer */
+	memcpy(record->Buffer,header,__CEOS_HEADER_LENGTH);
+	/* Now we copy the rest */
+	memcpy(record->Buffer+__CEOS_HEADER_LENGTH,buffer,record->Length-__CEOS_HEADER_LENGTH);
+
+	/* Now we fill in the rest of the structure! */
+	memcpy(&(record->TypeCode.Int32Code),header+__TYPE_OFF,sizeof(record->TypeCode.Int32Code));
+	CeosToNative(&(record->Sequence),header+__SEQUENCE_OFF,sizeof(record->Sequence), sizeof( record->Sequence ) );
+    }
+}
+
+int DetermineCeosRecordBodyLength(const uchar *header)
+{
+    int i;
+    
+    if(header)
+    {
+	CeosToNative(&i,header+__LENGTH_OFF,sizeof( i ), sizeof( i ) );
+
+	return i;
+    }
+
+    return -1;
+}
+
+void DeleteCeosRecord(CeosRecord_t *record)
+{
+    if(record)
+    {
+	if(record->Buffer)
+	{
+	    HFree(record->Buffer);
+	    record->Buffer = NULL;
+	}
+        HFree( record );
+    }
+}
+
+void GetCeosRecordStruct(const CeosRecord_t *record,void *struct_ptr)
+{
+    if(record && struct_ptr && record->Buffer)
+    {
+	memcpy(record->Buffer,struct_ptr,record->Length);
+    }
+}
+
+void PutCeosRecordStruct(CeosRecord_t *record,const void *struct_ptr)
+{
+    int Length;
+
+    if(record && struct_ptr)
+    {
+	CeosToNative( &Length, struct_ptr, sizeof( Length ), sizeof( Length ) );
+	memcpy(record->Buffer,struct_ptr,Length);
+	CeosUpdateHeaderFromBuffer(record);
+    }
+}
+
+void GetCeosField(CeosRecord_t *record, int32 start_byte, char *format, void *value)
+{
+    int field_size;
+    char *d_ptr;
+    char *mod_buf = NULL;
+
+    field_size = atoi(format+1);
+
+    if(field_size < 1)
+    {
+	return;
+    }
+
+    /* Check for out of bounds */
+    if(start_byte + field_size - 1 > record->Length)
+    {
+	return;
+    }
+
+    if((mod_buf = (char *) HMalloc(field_size + 1)) == NULL)
+    {
+	return;
+    }
+
+    memcpy(mod_buf,record->Buffer+(start_byte-1), field_size);
+    mod_buf[field_size] = '\0';
+
+    /* Switch on format type */
+    switch(format[0])
+    {
+    case 'b':
+    case 'B':
+	/* Binary data type */
+	if(field_size > 1)
+	{
+	    CeosToNative( value, mod_buf, field_size, field_size );
+	} else {
+	    memcpy( value, mod_buf, field_size );
+	}
+	break;
+
+    case 'i':
+    case 'I':
+	/* Integer type */
+	*( (int *)value) = atoi(mod_buf);
+	break;
+
+    case 'f':
+    case 'F':
+    case 'e':
+    case 'E':
+	/* Double precision float data type */
+
+	/* Change the 'D' exponent separators to 'e' */
+	if( ( d_ptr = strchr(mod_buf, 'd') ) != NULL)
+	{
+	    *d_ptr = 'e';
+	}
+	if( ( d_ptr = strchr(mod_buf, 'D') ) != NULL)
+	{
+	    *d_ptr = 'e';
+	}
+
+	*( (double *)value) = strtod(mod_buf,NULL);
+	break;
+    case 'a':
+    case 'A':
+	/* ASCII..  We just easily extract it */
+	( (char *)value)[field_size] = '\0';
+	memcpy( value, mod_buf, field_size );
+	break;
+
+    default:
+	/* Unknown format */
+	return;
+    }
+
+    HFree(mod_buf);
+
+}
+
+void SetCeosField(CeosRecord_t *record, int32 start_byte, char *format, void *value)
+{
+    int field_size;
+    char * temp_buf = NULL;
+    char printf_format[ 20 ];
+
+    field_size = 0;
+    sscanf(&format[1], "%d", &field_size);
+    if(field_size < 1)
+    {
+	return;
+    }
+
+    /* Check for bounds */
+    if(start_byte + field_size - 1 > record->Length)
+    {
+	return;
+    }
+
+    /* Make a local buffer to print into */
+    if((temp_buf = (char *) HMalloc(field_size+1)) == NULL)
+    {
+	return;
+    }
+    switch(format[0])
+    {
+    case 'b':
+    case 'B':
+	/* Binary data type */
+	if(field_size > 1)
+	{
+	    NativeToCeos( value, temp_buf, field_size, field_size );
+	} else {
+	    memcpy(value,temp_buf,field_size);
+	}
+	break;
+	
+    case 'i':
+    case 'I':
+	/* Integer data type */
+	sprintf( printf_format,"%%%s%c",format+1, 'd');
+	sprintf( temp_buf, printf_format, *(int *) value);
+	break;
+
+    case 'f':
+    case 'F':
+	/* Double precision floating point data type */
+	sprintf(printf_format, "%%%s%c", format+1, 'g');
+	sprintf(temp_buf, printf_format, *(double *)value);
+	break;
+
+    case 'e':
+    case 'E':
+	/* Double precision floating point data type (forced exponent) */
+	sprintf(printf_format,"%%%s%c", format+1, 'e');
+	sprintf(temp_buf, printf_format, *(double *)value);
+	break;
+
+    case 'a':
+    case 'A':
+	strncpy(temp_buf,value,field_size+1);
+	temp_buf[field_size] = '0';
+	break;
+
+    default:
+	/* Unknown format */
+	return;
+    }
+
+    memcpy(record->Buffer + start_byte -1, temp_buf, field_size);
+
+    HFree(temp_buf);
+}
+
+void SetIntCeosField(CeosRecord_t *record, int32 start_byte, int32 length, int32 value)
+{
+    int integer_value = value;
+    char total_len[12];   /* 12 because 2^32 -> 4294967296 + I + null */
+
+    sprintf(total_len,"I%d",length);
+    SetCeosField(record,start_byte,total_len,&integer_value);
+}
+
+CeosRecord_t *FindCeosRecord(Link_t *record_list, CeosTypeCode_t typecode, int32 fileid, int32 flavour, int32 subsequence)
+{
+    Link_t *Link;
+    CeosRecord_t *record;
+
+    for( Link = record_list; Link != NULL; Link = Link->next )
+    {
+	record = (CeosRecord_t *)Link->object;
+
+	if( (record->TypeCode.Int32Code == typecode.Int32Code)
+	    && ( ( fileid == -1 ) || ( record->FileId == fileid  ) )
+	    && ( ( flavour == -1 ) || ( record->Flavour == flavour ) )
+	    && ( ( subsequence == -1 ) || ( record->Subsequence == subsequence ) ) )
+	    return record;
+    }
+
+    return NULL;
+}
+
+void SerializeCeosRecordsToFile(Link_t *record_list, FILE *fp)
+{
+    Link_t *list;
+    CeosRecord_t crec;
+    unsigned char *Buffer;
+
+    list = record_list;
+
+    while(list != NULL)
+    {
+	memcpy(&crec,list->object,sizeof(CeosRecord_t));
+	Buffer = crec.Buffer;
+	crec.Buffer = NULL;
+	fwrite(&crec,sizeof(CeosRecord_t),1,fp);
+	fwrite(Buffer,crec.Length,1,fp);
+    }
+}
+
+void SerializeCeosRecordsFromFile(Link_t *record_list, FILE *fp)
+{
+    CeosRecord_t *crec;
+    Link_t *Link;
+
+    while(!feof(fp))
+    {
+	crec = HMalloc(sizeof(CeosRecord_t));
+	fread(crec,sizeof(CeosRecord_t),1,fp);
+	crec->Buffer = HMalloc(crec->Length * sizeof(char) );
+	fread(crec->Buffer,sizeof(char),crec->Length,fp);
+	Link = ceos2CreateLink(crec);
+	AddLink(record_list,Link);
+    }
+}
+
+void CeosUpdateHeaderFromBuffer(CeosRecord_t *record)
+{
+    if(record && record->Buffer)
+    {
+	CeosToNative( &( record->Length ), record->Buffer+__LENGTH_OFF, sizeof(record->Length ), sizeof( record->Length ) );
+	memcpy(&(record->TypeCode.Int32Code),record->Buffer+__TYPE_OFF,sizeof(record->TypeCode.Int32Code));
+	CeosToNative(&(record->Sequence),record->Buffer+__SEQUENCE_OFF,sizeof(record->Sequence ), sizeof( record->Sequence ) );
+    }
+    record->Subsequence = 0;
+}
+
+#ifdef CPL_LSB
+
+void swapbyte(void *dst,void *src,int toswap)
+{
+    int i,e;
+    unsigned char *in = (unsigned char *) src;
+    unsigned char *out = (unsigned char *) dst;
+
+    for(i = 0,e=toswap;i < toswap;i++,e--)
+    {
+	out[i] = in[e-1];
+    }
+}
+
+void NativeToCeos( void *dst, const void *src, const size_t len, const size_t swapunit)
+{
+    int i;
+    int remainder;
+    int units;
+
+
+    remainder = len % swapunit;
+
+    units = len - remainder;
+
+    for(i = 0;i < units; i += swapunit )
+    {
+	swapbyte( ( unsigned char *) dst + i, ( unsigned char * ) src + i, swapunit);
+    }
+
+    if(remainder)
+    {
+	memcpy( ( unsigned char * ) dst + i, ( unsigned char * ) src + i, remainder );
+    }
+}
+
+#endif
diff --git a/Utilities/GDAL/frmts/ceos2/ceos.h b/Utilities/GDAL/frmts/ceos2/ceos.h
new file mode 100644
index 0000000000..c4cf6b4627
--- /dev/null
+++ b/Utilities/GDAL/frmts/ceos2/ceos.h
@@ -0,0 +1,359 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  ASI CEOS Translator
+ * Purpose:  CEOS library prototypes
+ * Author:   Paul Lahaie, pjlahaie@atlsci.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: ceos.h,v $
+ * Revision 1.9  2004/11/01 18:22:07  fwarmerdam
+ * added PALSAR support
+ *
+ * Revision 1.8  2004/08/26 18:30:47  warmerda
+ * added preliminary SIR-C support
+ *
+ * Revision 1.7  2003/12/11 22:11:35  warmerda
+ * clean up recipes when dataset destroyed to avoid memory noise
+ *
+ * Revision 1.6  2003/02/28 18:45:20  gpotts
+ * Prefixed CreateLink with ceos2 since there are multiple symbol conflict on static build under mac. Garrett Potts (gpotts@imagelinks.com)
+ *
+ * Revision 1.5  2001/10/18 17:04:09  warmerda
+ * added hacks to determine record length and pd pixels/line for Telaviv ERS data
+ *
+ * Revision 1.4  2000/04/05 21:40:33  warmerda
+ * fixed CCL_MSB to CPL_MSB
+ *
+ * Revision 1.3  2000/04/04 15:09:03  warmerda
+ * Added calibration info.
+ *
+ * Revision 1.2  2000/03/31 13:33:51  warmerda
+ * ported to gdal
+ *
+ */
+
+
+#ifndef __CEOS_H
+#define __CEOS_H
+
+#include "cpl_conv.h"
+
+CPL_C_START
+
+#define int32 GInt32
+#define TBool int
+#define uchar unsigned char
+
+typedef struct Link_t_struct 
+{
+  struct Link_t_struct	*next;
+  void		*object;
+} Link_t;
+
+#define HMalloc CPLMalloc
+#define HFree CPLFree
+#define HCalloc CPLCalloc
+
+Link_t *ceos2CreateLink( void * pObject );
+Link_t *InsertLink(Link_t *psList, Link_t *psLink);
+void    DestroyList( Link_t *psList );
+Link_t *AddLink( Link_t *psList, Link_t *psLink );
+
+/* Basic CEOS header defs */
+
+#define __SEQUENCE_OFF 0
+#define __TYPE_OFF 4
+#define __LENGTH_OFF 8
+#define __CEOS_HEADER_LENGTH 12
+
+/* Defines for CEOS banding type */
+
+#define __CEOS_IL_PIXEL 1
+#define __CEOS_IL_LINE  2
+#define __CEOS_IL_BAND  3
+
+/* Defines for CEOS data types */
+
+#define __CEOS_TYP_CHAR 1
+#define __CEOS_TYP_UCHAR 2
+#define __CEOS_TYP_SHORT 3
+#define __CEOS_TYP_USHORT 4
+#define __CEOS_TYP_LONG 5
+#define __CEOS_TYP_ULONG 6
+#define __CEOS_TYP_FLOAT 7
+#define __CEOS_TYP_DOUBLE 8
+#define __CEOS_TYP_COMPLEX_CHAR 9
+#define __CEOS_TYP_COMPLEX_UCHAR 10
+#define __CEOS_TYP_COMPLEX_SHORT 11
+#define __CEOS_TYP_COMPLEX_USHORT 12
+#define __CEOS_TYP_COMPLEX_LONG 13
+#define __CEOS_TYP_COMPLEX_ULONG 14
+#define __CEOS_TYP_COMPLEX_FLOAT 15
+#define __CEOS_TYP_CCP_COMPLEX_FLOAT 16 /* COMPRESSED CROSS PRODUCT */
+#define __CEOS_TYP_PALSAR_COMPLEX_SHORT 17 /* PALSAR - treat as COMPLEX SHORT*/
+
+/* Defines for CEOS file names */
+
+#define __CEOS_VOLUME_DIR_FILE 0
+#define __CEOS_LEADER_FILE    1
+#define __CEOS_IMAGRY_OPT_FILE 2
+#define __CEOS_TRAILER_FILE    3
+#define __CEOS_NULL_VOL_FILE   4
+#define __CEOS_ANY_FILE -1
+
+/* Defines for Recipe values */
+
+#define __CEOS_REC_NUMCHANS 1
+#define __CEOS_REC_INTERLEAVE 2
+#define __CEOS_REC_DATATYPE 3
+#define __CEOS_REC_BPR 4
+#define __CEOS_REC_LINES 5
+#define __CEOS_REC_TBP 6
+#define __CEOS_REC_BBP 7
+#define __CEOS_REC_PPL 8
+#define __CEOS_REC_LBP 9
+#define __CEOS_REC_RBP 10
+#define __CEOS_REC_BPP 11
+#define __CEOS_REC_RPL 12
+#define __CEOS_REC_PPR 13
+#define __CEOS_REC_IDS 14
+#define __CEOS_REC_FDL 15
+#define __CEOS_REC_PIXORD 16
+#define __CEOS_REC_LINORD 17
+#define __CEOS_REC_PRODTYPE 18
+#define __CEOS_REC_RECORDSIZE 19
+#define __CEOS_REC_SUFFIX_SIZE 20
+#define __CEOS_REC_PDBPR 21
+
+/* Defines for Recipe Types */
+
+#define __CEOS_REC_TYP_A  1
+#define __CEOS_REC_TYP_B  2
+#define __CEOS_REC_TYP_I  3
+
+/* Defines for SAR Embedded info */
+
+#define __CEOS_SAR_ACQ_YEAR 1
+#define __CEOS_SAR_ACQ_DAY  2
+#define __CEOS_SAR_ACQ_MSEC 4
+#define __CEOS_SAR_TRANS_POL 8
+#define __CEOS_SAR_PULSE_REP 16
+#define __CEOS_SAR_SLANT_FIRST 32
+#define __CEOS_SAR_SLANT_MID 64
+#define __CEOS_SAR_SLANT_LAST 128
+
+/* Maximum size of LUT for Calibration records */
+#define __CEOS_RADAR_MAX_LUT 512
+#define __CEOS_RADAR_FLIP_DATE 19980101
+#define __CEOS_RADAR_FACILITY "CDPF-RSAT"
+
+
+typedef union
+{
+    int32          Int32Code;
+    struct
+    {
+        uchar      Subtype1;
+        uchar      Type;
+        uchar      Subtype2;
+        uchar      Subtype3;
+    } UCharCode;
+} CeosTypeCode_t;
+
+typedef struct
+{
+    int32          Sequence;
+    CeosTypeCode_t TypeCode;
+    int32          Length;
+    int32          Flavour;
+    int32          Subsequence;
+    int32          FileId;
+    uchar *        Buffer;
+} CeosRecord_t;
+
+struct CeosSARImageDesc
+{
+    TBool ImageDescValid;
+    int   NumChannels;
+    int32 ChannelInterleaving;
+    int32 DataType;
+    int   BytesPerRecord;
+    int   Lines;
+    int   TopBorderPixels;
+    int   BottomBorderPixels;
+    int   PixelsPerLine;
+    int   LeftBorderPixels;
+    int   RightBorderPixels;
+    int   BytesPerPixel;
+    int   RecordsPerLine;
+    int   PixelsPerRecord;
+    int   ImageDataStart;
+    int   ImageSuffixData;
+    int   FileDescriptorLength;
+    int32 PixelOrder;
+    int32 LineOrder;
+    int   PixelDataBytesPerRecord;
+};
+
+typedef struct
+{
+    int32          Flavour;
+    int32          Sensor;
+    int32          ProductType;
+    int32          FileNamingConvention;
+    TBool          VolumeDirectoryFile;
+    TBool          SARLeaderFile;
+    TBool          ImagryOptionsFile;
+    TBool          SARTrailerFile;
+    TBool          NullVolumeDirectoryFile;
+
+    struct         CeosSARImageDesc     ImageDesc;
+
+    Link_t *       RecordList;
+} CeosSARVolume_t;
+
+typedef struct 
+{
+    int            ImageDescValue;
+    int            Override;
+    int            FileId;
+    struct         { unsigned char Subtype1,
+                                   Type,
+                                   Subtype2,
+                                   Subtype3;
+                   } TypeCode;
+    int            Offset;
+    int            Length;
+    int            Type;
+} CeosRecipeType_t;
+
+
+typedef struct
+{
+    CeosRecipeType_t *Recipe;
+} CeosSARImageDescRecipe_t;
+
+
+typedef struct
+{
+    int32     ValidFields;
+    TBool     SensorUpdate;
+    int32     AcquisitionYear;
+    int32     AcquisitionDay;
+    int32     AcquisitionMsec;
+    int32     TransmittedPolarization;
+    int32     ReceivedPolarization;
+    int32     PulsRepetitionFrequency;
+    int32     SlantRangeFirstPixel;
+    int32     SlantRangeMidPixel;
+    int32     SlantRangeLastPixel;
+} CeosSAREmbeddedInfo_t;
+
+typedef struct
+{
+    double Slant[ 6 ];
+    double Lut[ 512 ];
+    double SemiMajorAxis;
+    double PlatformLatitude;
+    double CalibrationScale;
+    int NumberOfSamples;
+    int Increment;
+    TBool PossiblyFlipped;
+    
+} CeosRadarCalibration_t;
+
+
+/* Function prototypes */
+
+void InitEmptyCeosRecord(CeosRecord_t *record, int32 sequence, CeosTypeCode_t typecode, int32 length);
+
+void InitCeosRecord(CeosRecord_t *record, uchar *buffer);
+
+void InitCeosRecordWithHeader(CeosRecord_t *record, uchar *header, uchar *buffer);
+
+int DetermineCeosRecordBodyLength(const uchar *header);
+
+void DeleteCeosRecord(CeosRecord_t *record);
+
+void GetCeosRecordStruct(const CeosRecord_t *record, void *struct_ptr);
+
+void PutCeosRecordStruct(CeosRecord_t *record, const void *struct_ptr);
+
+void GetCeosField(CeosRecord_t *record, int32 start_byte, char *format, void *value);
+
+void SetCeosField(CeosRecord_t *record, int32 start_byte, char *format, void *value);
+
+void SetIntCeosField(CeosRecord_t *record, int32 start_byte, int32 length, int32 value);
+
+CeosRecord_t *FindCeosRecord(Link_t *record_list, CeosTypeCode_t typecode, int32 fileid, int32 flavour, int32 subsequence);
+
+void SerializeCeosRecordsToFile(Link_t *record_list, FILE *fp);
+
+void SerializeCeosRecordsFromFile( Link_t *record_list, FILE *fp );
+
+void InitCeosSARVolume( CeosSARVolume_t *volume, int32 file_name_convention );
+
+void GetCeosSARImageDesc( CeosSARVolume_t *volume );
+
+void GetCeosSARImageDescInfo(CeosSARVolume_t *volume, CeosSARImageDescRecipe_t *recipe);
+
+void CalcCeosSARImageFilePosition(CeosSARVolume_t *volume, int channel, int line, int *record, int *file_offset);
+
+int32 GetCeosSARImageData(CeosSARVolume_t *volume, CeosRecord_t *processed_data_record, int channel, int xoff, int xsize, int bufsize, uchar *buffer);
+
+void DetermineCeosSARPixelOrder(CeosSARVolume_t *volume, CeosRecord_t *record );
+
+void GetCeosSAREmbeddedInfo(CeosSARVolume_t *volume, CeosRecord_t *processed_data_record, CeosSAREmbeddedInfo_t *info);
+
+void DeleteCeosSARVolume(CeosSARVolume_t *volume);
+
+void RegisterRecipes(void);
+void FreeRecipes();
+
+void AddRecipe( int ( *function )( CeosSARVolume_t *volume, void *token ),
+		void *token, const char *name );
+
+int CeosDefaultRecipe( CeosSARVolume_t *volume, void *token );
+int ScanSARRecipeFCN( CeosSARVolume_t *volume, void *token );
+
+/* ceoscalib.c function declarations */
+
+CeosRadarCalibration_t *GetCeosRadarCalibration( CeosSARVolume_t *volume );
+
+/* CEOS byte swapping stuff */
+
+#if defined(CPL_MSB)
+#define NativeToCeos(a,b,c,d) memcpy(a,b,c)
+#define CeosToNative(a,b,c,d) memcpy(a,b,c)
+#else
+void NativeToCeos( void *dst, const void *src, const size_t len, const size_t swap_unit);
+#define CeosToNative(a,b,c,d) NativeToCeos(a,b,c,d)
+#endif
+
+/* Recipe defines */
+
+CPL_C_END
+
+#endif
diff --git a/Utilities/GDAL/frmts/ceos2/ceosrecipe.c b/Utilities/GDAL/frmts/ceos2/ceosrecipe.c
new file mode 100644
index 0000000000..587c34b7a0
--- /dev/null
+++ b/Utilities/GDAL/frmts/ceos2/ceosrecipe.c
@@ -0,0 +1,816 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  ASI CEOS Translator
+ * Purpose:  CEOS field layout recipes.
+ * Author:   Paul Lahaie, pjlahaie@atlsci.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: ceosrecipe.c,v $
+ * Revision 1.19  2005/02/24 13:49:35  gwalter
+ * Recognize another ERS 1/2 ceos variant.
+ *
+ * Revision 1.18  2004/11/11 00:16:01  gwalter
+ * Polarmetric->Polarimetric.
+ *
+ * Revision 1.17  2004/11/01 18:22:07  fwarmerdam
+ * added PALSAR support
+ *
+ * Revision 1.16  2004/08/26 18:30:47  warmerda
+ * added preliminary SIR-C support
+ *
+ * Revision 1.15  2004/07/06 15:43:01  gwalter
+ * Updated to extract more metadata and
+ * recognize more sar ceos files.
+ *
+ * Revision 1.14  2004/01/05 20:13:36  warmerda
+ * check guessed record length
+ *
+ * Revision 1.13  2003/12/12 15:30:56  warmerda
+ * Fixed yet another memory leak in the recipe stuff.
+ *
+ * Revision 1.12  2003/12/11 22:11:35  warmerda
+ * clean up recipes when dataset destroyed to avoid memory noise
+ *
+ * Revision 1.11  2003/02/28 18:45:20  gpotts
+ * Prefixed CreateLink with ceos2 since there are multiple symbol conflict on static build under mac. Garrett Potts (gpotts@imagelinks.com)
+ *
+ * Revision 1.10  2001/11/02 17:58:06  warmerda
+ * more hacks to handle ESA LANDSAT dataset from Markus
+ *
+ * Revision 1.9  2001/10/23 14:26:52  warmerda
+ * D-PAF ERS-1: allow CIS4 as alias for CI*4
+ *
+ * Revision 1.8  2001/10/18 17:04:09  warmerda
+ * added hacks to determine record length and pd pixels/line for Telaviv ERS data
+ *
+ * Revision 1.7  2001/08/22 16:54:08  warmerda
+ * fixed use of prefix field.  Zero prefix bytes(ie ERS-SLC) now believed.
+ *
+ * Revision 1.6  2001/07/18 04:51:56  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.5  2001/06/27 20:50:22  warmerda
+ * added UI2 and UI1 data types
+ *
+ * Revision 1.4  2001/02/05 15:46:14  warmerda
+ * added R*4 support
+ *
+ * Revision 1.3  2000/04/04 15:10:32  warmerda
+ * add code to compute BytesPerRecord
+ *
+ * Revision 1.2  2000/03/31 13:34:20  warmerda
+ * ported to gdal
+ *
+ */
+
+#include "ceos.h"
+
+CPL_CVSID("$Id$");
+
+/* Array of Datatypes and their names/values */
+
+typedef struct { 
+    char *String;
+    int Type;
+} CeosStringType_t;
+
+typedef struct { 
+    int (*function)(CeosSARVolume_t *volume, void *token);
+    void *token;
+    const char *name;
+} RecipeFunctionData_t;
+
+
+CeosStringType_t CeosDataType[] = { { "IU1", __CEOS_TYP_UCHAR },
+				    { "IU2", __CEOS_TYP_USHORT },
+				    { "UI1", __CEOS_TYP_UCHAR },
+				    { "UI2", __CEOS_TYP_USHORT },
+				    { "CI*2", __CEOS_TYP_COMPLEX_CHAR },
+				    { "CI*4", __CEOS_TYP_COMPLEX_SHORT },
+				    { "CIS4", __CEOS_TYP_COMPLEX_SHORT },
+				    { "CI*8", __CEOS_TYP_COMPLEX_LONG },
+				    { "C*8", __CEOS_TYP_COMPLEX_FLOAT },
+				    { "R*4", __CEOS_TYP_FLOAT },
+				    { NULL, 0 } };
+
+CeosStringType_t CeosInterleaveType[] = { { "BSQ", __CEOS_IL_BAND },
+					  { " BSQ", __CEOS_IL_BAND },
+					  { "BIL", __CEOS_IL_LINE },
+					  { " BIL", __CEOS_IL_LINE },
+					  { NULL, 0 } };
+
+#define IMAGE_OPT { 63, 192, 18, 18 }
+#define IMAGE_JERS_OPT { 50, 192, 18, 18 }    /* Some JERS data uses this instead of IMAGE_OPT */
+#define PROC_DATA_REC { 50, 11, 18, 20 }
+#define PROC_DATA_REC_ALT { 50, 11, 31, 20 }
+#define PROC_DATA_REC_ALT2 { 50, 11, 31, 50 }  /* Some cases of ERS 1, 2 */
+#define DATA_SET_SUMMARY { 18, 10, 18, 20 }
+
+/* NOTE: This seems to be the generic recipe used for most things */
+CeosRecipeType_t RadarSatRecipe[] =
+{
+    { __CEOS_REC_NUMCHANS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      233, 4, __CEOS_REC_TYP_I }, /* Number of channels */
+    { __CEOS_REC_INTERLEAVE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      269, 4, __CEOS_REC_TYP_A }, /* Interleaving type */
+    { __CEOS_REC_DATATYPE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      429, 4, __CEOS_REC_TYP_A }, /* Data type */
+    { __CEOS_REC_BPR, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      0, 0, __CEOS_REC_TYP_A }, /* For Defeault CEOS, this is done using other vals */
+    { __CEOS_REC_LINES, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      237, 8, __CEOS_REC_TYP_I }, /* How many lines */
+    { __CEOS_REC_TBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      261, 4, __CEOS_REC_TYP_I },
+    { __CEOS_REC_BBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      265, 4, __CEOS_REC_TYP_I }, /* Bottom border pixels */
+    { __CEOS_REC_PPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      249, 8, __CEOS_REC_TYP_I }, /* Pixels per line */
+    { __CEOS_REC_LBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      245, 4, __CEOS_REC_TYP_I }, /* Left Border Pixels */
+    { __CEOS_REC_RBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      257, 4, __CEOS_REC_TYP_I }, /* Isn't available for RadarSAT */
+    { __CEOS_REC_BPP, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      225, 4, __CEOS_REC_TYP_I }, /* Bytes Per Pixel */
+    { __CEOS_REC_RPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      273, 2, __CEOS_REC_TYP_I }, /* Records per line */
+    { __CEOS_REC_PPR, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      0, 0, __CEOS_REC_TYP_I }, /* Pixels Per Record -- need to fill record type */
+    { __CEOS_REC_PDBPR, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      281, 8, __CEOS_REC_TYP_I }, /* pixel data bytes per record */
+    { __CEOS_REC_IDS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      277, 4, __CEOS_REC_TYP_I }, /* Prefix data per record */
+    { __CEOS_REC_FDL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      9, 4, __CEOS_REC_TYP_B }, /* Length of Imagry Options Header */
+    { __CEOS_REC_PIXORD, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      0, 0, __CEOS_REC_TYP_I }, /* Must be calculated */
+    { __CEOS_REC_LINORD, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      0, 0, __CEOS_REC_TYP_I }, /* Must be calculated */
+    { __CEOS_REC_PRODTYPE, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      0, 0, __CEOS_REC_TYP_I },
+
+    { __CEOS_REC_RECORDSIZE, 1, __CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC,
+      9, 4, __CEOS_REC_TYP_B }, /* The processed image record size */
+
+    /* Some ERS-1 products use an alternate data record subtype2. */
+    { __CEOS_REC_RECORDSIZE, 1, __CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC_ALT,
+      9, 4, __CEOS_REC_TYP_B }, /* The processed image record size */
+
+    /* Yet another ERS-1 and ERS-2 alternate data record subtype2. */
+    { __CEOS_REC_RECORDSIZE, 1, __CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC_ALT2,
+      9, 4, __CEOS_REC_TYP_B }, /* The processed image record size */
+
+    { __CEOS_REC_SUFFIX_SIZE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      289, 4, __CEOS_REC_TYP_I }, /* Suffix data per record */
+    { 0, 0, 0, { 0, 0, 0, 0 }, 0, 0, 0 } /* Last record is Zero */
+} ;
+
+
+CeosRecipeType_t JersRecipe[] =
+{
+    { __CEOS_REC_NUMCHANS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      233, 4, __CEOS_REC_TYP_I }, /* Number of channels */
+    { __CEOS_REC_INTERLEAVE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      269, 4, __CEOS_REC_TYP_A }, /* Interleaving type */
+    { __CEOS_REC_DATATYPE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      429, 4, __CEOS_REC_TYP_A }, /* Data type */
+    { __CEOS_REC_BPR, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      0, 0, __CEOS_REC_TYP_A }, /* For Defeault CEOS, this is done using other vals */
+    { __CEOS_REC_LINES, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      237, 8, __CEOS_REC_TYP_I }, /* How many lines */
+    { __CEOS_REC_TBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      261, 4, __CEOS_REC_TYP_I },
+    { __CEOS_REC_BBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      265, 4, __CEOS_REC_TYP_I }, /* Bottom border pixels */
+    { __CEOS_REC_PPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      249, 8, __CEOS_REC_TYP_I }, /* Pixels per line */
+    { __CEOS_REC_LBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      245, 4, __CEOS_REC_TYP_I }, /* Left Border Pixels */
+    { __CEOS_REC_RBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      257, 4, __CEOS_REC_TYP_I }, /* Isn't available for RadarSAT */
+    { __CEOS_REC_BPP, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      225, 4, __CEOS_REC_TYP_I }, /* Bytes Per Pixel */
+    { __CEOS_REC_RPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      273, 2, __CEOS_REC_TYP_I }, /* Records per line */
+    { __CEOS_REC_PPR, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      0, 0, __CEOS_REC_TYP_I }, /* Pixels Per Record -- need to fill record type */
+    { __CEOS_REC_PDBPR, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      281, 8, __CEOS_REC_TYP_I }, /* pixel data bytes per record */
+    { __CEOS_REC_IDS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      277, 4, __CEOS_REC_TYP_I }, /* Prefix data per record */
+    { __CEOS_REC_FDL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      9, 4, __CEOS_REC_TYP_B }, /* Length of Imagry Options Header */
+    { __CEOS_REC_PIXORD, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      0, 0, __CEOS_REC_TYP_I }, /* Must be calculated */
+    { __CEOS_REC_LINORD, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      0, 0, __CEOS_REC_TYP_I }, /* Must be calculated */
+    { __CEOS_REC_PRODTYPE, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      0, 0, __CEOS_REC_TYP_I },
+
+    { __CEOS_REC_RECORDSIZE, 1, __CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC,
+      9, 4, __CEOS_REC_TYP_B }, /* The processed image record size */
+
+    { __CEOS_REC_SUFFIX_SIZE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
+      289, 4, __CEOS_REC_TYP_I }, /* Suffix data per record */
+    { 0, 0, 0, { 0, 0, 0, 0 }, 0, 0, 0 } /* Last record is Zero */
+} ;
+
+CeosRecipeType_t ScanSARRecipe[] =
+{
+    { __CEOS_REC_NUMCHANS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      233, 4, __CEOS_REC_TYP_I }, /* Number of channels */
+    { __CEOS_REC_INTERLEAVE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      269, 4, __CEOS_REC_TYP_A }, /* Interleaving type */
+    { __CEOS_REC_DATATYPE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      429, 4, __CEOS_REC_TYP_A }, /* Data type */
+    { __CEOS_REC_LINES, 1, __CEOS_ANY_FILE, DATA_SET_SUMMARY,
+      325, 8, __CEOS_REC_TYP_I }, /* How many lines */
+    { __CEOS_REC_PPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      249, 8, __CEOS_REC_TYP_I }, /* Pixels per line */
+    { __CEOS_REC_BPP, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      225, 4, __CEOS_REC_TYP_I }, /* Bytes Per Pixel */
+    { __CEOS_REC_RPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      273, 2, __CEOS_REC_TYP_I }, /* Records per line */
+    { __CEOS_REC_IDS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      277, 4, __CEOS_REC_TYP_I }, /* Prefix data per record */
+    { __CEOS_REC_FDL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      9, 4, __CEOS_REC_TYP_B }, /* Length of Imagry Options Header */
+    { __CEOS_REC_RECORDSIZE, 1, __CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC,
+      9, 4, __CEOS_REC_TYP_B }, /* The processed image record size */
+    { __CEOS_REC_SUFFIX_SIZE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      289, 4, __CEOS_REC_TYP_I }, /* Suffix data per record */
+    { 0, 0, 0, { 0, 0, 0, 0 }, 0, 0, 0 } /* Last record is Zero */
+} ;
+
+CeosRecipeType_t SIRCRecipe[] =
+{
+    { __CEOS_REC_NUMCHANS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      233, 4, __CEOS_REC_TYP_I }, /* Number of channels */
+    { __CEOS_REC_INTERLEAVE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      269, 4, __CEOS_REC_TYP_A }, /* Interleaving type */
+    { __CEOS_REC_DATATYPE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      429, 4, __CEOS_REC_TYP_A }, /* Data type */
+    { __CEOS_REC_LINES, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      237, 8, __CEOS_REC_TYP_I }, /* How many lines */
+    { __CEOS_REC_TBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      261, 4, __CEOS_REC_TYP_I },
+    { __CEOS_REC_BBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      265, 4, __CEOS_REC_TYP_I }, /* Bottom border pixels */
+    { __CEOS_REC_PPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      249, 8, __CEOS_REC_TYP_I }, /* Pixels per line */
+    { __CEOS_REC_LBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      245, 4, __CEOS_REC_TYP_I }, /* Left Border Pixels */
+    { __CEOS_REC_RBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      257, 4, __CEOS_REC_TYP_I }, /* Right Border Pixels */
+    { __CEOS_REC_BPP, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      225, 4, __CEOS_REC_TYP_I }, /* Bytes Per Pixel */
+    { __CEOS_REC_RPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      273, 2, __CEOS_REC_TYP_I }, /* Records per line */
+    { __CEOS_REC_IDS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      277, 4, __CEOS_REC_TYP_I }, /* Prefix data per record */
+    { __CEOS_REC_FDL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      9, 4, __CEOS_REC_TYP_B }, /* Length of Imagry Options Header */
+    { __CEOS_REC_RECORDSIZE, 1, __CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC,
+      9, 4, __CEOS_REC_TYP_B }, /* The processed image record size */
+    { __CEOS_REC_SUFFIX_SIZE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
+      289, 4, __CEOS_REC_TYP_I }, /* Suffix data per record */
+
+    { 0, 0, 0, { 0, 0, 0, 0 }, 0, 0, 0 } /* Last record is Zero */
+} ;
+
+#undef PROC_DATA_REC
+
+static void ExtractInt( CeosRecord_t *record, int type, unsigned int offset, unsigned int length, int *value );
+
+static char *ExtractString( CeosRecord_t *record, unsigned int offset, unsigned int length, char *string );
+
+static int GetCeosStringType(const CeosStringType_t *CeosType, const char *string);
+
+static int SIRCRecipeFCN( CeosSARVolume_t *volume, void *token );
+static int PALSARRecipeFCN( CeosSARVolume_t *volume, void *token );
+
+Link_t *RecipeFunctions = NULL;
+
+void RegisterRecipes( void )
+{
+
+    AddRecipe( SIRCRecipeFCN, SIRCRecipe, "SIR-C" );
+    AddRecipe( ScanSARRecipeFCN, ScanSARRecipe, "ScanSAR" );
+    AddRecipe( CeosDefaultRecipe, RadarSatRecipe, "RadarSat" );
+    AddRecipe( CeosDefaultRecipe, JersRecipe, "Jers" );
+    AddRecipe( PALSARRecipeFCN, RadarSatRecipe, "PALSAR-ALOS" );
+    /*  AddRecipe( CeosDefaultRecipe, AtlantisRecipe ); */
+}
+
+void FreeRecipes( void )
+
+{
+    Link_t *link;
+
+    for( link = RecipeFunctions; link != NULL; link = link->next )
+        HFree( link->object );
+
+    DestroyList( RecipeFunctions );
+    RecipeFunctions = NULL;
+}
+
+void AddRecipe( int (*function)(CeosSARVolume_t *volume,
+				void *token),
+		void *token,
+                const char *name )
+{
+
+    RecipeFunctionData_t *TempData;
+
+    Link_t *Link;
+
+    TempData = HMalloc( sizeof( RecipeFunctionData_t ) );
+
+    TempData->function = function;
+    TempData->token = token;
+    TempData->name = name;
+
+    Link = ceos2CreateLink( TempData );
+
+    if( RecipeFunctions == NULL)
+    {
+	RecipeFunctions = Link;
+    } else {
+	RecipeFunctions = InsertLink( RecipeFunctions, Link );
+    }
+}
+
+int CeosDefaultRecipe( CeosSARVolume_t *volume, void *token )
+{
+    CeosRecipeType_t *recipe;
+    CeosRecord_t *record;
+    CeosTypeCode_t TypeCode;
+    struct CeosSARImageDesc *ImageDesc = &(volume->ImageDesc);
+    char temp_str[1024];
+    int i, temp_int;
+    
+#define DoExtractInt(a) ExtractInt( record, recipe[i].Type, recipe[i].Offset, recipe[i].Length, &a)
+
+    if(token == NULL)
+    {
+	return 0;
+    }
+
+    memset(ImageDesc, 0, sizeof( struct CeosSARImageDesc ) );
+
+/*    temp_imagerecipe = (CeosSARImageDescRecipe_t *) token;
+    recipe = temp_imagerecipe->Recipe; */
+ 
+    recipe = token;
+
+    for(i = 0; recipe[i].ImageDescValue != 0; i++ )
+    {
+	if(recipe[i].Override)
+	{
+	    TypeCode.UCharCode.Subtype1 = recipe[i].TypeCode.Subtype1;
+	    TypeCode.UCharCode.Type = recipe[i].TypeCode.Type;
+	    TypeCode.UCharCode.Subtype2 = recipe[i].TypeCode.Subtype2;
+	    TypeCode.UCharCode.Subtype3 = recipe[i].TypeCode.Subtype3;
+
+	    record = FindCeosRecord( volume->RecordList, TypeCode, recipe[i].FileId, -1, -1 );
+
+	    if(record == NULL)
+	    {
+		temp_int = 0;
+	    } else {
+
+		switch( recipe[i].ImageDescValue )
+		{
+		case __CEOS_REC_NUMCHANS:
+		   DoExtractInt( ImageDesc->NumChannels );
+		   break;
+		case __CEOS_REC_LINES:
+		    DoExtractInt( ImageDesc->Lines );
+		    break;
+		case __CEOS_REC_BPP:
+		    DoExtractInt( ImageDesc->BytesPerPixel );
+		    break;
+		case __CEOS_REC_RPL:
+		    DoExtractInt( ImageDesc->RecordsPerLine );
+		    break;
+		case __CEOS_REC_PDBPR:
+		    DoExtractInt( ImageDesc->PixelDataBytesPerRecord );
+		    break;
+		case __CEOS_REC_FDL:
+		    DoExtractInt( ImageDesc->FileDescriptorLength );
+		    break;
+		case __CEOS_REC_IDS:
+		    DoExtractInt( ImageDesc->ImageDataStart );
+                    /* 
+                    ** This is really reading the quantity of prefix data
+                    ** per data record.  We want the offset from the very
+                    ** beginning of the record to the data, so we add another
+                    ** 12 to that.  I think some products incorrectly indicate
+                    ** 192 (prefix+12) instead of 180 so if we see 192 assume
+                    ** the 12 bytes of record start data has already been
+                    ** added.  Frank Warmerdam.
+                    */
+		    if( ImageDesc->ImageDataStart != 192 )
+                        ImageDesc->ImageDataStart += 12;
+		    break;
+		case __CEOS_REC_SUFFIX_SIZE:
+		    DoExtractInt( ImageDesc->ImageSuffixData );
+		    break;
+		case __CEOS_REC_RECORDSIZE:
+		    DoExtractInt( ImageDesc->BytesPerRecord );
+		    break;
+		case __CEOS_REC_PPL:
+		    DoExtractInt( ImageDesc->PixelsPerLine );
+		    break;
+		case __CEOS_REC_TBP:
+		    DoExtractInt( ImageDesc->TopBorderPixels );
+		    break;
+		case __CEOS_REC_BBP:
+		    DoExtractInt( ImageDesc->BottomBorderPixels );
+		    break;
+		case __CEOS_REC_LBP:
+		    DoExtractInt( ImageDesc->LeftBorderPixels );
+		    break;
+		case __CEOS_REC_RBP:
+		    DoExtractInt( ImageDesc->RightBorderPixels );
+		    break;
+		case __CEOS_REC_INTERLEAVE:
+		    ExtractString( record, recipe[i].Offset, recipe[i].Length, temp_str );
+
+		    ImageDesc->ChannelInterleaving = GetCeosStringType( CeosInterleaveType, temp_str );
+		    break;
+		case __CEOS_REC_DATATYPE:
+		    ExtractString( record, recipe[i].Offset, recipe[i].Length, temp_str );
+
+		    ImageDesc->DataType = GetCeosStringType( CeosDataType, temp_str );
+		    break;
+		}
+		
+	    }
+	}
+    }
+
+    /* Some files (Telaviv) don't record the number of pixel groups per line.
+     * Try to derive it from the size of a data group, and the number of
+     * bytes of pixel data if necessary.
+     */
+
+    if( ImageDesc->PixelsPerLine == 0 
+        && ImageDesc->PixelDataBytesPerRecord != 0 
+        && ImageDesc->BytesPerPixel != 0 )
+    {
+        ImageDesc->PixelsPerLine = 
+            ImageDesc->PixelDataBytesPerRecord / ImageDesc->BytesPerPixel;
+        CPLDebug( "SAR_CEOS", "Guessing PixelPerLine to be %d\n", 
+                  ImageDesc->PixelsPerLine );
+    }
+
+    /* Some files don't have the BytesPerRecord stuff, so we calculate it if possible */
+
+    if( ImageDesc->BytesPerRecord == 0 && ImageDesc->RecordsPerLine == 1 &&
+	ImageDesc->PixelsPerLine > 0 && ImageDesc->BytesPerPixel > 0 )
+    {
+        CeosRecord_t *img_rec;
+
+        ImageDesc->BytesPerRecord = ImageDesc->PixelsPerLine *
+	  ImageDesc->BytesPerPixel + ImageDesc->ImageDataStart +
+	  ImageDesc->ImageSuffixData ;
+
+        TypeCode.UCharCode.Subtype1 = 0xed;
+        TypeCode.UCharCode.Type = 0xed;
+        TypeCode.UCharCode.Subtype2 = 0x12;
+        TypeCode.UCharCode.Subtype3 = 0x12;
+        
+        img_rec = FindCeosRecord( volume->RecordList, TypeCode, 
+                                  __CEOS_IMAGRY_OPT_FILE, -1, -1 );
+        if( img_rec == NULL )
+        {
+            CPLDebug( "SAR_CEOS", 
+                      "Unable to find imagery rec to check record length." );
+            return 0;
+        }
+
+        if( img_rec->Length != ImageDesc->BytesPerRecord )
+        {
+            CPLDebug( "SAR_CEOS", 
+                      "Guessed record length (%d) did not match\n"
+                      "actual imagery record length (%d), recipe fails.", 
+                      ImageDesc->BytesPerRecord, img_rec->Length );
+            return 0;
+        }
+    }
+    
+    if( ImageDesc->PixelsPerRecord == 0 && 
+	ImageDesc->BytesPerRecord != 0 && ImageDesc->BytesPerPixel != 0 )
+    {
+	ImageDesc->PixelsPerRecord = ( ( ImageDesc->BytesPerRecord -
+					 (ImageDesc->ImageSuffixData +
+					  ImageDesc->ImageDataStart )) /
+				       ImageDesc->BytesPerPixel );
+
+	if(ImageDesc->PixelsPerRecord > ImageDesc->PixelsPerLine)
+	    ImageDesc->PixelsPerRecord = ImageDesc->PixelsPerLine;
+    }
+
+    /* If we didn't get a data type, try guessing. */
+    if( ImageDesc->DataType == 0
+        && ImageDesc->BytesPerPixel != 0
+        && ImageDesc->NumChannels != 0 )
+    {
+        int  nDataTypeSize = ImageDesc->BytesPerPixel / ImageDesc->NumChannels;
+
+        if( nDataTypeSize == 1 )
+            ImageDesc->DataType = __CEOS_TYP_UCHAR;
+        else if( nDataTypeSize == 2 )
+            ImageDesc->DataType = __CEOS_TYP_USHORT;
+    }
+
+    /* Sanity checking */
+    
+    if( ImageDesc->PixelsPerLine == 0 || ImageDesc->Lines == 0 ||
+	ImageDesc->RecordsPerLine == 0 || ImageDesc->ImageDataStart == 0 ||
+	ImageDesc->FileDescriptorLength == 0 || ImageDesc->DataType == 0 ||
+	ImageDesc->NumChannels == 0 || ImageDesc->BytesPerPixel == 0 ||
+	ImageDesc->ChannelInterleaving == 0 || ImageDesc->BytesPerRecord == 0)
+    {
+	return 0;
+    } else {
+	
+	ImageDesc->ImageDescValid = TRUE;
+	return 1;
+    }
+}
+
+int ScanSARRecipeFCN( CeosSARVolume_t *volume, void *token )
+{
+    struct CeosSARImageDesc *ImageDesc = &(volume->ImageDesc);
+
+    memset( ImageDesc, 0, sizeof( struct CeosSARImageDesc ) );
+
+    if( CeosDefaultRecipe( volume, token ) )
+    {
+	ImageDesc->Lines *= 2;
+	return 1;
+    }
+
+    return 0;
+}    
+
+static int SIRCRecipeFCN( CeosSARVolume_t *volume, void *token )
+{
+    struct CeosSARImageDesc *ImageDesc = &(volume->ImageDesc);
+    CeosTypeCode_t TypeCode;
+    CeosRecord_t *record;
+    char szSARDataFormat[29];
+
+    memset( ImageDesc, 0, sizeof( struct CeosSARImageDesc ) );
+
+/* -------------------------------------------------------------------- */
+/*      First, we need to check if the "SAR Data Format Type            */
+/*      identifier" is set to "COMPRESSED CROSS-PRODUCTS" which is      */
+/*      pretty idiosyncratic to SIRC products.  It might also appear    */
+/*      for some other similarly encoded Polarimetric data I suppose.    */
+/* -------------------------------------------------------------------- */
+    /* IMAGE_OPT */
+    TypeCode.UCharCode.Subtype1 = 63;
+    TypeCode.UCharCode.Type     = 192;
+    TypeCode.UCharCode.Subtype2 = 18;
+    TypeCode.UCharCode.Subtype3 = 18;
+
+    record = FindCeosRecord( volume->RecordList, TypeCode, 
+                             __CEOS_IMAGRY_OPT_FILE, -1, -1 );
+    if( record == NULL )
+        return 0;
+
+    ExtractString( record, 401, 28, szSARDataFormat );
+    if( !EQUALN( szSARDataFormat, "COMPRESSED CROSS-PRODUCTS", 25) )
+        return 0;
+
+/* -------------------------------------------------------------------- */
+/*      Apply normal handling...                                        */
+/* -------------------------------------------------------------------- */
+    CeosDefaultRecipe( volume, token );
+
+/* -------------------------------------------------------------------- */
+/*      Make sure this looks like the SIRC product we are expecting.    */
+/* -------------------------------------------------------------------- */
+    if( ImageDesc->BytesPerPixel != 10 )
+        return 0;
+
+/* -------------------------------------------------------------------- */
+/*      Then fix up a few values.                                       */
+/* -------------------------------------------------------------------- */
+    /* It seems the bytes of pixel data per record is just wrong.  Fix. */
+    ImageDesc->PixelDataBytesPerRecord = 
+        ImageDesc->BytesPerPixel * ImageDesc->PixelsPerLine;
+    
+    ImageDesc->DataType = __CEOS_TYP_CCP_COMPLEX_FLOAT;
+
+/* -------------------------------------------------------------------- */
+/*      Sanity checking                                                 */
+/* -------------------------------------------------------------------- */
+    if( ImageDesc->PixelsPerLine == 0 || ImageDesc->Lines == 0 ||
+	ImageDesc->RecordsPerLine == 0 || ImageDesc->ImageDataStart == 0 ||
+	ImageDesc->FileDescriptorLength == 0 || ImageDesc->DataType == 0 ||
+	ImageDesc->NumChannels == 0 || ImageDesc->BytesPerPixel == 0 ||
+	ImageDesc->ChannelInterleaving == 0 || ImageDesc->BytesPerRecord == 0)
+    {
+	return 0;
+    } else {
+	
+	ImageDesc->ImageDescValid = TRUE;
+	return 1;
+    }
+}    
+
+static int PALSARRecipeFCN( CeosSARVolume_t *volume, void *token )
+{
+    struct CeosSARImageDesc *ImageDesc = &(volume->ImageDesc);
+    CeosTypeCode_t TypeCode;
+    CeosRecord_t *record;
+    char szSARDataFormat[29], szProduct[32];
+
+    memset( ImageDesc, 0, sizeof( struct CeosSARImageDesc ) );
+
+/* -------------------------------------------------------------------- */
+/*      First, we need to check if the "SAR Data Format Type            */
+/*      identifier" is set to "COMPRESSED CROSS-PRODUCTS" which is      */
+/*      pretty idiosyncratic to SIRC products.  It might also appear    */
+/*      for some other similarly encoded Polarimetric data I suppose.    */
+/* -------------------------------------------------------------------- */
+    /* IMAGE_OPT */
+    TypeCode.UCharCode.Subtype1 = 63;
+    TypeCode.UCharCode.Type     = 192;
+    TypeCode.UCharCode.Subtype2 = 18;
+    TypeCode.UCharCode.Subtype3 = 18;
+
+    record = FindCeosRecord( volume->RecordList, TypeCode, 
+                             __CEOS_IMAGRY_OPT_FILE, -1, -1 );
+    if( record == NULL )
+        return 0;
+
+    ExtractString( record, 401, 28, szSARDataFormat );
+    if( !EQUALN( szSARDataFormat, "INTEGER*18                 ", 25) )
+        return 0;
+
+    ExtractString( record, 49, 16, szProduct );
+    if( !EQUALN( szProduct, "ALOS-", 5 ) )
+        return 0;
+
+/* -------------------------------------------------------------------- */
+/*      Apply normal handling...                                        */
+/* -------------------------------------------------------------------- */
+    CeosDefaultRecipe( volume, token );
+
+/* -------------------------------------------------------------------- */
+/*      Make sure this looks like the SIRC product we are expecting.    */
+/* -------------------------------------------------------------------- */
+    if( ImageDesc->BytesPerPixel != 18 )
+        return 0;
+
+/* -------------------------------------------------------------------- */
+/*      Then fix up a few values.                                       */
+/* -------------------------------------------------------------------- */
+    ImageDesc->DataType = __CEOS_TYP_PALSAR_COMPLEX_SHORT;
+    ImageDesc->NumChannels = 6;
+
+/* -------------------------------------------------------------------- */
+/*      Sanity checking                                                 */
+/* -------------------------------------------------------------------- */
+    if( ImageDesc->PixelsPerLine == 0 || ImageDesc->Lines == 0 ||
+	ImageDesc->RecordsPerLine == 0 || ImageDesc->ImageDataStart == 0 ||
+	ImageDesc->FileDescriptorLength == 0 || ImageDesc->DataType == 0 ||
+	ImageDesc->NumChannels == 0 || ImageDesc->BytesPerPixel == 0 ||
+	ImageDesc->ChannelInterleaving == 0 || ImageDesc->BytesPerRecord == 0)
+    {
+	return 0;
+    } else {
+	
+	ImageDesc->ImageDescValid = TRUE;
+	return 1;
+    }
+}    
+
+void GetCeosSARImageDesc( CeosSARVolume_t *volume )
+{
+    Link_t *link;
+    RecipeFunctionData_t *rec_data;
+    int (*function)(CeosSARVolume_t *volume, void *token);
+
+    if( RecipeFunctions == NULL )
+    {
+	RegisterRecipes();
+    }
+
+    if(RecipeFunctions == NULL )
+    {
+	return ;
+    }
+
+    for(link = RecipeFunctions; link != NULL; link = link->next)
+    {
+	if(link->object)
+	{
+	    rec_data = link->object;
+            function = rec_data->function;
+	    if(( *function )( volume, rec_data->token ) )
+            {
+                CPLDebug( "CEOS", "Using recipe '%s'.", 
+                          rec_data->name );
+		return;
+            }
+	}
+    }
+
+    return ;
+
+}
+
+
+static void ExtractInt(CeosRecord_t *record, int type, unsigned int offset, unsigned int length, int *value)
+{
+    void *buffer;
+    char format[32];
+
+    buffer = HMalloc( length + 1 );
+
+    switch(type)
+    {
+    case __CEOS_REC_TYP_A:
+	sprintf( format, "A%u", length );
+	GetCeosField( record, offset, format,  buffer );
+	*value = atoi( buffer );
+	break;
+    case __CEOS_REC_TYP_B:
+	sprintf( format, "B%u", length );
+#ifdef notdef
+	GetCeosField( record, offset, format, buffer );
+	if( length <= 4 )
+	    CeosToNative( value, buffer, length, length );
+	else
+	    *value = 0;
+#else
+	GetCeosField( record, offset, format, value );
+#endif
+	break;
+    case __CEOS_REC_TYP_I:
+	sprintf( format, "I%u", length );
+	GetCeosField( record, offset, format, value );
+	break;
+    }
+
+    HFree( buffer );
+
+}
+
+static char *ExtractString( CeosRecord_t *record, unsigned int offset, unsigned int length, char *string )
+{
+    char format[12];
+
+    if(string == NULL)
+    {
+	string = HMalloc( length + 1 );
+    }
+
+    sprintf( format, "A%u", length );
+
+    GetCeosField( record, offset, format, string );
+
+    return string;
+}
+
+static int GetCeosStringType(const CeosStringType_t *CeosStringType, const char *string)
+{
+    int i;
+
+    for(i = 0;CeosStringType[i].String != NULL;i++)
+    {
+	if(strncmp(CeosStringType[i].String ,string, strlen( CeosStringType[i].String ) ) == 0 )
+	{
+	    return CeosStringType[i].Type;
+	}
+    }
+
+    return 0;
+}
diff --git a/Utilities/GDAL/frmts/ceos2/ceossar.c b/Utilities/GDAL/frmts/ceos2/ceossar.c
new file mode 100644
index 0000000000..d82a0a498d
--- /dev/null
+++ b/Utilities/GDAL/frmts/ceos2/ceossar.c
@@ -0,0 +1,144 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  ASI CEOS Translator
+ * Purpose:  Functions related to CeosSARVolume_t.
+ * Author:   Paul Lahaie, pjlahaie@atlsci.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: ceossar.c,v $
+ * Revision 1.4  2001/07/18 04:51:56  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.3  2000/09/25 21:17:04  warmerda
+ * Avoid warnings.
+ *
+ * Revision 1.2  2000/03/31 13:34:43  warmerda
+ * ported to GDAL, replace headers
+ *
+ */
+
+#include "ceos.h"
+
+CPL_CVSID("$Id$");
+
+extern Link_t *RecipeFunctions;
+
+void InitCeosSARVolume(CeosSARVolume_t *volume, int32 file_name_convention)
+{
+    volume->Flavour = \
+	volume->Sensor = \
+	volume->ProductType = 0;
+
+    volume->FileNamingConvention = file_name_convention ;
+
+    volume->VolumeDirectoryFile =
+	volume->SARLeaderFile =
+        volume->SARTrailerFile =
+	volume->NullVolumeDirectoryFile =
+	volume->ImageDesc.ImageDescValid = FALSE;
+
+    volume->RecordList = NULL;
+}
+
+
+void CalcCeosSARImageFilePosition(CeosSARVolume_t *volume, int channel, int line, int *record, int *file_offset)
+{
+    struct CeosSARImageDesc *ImageDesc;
+    int TotalRecords=0, TotalBytes=0;
+
+    if(record)
+	*record = 0;
+    if(file_offset)
+	*file_offset = 0;
+
+    if( volume )
+    {
+	if( volume->ImageDesc.ImageDescValid )
+	{
+	    ImageDesc = &( volume->ImageDesc );
+
+	    switch( ImageDesc->ChannelInterleaving )
+	    {
+	    case __CEOS_IL_PIXEL:
+		TotalRecords = (line - 1) * ImageDesc->RecordsPerLine;
+		TotalBytes = (TotalRecords) * ( ImageDesc->BytesPerRecord );
+		break;
+	    case __CEOS_IL_LINE:
+		TotalRecords = (ImageDesc->NumChannels * (line - 1) + 
+				(channel - 1)) * ImageDesc->RecordsPerLine;
+		TotalBytes = (TotalRecords) * ( ImageDesc->BytesPerRecord ) ;
+		break;
+	    case __CEOS_IL_BAND:
+		TotalRecords = (((channel - 1) * ImageDesc->Lines) * 
+				ImageDesc->RecordsPerLine) +
+				(line - 1) * ImageDesc->RecordsPerLine;
+
+		TotalBytes = (TotalRecords) * ( ImageDesc->BytesPerRecord );
+		break;
+	    }
+	    if(file_offset)
+		*file_offset = ImageDesc->FileDescriptorLength + TotalBytes;
+	    if(record)
+		*record = TotalRecords + 1;
+	}
+    }
+}
+
+int32 GetCeosSARImageData(CeosSARVolume_t *volume, CeosRecord_t *processed_data_record, int channel, int xoff, int xsize, int bufsize, uchar *buffer)
+{
+    return 0;
+}
+
+void DetermineCeosSARPixelOrder( CeosSARVolume_t *volume, CeosRecord_t *record )
+{
+
+}
+
+void GetCeosSAREmbeddedInfo(CeosSARVolume_t *volume, CeosRecord_t *processed_data_record, CeosSAREmbeddedInfo_t *info)
+{
+}
+
+void DeleteCeosSARVolume(CeosSARVolume_t *volume)
+{
+    Link_t *Links;
+    
+    if( volume )
+    {
+	if( volume->RecordList )
+	{
+	    for(Links = volume->RecordList; Links != NULL; Links = Links->next)
+	    {
+		if(Links->object)
+		{
+		    DeleteCeosRecord( Links->object );
+		    Links->object = NULL;
+		}
+	    }
+	    DestroyList( volume->RecordList );
+	}
+	HFree( volume );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/ceos2/link.c b/Utilities/GDAL/frmts/ceos2/link.c
new file mode 100644
index 0000000000..6e35e572d3
--- /dev/null
+++ b/Utilities/GDAL/frmts/ceos2/link.c
@@ -0,0 +1,106 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  ASI CEOS Translator
+ * Purpose:  Link list function replacements.
+ * Author:   Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: link.c,v $
+ * Revision 1.3  2003/02/28 18:45:20  gpotts
+ * Prefixed CreateLink with ceos2 since there are multiple symbol conflict on static build under mac. Garrett Potts (gpotts@imagelinks.com)
+ *
+ * Revision 1.2  2001/07/18 04:51:56  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.1  2000/03/31 13:35:03  warmerda
+ * New
+ *
+ */
+
+#include "ceos.h"
+
+CPL_CVSID("$Id$");
+
+
+/************************************************************************/
+/*                             ceos2CreateLink()                             */
+/************************************************************************/
+
+Link_t *ceos2CreateLink( void *pObject )
+
+{
+    Link_t	*psLink = (Link_t *) CPLCalloc(sizeof(Link_t),1);
+
+    psLink->object = pObject;
+
+    return psLink;
+}
+
+/************************************************************************/
+/*                            DestroyList()                             */
+/************************************************************************/
+
+void DestroyList( Link_t * psList )
+
+{
+    while( psList != NULL )
+    {
+        Link_t	*psNext = psList->next;
+
+        CPLFree( psList );
+        psList = psNext;
+    }
+}
+
+/************************************************************************/
+/*                             InsertLink()                             */
+/************************************************************************/
+
+Link_t *InsertLink( Link_t *psList, Link_t *psLink )
+
+{
+    psLink->next = psList;
+
+    return psLink;
+}
+
+/************************************************************************/
+/*                              AddLink()                               */
+/************************************************************************/
+
+Link_t *AddLink( Link_t *psList, Link_t *psLink )
+
+{
+    Link_t	*psNode;
+
+    if( psList == NULL )
+        return psLink;
+
+    for( psNode = psList; psNode->next != NULL; psNode = psNode->next ) {}
+
+    psNode->next = psLink;
+
+    return psList;
+}
diff --git a/Utilities/GDAL/frmts/ceos2/makefile.vc b/Utilities/GDAL/frmts/ceos2/makefile.vc
new file mode 100644
index 0000000000..1a1d8245ad
--- /dev/null
+++ b/Utilities/GDAL/frmts/ceos2/makefile.vc
@@ -0,0 +1,16 @@
+
+OBJ	=	sar_ceosdataset.obj \
+		ceosrecipe.obj ceossar.obj ceos.obj link.obj 
+
+GDAL_ROOT	=	..\..
+
+EXTRAFLAGS = 	-I..\raw
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/ceos2/sar_ceosdataset.cpp b/Utilities/GDAL/frmts/ceos2/sar_ceosdataset.cpp
new file mode 100644
index 0000000000..5765cc25ab
--- /dev/null
+++ b/Utilities/GDAL/frmts/ceos2/sar_ceosdataset.cpp
@@ -0,0 +1,2106 @@
+/******************************************************************************
+ * $Id: sar_ceosdataset.cpp,v 1.43 2005/10/12 19:52:56 fwarmerdam Exp $
+ *
+ * Project:  ASI CEOS Translator
+ * Purpose:  GDALDataset driver for CEOS translator.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Atlantis Scientific Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: sar_ceosdataset.cpp,v $
+ * Revision 1.43  2005/10/12 19:52:56  fwarmerdam
+ * Use SetMetadataItem() for CEOS_BEAM_TYPE.
+ *
+ * Revision 1.42  2005/05/05 14:01:36  fwarmerdam
+ * PAM Enable
+ *
+ * Revision 1.41  2005/02/17 02:29:03  gwalter
+ * Check for more extensions.
+ *
+ * Revision 1.40  2004/11/15 19:56:06  gwalter
+ * Updated PALSAR band reading.
+ *
+ * Revision 1.39  2004/11/11 21:25:37  fwarmerdam
+ * Return PALSAR in proper covariance matrix format.
+ *
+ * Revision 1.38  2004/11/11 00:16:01  gwalter
+ * Polarmetric->Polarimetric.
+ *
+ * Revision 1.37  2004/11/01 18:22:07  fwarmerdam
+ * added PALSAR support
+ *
+ * Revision 1.36  2004/08/26 18:55:27  warmerda
+ * Optimized SIRC decoding to avoid redoing pow() alot.
+ *
+ * Revision 1.35  2004/08/26 18:30:47  warmerda
+ * added preliminary SIR-C support
+ *
+ * Revision 1.34  2004/07/06 15:43:01  gwalter
+ * Updated to extract more metadata and
+ * recognize more sar ceos files.
+ *
+ * Revision 1.33  2003/12/11 22:11:35  warmerda
+ * clean up recipes when dataset destroyed to avoid memory noise
+ *
+ * Revision 1.32  2003/12/10 18:02:21  warmerda
+ * fixed some minor memory leaks
+ *
+ * Revision 1.31  2003/07/08 21:28:22  warmerda
+ * avoid warnings
+ *
+ * Revision 1.30  2003/02/28 18:45:20  gpotts
+ * Prefixed CreateLink with ceos2 since there are multiple symbol conflict on static build under mac. Garrett Potts (gpotts@imagelinks.com)
+ *
+ * Revision 1.29  2002/09/04 06:50:36  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.28  2002/07/18 20:16:11  gwalter
+ * Fix to recognize esrin pri's gcps.
+ *
+ * Revision 1.27  2002/06/12 21:12:24  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.26  2002/06/07 14:19:07  warmerda
+ * avoid paths in include directives
+ *
+ * Revision 1.25  2002/04/26 14:52:50  warmerda
+ * added EscapedRecord for metadata
+ *
+ * Revision 1.24  2002/04/16 17:51:08  warmerda
+ * Avoid unitialized variable warnings.
+ *
+ * Revision 1.23  2002/04/03 22:12:49  warmerda
+ * Added special metadata access to raw record data
+ */
+
+#include "ceos.h"
+#include "gdal_priv.h"
+#include "rawdataset.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: sar_ceosdataset.cpp,v 1.43 2005/10/12 19:52:56 fwarmerdam Exp $");
+
+CPL_C_START
+void	GDALRegister_SAR_CEOS(void);
+CPL_C_END
+
+static GInt16 CastToGInt16(float val);
+
+static GInt16 CastToGInt16(float val)
+{
+    float temp;
+
+    temp = val;
+
+    if ( temp < -32768.0 )
+        temp = -32768.0;
+
+    if ( temp > 32767 )
+        temp = 32767.0;
+
+    return (GInt16) temp;    
+}
+
+char *CeosExtension[][6] = { 
+{ "vol", "led", "img", "trl", "nul", "ext" },
+{ "vol", "lea", "img", "trl", "nul", "ext" },
+{ "vol", "led", "img", "tra", "nul", "ext" },
+{ "vol", "lea", "img", "tra", "nul", "ext" },
+{ "vdf", "slf", "sdf", "stf", "nvd", "ext" },
+
+{ "vdf", "ldr", "img", "tra", "nul", "ext2" },
+
+/* Jers from Japan- not sure if this is generalized as much as it could be */
+{ "VOLD", "Sarl_01", "Imop_%02d", "Sart_01", "NULL", "base" },
+
+
+/* Radarsat: basename, not extension */
+{ "vdf_dat", "lea_%02d", "dat_%02d", "tra_%02d", "nul_vdf", "base" },
+
+/* Ers-1: basename, not extension */
+{ "vdf_dat", "lea_%02d", "dat_%02d", "tra_%02d", "nul_dat", "base" },
+
+/* Ers-2 from Telaviv */
+{ "volume", "leader", "image", "trailer", "nul_dat", "whole" },
+
+/* Ers-1 from D-PAF */
+{ "VDF", "LF", "SLC", "", "", "ext" },
+
+/* end marker */
+{ NULL, NULL, NULL, NULL, NULL, NULL } 
+};
+
+static int 
+ProcessData( FILE *fp, int fileid, CeosSARVolume_t *sar, int max_records, 
+             int max_bytes );
+
+
+static CeosTypeCode_t QuadToTC( int a, int b, int c, int d )
+{
+    CeosTypeCode_t   abcd;
+
+    abcd.UCharCode.Subtype1 = (unsigned char) a;
+    abcd.UCharCode.Type = (unsigned char) b;
+    abcd.UCharCode.Subtype2 = (unsigned char) c;
+    abcd.UCharCode.Subtype3 = (unsigned char) d;
+
+    return abcd;
+}
+
+#define LEADER_DATASET_SUMMARY_TC          QuadToTC( 18, 10, 18, 20 )
+#define LEADER_DATASET_SUMMARY_ERS2_TC     QuadToTC( 10, 10, 31, 20 )
+#define LEADER_RADIOMETRIC_COMPENSATION_TC QuadToTC( 18, 51, 18, 20 )
+#define VOLUME_DESCRIPTOR_RECORD_TC        QuadToTC( 192, 192, 18, 18 )
+#define IMAGE_HEADER_RECORD_TC             QuadToTC( 63, 192, 18, 18 )
+#define LEADER_RADIOMETRIC_DATA_RECORD_TC  QuadToTC( 18, 50, 18, 20 )
+#define LEADER_MAP_PROJ_RECORD_TC          QuadToTC( 10, 20, 31, 20 )
+
+/* JERS from Japan has MAP_PROJ recond with different identifiers */
+/* see CEOS-SAR-CCT Iss/Rev: 2/0 February 10, 1989 */
+#define LEADER_MAP_PROJ_RECORD_JERS_TC         QuadToTC( 18, 20, 18, 20 )
+
+/* For ERS calibration and incidence angle information */
+#define ERS_GENERAL_FACILITY_DATA_TC  QuadToTC( 10, 200, 31, 50 )
+#define ERS_GENERAL_FACILITY_DATA_ALT_TC QuadToTC( 10, 216, 31, 50 )
+
+
+#define RSAT_PROC_PARAM_TC QuadToTC( 18, 120, 18, 20 )
+
+/************************************************************************/
+/* ==================================================================== */
+/*				SAR_CEOSDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class SAR_CEOSRasterBand;
+class CCPRasterBand;
+class PALSARRasterBand;
+
+class SAR_CEOSDataset : public GDALPamDataset
+{
+    friend class SAR_CEOSRasterBand;
+    friend class CCPRasterBand;
+    friend class PALSARRasterBand;
+
+    CeosSARVolume_t sVolume;
+
+    FILE	*fpImage;
+
+    char        **papszTempMD;
+    
+    int         nGCPCount;
+    GDAL_GCP    *pasGCPList;
+
+    void        ScanForGCPs();
+    void        ScanForMetadata();
+    int         ScanForMapProjection();
+
+  public:
+                SAR_CEOSDataset();
+                ~SAR_CEOSDataset();
+
+    virtual int    GetGCPCount();
+    virtual const char *GetGCPProjection();
+    virtual const GDAL_GCP *GetGCPs();
+
+    virtual char **GetMetadata( const char * pszDomain );
+
+    static GDALDataset *Open( GDALOpenInfo * );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                          CCPRasterBand                               */
+/* ==================================================================== */
+/************************************************************************/
+
+class CCPRasterBand : public GDALPamRasterBand
+{
+    friend class SAR_CEOSDataset;
+
+  public:
+                   CCPRasterBand( SAR_CEOSDataset *, int, GDALDataType );
+
+    virtual CPLErr IReadBlock( int, int, void * );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                        PALSARRasterBand                              */
+/* ==================================================================== */
+/************************************************************************/
+
+class PALSARRasterBand : public GDALPamRasterBand
+{
+    friend class SAR_CEOSDataset;
+
+  public:
+                   PALSARRasterBand( SAR_CEOSDataset *, int );
+
+    virtual CPLErr IReadBlock( int, int, void * );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                       SAR_CEOSRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class SAR_CEOSRasterBand : public GDALPamRasterBand
+{
+    friend class SAR_CEOSDataset;
+
+  public:
+                   SAR_CEOSRasterBand( SAR_CEOSDataset *, int, GDALDataType );
+
+    virtual CPLErr IReadBlock( int, int, void * );
+};
+
+/************************************************************************/
+/*                         SAR_CEOSRasterBand()                         */
+/************************************************************************/
+
+SAR_CEOSRasterBand::SAR_CEOSRasterBand( SAR_CEOSDataset *poGDS, int nBand,
+                                        GDALDataType eType )
+
+{
+    this->poDS = poGDS;
+    this->nBand = nBand;
+    
+    eDataType = eType;
+
+    nBlockXSize = poGDS->nRasterXSize;
+    nBlockYSize = 1;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr SAR_CEOSRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                       void * pImage )
+
+{
+    struct CeosSARImageDesc *ImageDesc;
+    int	   offset;
+    GByte  *pabyRecord;
+    SAR_CEOSDataset *poGDS = (SAR_CEOSDataset *) poDS;
+
+    ImageDesc = &(poGDS->sVolume.ImageDesc);
+
+    CalcCeosSARImageFilePosition( &(poGDS->sVolume), nBand,
+                                  nBlockYOff + 1, NULL, &offset );
+
+    offset += ImageDesc->ImageDataStart;
+
+/* -------------------------------------------------------------------- */
+/*      Load all the pixel data associated with this scanline.          */
+/*      Ensure we handle multiple record scanlines properly.            */
+/* -------------------------------------------------------------------- */
+    int		iRecord, nPixelsRead = 0;
+
+    pabyRecord = (GByte *) CPLMalloc( ImageDesc->BytesPerPixel * nBlockXSize );
+    
+    for( iRecord = 0; iRecord < ImageDesc->RecordsPerLine; iRecord++ )
+    {
+        int	nPixelsToRead;
+
+        if( nPixelsRead + ImageDesc->PixelsPerRecord > nBlockXSize )
+            nPixelsToRead = nBlockXSize - nPixelsRead;
+        else
+            nPixelsToRead = ImageDesc->PixelsPerRecord;
+        
+        VSIFSeek( poGDS->fpImage, offset, SEEK_SET );
+        VSIFRead( pabyRecord + nPixelsRead * ImageDesc->BytesPerPixel, 
+                  1, nPixelsToRead * ImageDesc->BytesPerPixel, 
+                  poGDS->fpImage );
+
+        nPixelsRead += nPixelsToRead;
+        offset += ImageDesc->BytesPerRecord;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Copy the desired band out based on the size of the type, and    */
+/*      the interleaving mode.                                          */
+/* -------------------------------------------------------------------- */
+    int		nBytesPerSample = GDALGetDataTypeSize( eDataType ) / 8;
+
+    if( ImageDesc->ChannelInterleaving == __CEOS_IL_PIXEL )
+    {
+        GDALCopyWords( pabyRecord + (nBand-1) * nBytesPerSample, 
+                       eDataType, ImageDesc->BytesPerPixel, 
+                       pImage, eDataType, nBytesPerSample, 
+                       nBlockXSize );
+    }
+    else if( ImageDesc->ChannelInterleaving == __CEOS_IL_LINE )
+    {
+        GDALCopyWords( pabyRecord + (nBand-1) * nBytesPerSample * nBlockXSize, 
+                       eDataType, nBytesPerSample, 
+                       pImage, eDataType, nBytesPerSample, 
+                       nBlockXSize );
+    }
+    else if( ImageDesc->ChannelInterleaving == __CEOS_IL_BAND )
+    {
+        memcpy( pImage, pabyRecord, nBytesPerSample * nBlockXSize );
+    }
+
+#ifdef CPL_LSB
+    GDALSwapWords( pImage, nBytesPerSample, nBlockXSize, nBytesPerSample );
+#endif    
+
+    CPLFree( pabyRecord );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*				CCPRasterBand				*/
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                           CCPRasterBand()                            */
+/************************************************************************/
+
+CCPRasterBand::CCPRasterBand( SAR_CEOSDataset *poGDS, int nBand,
+                              GDALDataType eType )
+
+{
+    this->poDS = poGDS;
+    this->nBand = nBand;
+
+    eDataType = eType;
+
+    nBlockXSize = poGDS->nRasterXSize;
+    nBlockYSize = 1;
+
+    if( nBand == 1 )
+        SetMetadataItem( "POLARIMETRIC_INTERP", "HH" );
+    else if( nBand == 2 )
+        SetMetadataItem( "POLARIMETRIC_INTERP", "HV" );
+    else if( nBand == 3 )
+        SetMetadataItem( "POLARIMETRIC_INTERP", "VH" );
+    else if( nBand == 4 )
+        SetMetadataItem( "POLARIMETRIC_INTERP", "VV" );
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+/* From: http://southport.jpl.nasa.gov/software/dcomp/dcomp.html
+
+ysca = sqrt{ [ (Byte(2) / 254 ) + 1.5] 2Byte(1) }
+
+Re(SHH) = byte(3) ysca/127
+
+Im(SHH) = byte(4) ysca/127
+
+Re(SHV) = byte(5) ysca/127
+
+Im(SHV) = byte(6) ysca/127
+
+Re(SVH) = byte(7) ysca/127
+
+Im(SVH) = byte(8) ysca/127
+
+Re(SVV) = byte(9) ysca/127
+
+Im(SVV) = byte(10) ysca/127
+
+*/
+
+CPLErr CCPRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+
+{
+    struct CeosSARImageDesc *ImageDesc;
+    int	   offset;
+    GByte  *pabyRecord;
+    SAR_CEOSDataset *poGDS = (SAR_CEOSDataset *) poDS;
+    static float afPowTable[256];
+    static int bPowTableInitialized = FALSE;
+
+    ImageDesc = &(poGDS->sVolume.ImageDesc);
+
+    offset = ImageDesc->FileDescriptorLength
+        + ImageDesc->BytesPerRecord * nBlockYOff 
+        + ImageDesc->ImageDataStart;
+
+/* -------------------------------------------------------------------- */
+/*      Load all the pixel data associated with this scanline.          */
+/* -------------------------------------------------------------------- */
+    int	        nBytesToRead = ImageDesc->BytesPerPixel * nBlockXSize;
+
+    pabyRecord = (GByte *) CPLMalloc( nBytesToRead );
+    
+    if( VSIFSeek( poGDS->fpImage, offset, SEEK_SET ) != 0 
+        || (int) VSIFRead( pabyRecord, 1, nBytesToRead, 
+                           poGDS->fpImage ) != nBytesToRead )
+    {
+        CPLError( CE_Failure, CPLE_FileIO, 
+                  "Error reading %d bytes of CEOS record data at offset %d.\n"
+                  "Reading file %s failed.", 
+                  nBytesToRead, offset, poGDS->GetDescription() );
+        CPLFree( pabyRecord );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize our power table if this is our first time through.   */
+/* -------------------------------------------------------------------- */
+    if( !bPowTableInitialized )
+    {
+        int i;
+
+        bPowTableInitialized = TRUE;
+
+        for( i = 0; i < 256; i++ )
+        {
+            afPowTable[i] = pow( 2.0, i-128 );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Copy the desired band out based on the size of the type, and    */
+/*      the interleaving mode.                                          */
+/* -------------------------------------------------------------------- */
+    int iX;
+
+    for( iX = 0; iX < nBlockXSize; iX++ )
+    {
+        unsigned char *pabyGroup = pabyRecord + iX * ImageDesc->BytesPerPixel;
+        signed char *Byte = (signed char*)pabyGroup-1; /* A ones based alias */
+        double dfReSHH, dfImSHH, dfReSHV, dfImSHV, 
+            dfReSVH, dfImSVH, dfReSVV, dfImSVV, dfScale;
+
+        dfScale = sqrt( (Byte[2] / 254 + 1.5) * afPowTable[Byte[1] + 128] );
+        
+        if( nBand == 1 )
+        {
+            dfReSHH = Byte[3] * dfScale / 127.0;
+            dfImSHH = Byte[4] * dfScale / 127.0;
+
+            ((float *) pImage)[iX*2  ] = dfReSHH;
+            ((float *) pImage)[iX*2+1] = dfImSHH;
+        }        
+        else if( nBand == 2 )
+        {
+            dfReSHV = Byte[5] * dfScale / 127.0;
+            dfImSHV = Byte[6] * dfScale / 127.0;
+
+            ((float *) pImage)[iX*2  ] = dfReSHV;
+            ((float *) pImage)[iX*2+1] = dfImSHV;
+        }
+        else if( nBand == 3 )
+        {
+            dfReSVH = Byte[7] * dfScale / 127.0;
+            dfImSVH = Byte[8] * dfScale / 127.0;
+
+            ((float *) pImage)[iX*2  ] = dfReSVH;
+            ((float *) pImage)[iX*2+1] = dfImSVH;
+        }
+        else if( nBand == 4 )
+        {
+            dfReSVV = Byte[9] * dfScale / 127.0;
+            dfImSVV = Byte[10]* dfScale / 127.0;
+
+            ((float *) pImage)[iX*2  ] = dfReSVV;
+            ((float *) pImage)[iX*2+1] = dfImSVV;
+        }
+    }
+
+    CPLFree( pabyRecord );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*			      PALSARRasterBand				*/
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                           PALSARRasterBand()                         */
+/************************************************************************/
+
+PALSARRasterBand::PALSARRasterBand( SAR_CEOSDataset *poGDS, int nBand )
+
+{
+    this->poDS = poGDS;
+    this->nBand = nBand;
+
+    eDataType = GDT_CInt16;
+
+    nBlockXSize = poGDS->nRasterXSize;
+    nBlockYSize = 1;
+
+    if( nBand == 1 )
+        SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_11" );
+    else if( nBand == 2 )
+        SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_22" );
+    else if( nBand == 3 )
+        SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_33" );
+    else if( nBand == 4 )
+        SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_12" );
+    else if( nBand == 5 )
+        SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_13" );
+    else if( nBand == 6 )
+        SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_23" );
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/*                                                                      */
+/*      Based on ERSDAC-VX-CEOS-004                                     */
+/************************************************************************/
+
+CPLErr PALSARRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                     void * pImage )
+
+{
+    struct CeosSARImageDesc *ImageDesc;
+    int	   offset;
+    GByte  *pabyRecord;
+    SAR_CEOSDataset *poGDS = (SAR_CEOSDataset *) poDS;
+
+    ImageDesc = &(poGDS->sVolume.ImageDesc);
+
+    offset = ImageDesc->FileDescriptorLength
+        + ImageDesc->BytesPerRecord * nBlockYOff 
+        + ImageDesc->ImageDataStart;
+
+/* -------------------------------------------------------------------- */
+/*      Load all the pixel data associated with this scanline.          */
+/* -------------------------------------------------------------------- */
+    int	        nBytesToRead = ImageDesc->BytesPerPixel * nBlockXSize;
+
+    pabyRecord = (GByte *) CPLMalloc( nBytesToRead );
+    
+    if( VSIFSeek( poGDS->fpImage, offset, SEEK_SET ) != 0 
+        || (int) VSIFRead( pabyRecord, 1, nBytesToRead, 
+                           poGDS->fpImage ) != nBytesToRead )
+    {
+        CPLError( CE_Failure, CPLE_FileIO, 
+                  "Error reading %d bytes of CEOS record data at offset %d.\n"
+                  "Reading file %s failed.", 
+                  nBytesToRead, offset, poGDS->GetDescription() );
+        CPLFree( pabyRecord );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Copy the desired band out based on the size of the type, and    */
+/*      the interleaving mode.                                          */
+/* -------------------------------------------------------------------- */
+    if( nBand == 1 || nBand == 2 || nBand == 3 )
+    {
+        // we need to pre-initialize things to set the imaginary component to 0
+        memset( pImage, 0, nBlockXSize * 4 );
+
+        GDALCopyWords( pabyRecord + 4*(nBand - 1), GDT_Int16, 18, 
+                       pImage, GDT_Int16, 4, 
+                       nBlockXSize );
+#ifdef CPL_LSB
+        GDALSwapWords( pImage, 2, nBlockXSize, 4 );
+#endif        
+    }
+    else
+    {
+        GDALCopyWords( pabyRecord + 6 + 4*(nBand - 4), GDT_CInt16, 18, 
+                       pImage, GDT_CInt16, 4, 
+                       nBlockXSize );
+#ifdef CPL_LSB
+        GDALSwapWords( pImage, 2, nBlockXSize*2, 2 );
+#endif        
+    }
+    CPLFree( pabyRecord );
+
+/* -------------------------------------------------------------------- */
+/*      Convert the values into covariance form as per:                 */
+/* -------------------------------------------------------------------- */
+/*
+** 1) PALSAR- adjust so that it reads bands as a covariance matrix, and 
+** set polarimetric interpretation accordingly:
+**
+** Covariance_11=HH*conj(HH): already there
+** Covariance_22=2*HV*conj(HV): need a factor of 2
+** Covariance_33=VV*conj(VV): already there 
+** Covariance_12=sqrt(2)*HH*conj(HV): need the sqrt(2) factor
+** Covariance_13=HH*conj(VV): already there
+** Covariance_23=sqrt(2)*HV*conj(VV): need to take the conjugate, then 
+**               multiply by sqrt(2)
+**
+*/
+
+    if( nBand == 2 )
+    {
+        int i;
+        GInt16 *panLine = (GInt16 *) pImage;
+        
+        for( i = 0; i < nBlockXSize * 2; i++ )
+        {
+          panLine[i] = (GInt16) CastToGInt16(2.0 * panLine[i]);
+        }
+    }
+    else if( nBand == 4 )
+    {
+        int i;
+        double sqrt_2 = pow(2.0,0.5);
+        GInt16 *panLine = (GInt16 *) pImage;
+        
+        for( i = 0; i < nBlockXSize * 2; i++ )
+        {
+          panLine[i] = (GInt16) CastToGInt16(floor(panLine[i] * sqrt_2 + 0.5));
+        }
+    }
+    else if( nBand == 6 )
+    {
+        int i;
+        GInt16 *panLine = (GInt16 *) pImage;
+        double sqrt_2 = pow(2.0,0.5);
+        
+        // real portion - just multiple by sqrt(2)
+        for( i = 0; i < nBlockXSize * 2; i += 2 )
+        {
+          panLine[i] = (GInt16) CastToGInt16(floor(panLine[i] * sqrt_2 + 0.5));
+        }
+
+        // imaginary portion - conjugate and multiply
+        for( i = 1; i < nBlockXSize * 2; i += 2 )
+        {
+          panLine[i] = (GInt16) CastToGInt16(floor(-panLine[i] * sqrt_2 + 0.5));
+        }
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*				SAR_CEOSDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                          SAR_CEOSDataset()                           */
+/************************************************************************/
+
+SAR_CEOSDataset::SAR_CEOSDataset()
+
+{
+    fpImage = NULL;
+    nGCPCount = 0;
+    pasGCPList = NULL;
+
+    papszTempMD = NULL;
+}
+
+/************************************************************************/
+/*                          ~SAR_CEOSDataset()                          */
+/************************************************************************/
+
+SAR_CEOSDataset::~SAR_CEOSDataset()
+
+{
+    FlushCache();
+
+    CSLDestroy( papszTempMD );
+
+    if( fpImage != NULL )
+        VSIFClose( fpImage );
+
+    if( nGCPCount > 0 )
+    {
+        GDALDeinitGCPs( nGCPCount, pasGCPList );
+        CPLFree( pasGCPList );
+    }
+
+    if( sVolume.RecordList )
+    {
+        Link_t	*Links;
+
+        for(Links = sVolume.RecordList; Links != NULL; Links = Links->next)
+        {
+            if(Links->object)
+            {
+                DeleteCeosRecord( (CeosRecord_t *) Links->object );
+                Links->object = NULL;
+            }
+        }
+        DestroyList( sVolume.RecordList );
+    }
+    FreeRecipes();
+}
+
+/************************************************************************/
+/*                            GetGCPCount()                             */
+/************************************************************************/
+
+int SAR_CEOSDataset::GetGCPCount()
+
+{
+    return nGCPCount;
+}
+
+/************************************************************************/
+/*                          GetGCPProjection()                          */
+/************************************************************************/
+
+const char *SAR_CEOSDataset::GetGCPProjection()
+
+{
+    if( nGCPCount > 0 )
+        return "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",7030]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",6326]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"DMSH\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",4326]]";
+    else
+        return "";
+}
+
+/************************************************************************/
+/*                               GetGCP()                               */
+/************************************************************************/
+
+const GDAL_GCP *SAR_CEOSDataset::GetGCPs()
+
+{
+    return pasGCPList;
+}
+
+/************************************************************************/
+/*                            GetMetadata()                             */
+/*                                                                      */
+/*      We provide our own GetMetadata() so that we can override        */
+/*      behavior for some very specialized domain names intended to     */
+/*      give us access to raw record data.                              */
+/*                                                                      */
+/*      The domain must look like:                                      */
+/*        ceos-FFF-n-n-n-n:r                                            */
+/*                                                                      */
+/*        FFF - The file id - one of vol, lea, img, trl or nul.         */
+/*        n-n-n-n - the record type code such as 18-10-18-20 for the    */
+/*        dataset summary record in the leader file.                    */
+/*        :r - The zero based record number to fetch (optional)         */
+/*                                                                      */
+/*      Note that only records that are pre-loaded will be              */
+/*      accessable, and this normally means that most image records     */
+/*      are not available.                                              */
+/************************************************************************/
+
+char **SAR_CEOSDataset::GetMetadata( const char * pszDomain )
+
+{
+    if( pszDomain == NULL || !EQUALN(pszDomain,"ceos-",5) )
+        return GDALDataset::GetMetadata( pszDomain );
+
+/* -------------------------------------------------------------------- */
+/*      Identify which file to fetch the file from.                     */
+/* -------------------------------------------------------------------- */
+    int	nFileId = -1;
+    
+    if( EQUALN(pszDomain,"ceos-vol",8) )
+    {
+        nFileId = __CEOS_VOLUME_DIR_FILE;
+    }
+    else if( EQUALN(pszDomain,"ceos-lea",8) )
+    {
+        nFileId = __CEOS_LEADER_FILE;
+    }
+    else if( EQUALN(pszDomain,"ceos-img",8) )
+    {
+        nFileId = __CEOS_IMAGRY_OPT_FILE;
+    }
+    else if( EQUALN(pszDomain,"ceos-trl",8) )
+    {
+        nFileId = __CEOS_TRAILER_FILE;
+    }
+    else if( EQUALN(pszDomain,"ceos-nul",8) )
+    {
+        nFileId = __CEOS_NULL_VOL_FILE;
+    }
+    else
+        return NULL;
+
+    pszDomain += 8;
+
+/* -------------------------------------------------------------------- */
+/*      Identify the record type.                                       */
+/* -------------------------------------------------------------------- */
+    CeosTypeCode_t sTypeCode;
+    int  a, b, c, d, nRecordIndex = -1;
+
+    if( sscanf( pszDomain, "-%d-%d-%d-%d:%d", 
+                &a, &b, &c, &d, &nRecordIndex ) != 5 
+        && sscanf( pszDomain, "-%d-%d-%d-%d", 
+                   &a, &b, &c, &d ) != 4 )
+    {
+        return NULL;
+    }
+
+    sTypeCode = QuadToTC( a, b, c, d );
+
+/* -------------------------------------------------------------------- */
+/*      Try to fetch the record.                                        */
+/* -------------------------------------------------------------------- */
+    CeosRecord_t *record;
+
+    record = FindCeosRecord( sVolume.RecordList, sTypeCode, nFileId, 
+                             -1, nRecordIndex );
+
+    if( record == NULL )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Massage the data into a safe textual format.  The RawRecord     */
+/*      just has zero bytes turned into spaces while the                */
+/*      EscapedRecord has regular backslash escaping applied to zero    */
+/*      chars, double quotes, and backslashes.                          */
+/*      just turn zero bytes into spaces.                               */
+/* -------------------------------------------------------------------- */
+    char *pszSafeCopy;
+    int  i;
+
+    CSLDestroy( papszTempMD );
+
+    // Escaped version
+    pszSafeCopy = CPLEscapeString( (char *) record->Buffer, 
+                                   record->Length, 
+                                   CPLES_BackslashQuotable );
+    papszTempMD = CSLSetNameValue( NULL, "EscapedRecord", pszSafeCopy );
+    CPLFree( pszSafeCopy );
+
+
+    // Copy with '\0' replaced by spaces.
+
+    pszSafeCopy = (char *) CPLCalloc(1,record->Length+1);
+    memcpy( pszSafeCopy, record->Buffer, record->Length );
+    
+    for( i = 0; i < record->Length; i++ )
+        if( pszSafeCopy[i] == '\0' )
+            pszSafeCopy[i] = ' ';
+        
+    papszTempMD = CSLSetNameValue( papszTempMD, "RawRecord", pszSafeCopy );
+
+    CPLFree( pszSafeCopy );
+
+    return papszTempMD;
+}
+
+/************************************************************************/
+/*                          ScanForMetadata()                           */
+/************************************************************************/
+
+void SAR_CEOSDataset::ScanForMetadata() 
+
+{
+    char szField[128], szVolId[128];
+    CeosRecord_t *record;
+
+/* -------------------------------------------------------------------- */
+/*      Get the volume id (with the sensor name)                        */
+/* -------------------------------------------------------------------- */
+    record = FindCeosRecord( sVolume.RecordList, VOLUME_DESCRIPTOR_RECORD_TC,
+                             __CEOS_VOLUME_DIR_FILE, -1, -1 );
+    szVolId[0] = '\0';
+    if( record != NULL )
+    {
+        szVolId[16] = '\0';
+
+        GetCeosField( record, 61, "A16", szVolId );
+
+        SetMetadataItem( "CEOS_LOGICAL_VOLUME_ID", szVolId );
+
+/* -------------------------------------------------------------------- */
+/*      Processing facility                                             */
+/* -------------------------------------------------------------------- */
+        szField[0] = '\0';
+        szField[12] = '\0';
+
+        GetCeosField( record, 149, "A12", szField );
+
+        if( !EQUALN(szField,"            ",12) )
+            SetMetadataItem( "CEOS_PROCESSING_FACILITY", szField );
+
+/* -------------------------------------------------------------------- */
+/*      Agency                                                          */
+/* -------------------------------------------------------------------- */
+        szField[8] = '\0';
+
+        GetCeosField( record, 141, "A8", szField );
+
+        if( !EQUALN(szField,"            ",8) )
+            SetMetadataItem( "CEOS_PROCESSING_AGENCY", szField );
+
+/* -------------------------------------------------------------------- */
+/*      Country                                                         */
+/* -------------------------------------------------------------------- */
+        szField[12] = '\0';
+
+        GetCeosField( record, 129, "A12", szField );
+
+        if( !EQUALN(szField,"            ",12) )
+            SetMetadataItem( "CEOS_PROCESSING_COUNTRY", szField );
+
+/* -------------------------------------------------------------------- */
+/*      software id.                                                    */
+/* -------------------------------------------------------------------- */
+        szField[12] = '\0';
+
+        GetCeosField( record, 33, "A12", szField );
+
+        if( !EQUALN(szField,"            ",12) )
+            SetMetadataItem( "CEOS_SOFTWARE_ID", szField );
+    }
+
+/* ==================================================================== */
+/*      Dataset summary record.                                         */
+/* ==================================================================== */
+    record = FindCeosRecord( sVolume.RecordList, LEADER_DATASET_SUMMARY_TC,
+                             __CEOS_LEADER_FILE, -1, -1 );
+
+    if( record == NULL )
+        record = FindCeosRecord( sVolume.RecordList, LEADER_DATASET_SUMMARY_TC,
+                                 __CEOS_TRAILER_FILE, -1, -1 );
+
+    if( record == NULL )
+        record = FindCeosRecord( sVolume.RecordList, 
+                                 LEADER_DATASET_SUMMARY_ERS2_TC,
+                                 __CEOS_LEADER_FILE, -1, -1 );
+
+    if( record != NULL )
+    {
+/* -------------------------------------------------------------------- */
+/*      Get the acquisition date.                                       */
+/* -------------------------------------------------------------------- */
+        szField[0] = '\0';
+        szField[32] = '\0';
+
+        GetCeosField( record, 69, "A32", szField );
+
+        SetMetadataItem( "CEOS_ACQUISITION_TIME", szField );
+
+/* -------------------------------------------------------------------- */
+/*      Ascending/Descending                                            */
+/* -------------------------------------------------------------------- */
+        GetCeosField( record, 101, "A16", szField );
+        szField[16] = '\0';
+
+        if( strstr(szVolId,"RSAT") != NULL 
+            && !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_ASC_DES", szField );
+
+/* -------------------------------------------------------------------- */
+/*      True heading - at least for ERS2.                               */
+/* -------------------------------------------------------------------- */
+        GetCeosField( record, 149, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_TRUE_HEADING", szField );
+
+/* -------------------------------------------------------------------- */
+/*      Ellipsoid                                                       */
+/* -------------------------------------------------------------------- */
+        GetCeosField( record, 165, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_ELLIPSOID", szField );
+
+/* -------------------------------------------------------------------- */
+/*      Semimajor, semiminor axis                                       */
+/* -------------------------------------------------------------------- */
+        GetCeosField( record, 181, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_SEMI_MAJOR", szField );
+
+        GetCeosField( record, 197, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_SEMI_MINOR", szField );
+
+/* -------------------------------------------------------------------- */
+/*      Platform latitude                                               */
+/* -------------------------------------------------------------------- */
+        GetCeosField( record, 453, "A8", szField );
+        szField[8] = '\0';
+
+        if( !EQUALN(szField,"        ",8 ) )
+            SetMetadataItem( "CEOS_PLATFORM_LATITUDE", szField );
+
+/* -------------------------------------------------------------------- */
+/*      Platform longitude                                               */
+/* -------------------------------------------------------------------- */
+        GetCeosField( record, 461, "A8", szField );
+        szField[8] = '\0';
+
+        if( !EQUALN(szField,"        ",8 ) )
+            SetMetadataItem( "CEOS_PLATFORM_LONGITUDE", szField );
+
+/* -------------------------------------------------------------------- */
+/*      Platform heading - at least for ERS2.                           */
+/* -------------------------------------------------------------------- */
+        GetCeosField( record, 469, "A8", szField );
+        szField[8] = '\0';
+
+        if( !EQUALN(szField,"                ",8 ) )
+            SetMetadataItem( "CEOS_PLATFORM_HEADING", szField );
+
+/* -------------------------------------------------------------------- */
+/*      Look Angle.                                                     */
+/* -------------------------------------------------------------------- */
+        GetCeosField( record, 477, "A8", szField );
+        szField[8] = '\0';
+
+        if( !EQUALN(szField,"        ",8 ) )
+            SetMetadataItem( "CEOS_SENSOR_CLOCK_ANGLE", szField );
+
+/* -------------------------------------------------------------------- */
+/*      Incidence angle                                                 */
+/* -------------------------------------------------------------------- */
+        GetCeosField( record, 485, "A8", szField );
+        szField[8] = '\0';
+
+        if( !EQUALN(szField,"        ",8 ) )
+            SetMetadataItem( "CEOS_INC_ANGLE", szField );
+
+/* -------------------------------------------------------------------- */
+/*      Pixel time direction indicator                                  */
+/* -------------------------------------------------------------------- */
+        GetCeosField( record, 1527, "A8", szField );
+        szField[8] = '\0';
+
+        if( !EQUALN(szField,"                ",8 ) )
+            SetMetadataItem( "CEOS_PIXEL_TIME_DIR", szField );
+
+/* -------------------------------------------------------------------- */
+/*      Line spacing                                                    */
+/* -------------------------------------------------------------------- */
+        GetCeosField( record, 1687, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_LINE_SPACING_METERS", szField );
+/* -------------------------------------------------------------------- */
+/*      Pixel spacing                                                    */
+/* -------------------------------------------------------------------- */
+        GetCeosField( record, 1703, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_PIXEL_SPACING_METERS", szField );
+
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get the beam mode, for radarsat.                                */
+/* -------------------------------------------------------------------- */
+    record = FindCeosRecord( sVolume.RecordList, 
+                             LEADER_RADIOMETRIC_COMPENSATION_TC,
+                             __CEOS_LEADER_FILE, -1, -1 );
+
+    if( strstr(szVolId,"RSAT") != NULL && record != NULL )
+    {
+        szField[16] = '\0';
+
+        GetCeosField( record, 4189, "A16", szField );
+
+        SetMetadataItem( "CEOS_BEAM_TYPE", szField );
+    }
+
+/* ==================================================================== */
+/*      ERS calibration and incidence angle info                        */
+/* ==================================================================== */
+    record = FindCeosRecord( sVolume.RecordList, ERS_GENERAL_FACILITY_DATA_TC,
+                             __CEOS_LEADER_FILE, -1, -1 );
+
+    if( record == NULL )
+        record = FindCeosRecord( sVolume.RecordList, 
+                                 ERS_GENERAL_FACILITY_DATA_ALT_TC,
+                                 __CEOS_LEADER_FILE, -1, -1 );
+
+    if( record != NULL )
+    {   
+        GetCeosField( record, 13 , "A64", szField );
+        szField[64] = '\0';
+
+        /* Avoid PCS records, which don't contain necessary info */
+        if( strstr( szField, "GENERAL") == NULL )
+            record = NULL;
+    }
+
+    if( record != NULL )
+    {
+        GetCeosField( record, 583 , "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ", 16 ) )
+            SetMetadataItem( "CEOS_INC_ANGLE_FIRST_RANGE", szField );
+
+        GetCeosField( record, 599 , "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ", 16 ) )
+            SetMetadataItem( "CEOS_INC_ANGLE_CENTRE_RANGE", szField );
+
+        GetCeosField( record, 615, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ", 16 ) )
+            SetMetadataItem( "CEOS_INC_ANGLE_LAST_RANGE", szField );
+
+        GetCeosField( record, 663, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ", 16 ) )
+            SetMetadataItem( "CEOS_CALIBRATION_CONSTANT_K", szField );
+
+        GetCeosField( record, 1855, "A20", szField );
+        szField[20] = '\0';
+
+        if( !EQUALN(szField,"                    ", 20 ) )
+            SetMetadataItem( "CEOS_GROUND_TO_SLANT_C0", szField );
+
+        GetCeosField( record, 1875, "A20", szField );
+        szField[20] = '\0';
+
+        if( !EQUALN(szField,"                    ", 20 ) )
+            SetMetadataItem( "CEOS_GROUND_TO_SLANT_C1", szField );
+
+        GetCeosField( record, 1895, "A20", szField );
+        szField[20] = '\0';
+
+        if( !EQUALN(szField,"                    ", 20 ) )
+            SetMetadataItem( "CEOS_GROUND_TO_SLANT_C2", szField );
+
+        GetCeosField( record, 1915, "A20", szField );
+        szField[20] = '\0';
+
+        if( !EQUALN(szField,"                    ", 20 ) )
+            SetMetadataItem( "CEOS_GROUND_TO_SLANT_C3", szField );
+
+    }
+/* -------------------------------------------------------------------- */
+/*	Detailed Processing Parameters (Radarsat)                       */
+/* -------------------------------------------------------------------- */
+    record = FindCeosRecord( sVolume.RecordList, RSAT_PROC_PARAM_TC,
+                             __CEOS_LEADER_FILE, -1, -1 );
+
+    if( record == NULL )
+        record = FindCeosRecord( sVolume.RecordList, RSAT_PROC_PARAM_TC,
+                             __CEOS_TRAILER_FILE, -1, -1 );
+
+    if( record != NULL )
+    {
+        GetCeosField( record, 4649, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_EPH_ORB_DATA_0", szField );
+
+        GetCeosField( record, 4665, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_EPH_ORB_DATA_1", szField );
+
+        GetCeosField( record, 4681, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_EPH_ORB_DATA_2", szField );
+
+        GetCeosField( record, 4697, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_EPH_ORB_DATA_3", szField );
+
+        GetCeosField( record, 4713, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_EPH_ORB_DATA_4", szField );
+
+        GetCeosField( record, 4729, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_EPH_ORB_DATA_5", szField );
+
+        GetCeosField( record, 4745, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_EPH_ORB_DATA_6", szField );
+
+        GetCeosField( record, 4908, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_GROUND_TO_SLANT_C0", szField );
+
+        GetCeosField( record, 4924, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_GROUND_TO_SLANT_C1", szField );
+
+        GetCeosField( record, 4940, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_GROUND_TO_SLANT_C2", szField );
+
+        GetCeosField( record, 4956, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_GROUND_TO_SLANT_C3", szField );
+
+        GetCeosField( record, 4972, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_GROUND_TO_SLANT_C4", szField );
+
+        GetCeosField( record, 4988, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_GROUND_TO_SLANT_C5", szField );
+
+        GetCeosField( record, 7334, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_INC_ANGLE_FIRST_RANGE", szField );
+
+        GetCeosField( record, 7350, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ",16 ) )
+            SetMetadataItem( "CEOS_INC_ANGLE_LAST_RANGE", szField );
+
+    }
+/* -------------------------------------------------------------------- */
+/*	Get process-to-raw data coordinate translation values.  These	*/
+/*	are likely specific to Atlantis APP products.			*/
+/* -------------------------------------------------------------------- */
+    record = FindCeosRecord( sVolume.RecordList, 
+                             IMAGE_HEADER_RECORD_TC,
+                             __CEOS_IMAGRY_OPT_FILE, -1, -1 );
+
+    if( record != NULL )
+    {
+        GetCeosField( record, 449, "A4", szField );
+        szField[4] = '\0';
+
+        if( !EQUALN(szField,"    ",4 ) )
+            SetMetadataItem( "CEOS_DM_CORNER", szField );
+
+
+        GetCeosField( record, 453, "A4", szField );
+        szField[4] = '\0';
+
+        if( !EQUALN(szField,"    ",4 ) )
+            SetMetadataItem( "CEOS_DM_TRANSPOSE", szField );
+
+
+        GetCeosField( record, 457, "A4", szField );
+        szField[4] = '\0';
+
+        if( !EQUALN(szField,"    ",4 ) )
+            SetMetadataItem( "CEOS_DM_START_SAMPLE", szField );
+
+
+        GetCeosField( record, 461, "A5", szField );
+        szField[5] = '\0';
+
+        if( !EQUALN(szField,"     ",5 ) )
+            SetMetadataItem( "CEOS_DM_START_PULSE", szField );
+
+
+        GetCeosField( record, 466, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ", 16 ) )
+            SetMetadataItem( "CEOS_DM_FAST_ALPHA", szField );
+
+
+        GetCeosField( record, 482, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ", 16 ) )
+            SetMetadataItem( "CEOS_DM_FAST_BETA", szField );
+
+
+        GetCeosField( record, 498, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ", 16 ) )
+            SetMetadataItem( "CEOS_DM_SLOW_ALPHA", szField );
+
+
+        GetCeosField( record, 514, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ", 16 ) )
+            SetMetadataItem( "CEOS_DM_SLOW_BETA", szField );
+
+
+        GetCeosField( record, 530, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ", 16 ) )
+            SetMetadataItem( "CEOS_DM_FAST_ALPHA_2", szField );
+
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try to find calibration information from Radiometric Data       */
+/*      Record.                                                         */
+/* -------------------------------------------------------------------- */
+    record = FindCeosRecord( sVolume.RecordList, 
+                             LEADER_RADIOMETRIC_DATA_RECORD_TC,
+                             __CEOS_LEADER_FILE, -1, -1 );
+
+    if( record == NULL )
+        record = FindCeosRecord( sVolume.RecordList, 
+                             LEADER_RADIOMETRIC_DATA_RECORD_TC,
+                             __CEOS_TRAILER_FILE, -1, -1 );
+
+    if( record != NULL )
+    {
+        GetCeosField( record, 8317, "A16", szField );
+        szField[16] = '\0';
+
+        if( !EQUALN(szField,"                ", 16 ) )
+            SetMetadataItem( "CEOS_CALIBRATION_OFFSET", szField );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      For ERS Standard Format Landsat scenes we pick up the           */
+/*      calibration offset and gain from the Radiometric Ancillary      */
+/*      Record.                                                         */
+/* -------------------------------------------------------------------- */
+    record = FindCeosRecord( sVolume.RecordList, 
+                             QuadToTC( 0x3f, 0x24, 0x12, 0x09 ),
+                             __CEOS_LEADER_FILE, -1, -1 );
+    if( record != NULL )
+    {
+        GetCeosField( record, 29, "A20", szField );
+        szField[20] = '\0';
+
+        if( !EQUALN(szField,"                    ", 20 ) )
+            SetMetadataItem( "CEOS_OFFSET_A0", szField );
+
+        GetCeosField( record, 49, "A20", szField );
+        szField[20] = '\0';
+
+        if( !EQUALN(szField,"                    ", 20 ) )
+            SetMetadataItem( "CEOS_GAIN_A1", szField );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      For ERS Standard Format Landsat scenes we pick up the           */
+/*      gain setting from the Scene Header Record.			*/
+/* -------------------------------------------------------------------- */
+    record = FindCeosRecord( sVolume.RecordList, 
+                             QuadToTC( 0x12, 0x12, 0x12, 0x09 ),
+                             __CEOS_LEADER_FILE, -1, -1 );
+    if( record != NULL )
+    {
+        GetCeosField( record, 1486, "A1", szField );
+        szField[1] = '\0';
+
+        if( szField[0] == 'H' || szField[0] == 'V' )
+            SetMetadataItem( "CEOS_GAIN_SETTING", szField );
+    }
+}
+
+/************************************************************************/
+/*                        ScanForMapProjection()                        */
+/*                                                                      */
+/*      Try to find a map projection record, and read corner points     */
+/*      from it.  This has only been tested with ERS products.          */
+/************************************************************************/
+
+int SAR_CEOSDataset::ScanForMapProjection()
+
+{
+    CeosRecord_t *record;
+    char	 szField[100];
+    int          i;
+
+/* -------------------------------------------------------------------- */
+/*      Find record, and try to determine if it has useful GCPs.        */
+/* -------------------------------------------------------------------- */
+
+    record = FindCeosRecord( sVolume.RecordList, 
+                             LEADER_MAP_PROJ_RECORD_TC,
+                             __CEOS_LEADER_FILE, -1, -1 );
+
+    /* JERS from Japan */
+    if( record == NULL )
+        record = FindCeosRecord( sVolume.RecordList, 
+                             LEADER_MAP_PROJ_RECORD_JERS_TC,
+                             __CEOS_LEADER_FILE, -1, -1 );
+
+    if( record == NULL )
+        return FALSE;
+
+    memset( szField, 0, 17 );
+    GetCeosField( record, 29, "A16", szField );
+
+    if( !EQUALN(szField,"Slant Range",11) && !EQUALN(szField,"Ground Range",12) 
+        && !EQUALN(szField,"GEOCODED",8) )
+        return FALSE;
+
+    GetCeosField( record, 1073, "A16", szField );
+    if( EQUALN(szField,"        ",8) )
+        return FALSE;
+    
+/* -------------------------------------------------------------------- */
+/*      Read corner points.                                             */
+/* -------------------------------------------------------------------- */
+    nGCPCount = 4;
+    pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPCount);
+
+    GDALInitGCPs( nGCPCount, pasGCPList );
+
+    for( i = 0; i < nGCPCount; i++ )
+    {
+        char         szId[32];
+
+        sprintf( szId, "%d", i+1 );
+        pasGCPList[i].pszId = CPLStrdup( szId );
+    
+        GetCeosField( record, 1073+32*i, "A16", szField );
+        pasGCPList[i].dfGCPY = atof(szField);
+        GetCeosField( record, 1089+32*i, "A16", szField );
+        pasGCPList[i].dfGCPX = atof(szField);
+        pasGCPList[i].dfGCPZ = 0.0;
+    }
+    
+    pasGCPList[0].dfGCPLine = 0.5;
+    pasGCPList[0].dfGCPPixel = 0.5;
+
+    pasGCPList[1].dfGCPLine = 0.5;
+    pasGCPList[1].dfGCPPixel = nRasterXSize-0.5;
+
+    pasGCPList[2].dfGCPLine = nRasterYSize-0.5;
+    pasGCPList[2].dfGCPPixel = nRasterXSize-0.5;
+
+    pasGCPList[3].dfGCPLine = nRasterYSize-0.5;
+    pasGCPList[3].dfGCPPixel = 0.5;
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                            ScanForGCPs()                             */
+/************************************************************************/
+
+void SAR_CEOSDataset::ScanForGCPs()
+
+{
+    int    iScanline, nStep, nGCPMax = 15;
+
+/* -------------------------------------------------------------------- */
+/*      Do we have a standard 180 bytes of prefix data (192 bytes       */
+/*      including the record marker information)?  If not, it is        */
+/*      unlikely that the GCPs are available.                           */
+/* -------------------------------------------------------------------- */
+    if( sVolume.ImageDesc.ImageDataStart < 192 )
+    {
+        ScanForMapProjection();
+        return;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Just sample fix scanlines through the image for GCPs, to        */
+/*      return 15 GCPs.  That is an adequate coverage for most          */
+/*      purposes.  A GCP is collected from the beginning, middle and    */
+/*      end of each scanline.                                           */
+/* -------------------------------------------------------------------- */
+    nGCPCount = 0;
+    pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPMax);
+
+    nStep = (GetRasterYSize()-1) / (nGCPMax / 3 - 1);
+    for( iScanline = 0; iScanline < GetRasterYSize(); iScanline += nStep )
+    {
+        int   nFileOffset, iGCP;
+        GInt32 anRecord[192/4];
+
+        if( nGCPCount > nGCPMax-3 )
+            break;
+
+        CalcCeosSARImageFilePosition( &sVolume, 1, iScanline+1, NULL, 
+                                      &nFileOffset );
+
+        if( VSIFSeek( fpImage, nFileOffset, SEEK_SET ) != 0 
+            || VSIFRead( anRecord, 1, 192, fpImage ) != 192 )
+            break;
+        
+        /* loop over first, middle and last pixel gcps */
+
+        for( iGCP = 0; iGCP < 3; iGCP++ )
+        {
+            int nLat, nLong;
+
+            nLat  = CPL_MSBWORD32( anRecord[132/4 + iGCP] );
+            nLong = CPL_MSBWORD32( anRecord[144/4 + iGCP] );
+
+            if( nLat != 0 || nLong != 0 )
+            {
+                char      szId[32];
+
+                GDALInitGCPs( 1, pasGCPList + nGCPCount );
+
+                CPLFree( pasGCPList[nGCPCount].pszId );
+
+                sprintf( szId, "%d", nGCPCount+1 );
+                pasGCPList[nGCPCount].pszId = CPLStrdup( szId );
+                
+                pasGCPList[nGCPCount].dfGCPX = nLong / 1000000.0;
+                pasGCPList[nGCPCount].dfGCPY = nLat / 1000000.0;
+                pasGCPList[nGCPCount].dfGCPZ = 0.0;
+
+                pasGCPList[nGCPCount].dfGCPLine = iScanline + 0.5;
+
+                if( iGCP == 0 )
+                    pasGCPList[nGCPCount].dfGCPPixel = 0.5;
+                else if( iGCP == 1 )
+                    pasGCPList[nGCPCount].dfGCPPixel = 
+                        GetRasterXSize() / 2.0;
+                else 
+                    pasGCPList[nGCPCount].dfGCPPixel = 
+                        GetRasterXSize() - 0.5;
+
+                nGCPCount++;
+            }
+        }
+    }
+    /* If general GCP's weren't found, look for Map Projection (eg. JERS) */
+    if( nGCPCount == 0 )
+    {
+        ScanForMapProjection();
+        return;
+    }
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *SAR_CEOSDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    int		i, bNative;
+    
+/* -------------------------------------------------------------------- */
+/*      Does this appear to be a valid ceos leader record?              */
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->fp == NULL 
+        || poOpenInfo->nHeaderBytes < __CEOS_HEADER_LENGTH )
+        return NULL;
+
+    if( (poOpenInfo->pabyHeader[4] != 0x3f
+         && poOpenInfo->pabyHeader[4] != 0x32)
+        || poOpenInfo->pabyHeader[5] != 0xc0
+        || poOpenInfo->pabyHeader[6] != 0x12
+        || poOpenInfo->pabyHeader[7] != 0x12 )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    SAR_CEOSDataset 	*poDS;
+    CeosSARVolume_t     *psVolume;
+
+    poDS = new SAR_CEOSDataset();
+
+    psVolume = &(poDS->sVolume);
+    InitCeosSARVolume( psVolume, 0 );
+
+/* -------------------------------------------------------------------- */
+/*      Try to read the current file as an imagery file.                */
+/* -------------------------------------------------------------------- */
+    psVolume->ImagryOptionsFile = TRUE;
+    if( ProcessData( poOpenInfo->fp, __CEOS_IMAGRY_OPT_FILE, psVolume, 4, -1) )
+    {
+        delete poDS;
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "Corrupted or unknown CEOS format:\n%s", 
+                  poOpenInfo->pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try the various filenames.                                      */
+/* -------------------------------------------------------------------- */
+    char *pszPath;
+    char *pszBasename;
+    char *pszExtension;
+    int  nBand, iFile;
+
+    pszPath = CPLStrdup(CPLGetPath(poOpenInfo->pszFilename));
+    pszBasename = CPLStrdup(CPLGetBasename(poOpenInfo->pszFilename));
+    pszExtension = CPLStrdup(CPLGetExtension(poOpenInfo->pszFilename));
+    if( strlen(pszBasename) > 4 )
+        nBand = atoi( pszBasename + 4 );
+    else
+        nBand = 0;
+
+    for( iFile = 0; iFile < 5;iFile++ )
+    {
+        int	e;
+
+        /* skip image file ... we already did it */
+        if( iFile == 2 )
+            continue;
+
+        e = 0;
+        while( CeosExtension[e][iFile] != NULL )
+        {
+            FILE	*process_fp;
+            char *pszFilename = NULL;
+            
+            /* build filename */
+            if( EQUAL(CeosExtension[e][5],"base") )
+            {
+                char    szMadeBasename[32];
+
+                sprintf( szMadeBasename, CeosExtension[e][iFile], nBand );
+                pszFilename = CPLStrdup(
+                    CPLFormFilename(pszPath,szMadeBasename, pszExtension));
+            }
+            else if( EQUAL(CeosExtension[e][5],"ext") )
+            {
+                pszFilename = CPLStrdup(
+                    CPLFormFilename(pszPath,pszBasename,
+                                    CeosExtension[e][iFile]));
+            }
+            else if( EQUAL(CeosExtension[e][5],"whole") )
+            {
+                pszFilename = CPLStrdup(
+                    CPLFormFilename(pszPath,CeosExtension[e][iFile],""));
+            }
+            
+            // This is for SAR SLC as per the SAR Toolbox (from ASF).
+            else if( EQUAL(CeosExtension[e][5],"ext2") )
+            {
+                char szThisExtension[32];
+
+                sprintf( szThisExtension, "%s%s", 
+                         CeosExtension[e][iFile], 
+                         pszExtension+3 );
+                pszFilename = CPLStrdup(
+                    CPLFormFilename(pszPath,pszBasename,szThisExtension));
+            }
+
+            CPLAssert( pszFilename != NULL );
+            if( pszFilename == NULL ) 
+                return NULL;
+ 
+            /* try to open */
+            process_fp = VSIFOpen( pszFilename, "rb" );
+
+            /* try upper case */
+            if( process_fp == NULL )
+            {
+                for( i = strlen(pszFilename)-1; 
+                     i >= 0 && pszFilename[i] != '/' && pszFilename[i] != '\\';
+                     i-- )
+                {
+                    if( pszFilename[i] >= 'a' && pszFilename[i] <= 'z' )
+                        pszFilename[i] = pszFilename[i] - 'a' + 'A';
+                }
+
+                process_fp = VSIFOpen( pszFilename, "rb" );
+            }
+
+            if( process_fp != NULL )
+            {
+                CPLDebug( "CEOS", "Opened %s.\n", pszFilename );
+
+                VSIFSeek( process_fp, 0, SEEK_END );
+                if( ProcessData( process_fp, iFile, psVolume, -1, 
+                                 VSIFTell( process_fp ) ) == 0 )
+                {
+                    switch( iFile )
+                    {
+                      case 0: psVolume->VolumeDirectoryFile = TRUE;
+                        break;
+                      case 1: psVolume->SARLeaderFile = TRUE;
+                        break;
+                      case 3: psVolume->SARTrailerFile = TRUE;
+                        break;
+                      case 4: psVolume->NullVolumeDirectoryFile = TRUE;
+                        break;
+                    }
+
+                    VSIFClose( process_fp );
+                    CPLFree( pszFilename );
+                    break; /* Exit the while loop, we have this data type*/
+                }
+                    
+                VSIFClose( process_fp );
+            }
+
+            CPLFree( pszFilename );
+
+            e++;
+        }
+    }
+
+    CPLFree( pszPath );
+    CPLFree( pszBasename );
+    CPLFree( pszExtension );
+
+/* -------------------------------------------------------------------- */
+/*      Check that we have an image description.                        */
+/* -------------------------------------------------------------------- */
+    struct CeosSARImageDesc   *psImageDesc;
+    GetCeosSARImageDesc( psVolume );
+    psImageDesc = &(psVolume->ImageDesc);
+    if( !psImageDesc->ImageDescValid )
+    {
+        delete poDS;
+
+        CPLDebug( "CEOS", 
+                  "Unable to extract CEOS image description\n"
+                  "from %s.", 
+                  poOpenInfo->pszFilename );
+
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Establish image type.                                           */
+/* -------------------------------------------------------------------- */
+    GDALDataType eType;
+
+    switch( psImageDesc->DataType )
+    {
+      case __CEOS_TYP_CHAR:
+      case __CEOS_TYP_UCHAR:
+        eType = GDT_Byte;
+        break;
+
+      case __CEOS_TYP_SHORT:
+        eType = GDT_Int16;
+        break;
+
+      case __CEOS_TYP_COMPLEX_SHORT:
+      case __CEOS_TYP_PALSAR_COMPLEX_SHORT:
+        eType = GDT_CInt16;
+        break;
+
+      case __CEOS_TYP_USHORT:
+        eType = GDT_UInt16;
+        break;
+
+      case __CEOS_TYP_LONG:
+        eType = GDT_Int32;
+        break;
+
+      case __CEOS_TYP_ULONG:
+        eType = GDT_UInt32;
+        break;
+
+      case __CEOS_TYP_FLOAT:
+        eType = GDT_Float32;
+        break;
+
+      case __CEOS_TYP_DOUBLE:
+        eType = GDT_Float64;
+        break;
+
+      case __CEOS_TYP_COMPLEX_FLOAT:
+      case __CEOS_TYP_CCP_COMPLEX_FLOAT:
+        eType = GDT_CFloat32;
+        break;
+
+      default:
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Unsupported CEOS image data type %d.\n", 
+                  psImageDesc->DataType );
+        delete poDS;
+        return NULL;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Capture some information from the file that is of interest.     */
+/* -------------------------------------------------------------------- */
+    poDS->nRasterXSize = psImageDesc->PixelsPerLine;
+    poDS->nRasterYSize = psImageDesc->Lines;
+
+#ifdef CPL_LSB
+    bNative = FALSE;
+#else
+    bNative = TRUE;
+#endif
+
+/* -------------------------------------------------------------------- */
+/*      Special case for compressed cross products.                     */
+/* -------------------------------------------------------------------- */
+    if( psImageDesc->DataType == __CEOS_TYP_CCP_COMPLEX_FLOAT )
+    {
+        for( int iBand = 0; iBand < psImageDesc->NumChannels; iBand++ )
+        {
+            poDS->SetBand( poDS->nBands+1, 
+                           new CCPRasterBand( poDS, poDS->nBands+1, eType ) );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Special case for PALSAR data.                                   */
+/* -------------------------------------------------------------------- */
+    else if( psImageDesc->DataType == __CEOS_TYP_PALSAR_COMPLEX_SHORT )
+    {
+        for( int iBand = 0; iBand < psImageDesc->NumChannels; iBand++ )
+        {
+            poDS->SetBand( poDS->nBands+1, 
+                           new PALSARRasterBand( poDS, poDS->nBands+1 ) );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Roll our own ...                                                */
+/* -------------------------------------------------------------------- */
+    else if( psImageDesc->RecordsPerLine > 1
+             || psImageDesc->DataType == __CEOS_TYP_CHAR
+             || psImageDesc->DataType == __CEOS_TYP_LONG
+             || psImageDesc->DataType == __CEOS_TYP_ULONG
+             || psImageDesc->DataType == __CEOS_TYP_DOUBLE )
+    {
+        for( int iBand = 0; iBand < psImageDesc->NumChannels; iBand++ )
+        {
+            poDS->SetBand( poDS->nBands+1, 
+                           new SAR_CEOSRasterBand( poDS, poDS->nBands+1, 
+                                                   eType ) );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Use raw services for well behaved files.                        */
+/* -------------------------------------------------------------------- */
+    else
+    {
+        int	StartData;
+        int	nLineSize, nLineSize2;
+
+        CalcCeosSARImageFilePosition( psVolume, 1, 1, NULL, &StartData );
+        
+        StartData += psImageDesc->ImageDataStart;
+
+        CalcCeosSARImageFilePosition( psVolume, 1, 1, NULL, &nLineSize );
+        CalcCeosSARImageFilePosition( psVolume, 1, 2, NULL, &nLineSize2 );
+
+        nLineSize = nLineSize2 - nLineSize;
+        
+        for( int iBand = 0; iBand < psImageDesc->NumChannels; iBand++ )
+        {
+            int           nStartData, nPixelOffset, nLineOffset;
+
+            if( psImageDesc->ChannelInterleaving == __CEOS_IL_PIXEL )
+            {
+                CalcCeosSARImageFilePosition(psVolume,1,1,NULL,&nStartData);
+
+                nStartData += psImageDesc->ImageDataStart;
+                nStartData += psImageDesc->BytesPerPixel * iBand;
+
+                nPixelOffset = 
+                    psImageDesc->BytesPerPixel * psImageDesc->NumChannels;
+                nLineOffset = nLineSize;
+            }
+            else if( psImageDesc->ChannelInterleaving == __CEOS_IL_LINE )
+            {
+                CalcCeosSARImageFilePosition(psVolume, iBand+1, 1, NULL,
+                                             &nStartData);
+
+                nStartData += psImageDesc->ImageDataStart;
+                nPixelOffset = psImageDesc->BytesPerPixel;
+                nLineOffset = nLineSize * psImageDesc->NumChannels;
+            }
+            else if( psImageDesc->ChannelInterleaving == __CEOS_IL_BAND )
+            {
+                CalcCeosSARImageFilePosition(psVolume, iBand+1, 1, NULL,
+                                             &nStartData);
+
+                nStartData += psImageDesc->ImageDataStart;
+                nPixelOffset = psImageDesc->BytesPerPixel;
+                nLineOffset = nLineSize;
+            }
+            else
+            {
+                CPLAssert( FALSE );
+                return NULL;
+            }
+
+            
+            poDS->SetBand( poDS->nBands+1, 
+                    new RawRasterBand( 
+                        poDS, poDS->nBands+1, poOpenInfo->fp, 
+                        nStartData, nPixelOffset, nLineOffset, 
+                        eType, bNative ) );
+        }
+        
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Adopt the file pointer.                                         */
+/* -------------------------------------------------------------------- */
+    poDS->fpImage = poOpenInfo->fp;
+    poOpenInfo->fp = NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Collect metadata.                                               */
+/* -------------------------------------------------------------------- */
+    poDS->ScanForMetadata();
+
+/* -------------------------------------------------------------------- */
+/*      Check for GCPs.                                                 */
+/* -------------------------------------------------------------------- */
+    poDS->ScanForGCPs();
+    
+/* -------------------------------------------------------------------- */
+/*      Open overviews.                                                 */
+/* -------------------------------------------------------------------- */
+    poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                            ProcessData()                             */
+/************************************************************************/
+static int 
+ProcessData( FILE *fp, int fileid, CeosSARVolume_t *sar, int max_records, 
+             int max_bytes )
+
+{
+    unsigned char      temp_buffer[__CEOS_HEADER_LENGTH];
+    unsigned char      *temp_body = NULL;
+    int                start = 0;
+    int                CurrentBodyLength = 0;
+    int                CurrentType = 0;
+    int                CurrentSequence = 0;
+    Link_t             *TheLink;
+    CeosRecord_t       *record;
+
+    while(max_records != 0 && max_bytes != 0)
+    {
+	record = (CeosRecord_t *) CPLMalloc( sizeof( CeosRecord_t ) );
+        VSIFSeek( fp, start, SEEK_SET );
+        VSIFRead( temp_buffer, 1, __CEOS_HEADER_LENGTH, fp );
+	record->Length = DetermineCeosRecordBodyLength( temp_buffer );
+
+	if( record->Length > CurrentBodyLength )
+	{
+	    if(CurrentBodyLength == 0 )
+            {
+		temp_body = (unsigned char *) CPLMalloc( record->Length );
+                CurrentBodyLength = record->Length;
+            }
+	    else
+	    {
+		temp_body = (unsigned char *) 
+                    CPLRealloc( temp_body, record->Length );
+                CurrentBodyLength = record->Length;
+            }
+	}
+
+        VSIFRead( temp_body, 1, record->Length - __CEOS_HEADER_LENGTH, fp );
+
+	InitCeosRecordWithHeader( record, temp_buffer, temp_body );
+
+	if( CurrentType == record->TypeCode.Int32Code )
+	    record->Subsequence = ++CurrentSequence;
+	else {
+	    CurrentType = record->TypeCode.Int32Code;
+	    record->Subsequence = CurrentSequence = 0;
+	}
+
+	record->FileId = fileid;
+
+	TheLink = ceos2CreateLink( record );
+
+	if( sar->RecordList == NULL )
+	    sar->RecordList = TheLink;
+	else
+	    sar->RecordList = InsertLink( sar->RecordList, TheLink );
+
+	start += record->Length;
+
+	if(max_records > 0)
+	    max_records--;
+	if(max_bytes > 0)
+        {
+	    max_bytes -= record->Length;
+            if(max_bytes < 0)
+                max_bytes = 0;
+        }
+    }
+
+    CPLFree(temp_body);
+
+    return 0;
+}
+
+/************************************************************************/
+/*                       GDALRegister_SAR_CEOS()                        */
+/************************************************************************/
+
+void GDALRegister_SAR_CEOS()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "SAR_CEOS" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "SAR_CEOS" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "CEOS SAR Image" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_various.html#SAR_CEOS" );
+
+        poDriver->pfnOpen = SAR_CEOSDataset::Open;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
diff --git a/Utilities/GDAL/frmts/dods/GNUmakefile b/Utilities/GDAL/frmts/dods/GNUmakefile
new file mode 100644
index 0000000000..9d5f146086
--- /dev/null
+++ b/Utilities/GDAL/frmts/dods/GNUmakefile
@@ -0,0 +1,35 @@
+
+
+include ../../GDALmake.opt
+
+OBJ	=	dodsdataset2.o 
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(CPPFLAGS) $(DODS_INC)
+
+default:	$(OBJ)
+
+# By linking with dodsdataset.o explicitly, we don't have to build the
+# library to get changes in that code into using_dods for testing. However,
+# make sure you do rebuild the library (run make two directories up) once
+# you're done. 12/27/02 jhrg
+using_dods: using_dods.o dodsdataset.o
+	$(LD) $(LNK_FLAGS) $^ $(XTRAOBJ) $(CONFIG_LIBS) -o $@$(EXE)
+
+# There's a note in GDALmake.opt that local programs should link against
+# CONFIG_LIBS, but that doesn't work here, maybe because dodsdataset_test
+# links statically (which it needs to do to run in a debugger). 10/31/03 jhrg 
+dodsdataset_test: dodsdataset_test.o dodsdataset.o
+	$(LD) $(LNK_FLAGS) -static -g3 $^ $(XTRAOBJ) $(LIBS) $(LIBGDAL) \
+		-lcppunit -lxml2 -o $@$(EXE)
+
+docs:
+	doxygen
+
+clean:
+	-rm -f *.o *.lo *~ $(O_OBJ)
+	-rm -rf docs
+	-rm -f dodsdataset_test using_dods
+
+install-obj:	$(O_OBJ)
+
+dodsdataset.o: dodsdataset.cpp dodsdataset.h
diff --git a/Utilities/GDAL/frmts/dods/dodsdataset2.cpp b/Utilities/GDAL/frmts/dods/dodsdataset2.cpp
new file mode 100644
index 0000000000..839dae8799
--- /dev/null
+++ b/Utilities/GDAL/frmts/dods/dodsdataset2.cpp
@@ -0,0 +1,1685 @@
+/******************************************************************************
+ * $Id: dodsdataset2.cpp,v 1.9 2006/05/02 17:46:32 fwarmerdam Exp $
+ *
+ * Project:  OPeNDAP Raster Driver
+ * Purpose:  Implements DODSDataset and DODSRasterBand classes.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
+ * Copyright (c) 2003 OPeNDAP, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * $Log: dodsdataset2.cpp,v $
+ * Revision 1.9  2006/05/02 17:46:32  fwarmerdam
+ * Added [-x] and [-y] clues for x/y flipping.
+ *
+ * Revision 1.8  2006/05/01 16:21:26  fwarmerdam
+ * update to work with libdap 1.6.2
+ *
+ * Revision 1.7  2006/02/19 16:22:20  fwarmerdam
+ * Added nodata support
+ *
+ * Revision 1.6  2005/07/18 14:13:12  fwarmerdam
+ * bug 891: added DEFAULT_BASETYPE_FACTORY define for 3.5.x compatability
+ *
+ * Revision 1.5  2005/04/09 00:34:35  fwarmerdam
+ * Added prototype Grid Map support for deriving geotransform.
+ *
+ * Revision 1.4  2005/01/14 22:51:18  fwarmerdam
+ * implemented better url constrains, flipping, and transposing
+ *
+ * Revision 1.3  2004/10/18 17:31:11  fwarmerdam
+ * minor tweaks to work with older DAP version
+ *
+ * Revision 1.2  2004/10/15 20:31:24  fwarmerdam
+ * Added most of the DAS information parsing support.
+ *
+ * Revision 1.1  2004/10/14 20:54:23  fwarmerdam
+ * New
+ *
+ */
+
+#include <string>
+#include <sstream>
+#include <algorithm>
+#include <exception>
+
+#include <debug.h>
+
+#include <BaseType.h>		// DODS
+#include <Byte.h>
+#include <Int16.h>
+#include <UInt16.h>
+#include <Int32.h>
+#include <UInt32.h>
+#include <Float32.h>
+#include <Float64.h>
+#include <Str.h>
+#include <Url.h>
+#include <Array.h>
+#include <Structure.h>
+#include <Sequence.h>
+#include <Grid.h>
+
+#include <AISConnect.h>		
+#include <DDS.h>
+#include <DAS.h>
+#include <BaseTypeFactory.h>
+#include <Error.h>
+#include <escaping.h>
+
+#include "gdal_priv.h"		// GDAL
+#include "ogr_spatialref.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: dodsdataset2.cpp,v 1.9 2006/05/02 17:46:32 fwarmerdam Exp $");
+
+CPL_C_START
+void GDALRegister_DODS(void);
+CPL_C_END
+
+/** Attribute names used to encode geo-referencing information. Note that
+    these are not C++ objects to avoid problems with static global
+    constructors. 
+
+    @see get_geo_info
+
+    @name Attribute names */
+//@{
+const char *nlat = "Northernmost_Latitude"; ///<
+const char *slat = "Southernmost_Latitude"; ///<
+const char *wlon = "Westernmost_Longitude"; ///<
+const char *elon = "Easternmost_Longitude"; ///<
+const char *gcs = "GeographicCS";	    ///<
+const char *pcs = "ProjectionCS";	    ///<
+const char *norm_proj_param = "Norm_Proj_Param"; ///<
+const char *spatial_ref = "spatial_ref";    ///<
+//@}
+
+/************************************************************************/
+/*                            get_variable()                            */
+/************************************************************************/
+
+/** Find the variable in the DDS or DataDDS, given its name. This function
+    first looks for the name as given. If that can't be found, it determines
+    the leaf name of a fully qualified name and looks for that (the DAP
+    supports searching for leaf names as a short cut). In this case the
+    driver is using that feature because of an odd problem in the responses
+    returned by some servers when they are asked for a single array variable
+    from a Grid. Instead of returning GRID_NAME.ARRAY_NAME, they return just
+    ARRAY_NAME. That's really a bug in the spec. However, it means that if a
+    CE says GRID_NAME.ARRAY_NAME and the code looks only for that, it may not
+    be found because the nesting has been removed and only an array called
+    ARRAY_NAME returned.
+
+    @param dds Look in this DDS object
+    @param n Names the variable to find.
+    @return A BaseType pointer to the object/variable in the DDS \e dds. */
+
+static BaseType *
+get_variable(DDS &dds, const string &n)
+{
+    BaseType *poBT = dds.var(www2id(n));
+    if (!poBT) {
+	try {
+	    string leaf = n.substr(n.find_last_of('.')+1);
+	    poBT = dds.var(www2id(leaf));
+	}
+	catch (const std::exception &e) {
+	    poBT = 0;
+	}
+    }
+
+    return poBT;
+}
+
+/************************************************************************/
+/*                            StripQuotes()                             */
+/*                                                                      */
+/*      Strip the quotes off a string value and remove internal         */
+/*      quote escaping.                                                 */
+/************************************************************************/
+
+static string StripQuotes( string oInput )
+
+{
+    char *pszResult;
+
+    if( oInput.length() < 2 )
+        return oInput;
+
+    oInput = oInput.substr(1,oInput.length()-2);
+
+    pszResult = CPLUnescapeString( oInput.c_str(), NULL, 
+                                   CPLES_BackslashQuotable );
+
+    oInput = pszResult;
+    
+    CPLFree( pszResult );
+
+    return oInput;
+}
+
+/************************************************************************/
+/*                            GetDimension()                            */
+/*                                                                      */
+/*      Get the dimension for the named constrain dimension, -1 is      */
+/*      returned if not found.  We would pass in a CE like              */
+/*      "[band][x][y]" or "[1][x][y]" and a dimension name like "y"     */
+/*      and get back the dimension index (2 if it is the 3rd            */
+/*      dimension).                                                     */
+/*                                                                      */
+/*      eg. GetDimension("[1][y][x]","y") -> 1                          */
+/************************************************************************/
+
+static int GetDimension( string oCE, const char *pszDimName,
+                         int *pnDirection )
+
+{
+    int iCount = 0, i;
+    const char *pszCE = oCE.c_str();
+
+    if( pnDirection != NULL )
+        *pnDirection = 1;
+
+    for( i = 0; pszCE[i] != '\0'; i++ )
+    {
+        if( pszCE[i] == '[' && pszCE[i+1] == pszDimName[0] )
+            return iCount;
+
+        else if( pszCE[i] == '[' 
+                 && pszCE[i+1] == '-'
+                 && pszCE[i+2] == pszDimName[0] )
+        {
+            if( pnDirection != NULL )
+                *pnDirection = -1;
+
+            return iCount;
+        }
+        else if( pszCE[i] == '[' )
+            iCount++;
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                              DODSDataset                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class DODSDataset : public GDALDataset
+{
+private:
+    AISConnect *poConnect; 	//< Virtual connection to the data source
+
+    string oURL;		//< data source URL
+    double adfGeoTransform[6];
+    int    bGotGeoTransform;
+    string oWKT;		//< Constructed WKT string
+
+    DAS    oDAS;
+    DDS   *poDDS;
+    BaseTypeFactory *poBaseTypeFactory;
+
+    AISConnect *connect_to_server() throw(Error);
+
+    static string      SubConstraint( string raw_constraint, 
+                                      string x_constraint, 
+                                      string y_constraint );
+
+    char            **CollectBandsFromDDS();
+    char            **CollectBandsFromDDSVar( string, char ** );
+    char            **ParseBandsFromURL( string );
+
+    void            HarvestDAS();
+    static void     HarvestMetadata( GDALMajorObject *, AttrTable * );
+    void            HarvestMaps( string oVarName, string oCE );
+    
+    friend class DODSRasterBand;
+
+public:
+                    DODSDataset();
+    virtual        ~DODSDataset();
+
+    // Overridden GDALDataset methods
+    CPLErr GetGeoTransform(double *padfTransform);
+    const char *GetProjectionRef();
+
+    /// Open is not a method in GDALDataset; it's the driver.
+    static GDALDataset *Open(GDALOpenInfo *);
+
+    /// Return the connection object
+    AISConnect *GetConnect() { return poConnect; }
+
+    /// Return the data source URL
+    string GetUrl() { return oURL; }
+    DAS &GetDAS() { return oDAS; }
+    DDS &GetDDS() { return *poDDS; }
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            DODSRasterBand                            */
+/* ==================================================================== */
+/************************************************************************/
+
+class DODSRasterBand : public GDALRasterBand 
+{
+private:
+    string oVarName;          
+    string oCE;		        // Holds the CE (with [x] and [y] still there
+
+    friend class DODSDataset;
+
+    GDALColorInterp eColorInterp;
+    GDALColorTable  *poCT;
+
+    int		   nOverviewCount;
+    DODSRasterBand **papoOverviewBand;
+
+    int    nOverviewFactor;     // 1 for base, or 2/4/8 for overviews.
+    int    bTranspose;
+    int    bFlipX;
+    int    bFlipY;
+		int    bNoDataSet;
+    double dfNoDataValue;
+
+    void            HarvestDAS();
+
+public:
+    DODSRasterBand( DODSDataset *poDS, string oVarName, string oCE, 
+                    int nOverviewFactor );
+    virtual ~DODSRasterBand();
+
+    virtual int    GetOverviewCount();
+    virtual GDALRasterBand *GetOverview( int );
+    virtual CPLErr IReadBlock(int, int, void *);
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual GDALColorTable *GetColorTable();
+		virtual CPLErr          SetNoDataValue( double );
+    virtual double          GetNoDataValue( int * );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                              DODSDataset                             */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                            DODSDataset()                             */
+/************************************************************************/
+
+DODSDataset::DODSDataset()
+
+{
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+    bGotGeoTransform = FALSE;
+
+    poConnect = NULL;
+    poBaseTypeFactory = new BaseTypeFactory();
+    poDDS = new DDS( poBaseTypeFactory );
+}
+
+/************************************************************************/
+/*                            ~DODSDataset()                            */
+/************************************************************************/
+
+DODSDataset::~DODSDataset()
+
+{
+    if( poConnect )
+        delete poConnect;
+
+    if( poDDS )
+        delete poDDS;
+    if( poBaseTypeFactory )
+        delete poBaseTypeFactory;
+}
+
+/************************************************************************/
+/*                         connect_to_server()                          */
+/************************************************************************/
+
+AISConnect *
+DODSDataset::connect_to_server() throw(Error)
+{
+    // does the string start with 'http?'
+    if (oURL.find("http://") == string::npos
+	&& oURL.find("https://") == string::npos)
+	throw Error(
+            "The URL does not start with 'http' or 'https,' I won't try connecting.");
+
+/* -------------------------------------------------------------------- */
+/*      Do we want to override the .dodsrc file setting?  Only do       */
+/*      the putenv() if there isn't already a DODS_CONF in the          */
+/*      environment.                                                    */
+/* -------------------------------------------------------------------- */
+    if( CPLGetConfigOption( "DODS_CONF", NULL ) != NULL 
+        && getenv("DODS_CONF") == NULL )
+    {
+        static char szDODS_CONF[1000];
+            
+        sprintf( szDODS_CONF, "DODS_CONF=%.980s", 
+                 CPLGetConfigOption( "DODS_CONF", "" ) );
+        putenv( szDODS_CONF );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If we have a overridding AIS file location, apply it now.       */
+/* -------------------------------------------------------------------- */
+    if( CPLGetConfigOption( "DODS_AIS_FILE", NULL ) != NULL )
+    {
+        string oAISFile = CPLGetConfigOption( "DODS_AIS_FILE", "" );
+        RCReader::instance()->set_ais_database( oAISFile );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Connect, and fetch version information.                         */
+/* -------------------------------------------------------------------- */
+    AISConnect *poConnection = new AISConnect(oURL);
+    string version = poConnection->request_version();
+    if (version.empty() || version.find("/3.") == string::npos)
+    {
+        CPLError( CE_Warning, CPLE_AppDefined, 
+                  "I connected to the URL but could not get a DAP 3.x version string\n"
+                  "from the server.  I will continue to connect but access may fail.");
+    }
+
+    return poConnection;
+}
+
+/************************************************************************/
+/*                           SubConstraint()                            */
+/*                                                                      */
+/*      Substitute into x and y constraint expressions in template      */
+/*      constraint string for the [x] and [y] parts.                    */
+/************************************************************************/
+
+string DODSDataset::SubConstraint( string raw_constraint, 
+                                   string x_constraint, 
+                                   string y_constraint )
+
+{
+    string::size_type x_off, y_off, x_len=3, y_len=3;
+    string final_constraint;
+
+    x_off = raw_constraint.find( "[x]" );
+    if( x_off == string::npos )
+    {
+        x_off = raw_constraint.find( "[-x]" );
+        x_len = 4;
+    }
+
+    y_off = raw_constraint.find( "[y]" );
+    if( y_off == string::npos )
+    {
+        y_off = raw_constraint.find( "[-y]" );
+        y_len = 4;
+    }
+
+    CPLAssert( x_off != string::npos && y_off != string::npos );
+
+    if( x_off < y_off )
+        final_constraint = 
+            raw_constraint.substr( 0, x_off )
+            + x_constraint
+            + raw_constraint.substr( x_off + x_len, y_off - x_off - x_len )
+            + y_constraint
+            + raw_constraint.substr( y_off + y_len );
+    else
+        final_constraint = 
+            raw_constraint.substr( 0, y_off )
+            + y_constraint
+            + raw_constraint.substr( y_off + y_len, x_off - y_off - y_len )
+            + x_constraint
+            + raw_constraint.substr( x_off + x_len );
+
+    return final_constraint;
+}
+
+/************************************************************************/
+/*                        CollectBandsFromDDS()                         */
+/*                                                                      */
+/*      If no constraint/variable list is provided we will scan the     */
+/*      DDS output for arrays or grids that look like bands and         */
+/*      return the list of them with "guessed" [y][x] constraint        */
+/*      strings.                                                        */
+/*                                                                      */
+/*      We pick arrays or grids with at least two dimensions as         */
+/*      candidates.  After the first we only accept additional          */
+/*      objects as bands if they match the size of the original.        */
+/*                                                                      */
+/*      Auto-recognision rules will presumably evolve over time to      */
+/*      recognise different common configurations and to support        */
+/*      more variations.                                                */
+/************************************************************************/
+
+char **DODSDataset::CollectBandsFromDDS()
+
+{
+    DDS::Vars_iter v_i;
+    char **papszResultList = NULL;
+
+    for( v_i = poDDS->var_begin(); v_i != poDDS->var_end(); v_i++ )
+    {
+        BaseType *poVar = *v_i;
+        papszResultList = CollectBandsFromDDSVar( poVar->name(), 
+                                                  papszResultList );
+    }
+
+    return papszResultList;
+}
+
+/************************************************************************/
+/*                       CollectBandsFromDDSVar()                       */
+/*                                                                      */
+/*      Collect zero or more band definitions (varname + CE) for the    */
+/*      passed variable.  If it is inappropriate then nothing is        */
+/*      added to the list.  This method is shared by                    */
+/*      CollectBandsFromDDS(), and by ParseBandsFromURL() when it       */
+/*      needs a default constraint expression generated.                */
+/************************************************************************/
+
+char **DODSDataset::CollectBandsFromDDSVar( string oVarName, 
+                                            char **papszResultList )
+
+{
+    Array *poArray;
+    Grid *poGrid = NULL;
+    
+/* -------------------------------------------------------------------- */
+/*      Is this a grid or array?                                        */
+/* -------------------------------------------------------------------- */
+    BaseType *poVar = get_variable( GetDDS(), oVarName );
+
+    if( poVar->type() == dods_array_c )
+    {
+        poGrid = NULL;
+        poArray = dynamic_cast<Array *>( poVar );
+    }
+    else if( poVar->type() == dods_grid_c )
+    {
+        poGrid = dynamic_cast<Grid *>( poVar );
+        poArray = dynamic_cast<Array *>( poGrid->array_var() );
+    }
+    else
+        return papszResultList;
+
+/* -------------------------------------------------------------------- */
+/*      Eventually we will want to support arrays with more than two    */
+/*      dimensions ... but not quite yet.                               */
+/* -------------------------------------------------------------------- */
+    if( poArray->dimensions() != 2 )
+        return papszResultList;
+
+/* -------------------------------------------------------------------- */
+/*      Get the dimension information for this variable.                */
+/* -------------------------------------------------------------------- */
+    Array::Dim_iter dim1 = poArray->dim_begin() + 0;
+    Array::Dim_iter dim2 = poArray->dim_begin() + 1;
+    
+    int nDim1Size = poArray->dimension_size( dim1 );
+    int nDim2Size = poArray->dimension_size( dim2 );
+    
+    if( nDim1Size == 1 || nDim2Size == 1 )
+        return papszResultList;
+
+/* -------------------------------------------------------------------- */
+/*      Try to guess which is x and y.                                  */
+/* -------------------------------------------------------------------- */
+    string dim1_name = poArray->dimension_name( dim1 );
+    string dim2_name = poArray->dimension_name( dim2 );
+    int iXDim=-1, iYDim=-1;
+
+    if( dim1_name == "easting" && dim2_name == "northing" )
+    {
+        iXDim = 0;
+        iYDim = 1;
+    }
+    else if( dim1_name == "easting" && dim2_name == "northing" )
+    {
+        iXDim = 1;
+        iYDim = 0;
+    }
+    else if( EQUALN(dim1_name.c_str(),"lat",3) 
+             && EQUALN(dim2_name.c_str(),"lon",3) )
+    {
+        iXDim = 0;
+        iYDim = 1;
+    }
+    else if( EQUALN(dim1_name.c_str(),"lon",3) 
+             && EQUALN(dim2_name.c_str(),"lat",3) )
+    {
+        iXDim = 1;
+        iYDim = 0;
+    }
+    else 
+    {
+        iYDim = 0;
+        iXDim = 1;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Does this match the established dimension?                      */
+/* -------------------------------------------------------------------- */
+    Array::Dim_iter dimx = poArray->dim_begin() + iXDim;
+    Array::Dim_iter dimy = poArray->dim_begin() + iYDim;
+
+    if( nRasterXSize == 0 && nRasterYSize == 0 )
+    {
+        nRasterXSize = poArray->dimension_size( dimx );
+        nRasterYSize = poArray->dimension_size( dimy );
+    }
+
+    if( nRasterXSize != poArray->dimension_size( dimx )
+        || nRasterYSize != poArray->dimension_size( dimy ) )
+        return papszResultList;
+
+/* -------------------------------------------------------------------- */
+/*      OK, we have an acceptable candidate!                            */
+/* -------------------------------------------------------------------- */
+    string oConstraint;
+
+    if( iXDim == 0 && iYDim == 1 )
+        oConstraint = "[x][y]";
+    else if( iXDim == 1 && iYDim == 0 )
+        oConstraint = "[y][x]";
+    else
+        return papszResultList;
+
+    papszResultList = CSLAddString( papszResultList, 
+                                    poVar->name().c_str() );
+    papszResultList = CSLAddString( papszResultList, 
+                                    oConstraint.c_str() );
+
+    return papszResultList;
+}
+
+/************************************************************************/
+/*                         ParseBandsFromURL()                          */
+/************************************************************************/
+
+char **DODSDataset::ParseBandsFromURL( string oVarList )
+
+{
+    char **papszResultList = NULL;
+    char **papszVars = CSLTokenizeString2( oVarList.c_str(), ",", 0 );
+    int i;
+
+    for( i = 0; papszVars != NULL && papszVars[i] != NULL; i++ )
+    {
+        string oVarName;
+        string oCE;
+
+/* -------------------------------------------------------------------- */
+/*      Split into a varname and constraint equation.                   */
+/* -------------------------------------------------------------------- */
+        char *pszCEStart = strstr(papszVars[i],"[");
+
+        // If we have no constraints we will have to try to guess
+        // reasonable values from the DDS.  In fact, we might end up
+        // deriving multiple bands from one variable in this case.
+        if( pszCEStart == NULL )
+        {
+            oVarName = papszVars[i];
+
+            papszResultList = 
+                CollectBandsFromDDSVar( oVarName, papszResultList );
+        }
+        else
+        {
+            oCE = pszCEStart;
+            *pszCEStart = '\0';
+            oVarName = papszVars[i];
+
+            // Eventually we should consider supporting a [band] keyword
+            // to select a constraint variable that should be used to
+            // identify a band dimension ... but not for now. 
+
+            papszResultList = CSLAddString( papszResultList,
+                                            oVarName.c_str() );
+            papszResultList = CSLAddString( papszResultList, 
+                                            oCE.c_str() );
+        }
+    }
+
+    return papszResultList;
+}
+
+/************************************************************************/
+/*                          HarvestMetadata()                           */
+/*                                                                      */
+/*      Capture metadata items from an AttrTable, and assign as         */
+/*      metadata to the target object.                                  */
+/************************************************************************/
+
+void DODSDataset::HarvestMetadata( GDALMajorObject *poTarget, 
+                                   AttrTable *poSrcTable )
+
+{
+    if( poSrcTable == NULL )
+        return;
+
+/* -------------------------------------------------------------------- */
+/*      Find Metadata container.                                        */
+/* -------------------------------------------------------------------- */
+    AttrTable *poMDTable = poSrcTable->find_container("Metadata");
+    
+    if( poMDTable == NULL )
+        return;
+
+/* -------------------------------------------------------------------- */
+/*      Collect each data item from it.                                 */
+/* -------------------------------------------------------------------- */
+    AttrTable::Attr_iter dv_i;
+
+    for( dv_i=poMDTable->attr_begin(); dv_i != poMDTable->attr_end(); dv_i++ )
+    {
+        if( poMDTable->get_attr_type( dv_i ) != Attr_string )
+            continue;
+
+        poTarget->SetMetadataItem( 
+            poMDTable->get_name( dv_i ).c_str(), 
+            StripQuotes( poMDTable->get_attr(dv_i) ).c_str() );
+    }
+}
+
+/************************************************************************/
+/*                             HarvestDAS()                             */
+/************************************************************************/
+
+void DODSDataset::HarvestDAS()
+
+{
+/* -------------------------------------------------------------------- */
+/*      Try and fetch the corresponding DAS subtree if it exists.       */
+/* -------------------------------------------------------------------- */
+    AttrTable *poFileInfo = oDAS.find_container( "GLOBAL" );
+
+    if( poFileInfo == NULL )
+    {
+        CPLDebug( "DODS", "No GLOBAL DAS info." );
+        return;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try and fetch the bounds                                        */
+/* -------------------------------------------------------------------- */
+    string oNorth, oSouth, oEast, oWest;
+
+    oNorth = poFileInfo->get_attr( "Northernmost_Northing" );
+    oSouth = poFileInfo->get_attr( "Southernmost_Northing" );
+    oEast = poFileInfo->get_attr( "Easternmost_Easting" );
+    oWest = poFileInfo->get_attr( "Westernmost_Easting" );
+
+    if( oNorth != "" && oSouth != "" && oEast != "" && oWest != "" )
+    {
+        adfGeoTransform[0] = atof(oWest.c_str());
+        adfGeoTransform[1] = 
+            (atof(oEast.c_str()) - atof(oWest.c_str())) / nRasterXSize;
+        adfGeoTransform[2] = 0.0;
+        adfGeoTransform[3] = atof(oNorth.c_str());
+        adfGeoTransform[4] = 0.0;
+        adfGeoTransform[5] = 
+            (atof(oSouth.c_str()) - atof(oNorth.c_str())) / nRasterYSize;
+
+        bGotGeoTransform = TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try and fetch a GeoTransform.  The result will override the     */
+/*      geotransform derived from the bounds if it is present.  This    */
+/*      allows us to represent rotated and sheared images.              */
+/* -------------------------------------------------------------------- */
+    string oValue;
+
+    oValue = StripQuotes(poFileInfo->get_attr( "GeoTransform" ));
+    if( oValue != "" )
+    {
+        char **papszItems = CSLTokenizeString( oValue.c_str() );
+        if( CSLCount(papszItems) == 6 )
+        {
+            adfGeoTransform[0] = atof(papszItems[0]);
+            adfGeoTransform[1] = atof(papszItems[1]);
+            adfGeoTransform[2] = atof(papszItems[2]);
+            adfGeoTransform[3] = atof(papszItems[3]);
+            adfGeoTransform[4] = atof(papszItems[4]);
+            adfGeoTransform[5] = atof(papszItems[5]);
+            bGotGeoTransform = TRUE;
+        }
+        else
+        {
+            CPLError( CE_Warning, CPLE_AppDefined, 
+                      "Failed to parse GeoTransform DAS value: %s",
+                      oValue.c_str() );
+        }
+        CSLDestroy( papszItems );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get the Projection.  If it doesn't look like "pure" WKT then    */
+/*      try to process it through SetFromUserInput().  This expands     */
+/*      stuff like "WGS84".                                             */
+/* -------------------------------------------------------------------- */
+    oWKT = StripQuotes(poFileInfo->get_attr( "spatial_ref" ));
+    if( oWKT.length() > 0 
+        && !EQUALN(oWKT.c_str(),"GEOGCS",6)
+        && !EQUALN(oWKT.c_str(),"PROJCS",6)
+        && !EQUALN(oWKT.c_str(),"LOCAL_CS",8) )
+    {
+        OGRSpatialReference oSRS;
+
+        if( oSRS.SetFromUserInput( oWKT.c_str() ) != OGRERR_NONE )
+        {
+            CPLError( CE_Warning, CPLE_AppDefined, 
+                      "Failed to recognise 'spatial_ref' value of: %s", 
+                      oWKT.c_str() );
+            oWKT = "";
+        }
+        else
+        {
+            char *pszProcessedWKT = NULL;
+            oSRS.exportToWkt( &pszProcessedWKT );
+            oWKT = pszProcessedWKT;
+            CPLFree( pszProcessedWKT );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Collect Metadata.                                               */
+/* -------------------------------------------------------------------- */
+    HarvestMetadata( this, poFileInfo );
+}
+
+/************************************************************************/
+/*                            HarvestMaps()                             */
+/************************************************************************/
+
+void DODSDataset::HarvestMaps( string oVarName, string oCE )
+
+{
+    BaseType *poDDSDef = get_variable( GetDDS(), oVarName );
+    if( poDDSDef == NULL || poDDSDef->type() != dods_grid_c )
+        return;
+
+/* -------------------------------------------------------------------- */
+/*      Get the grid.                                                   */
+/* -------------------------------------------------------------------- */
+    Grid  *poGrid = NULL;
+    poGrid = dynamic_cast<Grid *>( poDDSDef );
+
+/* -------------------------------------------------------------------- */
+/*      Get the map arrays for x and y.                                 */
+/* -------------------------------------------------------------------- */
+    Array *poXMap = NULL, *poYMap = NULL;
+    int iXDim = GetDimension( oCE, "x", NULL );
+    int iYDim = GetDimension( oCE, "y", NULL );
+    int iMap;
+    Grid::Map_iter iterMap;
+    
+    for( iterMap = poGrid->map_begin(), iMap = 0;
+         iterMap != poGrid->map_end(); 
+         iterMap++, iMap++ )
+    {
+        if( iMap == iXDim )
+            poXMap = dynamic_cast<Array *>(*iterMap);
+        else if( iMap == iYDim )
+            poYMap = dynamic_cast<Array *>(*iterMap);
+    }
+
+    if( poXMap == NULL || poYMap == NULL )
+        return;
+
+    if( poXMap->var()->type() != dods_float64_c 
+        || poYMap->var()->type() != dods_float64_c )
+    {
+        CPLDebug( "DODS", "Ignoring Grid Map - not a supported data type." );
+        return;							       
+    }
+
+/* -------------------------------------------------------------------- */
+/*      TODO: We ought to validate the dimension of the map against our */
+/*      expected size.                                                  */
+/* -------------------------------------------------------------------- */
+
+/* -------------------------------------------------------------------- 
+ *  Fetch maps.  We need to construct a seperate request like:     
+ *    http://dods.gso.uri.edu/cgi-bin/nph-dods/MCSST/Northwest_Atlantic/5km/raw/1982/9/m82258070000.pvu.Z?dsp_band_1.lat,dsp_band_1.lon
+ *
+ *  to fetch just the maps, and not the actual dataset. 
+ * -------------------------------------------------------------------- */
+/* -------------------------------------------------------------------- */
+/*      Build constraint expression.                                    */
+/* -------------------------------------------------------------------- */
+    string constraint;
+    string xdimname = "lon";
+    string ydimname = "lat";
+    
+
+    constraint = oVarName + "." + xdimname + "," 
+        + oVarName + "." + ydimname;
+
+/* -------------------------------------------------------------------- */
+/*      Request data from server.                                       */
+/* -------------------------------------------------------------------- */
+    DataDDS data( poBaseTypeFactory );
+    
+    GetConnect()->request_data(data, constraint );
+
+/* -------------------------------------------------------------------- */
+/*      Get the DataDDS Array object from the response.                 */
+/* -------------------------------------------------------------------- */
+    BaseType *poBtX = get_variable(data, oVarName + "." + xdimname );
+    BaseType *poBtY = get_variable(data, oVarName + "." + ydimname );
+    if (!poBtX || !poBtY 
+        || poBtX->type() != dods_array_c 
+        || poBtY->type() != dods_array_c )
+        return;
+
+    Array *poAX = dynamic_cast<Array *>(poBtX);
+    Array *poAY = dynamic_cast<Array *>(poBtY);
+
+/* -------------------------------------------------------------------- */
+/*      Pre-initialize the output buffer to zero.                       */
+/* -------------------------------------------------------------------- */
+    double *padfXMap, *padfYMap;
+
+    padfXMap = (double *) CPLCalloc(sizeof(double),nRasterXSize );
+    padfYMap = (double *) CPLCalloc(sizeof(double),nRasterYSize );
+
+/* -------------------------------------------------------------------- */
+/*      Dump the contents of the Array data into our output image       */
+/*      buffer.                                                         */
+/* -------------------------------------------------------------------- */
+    poAX->buf2val( (void **) &padfXMap );
+    poAY->buf2val( (void **) &padfYMap );
+
+/* -------------------------------------------------------------------- */
+/*      Compute a geotransform from the maps.  We are implicitly        */
+/*      assuming the maps are linear and refer to the center of the     */
+/*      pixels.                                                         */
+/* -------------------------------------------------------------------- */
+    bGotGeoTransform = TRUE;
+
+    // pixel size. 
+    adfGeoTransform[1] = (padfXMap[nRasterXSize-1] - padfXMap[0])
+        / (nRasterXSize-1);
+    adfGeoTransform[5] = (padfYMap[nRasterYSize-1] - padfYMap[0])
+        / (nRasterYSize-1);
+
+    // rotational coefficients. 
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[4] = 0.0;
+
+    // origin/ 
+    adfGeoTransform[0] = padfXMap[0] - adfGeoTransform[1] * 0.5;
+    adfGeoTransform[3] = padfYMap[0] - adfGeoTransform[5] * 0.5;
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *
+DODSDataset::Open(GDALOpenInfo *poOpenInfo)
+{
+    if( !EQUALN(poOpenInfo->pszFilename,"http://",7) 
+        && !EQUALN(poOpenInfo->pszFilename,"https://",8) )
+        return NULL;
+
+    
+    DODSDataset *poDS = new DODSDataset();
+
+    poDS->nRasterXSize = 0;
+    poDS->nRasterYSize = 0;
+
+    try {
+/* -------------------------------------------------------------------- */
+/*      Split the URL from the projection/CE portion of the name.       */
+/* -------------------------------------------------------------------- */
+        string oWholeName( poOpenInfo->pszFilename );
+        string oVarList;
+        string::size_type t_char;
+
+        t_char = oWholeName.find('?');
+        if( t_char == string::npos )
+        {
+            oVarList = "";
+            poDS->oURL = oWholeName;
+        }
+        else
+        {
+            poDS->oURL = oWholeName.substr(0,t_char);
+            oVarList = oWholeName.substr(t_char+1,oWholeName.length());
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Get the AISConnect instance and the DAS and DDS for this        */
+/*      server.                                                         */
+/* -------------------------------------------------------------------- */
+	poDS->poConnect = poDS->connect_to_server();
+	poDS->poConnect->request_das(poDS->oDAS);
+	poDS->poConnect->request_dds(*(poDS->poDDS));
+
+/* -------------------------------------------------------------------- */
+/*      If we are given a constraint/projection list, then parse it     */
+/*      into a list of varname/constraint pairs.  Otherwise walk the    */
+/*      DDS and try to identify grids or arrays that are good           */
+/*      targets and return them in the same format.                     */
+/* -------------------------------------------------------------------- */
+        char **papszVarConstraintList = NULL;
+
+        if( oVarList.length() == 0 )
+            papszVarConstraintList = poDS->CollectBandsFromDDS();
+        else
+            papszVarConstraintList = poDS->ParseBandsFromURL( oVarList );
+
+/* -------------------------------------------------------------------- */
+/*      Did we get any target variables?                                */
+/* -------------------------------------------------------------------- */
+        if( CSLCount(papszVarConstraintList) == 0 )
+            throw Error( "No apparent raster grids or arrays found in DDS.");
+
+/* -------------------------------------------------------------------- */
+/*      For now we support only a single band.                          */
+/* -------------------------------------------------------------------- */
+        DODSRasterBand *poBaseBand = 
+            new DODSRasterBand(poDS, 
+                               string(papszVarConstraintList[0]),
+                               string(papszVarConstraintList[1]), 
+                               1 );
+
+        poDS->nRasterXSize = poBaseBand->GetXSize();
+        poDS->nRasterYSize = poBaseBand->GetYSize();
+            
+	poDS->SetBand(1, poBaseBand );
+
+        for( int iBand = 1; papszVarConstraintList[iBand*2] != NULL; iBand++ )
+        {
+            poDS->SetBand( iBand+1, 
+                           new DODSRasterBand(poDS, 
+                                   string(papszVarConstraintList[iBand*2+0]),
+                                   string(papszVarConstraintList[iBand*2+1]), 
+                                              1 ) );
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Harvest DAS dataset level information including                 */
+/*      georeferencing, and metadata.                                   */
+/* -------------------------------------------------------------------- */
+        poDS->HarvestDAS();
+
+/* -------------------------------------------------------------------- */
+/*      If we don't have georeferencing, look for "map" information     */
+/*      for a grid.                                                     */
+/* -------------------------------------------------------------------- */
+        if( !poDS->bGotGeoTransform )
+        {
+            poDS->HarvestMaps( string(papszVarConstraintList[0]),
+                               string(papszVarConstraintList[1]) );
+        }
+    }
+
+    catch (Error &e) {
+	string msg =
+"An error occurred while creating a virtual connection to the DAP server:\n";
+	msg += e.get_error_message();
+        CPLError(CE_Failure, CPLE_AppDefined, msg.c_str());
+	return 0;
+    }
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr 
+DODSDataset::GetGeoTransform( double * padfTransform )
+{
+    memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
+
+    return bGotGeoTransform ? CE_None : CE_Failure;
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *
+DODSDataset::GetProjectionRef()
+{
+    return oWKT.c_str();
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            DODSRasterBand                            */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                           DODSRasterBand()                           */
+/************************************************************************/
+
+DODSRasterBand::DODSRasterBand(DODSDataset *poDSIn, string oVarNameIn, 
+                               string oCEIn, int nOverviewFactorIn )
+{
+    poDS = poDSIn;
+
+    bTranspose = FALSE;
+    bFlipX = FALSE;
+    bFlipY = FALSE;
+
+    oVarName = oVarNameIn;
+    oCE = oCEIn;
+    nOverviewFactor = nOverviewFactorIn;
+    eColorInterp = GCI_Undefined;
+    poCT = NULL;
+
+    nOverviewCount = 0;
+    papoOverviewBand = NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Fetch the DDS definition, and isolate the Array.                */
+/* -------------------------------------------------------------------- */
+    BaseType *poDDSDef = get_variable( poDSIn->GetDDS(), oVarNameIn );
+    if( poDDSDef == NULL )
+    {
+        throw InternalErr(
+            CPLSPrintf( "Could not find DDS definition for variable %s.", 
+                        oVarNameIn.c_str() ) );
+        return;
+    }
+
+    Array *poArray = NULL;
+    Grid  *poGrid = NULL;
+
+    if( poDDSDef->type() == dods_grid_c )
+    {
+        poGrid = dynamic_cast<Grid *>( poDDSDef );
+        poArray = dynamic_cast<Array *>( poGrid->array_var() );
+    }
+    else if( poDDSDef->type() == dods_array_c )
+    {
+        poArray = dynamic_cast<Array *>( poDDSDef );
+    }
+    else
+    {
+        throw InternalErr(
+            CPLSPrintf( "Variable %s is not a grid or an array.",
+                        oVarNameIn.c_str() ) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Determine the datatype.                                         */
+/* -------------------------------------------------------------------- */
+    
+    // Now grab the data type of the variable.
+    switch (poArray->var()->type()) {
+      case dods_byte_c: eDataType = GDT_Byte; break;
+      case dods_int16_c: eDataType = GDT_Int16; break;
+      case dods_uint16_c: eDataType = GDT_UInt16; break;
+      case dods_int32_c: eDataType = GDT_Int32; break;
+      case dods_uint32_c: eDataType = GDT_UInt32; break;
+      case dods_float32_c: eDataType = GDT_Float32; break;
+      case dods_float64_c: eDataType = GDT_Float64; break;
+      default:
+	throw Error("The DODS GDAL driver supports only numeric data types.");
+    }
+
+/* -------------------------------------------------------------------- */
+/*      For now we hard code to assume that the two dimensions are      */
+/*      ysize and xsize.                                                */
+/* -------------------------------------------------------------------- */
+    if( poArray->dimensions() < 2 )
+    {
+        throw Error("Variable does not have even 2 dimensions.  For now this is required." );
+    }
+
+    int nXDir = 1, nYDir = 1;
+    int iXDim = GetDimension( oCE, "x", &nXDir );
+    int iYDim = GetDimension( oCE, "y", &nYDir );
+
+    if( iXDim == -1 || iYDim == -1 )
+    {
+        throw Error("Missing [x] or [y] in constraint." );
+    }
+
+    Array::Dim_iter x_dim = poArray->dim_begin() + iXDim;
+    Array::Dim_iter y_dim = poArray->dim_begin() + iYDim;
+
+    nRasterXSize = poArray->dimension_size( x_dim ) / nOverviewFactor;
+    nRasterYSize = poArray->dimension_size( y_dim ) / nOverviewFactor;
+
+    bTranspose = iXDim < iYDim;
+
+    bFlipX = (nXDir == -1);
+    bFlipY = (nYDir == -1);
+
+/* -------------------------------------------------------------------- */
+/*      Decide on a block size.  We aim for a block size of roughly     */
+/*      256K.  This should be a big enough chunk to justify a           */
+/*      roundtrip to get the data, but small enough to avoid reading    */
+/*      too much data.                                                  */
+/* -------------------------------------------------------------------- */
+    int nBytesPerPixel = GDALGetDataTypeSize( eDataType ) / 8;
+
+    if( nBytesPerPixel == 1 )
+    {
+        nBlockXSize = 1024;
+        nBlockYSize= 256;
+    }
+    else if( nBytesPerPixel == 2 )
+    {
+        nBlockXSize = 512;
+        nBlockYSize= 256;
+    }
+    else if( nBytesPerPixel == 4 )
+    {
+        nBlockXSize = 512;
+        nBlockYSize= 128;
+    }
+    else
+    {
+        nBlockXSize = 256;
+        nBlockYSize= 128;
+    }
+
+    if( nRasterXSize < nBlockXSize * 2 )
+        nBlockXSize = nRasterXSize;
+    
+    if( nRasterYSize < nBlockYSize * 2 )
+        nBlockYSize = nRasterYSize;
+
+/* -------------------------------------------------------------------- */
+/*      Get other information from the DAS for this band.               */
+/* -------------------------------------------------------------------- */
+    if( nOverviewFactorIn == 1 )
+        HarvestDAS();
+
+/* -------------------------------------------------------------------- */
+/*      Create overview band objects.                                   */
+/* -------------------------------------------------------------------- */
+    if( nOverviewFactorIn == 1 )
+    {
+        int iOverview;
+
+        nOverviewCount = 0;
+        papoOverviewBand = (DODSRasterBand **) 
+            CPLCalloc( sizeof(void*), 8 );
+
+        for( iOverview = 1; iOverview < 8; iOverview++ )
+        {
+            int nThisFactor = 1 << iOverview;
+            
+            if( nRasterXSize / nThisFactor < 128
+                && nRasterYSize / nThisFactor < 128 )
+                break;
+
+            papoOverviewBand[nOverviewCount++] = 
+                new DODSRasterBand( poDSIn, oVarNameIn, oCEIn, 
+                                    nThisFactor );
+
+            papoOverviewBand[nOverviewCount-1]->bFlipX = bFlipX;
+            papoOverviewBand[nOverviewCount-1]->bFlipY = bFlipY;
+        }
+    }
+}
+
+/************************************************************************/
+/*                          ~DODSRasterBand()                           */
+/************************************************************************/
+
+DODSRasterBand::~DODSRasterBand()
+
+{
+    for( int iOverview = 0; iOverview < nOverviewCount; iOverview++ )
+        delete papoOverviewBand[iOverview];
+    CPLFree( papoOverviewBand );
+
+    if( poCT )
+        delete poCT;
+}
+
+/************************************************************************/
+/*                             HarvestDAS()                             */
+/************************************************************************/
+
+void DODSRasterBand::HarvestDAS()
+
+{
+    DODSDataset *poDODS = dynamic_cast<DODSDataset *>(poDS);
+    
+/* -------------------------------------------------------------------- */
+/*      Try and fetch the corresponding DAS subtree if it exists.       */
+/* -------------------------------------------------------------------- */
+    AttrTable *poBandInfo = poDODS->GetDAS().find_container( oVarName );
+
+    if( poBandInfo == NULL )
+    {
+        CPLDebug( "DODS", "No band DAS info for %s.", oVarName.c_str() );
+        return;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      collect metadata.                                               */
+/* -------------------------------------------------------------------- */
+    poDODS->HarvestMetadata( this, poBandInfo );
+
+/* -------------------------------------------------------------------- */
+/*      Get photometric interpretation.                                 */
+/* -------------------------------------------------------------------- */
+    string oValue;
+    int  iInterp;
+
+    oValue = StripQuotes(poBandInfo->get_attr( "PhotometricInterpretation" ));
+    for( iInterp = 0; iInterp < (int) GCI_Max; iInterp++ )
+    {
+        if( oValue == GDALGetColorInterpretationName( 
+                (GDALColorInterp) iInterp ) )
+            eColorInterp = (GDALColorInterp) iInterp;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Get band description.                                           */
+/* -------------------------------------------------------------------- */
+    oValue = StripQuotes(poBandInfo->get_attr( "Description" ));
+    if( oValue != "" )
+        SetDescription( oValue.c_str() );
+
+/* --- */
+/* missing_value */
+/* --- */
+    oValue = poBandInfo->get_attr( "missing_value" );
+    if( oValue != "" )
+        SetNoDataValue( atof(oValue.c_str()) );
+
+/* -------------------------------------------------------------------- */
+/*      Collect color table                                             */
+/* -------------------------------------------------------------------- */
+    AttrTable *poCTable = poBandInfo->find_container("Colormap");
+    if( poCTable != NULL )
+    {
+        AttrTable::Attr_iter dv_i;
+
+        poCT = new GDALColorTable();
+
+        for( dv_i = poCTable->attr_begin(); 
+             dv_i != poCTable->attr_end(); 
+             dv_i++ )
+        {
+            if( !poCTable->is_container( dv_i ) )
+                continue;
+
+            AttrTable *poColor = poCTable->get_attr_table( dv_i );
+            GDALColorEntry sEntry;
+
+            if( poColor == NULL )
+                continue;
+
+            sEntry.c1 = atoi(poColor->get_attr( "red" ).c_str());
+            sEntry.c2 = atoi(poColor->get_attr( "green" ).c_str());
+            sEntry.c3 = atoi(poColor->get_attr( "blue" ).c_str());
+            if( poColor->get_attr( "alpha" ) == "" )
+                sEntry.c4 = 255;
+            else
+                sEntry.c4 = atoi(poColor->get_attr( "alpha" ).c_str());
+            
+            poCT->SetColorEntry( poCT->GetColorEntryCount(), &sEntry );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for flipping instructions.                                */
+/* -------------------------------------------------------------------- */
+    oValue = StripQuotes(poBandInfo->get_attr( "FlipX" ));
+    if( oValue != "no" && oValue != "NO" && oValue != "" )
+        bFlipX = TRUE;
+
+    oValue = StripQuotes(poBandInfo->get_attr( "FlipY" ));
+    if( oValue != "no" && oValue != "NO" && oValue != "" )
+        bFlipY = TRUE;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr 
+DODSRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
+{
+    DODSDataset *poDODS = dynamic_cast<DODSDataset *>(poDS);
+    int nBytesPerPixel = GDALGetDataTypeSize(eDataType) / 8;
+
+/* -------------------------------------------------------------------- */
+/*      What is the actual rectangle we want to read?  We can't read    */
+/*      full blocks that go off the edge of the original data.          */
+/* -------------------------------------------------------------------- */
+    int nXOff, nXSize, nYOff, nYSize;
+
+    nXOff = nBlockXOff * nBlockXSize;
+    nYOff = nBlockYOff * nBlockYSize;
+    nXSize = nBlockXSize;
+    nYSize = nBlockYSize;
+
+    if( nXOff + nXSize > nRasterXSize )
+        nXSize = nRasterXSize - nXOff;
+    if( nYOff + nYSize > nRasterYSize )
+        nYSize = nRasterYSize - nYOff;
+
+/* -------------------------------------------------------------------- */
+/*      If we are working with a flipped image, we need to transform    */
+/*      the requested window accordingly.                               */
+/* -------------------------------------------------------------------- */
+    if( bFlipY )
+        nYOff = (nRasterYSize - nYOff - nYSize);
+
+    if( bFlipX )
+        nXOff = (nRasterXSize - nXOff - nXSize);
+
+/* -------------------------------------------------------------------- */
+/*      Prepare constraint expression for this request.                 */
+/* -------------------------------------------------------------------- */
+    string x_constraint, y_constraint, raw_constraint, final_constraint;
+
+    x_constraint = 
+        CPLSPrintf( "[%d:%d:%d]", 
+                    nXOff * nOverviewFactor, 
+                    nOverviewFactor, 
+                    (nXOff + nXSize - 1) * nOverviewFactor );
+    y_constraint = 
+        CPLSPrintf( "[%d:%d:%d]", 
+                    nYOff * nOverviewFactor,
+                    nOverviewFactor,
+                    (nYOff + nYSize - 1) * nOverviewFactor );
+
+    raw_constraint = oVarName + oCE;
+
+    final_constraint = poDODS->SubConstraint( raw_constraint, 
+                                              x_constraint, 
+                                              y_constraint );
+
+    CPLDebug( "DODS", "constraint = %s", final_constraint.c_str() );
+
+/* -------------------------------------------------------------------- */
+/*      Request data from server.                                       */
+/* -------------------------------------------------------------------- */
+    try {
+        DataDDS data( poDODS->poBaseTypeFactory );
+
+        poDODS->GetConnect()->request_data(data, final_constraint );
+
+/* -------------------------------------------------------------------- */
+/*      Get the DataDDS Array object from the response.                 */
+/* -------------------------------------------------------------------- */
+        BaseType *poBt = get_variable(data, oVarName );
+        if (!poBt)
+            throw Error(string("I could not read the variable '")
+		    + oVarName
+		    + string("' from the data source at:\n")
+		    + poDODS->GetUrl() );
+
+        Array *poA;
+        switch (poBt->type()) {
+          case dods_grid_c:
+            poA = dynamic_cast<Array*>(dynamic_cast<Grid*>(poBt)->array_var());
+            break;
+            
+          case dods_array_c:
+            poA = dynamic_cast<Array *>(poBt);
+            break;
+            
+          default:
+            throw InternalErr("Expected an Array or Grid variable!");
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Pre-initialize the output buffer to zero.                       */
+/* -------------------------------------------------------------------- */
+        if( nXSize < nBlockXSize || nYSize < nBlockYSize )
+            memset( pImage, 0, 
+                    nBlockXSize * nBlockYSize 
+                    * GDALGetDataTypeSize(eDataType) / 8 );
+
+/* -------------------------------------------------------------------- */
+/*      Dump the contents of the Array data into our output image buffer.*/
+/*                                                                      */
+/* -------------------------------------------------------------------- */
+        poA->buf2val(&pImage);	// !Suck the data out of the Array!
+
+/* -------------------------------------------------------------------- */
+/*      If the [x] dimension comes before [y], we need to transpose     */
+/*      the data we just got back.                                      */
+/* -------------------------------------------------------------------- */
+        if( bTranspose )
+        {
+            GByte *pabyDataCopy;
+            int iY;
+            
+            CPLDebug( "DODS", "Applying transposition" );
+            
+            // make a copy of the original
+            pabyDataCopy = (GByte *) 
+                CPLMalloc(nBytesPerPixel * nXSize * nYSize);
+            memcpy( pabyDataCopy, pImage, nBytesPerPixel * nXSize * nYSize );
+            memset( pImage, 0, nBytesPerPixel * nXSize * nYSize );
+            
+            for( iY = 0; iY < nYSize; iY++ )
+            {
+                GDALCopyWords( pabyDataCopy + iY * nBytesPerPixel, 
+                               eDataType, nBytesPerPixel * nYSize,
+                               ((GByte *) pImage) + iY * nXSize * nBytesPerPixel, 
+                               eDataType, nBytesPerPixel, nXSize );
+            }
+            
+            // cleanup
+            CPLFree( pabyDataCopy );
+        }
+        
+/* -------------------------------------------------------------------- */
+/*      Do we need "x" flipping?                                        */
+/* -------------------------------------------------------------------- */
+        if( bFlipX )
+        {
+            GByte *pabyDataCopy;
+            int iY;
+            
+            CPLDebug( "DODS", "Applying X flip." );
+            
+            // make a copy of the original
+            pabyDataCopy = (GByte *) 
+                CPLMalloc(nBytesPerPixel * nXSize * nYSize);
+            memcpy( pabyDataCopy, pImage, nBytesPerPixel * nXSize * nYSize );
+            memset( pImage, 0, nBytesPerPixel * nXSize * nYSize );
+            
+            for( iY = 0; iY < nYSize; iY++ )
+            {
+                GDALCopyWords( pabyDataCopy + iY*nXSize*nBytesPerPixel,
+                               eDataType, nBytesPerPixel,
+                               ((GByte *) pImage) + ((iY+1)*nXSize-1)*nBytesPerPixel, 
+                               eDataType, -nBytesPerPixel, nXSize );
+            }
+            
+            // cleanup
+            CPLFree( pabyDataCopy );
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Do we need "y" flipping?                                        */
+/* -------------------------------------------------------------------- */
+        if( bFlipY )
+        {
+            GByte *pabyDataCopy;
+            int iY;
+            
+            CPLDebug( "DODS", "Applying Y flip." );
+            
+            // make a copy of the original
+            pabyDataCopy = (GByte *) 
+                CPLMalloc(nBytesPerPixel * nXSize * nYSize);
+            memcpy( pabyDataCopy, pImage, nBytesPerPixel * nXSize * nYSize );
+            
+            for( iY = 0; iY < nYSize; iY++ )
+            {
+                GDALCopyWords( pabyDataCopy + iY*nXSize*nBytesPerPixel,
+                               eDataType, nBytesPerPixel,
+                               ((GByte *) pImage) + (nYSize-iY-1)*nXSize*nBytesPerPixel,
+                               eDataType, nBytesPerPixel,
+                               nXSize );
+            }
+            
+            // cleanup
+            CPLFree( pabyDataCopy );
+        }
+
+/* -------------------------------------------------------------------- */
+/*      If we only read a partial block we need to re-organize the      */
+/*      data.                                                           */
+/* -------------------------------------------------------------------- */
+        if( nXSize < nBlockXSize )
+        {
+            int iLine;
+
+            for( iLine = nYSize-1; iLine >= 0; iLine-- )
+            {
+                memmove( ((GByte *) pImage) + iLine*nBlockXSize*nBytesPerPixel,
+                         ((GByte *) pImage) + iLine * nXSize * nBytesPerPixel,
+                         nBytesPerPixel * nXSize );
+                memset( ((GByte *) pImage) 
+                        + (iLine*nBlockXSize + nXSize)*nBytesPerPixel,
+                        0, nBytesPerPixel * (nBlockXSize - nXSize) );
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Catch exceptions                                                */
+/* -------------------------------------------------------------------- */
+    catch (Error &e) {
+        CPLError(CE_Failure, CPLE_AppDefined, e.get_error_message().c_str());
+        return CE_Failure;
+    }
+    
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetOverviewCount()                          */
+/************************************************************************/
+
+int DODSRasterBand::GetOverviewCount()
+
+{
+    return nOverviewCount;
+}
+
+/************************************************************************/
+/*                            GetOverview()                             */
+/************************************************************************/
+
+GDALRasterBand *DODSRasterBand::GetOverview( int iOverview )
+
+{
+    if( iOverview < 0 || iOverview >= nOverviewCount )
+        return NULL;
+    else
+        return papoOverviewBand[iOverview];
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp DODSRasterBand::GetColorInterpretation()
+
+{
+    return eColorInterp;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *DODSRasterBand::GetColorTable()
+
+{
+    return poCT;
+}
+
+/************************************************************************/
+/*                           SetNoDataValue()                           */
+/************************************************************************/
+
+CPLErr DODSRasterBand::SetNoDataValue( double dfNoData )
+
+{
+    bNoDataSet = TRUE;
+    dfNoDataValue = dfNoData;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           GetNoDataValue()                           */
+/************************************************************************/
+
+double DODSRasterBand::GetNoDataValue( int * pbSuccess )
+
+{
+    if( pbSuccess )
+        *pbSuccess = bNoDataSet;
+
+    return dfNoDataValue;
+}
+
+/************************************************************************/
+/*                         GDALRegister_DODS()                          */
+/************************************************************************/
+
+void 
+GDALRegister_DODS()
+{
+    GDALDriver *poDriver;
+
+    if( GDALGetDriverByName( "DODS" ) == NULL ) {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "DODS" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "DAP 3.x servers" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_various.html#DODS" );
+
+        poDriver->pfnOpen = DODSDataset::Open;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/dods/frmt_dods.html b/Utilities/GDAL/frmts/dods/frmt_dods.html
new file mode 100644
index 0000000000..4c39f0c400
--- /dev/null
+++ b/Utilities/GDAL/frmts/dods/frmt_dods.html
@@ -0,0 +1,185 @@
+<html>
+<head>
+<title>DODS -- OPeNDAP Grid Client</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>DODS -- OPeNDAP Grid Client</h1>
+
+GDAL optionally includes read support for 2D grids and arrays via the OPeNDAP
+(DODS) protocol. <p>
+
+<h2>Dataset Naming</h2>
+
+The full dataset name specification consists of the OPeNDAP dataset
+url, the full path to the desired array or grid variable, and an indicator
+of the array indices to be accessed.<p>
+
+For instance, if the url http://maps.gdal.org/daac-bin/nph-hdf/3B42.HDF.dds 
+returns a DDS definition like this:<p>
+
+<pre>
+Dataset {
+  Structure {
+    Structure {
+      Float64 percipitate[scan = 5][longitude = 360][latitude = 80];
+      Float64 relError[scan = 5][longitude = 360][latitude = 80];
+    } PlanetaryGrid;
+  } DATA_GRANULE;
+} 3B42.HDF;
+</pre>
+<p>
+
+then the percipitate grid can be accessed using the following GDAL 
+dataset name:<p>
+
+http://maps.gdal.org/daac-bin/nph-hdf/3B42.HDF?DATA_GRANULE.PlanetaryGrid.percipitate[0][x][y]
+<p>
+
+The full path to the grid or array to be accessed needs to be specified (not
+counting the outer Dataset name).  GDAL needs to know which indices of 
+the array to treat as x (longitude or easting) and y (latitude or northing).
+Any other dimensions need to be restricted to a single value.<p>
+
+In cases of data servers with only 2D arrays and grids as immediate
+children of the Dataset it may not be necessary to name the grid or array
+variable. <p>
+
+In cases where there are a number of 2D arrays or grids at the dataset
+level, they may be each automatically treated as seperate bands.<p>
+
+<h2>Specialized AIS/DAS Metadata</h2>
+
+A variety of information will be transported via the DAS describing
+the dataset.  Some DODS drivers (such as the GDAL based one!) already
+return the following DAS information, but in other cases it can be
+supplied locally using the AIX mechanism.  See the DODS documentation
+for details of how the AIS mechanism works. <p>
+
+<pre>
+Attributes {
+
+    GLOBAL { 
+        Float64 Northernmost_Northing 71.1722;
+	Float64 Southernmost_Northing  4.8278;
+	Float64	Easternmost_Easting  -27.8897;
+	Float64	Westernmost_Easting -112.11;
+        Float64 GeoTransform "71.1722 0.001 0.0 -112.11 0.0 -0.001";
+	String spatial_ref "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]]";
+        Metadata { 
+          String TIFFTAG_XRESOLUTION "400";
+          String TIFFTAG_YRESOLUTION "400";
+          String TIFFTAG_RESOLUTIONUNIT "2 (pixels/inch)";
+        }
+    }
+
+    band_1 {
+        String Description "...";
+        String 
+    }
+}
+</pre>
+
+<h3>Dataset</h3>
+
+There will be an object in the DAS named GLOBAL containing
+attributes of the dataset as a whole. <p>
+
+It will have the following subitems:<p>
+
+<ul>
+
+<li> <b>Northernmost_Northing</b>: The latitude or northing of the north edge of the image. <p>
+<li> <b>Southernmost_Northing</b>: The latitude or northing of the south edge of the image. <p>
+<li> <b>Easternmost_Easting</b>: The longitude or easting of the east edge of the image. <p>
+<li> <b>Westernmost_Easting</b>: The longitude or easting of the west edge of the image.<p>
+
+<li> <b>GeoTransform</b>: The six parameters defining the affine transformation
+between pixel/line space and georeferenced space if applicable.  Stored
+as a single string with values seperated by sapces.  Note this 
+allows for rotated or sheared images. (optional) <p>
+
+<li> <b>SpatialRef</b>: The OpenGIS WKT description of the coordinate system.
+If not provided it will be assumed that the coordinate system is WGS84.
+(optional) <p>
+
+<li> <b>Metadata</b>: a container with a list of string attributes for
+each available metadata item.  The metadata item keyword name will be used
+as the attribute name.  Metadata values will always be strings. (optional)
+
+<li> <i>address GCPs</i><p>
+
+</ul>
+
+Note that the edge northing and easting values can be computed based
+on the grid size and the geotransform. They are included primarily as
+extra documentation that is easier to interprete by a user than the
+GeoTransform.  They will also be used to compute a GeoTransform internally
+if one is note provided, but if both are provided the GeoTransform will
+take precidence.<p>
+
+<h3>Band</h3>
+
+There will be an object in the DAS named after each band containing
+attribute of the specific band.   <p>
+
+It will have the following subitems:<p>
+
+<ul>
+
+<li> <b>Metadata</b>: a container with a list of string attributes for
+each available metadata item.  The metadata item keyword name will be used
+as the attribute name.  Metadata values will always be strings. (optional)<p>
+
+<li> <b>PhotometricInterpretation</b>: Will have a string value that is
+one of "Undefined", "GrayIndex", "PaletteIndex", "Red", "Green",
+"Blue", "Alpha", "Hue", "Saturation", "Lightness", "Cyan", "Magenta",
+"Yellow" or "Black".  (optional)<p>
+
+<li> <b>units</b>: name of units (one of "ft" or "m" for elevation data).
+(optional)<p>
+
+<li> <b>add_offset</b>: Offset to be applied to pixel values (after 
+scale_factor) to compute a "real" pixel value.  Defaults to 0.0. (optional)<p>
+
+<li> <b>scale_factor</b>: Scale to be applied to pixel values (before
+add_offset) to compute "real" pixel value.  Defaults to 1.0. (optional)<p>
+
+<li> <b>Description</b>: Descriptive text about the band. (optional)<p>
+
+<li> <b>missing_value</b>: The nodata value for the raster. (optional)<p>
+
+<li> <b>Colormap</b>: A container with a subcontainer for each color in
+the color table, looking like the following.  The alpha component is
+optional and assumed to be 255 (opaque) if not provided.<p>
+<pre>									
+    Colormap { 
+      Color_0 { 
+        Byte red 0;
+        Byte green 0;
+        Byte blue 0;
+        Byte alpha 255;
+      }
+      Color_1 { 
+        Byte red 255;
+        Byte green 255;
+        Byte blue 255;
+        Byte alpha 255;
+      }
+      ...
+    }
+</pre>
+
+</ul>
+
+<hr>
+
+See Also:<p>
+
+<ul>
+<li> <a href="http://www.opendap.org/">OPeNDAP Website</a>
+</ul>
+
+</body>
+</html>
diff --git a/Utilities/GDAL/frmts/ecw/GNUmakefile b/Utilities/GDAL/frmts/ecw/GNUmakefile
new file mode 100644
index 0000000000..2dd935affc
--- /dev/null
+++ b/Utilities/GDAL/frmts/ecw/GNUmakefile
@@ -0,0 +1,13 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	ecwdataset.o ecwcreatecopy.o jp2userbox.o
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) -DFRMT_ecw $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/ecw/ecwcreatecopy.cpp b/Utilities/GDAL/frmts/ecw/ecwcreatecopy.cpp
new file mode 100644
index 0000000000..90289d0d15
--- /dev/null
+++ b/Utilities/GDAL/frmts/ecw/ecwcreatecopy.cpp
@@ -0,0 +1,1652 @@
+/******************************************************************************
+ * $Id: ecwcreatecopy.cpp,v 1.25 2006/04/28 04:18:18 fwarmerdam Exp $
+ *
+ * Project:  GDAL ECW Driver
+ * Purpose:  ECW CreateCopy method implementation.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, 2004, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************
+ *
+ * $Log: ecwcreatecopy.cpp,v $
+ * Revision 1.25  2006/04/28 04:18:18  fwarmerdam
+ * Added support for writing GCPs in GeoTIFF box.
+ *
+ * Revision 1.24  2006/04/19 14:21:47  fwarmerdam
+ * defer initialization till we need it, since shutdown is expensive
+ *
+ * Revision 1.23  2006/04/02 15:27:16  fwarmerdam
+ * Fixed char* / const char * problem for VS8.
+ *
+ * Revision 1.22  2005/12/09 20:43:43  fwarmerdam
+ * create the GeoTIFF box ourselves, we think we do it better
+ *
+ * Revision 1.21  2005/07/30 03:06:33  fwarmerdam
+ * added LARGE_OK create option, and much improved error reporting
+ *
+ * Revision 1.20  2005/05/17 20:13:28  fwarmerdam
+ * use gmljp2://xml format for uri
+ *
+ * Revision 1.19  2005/05/11 14:38:42  fwarmerdam
+ * added dictionary support, updated to latest urn and gml.root-instance plans
+ *
+ * Revision 1.18  2005/05/03 21:11:47  fwarmerdam
+ * upgraded to copy PAM information
+ *
+ * Revision 1.17  2005/04/12 03:58:34  fwarmerdam
+ * turn ownership of fpVSIL over to vsiiostream
+ *
+ * Revision 1.16  2005/04/02 22:05:21  fwarmerdam
+ * ifdef out some stuff on ECW_FW
+ *
+ * Revision 1.15  2005/04/02 21:26:34  fwarmerdam
+ * preliminary support for creating GML coverage boxes
+ *
+ * Revision 1.14  2005/03/22 04:37:47  fwarmerdam
+ * Integrated ECW_LARGE_OK option.
+ *
+ * Revision 1.13  2005/03/02 14:40:33  fwarmerdam
+ * Fixed up TARGET validation.
+ *
+ * Revision 1.12  2005/02/25 17:01:47  fwarmerdam
+ * initialize adfGeoTransform in case GetGeoTransform fails
+ *
+ * Revision 1.11  2005/02/25 16:44:39  fwarmerdam
+ * modified WriteReadLine() to do a dataset level read
+ *
+ * Revision 1.10  2005/02/22 08:22:03  fwarmerdam
+ * added support for capturing color interp on writable bands
+ *
+ * Revision 1.9  2005/02/17 15:32:27  fwarmerdam
+ * Ensure that datatype is preserved on writable rasterbands. Otherwise
+ * non-8bit support is broken.
+ *
+ * Revision 1.8  2005/02/08 04:50:37  fwarmerdam
+ * added FlushCache method on writeable dataset
+ *
+ * Revision 1.7  2005/02/07 22:53:54  fwarmerdam
+ * added preliminary Create support for JP2ECW driver
+ *
+ * Revision 1.6  2005/01/26 20:30:07  fwarmerdam
+ * Change GetGDTProjDat to GetProjectionAndDatum().
+ *
+ * Revision 1.5  2005/01/15 07:48:41  fwarmerdam
+ * dos to unix linefeeds
+ *
+ * Revision 1.4  2004/12/20 22:16:35  fwarmerdam
+ * implement seperate ECW/JPEG2000 create entry points
+ *
+ * Revision 1.3  2004/12/20 05:03:00  fwarmerdam
+ * implemented preliminary coordinate system export
+ *
+ * Revision 1.2  2004/12/10 22:13:58  fwarmerdam
+ * convert to unix format
+ *
+ * Revision 1.1  2004/12/10 19:15:52  fwarmerdam
+ * New
+ *
+ */
+
+#include "gdal_pam.h"
+#include "ogr_spatialref.h"
+#include "cpl_string.h"
+#include "cpl_conv.h"
+#include "vsiiostream.h"
+#include "jp2userbox.h"
+
+CPL_CVSID("$Id: ecwcreatecopy.cpp,v 1.25 2006/04/28 04:18:18 fwarmerdam Exp $");
+
+CPL_C_START
+CPLErr CPL_DLL GTIFMemBufFromWkt( const char *pszWKT, 
+                                  const double *padfGeoTransform,
+                                  int nGCPCount, const GDAL_GCP *pasGCPList,
+                                  int *pnSize, unsigned char **ppabyBuffer );
+CPL_C_END
+
+void ECWInitialize( void );
+
+#if defined(FRMT_ecw) && defined(HAVE_COMPRESS)
+
+class GDALECWCompressor : public CNCSFile {
+
+public:
+    GDALECWCompressor();
+    virtual ~GDALECWCompressor();
+
+    virtual CNCSError WriteReadLine(UINT32 nNextLine, void **ppInputArray);
+    virtual void WriteStatus(UINT32 nCurrentLine);
+    virtual bool WriteCancel();
+
+    CPLErr  Initialize( const char *pszFilename, char **papszOptions, 
+                        int nXSize, int nYSize, int nBands, 
+                        GDALDataType eType, 
+                        const char *pszWKT, double *padfGeoTransform,
+                        int nGCPCount, const GDAL_GCP *pasGCPList,
+                        int bIsJPEG2000 );
+    CPLErr  CloseDown();
+
+    CPLErr  PrepareCoverageBox( const char *pszWKT, double *padfGeoTransform );
+    CPLErr  PrepareGeoTIFFBox( const char *pszWKT, double *padfGeoTransform,
+                               int nGCPCount, const GDAL_GCP *pasGCPList );
+
+#ifdef ECW_FW
+    CNCSJP2File::CNCSJPXAssocBox  m_oGMLAssoc;
+#endif
+
+    // Data
+
+    GDALDataset *m_poSrcDS;
+
+    VSIIOStream m_OStream;
+    int m_nPercentComplete;
+
+    int m_bCancelled;
+
+    GDALProgressFunc  pfnProgress;
+    void             *pProgressData;
+
+    NCSFileViewFileInfoEx sFileInfo;
+    GDALDataType eWorkDT;
+};
+
+/************************************************************************/
+/*                         GDALECWCompressor()                          */
+/************************************************************************/
+
+GDALECWCompressor::GDALECWCompressor()
+
+{
+    m_poSrcDS = NULL;
+    m_nPercentComplete = -1;
+    m_bCancelled = FALSE;
+    pfnProgress = GDALDummyProgress;
+    pProgressData = NULL;
+}
+
+/************************************************************************/
+/*                         ~GDALECWCompressor()                         */
+/************************************************************************/
+
+GDALECWCompressor::~GDALECWCompressor()
+
+{
+}
+
+/************************************************************************/
+/*                             CloseDown()                              */
+/************************************************************************/
+
+CPLErr GDALECWCompressor::CloseDown()
+
+{
+    for( int i = 0; i < sFileInfo.nBands; i++ )
+    {
+        CPLFree( sFileInfo.pBands[i].szDesc );
+    }
+    CPLFree( sFileInfo.pBands );
+
+    Close( true );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           WriteReadLine()                            */
+/************************************************************************/
+
+CNCSError GDALECWCompressor::WriteReadLine( UINT32 nNextLine, 
+                                            void **ppInputArray )
+
+{
+    int    iBand, *panBandMap;
+    CPLErr eErr;
+    GByte *pabyLineBuf;
+    int nWordSize = GDALGetDataTypeSize( eWorkDT ) / 8;
+
+    panBandMap = (int *) CPLMalloc(sizeof(int) * sFileInfo.nBands);
+    for( iBand = 0; iBand < sFileInfo.nBands; iBand++ )
+        panBandMap[iBand] = iBand+1;
+
+    pabyLineBuf = (GByte *) CPLMalloc( sFileInfo.nSizeX * sFileInfo.nBands
+                                       * nWordSize );
+
+    eErr = m_poSrcDS->RasterIO( GF_Read, 0, nNextLine, sFileInfo.nSizeX, 1, 
+                                pabyLineBuf, sFileInfo.nSizeX, 1, 
+                                eWorkDT, 
+                                sFileInfo.nBands, panBandMap,
+                                nWordSize, 0, nWordSize * sFileInfo.nSizeX );
+
+    for( iBand = 0; iBand < (int) sFileInfo.nBands; iBand++ )
+    {
+        memcpy( ppInputArray[iBand],
+                pabyLineBuf + nWordSize * sFileInfo.nSizeX * iBand, 
+                nWordSize * sFileInfo.nSizeX );
+    }
+
+    CPLFree( pabyLineBuf );
+    CPLFree( panBandMap );
+
+    if( eErr == CE_None )
+        return NCS_SUCCESS;
+    else
+        return NCS_FILEIO_ERROR;
+}
+
+/************************************************************************/
+/*                            WriteStatus()                             */
+/************************************************************************/
+
+void GDALECWCompressor::WriteStatus( UINT32 nCurrentLine )
+
+{
+    m_bCancelled = 
+        !pfnProgress( nCurrentLine / (float) sFileInfo.nSizeY, 
+                      NULL, pProgressData );
+}
+
+/************************************************************************/
+/*                            WriteCancel()                             */
+/************************************************************************/
+
+bool GDALECWCompressor::WriteCancel()
+
+{
+    return (bool) m_bCancelled;
+}
+
+/************************************************************************/
+/*                         PrepareCoverageBox()                         */
+/************************************************************************/
+
+CPLErr  GDALECWCompressor::PrepareCoverageBox( const char *pszWKT, 
+                                               double *padfGeoTransform )
+
+{
+#ifndef ECW_FW
+    return CE_Failure;
+#else
+/* -------------------------------------------------------------------- */
+/*      Try do determine a PCS or GCS code we can use.                  */
+/* -------------------------------------------------------------------- */
+    OGRSpatialReference oSRS;
+    char *pszWKTCopy = (char *) pszWKT;
+    int nEPSGCode = 0;
+    char szSRSName[100];
+
+    if( oSRS.importFromWkt( &pszWKTCopy ) != OGRERR_NONE )
+        return CE_Failure;
+
+    if( oSRS.IsProjected() )
+    {
+        const char *pszAuthName = oSRS.GetAuthorityName( "PROJCS" );
+
+        if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") )
+        {
+            nEPSGCode = atoi(oSRS.GetAuthorityCode( "PROJCS" ));
+        }
+    }
+    else if( oSRS.IsGeographic() )
+    {
+        const char *pszAuthName = oSRS.GetAuthorityName( "GEOGCS" );
+
+        if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") )
+        {
+            nEPSGCode = atoi(oSRS.GetAuthorityCode( "GEOGCS" ));
+        }
+    }
+
+    if( nEPSGCode != 0 )
+        sprintf( szSRSName, "urn:ogc:def:crs:EPSG::%d", nEPSGCode );
+    else
+        strcpy( szSRSName, 
+                "gmljp2://xml/CRSDictionary.gml#ogrcrs1" );
+
+/* -------------------------------------------------------------------- */
+/*      For now we hardcode for a minimal instance format.              */
+/* -------------------------------------------------------------------- */
+    char szDoc[4000];
+
+    sprintf( szDoc, 
+"<gml:FeatureCollection\n"
+"   xmlns:gml=\"http://www.opengis.net/gml\"\n"
+"   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+"   xsi:schemaLocation=\"http://www.opengis.net/gml http://www.math.ubc.ca/~burggraf/gml/gml4jp2.xsd\">\n"
+"  <gml:boundedBy>\n"
+"    <gml:Null>withheld</gml:Null>\n"
+"  </gml:boundedBy>\n"
+"  <gml:featureMember>\n"
+"    <gml:FeatureCollection>\n"
+"      <gml:featureMember>\n"
+"        <gml:RectifiedGridCoverage dimension=\"2\" gml:id=\"RGC0001\">\n"
+"          <gml:rectifiedGridDomain>\n"
+"            <gml:RectifiedGrid dimension=\"2\">\n"
+"              <gml:limits>\n"
+"                <gml:GridEnvelope>\n"
+"                  <gml:low>0 0</gml:low>\n"
+"                  <gml:high>%d %d</gml:high>\n"
+"                </gml:GridEnvelope>\n"
+"              </gml:limits>\n"
+"              <gml:axisName>x</gml:axisName>\n"
+"              <gml:axisName>y</gml:axisName>\n"
+"              <gml:origin>\n"
+"                <gml:Point gml:id=\"P0001\" srsName=\"%s\">\n"
+"                  <gml:pos>%.15g %.15g</gml:pos>\n"
+"                </gml:Point>\n"
+"              </gml:origin>\n"
+"              <gml:offsetVector srsName=\"%s\">%.15g %.15g</gml:offsetVector>\n"
+"              <gml:offsetVector srsName=\"%s\">%.15g %.15g</gml:offsetVector>\n"
+"            </gml:RectifiedGrid>\n"
+"          </gml:rectifiedGridDomain>\n"
+"          <gml:rangeSet>\n"
+"            <gml:File>\n"
+"              <gml:fileName>urn:ogc:tc:gmljp2:codestream:0</gml:fileName>\n"
+"              <gml:fileStructure>Record Interleaved</gml:fileStructure>\n"
+"            </gml:File>\n"
+"          </gml:rangeSet>\n"
+"        </gml:RectifiedGridCoverage>\n"
+"      </gml:featureMember>\n"
+"    </gml:FeatureCollection>\n"
+"  </gml:featureMember>\n"
+"</gml:FeatureCollection>\n",
+             sFileInfo.nSizeX-1, sFileInfo.nSizeY-1, 
+             szSRSName,
+             padfGeoTransform[0] + padfGeoTransform[1] * 0.5
+                                 + padfGeoTransform[4] * 0.5, 
+             padfGeoTransform[3] + padfGeoTransform[2] * 0.5
+                                 + padfGeoTransform[5] * 0.5,
+             szSRSName, 
+             padfGeoTransform[1], padfGeoTransform[2],
+             szSRSName,
+             padfGeoTransform[4], padfGeoTransform[5] );
+
+/* -------------------------------------------------------------------- */
+/*      If we need a user defined CRSDictionary entry, prepare it       */
+/*      here.                                                           */
+/* -------------------------------------------------------------------- */
+    char *pszDictBox = NULL;
+
+    if( nEPSGCode == 0 )
+    {
+        char *pszGMLDef = NULL;
+
+        if( oSRS.exportToXML( &pszGMLDef, NULL ) == OGRERR_NONE )
+        {
+            pszDictBox = (char *) CPLMalloc(strlen(pszGMLDef) + 4000);
+            
+            sprintf( pszDictBox, 
+"<gml:Dictionary gml:id=\"CRSU1\" \n"
+"        xmlns:gml=\"http://www.opengis.net/gml\"\n"
+"        xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
+"        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"
+"  <gml:dictionaryEntry>\n"
+"%s\n"
+"  </gml:dictionaryEntry>\n"
+"</gml:Dictionary>\n",
+                     pszGMLDef );
+        }
+        CPLFree( pszGMLDef );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Setup the various required boxes.                               */
+/* -------------------------------------------------------------------- */
+    JP2UserBox *poGMLData;
+    CNCSJP2File::CNCSJPXAssocBox *poAssoc;
+    CNCSJP2File::CNCSJPXLabelBox *poLabel;
+
+    poLabel = new CNCSJP2File::CNCSJPXLabelBox();
+    poLabel->SetLabel( "gml.data" );
+    poLabel->m_bValid = true;
+    m_oGMLAssoc.m_OtherBoxes.push_back( poLabel );
+    m_oGMLAssoc.m_OwnedBoxes.push_back( poLabel );
+    
+    poAssoc = new CNCSJP2File::CNCSJPXAssocBox();
+    m_oGMLAssoc.m_OtherBoxes.push_back( poAssoc );
+    m_oGMLAssoc.m_OwnedBoxes.push_back( poAssoc );
+    poAssoc->m_bValid = true;
+
+    poLabel = new CNCSJP2File::CNCSJPXLabelBox();
+    poLabel->SetLabel( "gml.root-instance" );
+    poLabel->m_bValid = true;
+    poAssoc->m_OtherBoxes.push_back( poLabel );
+    poAssoc->m_OwnedBoxes.push_back( poLabel );
+
+    poGMLData = new JP2UserBox();
+    poGMLData->m_nTBox = 'xml ';
+    poGMLData->SetData( strlen( szDoc ), (unsigned char *) szDoc );
+    poAssoc->m_OtherBoxes.push_back( poGMLData );
+    poAssoc->m_OwnedBoxes.push_back( poGMLData );
+
+    if( pszDictBox != NULL )
+    {
+        poAssoc = new CNCSJP2File::CNCSJPXAssocBox();
+        m_oGMLAssoc.m_OtherBoxes.push_back( poAssoc );
+        m_oGMLAssoc.m_OwnedBoxes.push_back( poAssoc );
+        poAssoc->m_bValid = true;
+        
+        poLabel = new CNCSJP2File::CNCSJPXLabelBox();
+        poLabel->SetLabel( "CRSDictionary.gml" );
+        poLabel->m_bValid = true;
+        poAssoc->m_OtherBoxes.push_back( poLabel );
+        poAssoc->m_OwnedBoxes.push_back( poLabel );
+
+        poGMLData = new JP2UserBox();
+        poGMLData->m_nTBox = 'xml ';
+        poGMLData->SetData( strlen(pszDictBox), 
+                            (unsigned char *) pszDictBox );
+        poAssoc->m_OtherBoxes.push_back( poGMLData );
+        poAssoc->m_OwnedBoxes.push_back( poGMLData );
+
+        CPLFree( pszDictBox );
+    }
+
+    m_oGMLAssoc.m_bValid = true;
+    AddBox( &m_oGMLAssoc );
+
+    return CE_None;
+#endif /* def ECW_FW */
+}
+
+/************************************************************************/
+/*                         PrepareGeoTIFFBox()                          */
+/************************************************************************/
+
+CPLErr GDALECWCompressor::PrepareGeoTIFFBox( const char *pszWKT,
+                                             double *padfGeoTransform,
+                                             int nGCPCount, 
+                                             const GDAL_GCP *pasGCPList )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Prepare the memory buffer containing the degenerate GeoTIFF     */
+/*      file.                                                           */
+/* -------------------------------------------------------------------- */
+    int         nGTBufSize = 0;
+    unsigned char *pabyGTBuf = NULL;
+
+    if( GTIFMemBufFromWkt( pszWKT, padfGeoTransform, nGCPCount, pasGCPList,
+                           &nGTBufSize, &pabyGTBuf ) != CE_None )
+        return CE_Failure;
+
+    if( nGTBufSize == 0 )
+        return CE_None;
+
+/* -------------------------------------------------------------------- */
+/*      Write to a box on the JP2 file.                                 */
+/* -------------------------------------------------------------------- */
+    JP2UserBox  *poGTData;
+    GByte *pabyUUIDBoxData;
+    int   nUUIDBoxDataLength;
+
+    static unsigned char msi_uuid2[16] =
+    {0xb1,0x4b,0xf8,0xbd,0x08,0x3d,0x4b,0x43,
+     0xa5,0xae,0x8c,0xd7,0xd5,0xa6,0xce,0x03}; 
+
+    poGTData = new JP2UserBox();
+    poGTData->m_nTBox = 'uuid';
+
+    nUUIDBoxDataLength = nGTBufSize + 16;
+    pabyUUIDBoxData = (GByte *) CPLMalloc(nUUIDBoxDataLength);
+    memcpy( pabyUUIDBoxData, msi_uuid2, 16 );
+    memcpy( pabyUUIDBoxData + 16, pabyGTBuf, nGTBufSize );
+
+    poGTData->SetData( nUUIDBoxDataLength, pabyUUIDBoxData );
+
+    CPLFree( pabyUUIDBoxData );
+    CPLFree( pabyGTBuf );
+
+    AddBox( poGTData ); // never freed?
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                        ECWTranslateFromWKT()                         */
+/************************************************************************/
+
+static int ECWTranslateFromWKT( const char *pszWKT, 
+                                char *pszProjection,
+                                char *pszDatum )
+
+{
+    OGRSpatialReference oSRS;
+    char *pszWKTIn = (char *) pszWKT;
+    char **papszCSLookup;
+
+    strcpy( pszProjection, "RAW" );
+    strcpy( pszDatum, "RAW" );
+
+    if( pszWKT == NULL || strlen(pszWKT) == 0 )
+        return FALSE;
+    
+    oSRS.importFromWkt( &pszWKTIn );
+    
+    if( oSRS.IsLocal() )
+        return TRUE;
+
+/* -------------------------------------------------------------------- */
+/*      Do we have an overall EPSG number for this coordinate system?   */
+/* -------------------------------------------------------------------- */
+    const char *pszAuthorityCode = NULL;
+    const char *pszAuthorityName = NULL;
+    UINT32 nEPSGCode = 0;
+
+    if( oSRS.IsProjected() )
+    {
+        pszAuthorityCode =  oSRS.GetAuthorityCode( "PROJCS" );
+        pszAuthorityName =  oSRS.GetAuthorityName( "PROJCS" );
+    }
+    else if( oSRS.IsGeographic() )
+    {
+        pszAuthorityCode =  oSRS.GetAuthorityCode( "GEOGCS" );
+        pszAuthorityName =  oSRS.GetAuthorityName( "GEOGCS" );
+    }
+
+    if( pszAuthorityName != NULL && EQUAL(pszAuthorityName,"EPSG") 
+        && pszAuthorityCode != NULL && atoi(pszAuthorityCode) > 0 )
+        nEPSGCode = (UINT32) atoi(pszAuthorityCode);
+
+    if( nEPSGCode != 0 )
+    {
+        char *pszEPSGProj = NULL, *pszEPSGDatum = NULL;
+        CNCSError oErr;
+
+        oErr = 
+            CNCSJP2FileView::GetProjectionAndDatum( atoi(pszAuthorityCode), 
+                                                 &pszEPSGProj, &pszEPSGDatum );
+
+        CPLDebug( "ECW", "GetGDTProjDat(%d) = %s/%s", 
+                  atoi(pszAuthorityCode), pszEPSGProj, pszEPSGDatum );
+
+        if( oErr.GetErrorNumber() == NCS_SUCCESS
+            && pszEPSGProj != NULL && pszEPSGDatum != NULL )
+        {
+            strcpy( pszProjection, pszEPSGProj );
+            strcpy( pszDatum, pszEPSGDatum );
+            return TRUE;
+        }
+    }
+        
+/* -------------------------------------------------------------------- */
+/*      Is our GEOGCS name already defined in ecw_cs.dat?               */
+/* -------------------------------------------------------------------- */
+    const char *pszMatch = NULL;
+    const char *pszGEOGCS = oSRS.GetAttrValue( "GEOGCS" );
+    const char *pszWKTDatum = oSRS.GetAttrValue( "DATUM" );
+
+    papszCSLookup = ECWGetCSList();
+
+    if( pszGEOGCS != NULL )
+        pszMatch = CSLFetchNameValue( papszCSLookup, pszGEOGCS );
+
+    if( pszMatch != NULL && EQUALN(pszMatch,"GEOGCS",6) )
+    {
+        strcpy( pszDatum, pszGEOGCS );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Is this a "well known" geographic coordinate system?            */
+/* -------------------------------------------------------------------- */
+    if( EQUAL(pszDatum,"RAW") )
+    {
+        if( nEPSGCode == 4326 
+            || (strstr(pszGEOGCS,"WGS") && strstr(pszGEOGCS,"84") )
+            || (strstr(pszWKTDatum,"WGS") && strstr(pszWKTDatum,"84") ) )
+            strcpy( pszDatum, "WGS84" );
+
+        else if( nEPSGCode == 4322 
+            || (strstr(pszGEOGCS,"WGS") && strstr(pszGEOGCS,"72") )
+            || (strstr(pszWKTDatum,"WGS") && strstr(pszWKTDatum,"72") ) )
+            strcpy( pszDatum, "WGS72DOD" );
+        
+        else if( nEPSGCode == 4267 
+            || (strstr(pszGEOGCS,"NAD") && strstr(pszGEOGCS,"27") )
+            || (strstr(pszWKTDatum,"NAD") && strstr(pszWKTDatum,"27") ) )
+            strcpy( pszDatum, "NAD27" );
+        
+        else if( nEPSGCode == 4269 
+            || (strstr(pszGEOGCS,"NAD") && strstr(pszGEOGCS,"83") )
+            || (strstr(pszWKTDatum,"NAD") && strstr(pszWKTDatum,"83") ) )
+            strcpy( pszDatum, "NAD83" );
+
+        else if( nEPSGCode == 4277 )
+            strcpy( pszDatum, "OSGB36" );
+
+        else if( nEPSGCode == 4278 )
+            strcpy( pszDatum, "OSGB78" );
+
+        else if( nEPSGCode == 4201 )
+            strcpy( pszDatum, "ADINDAN" );
+
+        else if( nEPSGCode == 4202 )
+            strcpy( pszDatum, "AGD66" );
+
+        else if( nEPSGCode == 4203 )
+            strcpy( pszDatum, "AGD84" );
+
+        else if( nEPSGCode == 4209 )
+            strcpy( pszDatum, "ARC1950" );
+
+        else if( nEPSGCode == 4210 )
+            strcpy( pszDatum, "ARC1960" );
+
+        else if( nEPSGCode == 4275 )
+            strcpy( pszDatum, "NTF" );
+
+        else if( nEPSGCode == 4284 )
+            strcpy( pszDatum, "PULKOVO" );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Are we working with a geographic (geodetic) coordinate system?  */
+/* -------------------------------------------------------------------- */
+
+    if( oSRS.IsGeographic() )
+    {
+        strcpy( pszProjection, "GEODETIC" );
+        return TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Is this a UTM projection?                                       */
+/* -------------------------------------------------------------------- */
+    int bNorth, nZone;
+
+    nZone = oSRS.GetUTMZone( &bNorth );
+    if( nZone > 0 )
+    {
+        if( bNorth )
+            sprintf( pszProjection, "NUTM%02d", nZone );
+        else
+            sprintf( pszProjection, "SUTM%02d", nZone );
+        return TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Is our GEOGCS name already defined in ecw_cs.dat?               */
+/* -------------------------------------------------------------------- */
+    const char *pszPROJCS = oSRS.GetAttrValue( "PROJCS" );
+
+    if( pszPROJCS != NULL )
+        pszMatch = CSLFetchNameValue( papszCSLookup, pszGEOGCS );
+    else
+        pszMatch = NULL;
+
+    if( pszMatch != NULL && EQUALN(pszMatch,"PROJCS",6) )
+    {
+        strcpy( pszProjection, pszPROJCS );
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/************************************************************************/
+/*                             Initialize()                             */
+/*                                                                      */
+/*      Initialize compressor output.                                   */
+/************************************************************************/
+
+CPLErr GDALECWCompressor::Initialize( 
+    const char *pszFilename, char **papszOptions, 
+    int nXSize, int nYSize, int nBands, 
+    GDALDataType eType, 
+    const char *pszWKT, double *padfGeoTransform,
+    int nGCPCount, const GDAL_GCP *pasGCPList,
+    int bIsJPEG2000 )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Do some rudimentary checking in input.                          */
+/* -------------------------------------------------------------------- */
+    if( nBands == 0 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "ECW driver requires at least one band." );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Parse out some known options.                                   */
+/* -------------------------------------------------------------------- */
+    float      fTargetCompression = 75.0;
+
+    if( CSLFetchNameValue(papszOptions, "TARGET") != NULL )
+    {
+        fTargetCompression = (float) 
+            atof(CSLFetchNameValue(papszOptions, "TARGET"));
+        
+        if( fTargetCompression < 0.0 || fTargetCompression > 99.0 )
+        {
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "TARGET compression of %.3f invalid, should be a\n"
+                      "value between 0 and 99 percent.\n", 
+                      (double) fTargetCompression );
+            return CE_Failure;
+        }
+    }
+        
+/* -------------------------------------------------------------------- */
+/*      Create and initialize compressor.                               */
+/* -------------------------------------------------------------------- */
+    NCSFileViewFileInfoEx    *psClient = &(sFileInfo);
+    
+    psClient->nBands = nBands;
+    psClient->nSizeX = nXSize;
+    psClient->nSizeY = nYSize;
+    psClient->nCompressionRate = (int) MAX(1,100 / (100-fTargetCompression));
+    psClient->eCellSizeUnits = ECW_CELL_UNITS_METERS;
+
+    if( nBands == 1 )
+        psClient->eColorSpace = NCSCS_GREYSCALE;
+    else if( nBands == 3 )
+        psClient->eColorSpace = NCSCS_sRGB;
+    else
+        psClient->eColorSpace = NCSCS_MULTIBAND;
+
+/* -------------------------------------------------------------------- */
+/*      Figure out the data type.                                       */
+/* -------------------------------------------------------------------- */
+    int bSigned = FALSE;
+    int nBits = 8;
+    eWorkDT = eType;
+
+    switch( eWorkDT  )
+    {
+        case GDT_Byte:
+            psClient->eCellType = NCSCT_UINT8;
+            nBits = 8;
+            bSigned = FALSE;
+            break;
+            
+        case GDT_UInt16:
+            psClient->eCellType = NCSCT_UINT16;
+            nBits = 16;
+            bSigned = FALSE;
+            break;
+            
+        case GDT_UInt32:
+            psClient->eCellType = NCSCT_UINT32;
+            nBits = 32;
+            bSigned = FALSE;
+            break;
+            
+        case GDT_Int16:
+            psClient->eCellType = NCSCT_INT16;
+            nBits = 16;
+            bSigned = TRUE;
+            break;
+            
+        case GDT_Int32:
+            psClient->eCellType = NCSCT_INT32;
+            nBits = 32;
+            bSigned = TRUE;
+            break;
+            
+        case GDT_Float32:
+            psClient->eCellType = NCSCT_IEEE4;
+            nBits = 32;
+            bSigned = TRUE;
+            break;
+            
+        case GDT_Float64:
+            psClient->eCellType = NCSCT_IEEE8;
+            nBits = 64;
+            bSigned = TRUE;
+            break;
+
+        default:
+            // We treat complex types as float.  
+            psClient->eCellType = NCSCT_IEEE4;
+            nBits = 32;
+            bSigned = TRUE;
+            eWorkDT = GDT_Float32;
+            break;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create band information structures.                             */
+/* -------------------------------------------------------------------- */
+    int iBand;
+
+    psClient->pBands = (NCSFileBandInfo *) 
+        CPLMalloc( sizeof(NCSFileBandInfo) * nBands );
+    for( iBand = 0; iBand < nBands; iBand++ )
+    {
+        psClient->pBands[iBand].nBits = nBits;
+        psClient->pBands[iBand].bSigned = bSigned;
+        psClient->pBands[iBand].szDesc = CPLStrdup(
+            CPLSPrintf("Band%d",iBand+1) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Allow CNCSFile::SetParameter() requests.                        */
+/* -------------------------------------------------------------------- */
+    const char *pszOption;
+
+    if( bIsJPEG2000 )
+    {
+        pszOption = CSLFetchNameValue(papszOptions, "PROFILE");
+        if( pszOption != NULL && EQUAL(pszOption,"BASELINE_0") ) 
+            SetParameter( 
+                CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_0 );
+        else if( pszOption != NULL && EQUAL(pszOption,"BASELINE_1") ) 
+            SetParameter( 
+                CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_1 );
+        else if( pszOption != NULL && EQUAL(pszOption,"BASELINE_2") )
+            SetParameter( 
+                CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_2 );
+        else if( pszOption != NULL && EQUAL(pszOption,"NPJE") ) 
+            SetParameter( 
+                CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_NPJE );
+        else if( pszOption != NULL && EQUAL(pszOption,"EPJE") ) 
+            SetParameter( 
+                CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_EPJE );
+        
+        pszOption = CSLFetchNameValue(papszOptions, "CODESTREAM_ONLY" );
+        if( pszOption != NULL ) 
+            SetParameter(
+                CNCSJP2FileView::JP2_COMPRESS_CODESTREAM_ONLY, 
+                (bool) CSLTestBoolean( pszOption ) );
+        
+        pszOption = CSLFetchNameValue(papszOptions, "LEVELS");
+        if( pszOption != NULL )
+            SetParameter( CNCSJP2FileView::JP2_COMPRESS_LEVELS, 
+                                      (UINT32) atoi(pszOption) );
+        
+        pszOption = CSLFetchNameValue(papszOptions, "LAYERS");
+        if( pszOption != NULL )
+            SetParameter( CNCSJP2FileView::JP2_COMPRESS_LAYERS, 
+                                      (UINT32) atoi(pszOption) );
+
+        pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_WIDTH");
+        if( pszOption != NULL )
+            SetParameter( CNCSJP2FileView::JP2_COMPRESS_PRECINCT_WIDTH,
+                                      (UINT32) atoi(pszOption) );
+
+        pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_HEIGHT");
+        if( pszOption != NULL )
+            SetParameter(CNCSJP2FileView::JP2_COMPRESS_PRECINCT_HEIGHT, 
+                                     (UINT32) atoi(pszOption) );
+
+        pszOption = CSLFetchNameValue(papszOptions, "TILE_WIDTH");
+        if( pszOption != NULL )
+            SetParameter( CNCSJP2FileView::JP2_COMPRESS_TILE_WIDTH, 
+                                      (UINT32) atoi(pszOption) );
+
+        pszOption = CSLFetchNameValue(papszOptions, "TILE_HEIGHT");
+        if( pszOption != NULL )
+            SetParameter( CNCSJP2FileView::JP2_COMPRESS_TILE_HEIGHT, 
+                                      (UINT32) atoi(pszOption) );
+
+        pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_SOP");
+        if( pszOption != NULL )
+            SetParameter( CNCSJP2FileView::JP2_COMPRESS_INCLUDE_SOP, 
+                                      (bool) CSLTestBoolean( pszOption ) );
+    
+        pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_EPH");
+        if( pszOption != NULL )
+            SetParameter( CNCSJP2FileView::JP2_COMPRESS_INCLUDE_EPH, 
+                                      (bool) CSLTestBoolean( pszOption ) );
+    
+        pszOption = CSLFetchNameValue(papszOptions, "PROGRESSION");
+        if( pszOption != NULL && EQUAL(pszOption,"LRCP") )
+            SetParameter( 
+                CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_LRCP );
+                                  
+        else if( pszOption != NULL && EQUAL(pszOption,"RLCP") )
+            SetParameter( 
+                CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RLCP );
+
+        else if( pszOption != NULL && EQUAL(pszOption,"RPCL") )
+            SetParameter( 
+                CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RPCL );
+
+        pszOption = CSLFetchNameValue(papszOptions, "GEODATA_USAGE");
+        if( pszOption == NULL )
+            // Default to supressing ECW SDK geodata, just use our geotiff.
+            SetGeodataUsage( JP2_GEODATA_USE_GML_ONLY );
+        else if( EQUAL(pszOption,"NONE") )
+            SetGeodataUsage( JP2_GEODATA_USE_NONE );
+        else if( EQUAL(pszOption,"PCS_ONLY") )
+            SetGeodataUsage( JP2_GEODATA_USE_PCS_ONLY );
+        else if( EQUAL(pszOption,"GML_ONLY") )
+            SetGeodataUsage( JP2_GEODATA_USE_GML_ONLY );
+        else if( EQUAL(pszOption,"PCS_GML") )
+            SetGeodataUsage( JP2_GEODATA_USE_PCS_GML );
+        else if( EQUAL(pszOption,"GML_PCS") )
+            SetGeodataUsage( JP2_GEODATA_USE_GML_PCS );
+        else if( EQUAL(pszOption,"ALL") )
+            SetGeodataUsage( JP2_GEODATA_USE_GML_PCS_WLD );
+
+        pszOption = CSLFetchNameValue(papszOptions, "DECOMPRESS_LAYERS");
+        if( pszOption != NULL )
+            SetParameter( 
+                CNCSJP2FileView::JP2_DECOMPRESS_LAYERS, 
+                (UINT32) atoi(pszOption) );
+
+        pszOption = CSLFetchNameValue(papszOptions, 
+                                      "DECOMPRESS_RECONSTRUCTION_PARAMETER");
+        if( pszOption != NULL )
+            SetParameter( 
+                CNCSJP2FileView::JPC_DECOMPRESS_RECONSTRUCTION_PARAMETER, 
+                (IEEE4) atof(pszOption) );
+    }
+                                  
+/* -------------------------------------------------------------------- */
+/*      Georeferencing.                                                 */
+/* -------------------------------------------------------------------- */
+
+    psClient->fOriginX = 0.0;
+    psClient->fOriginY = psClient->nSizeY;
+    psClient->fCellIncrementX = 1.0;
+    psClient->fCellIncrementY = -1.0;
+    psClient->fCWRotationDegrees = 0.0;
+    
+    if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0 )
+        CPLError( CE_Warning, CPLE_NotSupported, 
+                  "Rotational coefficients ignored, georeferencing of\n"
+                  "output ECW file will be incorrect.\n" );
+    else
+    {
+        psClient->fOriginX = padfGeoTransform[0];
+        psClient->fOriginY = padfGeoTransform[3];
+        psClient->fCellIncrementX = padfGeoTransform[1];
+        psClient->fCellIncrementY = padfGeoTransform[5];
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Projection.                                                     */
+/* -------------------------------------------------------------------- */
+    char szProjection[128];
+    char szDatum[128];
+
+    strcpy( szProjection, "RAW" );
+    strcpy( szDatum, "RAW" );
+    
+    if( CSLFetchNameValue(papszOptions, "PROJ") != NULL )
+        strcpy( szProjection, 
+                CSLFetchNameValue(papszOptions, "PROJ") );
+
+    if( CSLFetchNameValue(papszOptions, "DATUM") != NULL )
+    {
+        strcpy( szDatum, CSLFetchNameValue(papszOptions, "DATUM") );
+        if( EQUAL(szProjection,"RAW") )
+            strcpy( szProjection, "GEODETIC" );
+    }
+
+    if( EQUAL(szProjection,"RAW") && pszWKT != NULL )
+    {
+        ECWTranslateFromWKT( pszWKT, szProjection, szDatum );
+    }
+
+    psClient->szDatum = szDatum;
+    psClient->szProjection = szProjection;
+
+    CPLDebug( "ECW", "Writing with PROJ=%s, DATUM=%s", 
+              szProjection, szDatum );
+
+/* -------------------------------------------------------------------- */
+/*      Setup GML information.                                          */
+/* -------------------------------------------------------------------- */
+    PrepareCoverageBox( pszWKT, padfGeoTransform );
+    PrepareGeoTIFFBox( pszWKT, padfGeoTransform,
+                       nGCPCount, pasGCPList );
+    
+/* -------------------------------------------------------------------- */
+/*      Handle special case of a JPEG2000 data stream in another file.  */
+/* -------------------------------------------------------------------- */
+    FILE *fpVSIL = NULL;
+
+    if( EQUALN(pszFilename,"J2K_SUBFILE:",12) )
+    {
+        int  subfile_offset=-1, subfile_size=-1;
+        const char *real_filename = NULL;
+
+        if( sscanf( pszFilename, "J2K_SUBFILE:%d,%d", 
+                    &subfile_offset, &subfile_size ) != 2 )
+        {
+            CPLError( CE_Failure, CPLE_OpenFailed, 
+                      "Failed to parse J2K_SUBFILE specification." );
+            return CE_Failure;
+        }
+
+        real_filename = strstr(pszFilename,",");
+        if( real_filename != NULL )
+            real_filename = strstr(real_filename+1,",");
+        if( real_filename != NULL )
+            real_filename++;
+        else
+        {
+            CPLError( CE_Failure, CPLE_OpenFailed, 
+                      "Failed to parse J2K_SUBFILE specification." );
+            return CE_Failure;
+        }
+
+        fpVSIL = VSIFOpenL( real_filename, "rb+" );
+        if( fpVSIL == NULL )
+        {
+            CPLError( CE_Failure, CPLE_OpenFailed, 
+                      "Failed to open %s.",  real_filename );
+            return CE_Failure;
+        }
+
+        m_OStream.Access( fpVSIL, TRUE, real_filename,
+                          subfile_offset, subfile_size );
+    }
+
+
+/* -------------------------------------------------------------------- */
+/*      Check if we can enable large files.  This option should only    */
+/*      be set when the application is adhering to one of the           */
+/*      ERMapper options for licensing larger than 500MB input          */
+/*      files.  See Bug 767.                                            */
+/* -------------------------------------------------------------------- */
+    const char *pszLargeOK = CSLFetchNameValue(papszOptions, "LARGE_OK");
+    if( pszLargeOK == NULL )
+        pszLargeOK = "NO";
+
+    pszLargeOK = CPLGetConfigOption( "ECW_LARGE_OK", pszLargeOK );
+
+    if( CSLTestBoolean(pszLargeOK) )
+    {
+        CNCSFile::SetKeySize();
+        CPLDebug( "ECW", "Large file generation enabled." );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Set the file info.                                              */
+/* -------------------------------------------------------------------- */
+    CNCSError oError;
+
+    oError = SetFileInfo( sFileInfo );
+
+    if( oError.GetErrorNumber() == NCS_SUCCESS )
+    {
+        if( fpVSIL == NULL )
+            oError = Open( (char *) pszFilename, false, true );
+        else
+            oError = CNCSJP2FileView::Open( &(m_OStream) );
+    }
+
+    if( oError.GetErrorNumber() == NCS_SUCCESS )
+        return CE_None;
+    else if( oError.GetErrorNumber() == NCS_INPUT_SIZE_EXCEEDED )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "ECW SDK 500MB compress limit exceeded." );
+        return CE_Failure;
+    }
+    else
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "%s", oError.GetErrorMessage() );
+        return CE_Failure;
+    }
+}
+
+/************************************************************************/
+/*                           ECWCreateCopy()                            */
+/************************************************************************/
+
+static GDALDataset *
+ECWCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, 
+               int bStrict, char ** papszOptions, 
+               GDALProgressFunc pfnProgress, void * pProgressData,
+               int bIsJPEG2000 )
+
+{
+    ECWInitialize();
+
+/* -------------------------------------------------------------------- */
+/*      Get various values from the source dataset.                     */
+/* -------------------------------------------------------------------- */
+    int  nBands = poSrcDS->GetRasterCount();
+    int  nXSize = poSrcDS->GetRasterXSize();
+    int  nYSize = poSrcDS->GetRasterYSize();
+    GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
+
+    const char *pszWKT = poSrcDS->GetProjectionRef();
+    double adfGeoTransform[6] = { 0, 1, 0, 0, 0, 1 };;
+
+    poSrcDS->GetGeoTransform( adfGeoTransform );
+
+    if( poSrcDS->GetGCPCount() > 0 )
+        pszWKT = poSrcDS->GetGCPProjection();
+
+/* -------------------------------------------------------------------- */
+/*      Setup the compressor.                                           */
+/* -------------------------------------------------------------------- */
+    GDALECWCompressor         oCompressor;
+
+    oCompressor.pfnProgress = pfnProgress;
+    oCompressor.pProgressData = pProgressData;
+    oCompressor.m_poSrcDS = poSrcDS;
+
+    if( !pfnProgress( 0.0, NULL, pProgressData ) )
+        return NULL;
+
+    if( oCompressor.Initialize( pszFilename, papszOptions, 
+                                nXSize, nYSize, nBands,
+                                eType, pszWKT, adfGeoTransform, 
+                                poSrcDS->GetGCPCount(), 
+                                poSrcDS->GetGCPs(),
+                                bIsJPEG2000 )
+        != CE_None )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Start the compression.                                          */
+/* -------------------------------------------------------------------- */
+    oCompressor.Write();
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup, and return read-only handle.                           */
+/* -------------------------------------------------------------------- */
+    oCompressor.CloseDown();
+    pfnProgress( 1.001, NULL, pProgressData );
+
+/* -------------------------------------------------------------------- */
+/*      Re-open dataset, and copy any auxilary pam information.         */
+/* -------------------------------------------------------------------- */
+    GDALDataset *poDS = (GDALDataset *) 
+        GDALOpen( pszFilename, GA_ReadOnly );
+
+    if( poDS )
+        ((GDALPamDataset *) poDS)->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                          ECWCreateCopyECW()                          */
+/************************************************************************/
+
+GDALDataset *
+ECWCreateCopyECW( const char * pszFilename, GDALDataset *poSrcDS, 
+                  int bStrict, char ** papszOptions, 
+                  GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    if( !EQUAL(CPLGetExtension(pszFilename),"ecw") )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "ECW driver does not support creating ECW files\n"
+                  "with an extension other than .ecw" );
+        return NULL;
+    }
+
+    if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte 
+        && bStrict )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "ECW driver doesn't support data type %s. "
+                  "Only eight bit bands supported.\n", 
+                  GDALGetDataTypeName( 
+                      poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
+
+        return NULL;
+    }
+
+    if( poSrcDS->GetRasterXSize() < 128 
+        || poSrcDS->GetRasterYSize() < 128 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "ECW driver requires image to be at least 128x128,\n"
+                  "the source image is %dx%d.\n", 
+                  poSrcDS->GetRasterXSize(),
+                  poSrcDS->GetRasterYSize() );
+                  
+        return NULL;
+    }
+
+    return ECWCreateCopy( pszFilename, poSrcDS, bStrict, papszOptions, 
+                          pfnProgress, pProgressData, FALSE );
+}
+
+/************************************************************************/
+/*                       ECWCreateCopyJPEG2000()                        */
+/************************************************************************/
+
+GDALDataset *
+ECWCreateCopyJPEG2000( const char * pszFilename, GDALDataset *poSrcDS, 
+                       int bStrict, char ** papszOptions, 
+                       GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    if( EQUAL(CPLGetExtension(pszFilename),"ecw") )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "JP2ECW driver does not support creating JPEG2000 files\n"
+                  "with a .ecw extension.  Please use anything else." );
+        return NULL;
+    }
+
+    if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte 
+        && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte 
+        && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Int16
+        && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16
+        && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Int32
+        && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt32
+        && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Float32
+        && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Float64
+        && bStrict )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "JP2ECW driver doesn't support data type %s. ",
+                  GDALGetDataTypeName( 
+                      poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
+
+        return NULL;
+    }
+
+    return ECWCreateCopy( pszFilename, poSrcDS, bStrict, papszOptions, 
+                          pfnProgress, pProgressData, TRUE );
+}
+
+/************************************************************************/
+/************************************************************************
+ 
+               ECW/JPEG200 Create() Support
+               ----------------------------
+
+  The remainder of the file is code to implement the Create() method. 
+  New dataset and raster band classes are defined specifically for the
+  purpose of being write-only.  In particular, you cannot read back data
+  from these datasets, and writing must occur in a pretty specific order.
+  
+  That is, you need to write all metadata (projection, georef, etc) first
+  and then write the image data.  All bands data for the first scanline
+  should be written followed by all bands for the second scanline and so on.
+
+  Creation supports the same virtual subfile names as CreateCopy() supports. 
+
+ ************************************************************************/
+/************************************************************************/
+
+/************************************************************************/
+/* ==================================================================== */
+/*				ECWWriteDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class ECWWriteRasterBand;
+
+class CPL_DLL ECWWriteDataset : public GDALDataset
+{
+    friend class ECWWriteRasterBand;
+
+    char      *pszFilename;
+
+    int       bIsJPEG2000;
+    GDALDataType eDataType;
+    char    **papszOptions;
+  
+    char     *pszProjection;
+    double    adfGeoTransform[6];
+
+    GDALECWCompressor oCompressor;
+    int       bCrystalized;
+    
+    int       nLoadedLine;
+    GByte     *pabyBILBuffer;
+    
+    CPLErr    Crystalize();
+    CPLErr    FlushLine();
+
+  public:
+    		ECWWriteDataset( const char *, int, int, int, 
+                                 GDALDataType, char **papszOptions,
+                                 int );
+    		~ECWWriteDataset();
+
+    virtual void   FlushCache( void );
+                
+    virtual CPLErr SetGeoTransform( double * );
+    virtual CPLErr SetProjection( const char *pszWKT );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                         ECWWriteRasterBand                           */
+/* ==================================================================== */
+/************************************************************************/
+ 
+class ECWWriteRasterBand : public GDALRasterBand
+{
+    friend class ECWWriteDataset;
+    
+    // NOTE: poDS may be altered for NITF/JPEG2000 files!
+    ECWWriteDataset     *poGDS;
+
+    GDALColorInterp     eInterp;
+
+  public:
+
+                   ECWWriteRasterBand( ECWWriteDataset *, int );
+
+    virtual CPLErr SetColorInterpretation( GDALColorInterp eInterpIn ) 
+        { eInterp = eInterpIn; return CE_None; }
+    virtual GDALColorInterp GetColorInterpretation() 
+        { return eInterp; }
+
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual CPLErr IWriteBlock( int, int, void * );
+};
+
+/************************************************************************/
+/*                          ECWWriteDataset()                           */
+/************************************************************************/
+
+ECWWriteDataset::ECWWriteDataset( const char *pszFilename, 
+                                  int nXSize, int nYSize, int nBandCount, 
+                                  GDALDataType eType,
+                                  char **papszOptions, int bIsJPEG2000 )
+
+{
+    bCrystalized = FALSE;
+    pabyBILBuffer = NULL;
+    nLoadedLine = -1;
+
+    this->bIsJPEG2000 = bIsJPEG2000;
+    this->eDataType = eType;
+    this->papszOptions = CSLDuplicate( papszOptions );
+    this->pszFilename = CPLStrdup( pszFilename );
+
+    nRasterXSize = nXSize;
+    nRasterYSize = nYSize;
+    pszProjection = NULL;
+
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+
+    // create band objects.
+    for( int iBand = 1; iBand <= nBandCount; iBand++ )
+    {
+        SetBand( iBand, new ECWWriteRasterBand( this, iBand ) );
+    }
+}
+
+/************************************************************************/
+/*                          ~ECWWriteDataset()                          */
+/************************************************************************/
+
+ECWWriteDataset::~ECWWriteDataset()
+
+{
+    FlushCache();
+
+    if( bCrystalized )
+    {
+        if( nLoadedLine == nRasterYSize - 1 )
+            FlushLine();
+        oCompressor.CloseDown();
+    }
+
+    CPLFree( pszProjection );
+    CSLDestroy( papszOptions );
+    CPLFree( pszFilename );
+}
+
+/************************************************************************/
+/*                             FlushCache()                             */
+/************************************************************************/
+
+void ECWWriteDataset::FlushCache()
+
+{
+    BlockBasedFlushCache();
+}
+
+/************************************************************************/
+/*                          SetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr ECWWriteDataset::SetGeoTransform( double *padfGeoTransform )
+
+{
+    memcpy( adfGeoTransform, padfGeoTransform, sizeof(double) * 6 );
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           SetProjection()                            */
+/************************************************************************/
+
+CPLErr ECWWriteDataset::SetProjection( const char *pszWKT )
+
+{
+    CPLFree( pszProjection );
+    pszProjection = CPLStrdup( pszWKT );
+
+    return CE_None;
+}
+
+
+/************************************************************************/
+/*                             Crystalize()                             */
+/************************************************************************/
+
+CPLErr ECWWriteDataset::Crystalize()
+
+{
+    int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
+
+    CPLErr eErr;
+    CNCSError oError;
+
+    if( bCrystalized )
+        return CE_None;
+
+    eErr = oCompressor.Initialize( pszFilename, papszOptions, 
+                                   nRasterXSize, nRasterYSize, nBands, 
+                                   eDataType, 
+                                   pszProjection, adfGeoTransform, 
+                                   0, NULL,
+                                   bIsJPEG2000 );
+
+    if( eErr == CE_None )
+        bCrystalized = TRUE;
+
+    nLoadedLine = -1;
+    pabyBILBuffer = (GByte *) CPLMalloc( nWordSize * nBands * nRasterXSize );
+
+    return eErr;
+}
+
+/************************************************************************/
+/*                             FlushLine()                              */
+/************************************************************************/
+
+CPLErr ECWWriteDataset::FlushLine()
+
+{
+    int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
+    CPLErr eErr;
+
+/* -------------------------------------------------------------------- */
+/*      Crystalize if not already done.                                 */
+/* -------------------------------------------------------------------- */
+    if( !bCrystalized )
+    {
+        eErr = Crystalize();
+
+        if( eErr != CE_None )
+            return eErr;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Write out the currently loaded line.                            */
+/* -------------------------------------------------------------------- */
+    if( nLoadedLine != -1 )
+    {
+        CNCSError oError;
+        void **papOutputLine;
+
+        papOutputLine = (void **) CPLMalloc(sizeof(void*) * nBands);
+        for( int i = 0; i < nBands; i++ )
+            papOutputLine[i] = 
+                (void *) (pabyBILBuffer + i * nWordSize * nRasterXSize);
+        
+
+        oError = oCompressor.WriteLineBIL( oCompressor.sFileInfo.eCellType, 
+                                           (UINT16) nBands, papOutputLine );
+        CPLFree( papOutputLine );
+        if( oError.GetErrorNumber() != NCS_SUCCESS )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "Scanline write write failed.\n%s",
+                      oError.GetErrorMessage() );
+            return CE_Failure;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Clear the buffer and increment the "current line" indicator.    */
+/* -------------------------------------------------------------------- */
+    memset( pabyBILBuffer, 0, nWordSize * nRasterXSize * nBands );
+    nLoadedLine++;
+
+    return CE_None;
+}
+
+
+/************************************************************************/
+/* ==================================================================== */
+/*                          ECWWriteRasterBand                          */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                         ECWWriteRasterBand()                         */
+/************************************************************************/
+
+ECWWriteRasterBand::ECWWriteRasterBand( ECWWriteDataset *poDSIn,
+                                        int nBandIn )
+
+{
+    nBand = nBandIn;
+    poDS = poDSIn;
+    poGDS = poDSIn;
+    nBlockXSize = poDSIn->GetRasterXSize();
+    nBlockYSize = 1;
+    eDataType = poDSIn->eDataType;
+    eInterp = GCI_Undefined;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr ECWWriteRasterBand::IReadBlock( int nBlockX, int nBlockY, 
+                                       void *pBuffer )
+
+{
+    int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
+
+    // We zero stuff out here, but we can't really read stuff from
+    // a write only stream. 
+
+    memset( pBuffer, 0, nBlockXSize * nWordSize );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                            IWriteBlock()                             */
+/************************************************************************/
+
+CPLErr ECWWriteRasterBand::IWriteBlock( int nBlockX, int nBlockY, 
+                                        void *pBuffer )
+
+{
+    int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
+    CPLErr eErr;
+
+/* -------------------------------------------------------------------- */
+/*      Flush previous line if needed.                                  */
+/* -------------------------------------------------------------------- */
+    if( nBlockY == poGDS->nLoadedLine + 1 )
+    {
+        eErr = poGDS->FlushLine();
+        if( eErr != CE_None )
+            return eErr;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Blow a gasket if we have been asked to write something out      */
+/*      of order.                                                       */
+/* -------------------------------------------------------------------- */
+    if( nBlockY != poGDS->nLoadedLine )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Apparent attempt to write to ECW non-sequentially.\n"
+                  "Loaded line is %d, but %d of band %d was written to.",
+                  poGDS->nLoadedLine, nBlockY, nBand );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Copy passed data into current line buffer.                      */
+/* -------------------------------------------------------------------- */
+    memcpy( poGDS->pabyBILBuffer + (nBand-1) * nWordSize * nRasterXSize, 
+            pBuffer, 
+            nWordSize * nRasterXSize );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                         ECWCreateJPEG2000()                          */
+/************************************************************************/
+
+GDALDataset *
+ECWCreateJPEG2000(const char *pszFilename, int nXSize, int nYSize, int nBands, 
+                  GDALDataType eType, char **papszOptions )
+
+{
+    ECWInitialize();
+
+    return new ECWWriteDataset( pszFilename, nXSize, nYSize, nBands, 
+                                eType, papszOptions, TRUE );
+}
+
+/************************************************************************/
+/*                            ECWCreateECW()                            */
+/************************************************************************/
+
+GDALDataset *
+ECWCreateECW( const char *pszFilename, int nXSize, int nYSize, int nBands, 
+              GDALDataType eType, char **papszOptions )
+
+{
+    ECWInitialize();
+
+    return new ECWWriteDataset( pszFilename, nXSize, nYSize, nBands, 
+                                eType, papszOptions, FALSE );
+}
+
+#endif /* def FRMT_ecw && def HAVE_COMPRESS */
diff --git a/Utilities/GDAL/frmts/ecw/ecwdataset.cpp b/Utilities/GDAL/frmts/ecw/ecwdataset.cpp
new file mode 100644
index 0000000000..43e7eb3928
--- /dev/null
+++ b/Utilities/GDAL/frmts/ecw/ecwdataset.cpp
@@ -0,0 +1,1784 @@
+/******************************************************************************
+ * $Id: ecwdataset.cpp,v 1.62 2006/04/28 04:22:32 fwarmerdam Exp $
+ *
+ * Project:  GDAL 
+ * Purpose:  ECW (ERMapper Wavelet Compression Format) Driver
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************
+ *
+ * $Log: ecwdataset.cpp,v $
+ * Revision 1.62  2006/04/28 04:22:32  fwarmerdam
+ * make PAM fallbacks work properly
+ *
+ * Revision 1.61  2006/04/28 02:40:18  fwarmerdam
+ * Fix some problems with GCP handling.
+ *
+ * Revision 1.60  2006/04/19 14:21:47  fwarmerdam
+ * defer initialization till we need it, since shutdown is expensive
+ *
+ * Revision 1.59  2006/04/18 19:32:58  fwarmerdam
+ * Don't predicate all cleanup in Deregister() on gpaspzCSLookup.
+ * http://bugzilla.remotesensing.org/show_bug.cgi?id=1156
+ *
+ * Revision 1.58  2006/04/13 15:20:27  fwarmerdam
+ * Fixed bug in non-direct case with buffer/request different sizes
+ * and more than one band.
+ * http://bugzilla.remotesensing.org/show_bug.cgi?id=1148
+ *
+ * Revision 1.57  2006/04/07 05:50:07  fwarmerdam
+ * Modified to use GDALJP2Metadata to read all the various kinds of jpeg2000
+ * georeferencing information, including world files.
+ *
+ * Revision 1.56  2006/02/13 23:00:55  fwarmerdam
+ * cleanup up mutex when the driver is unloaded.
+ *
+ * Revision 1.55  2006/01/10 01:18:09  fwarmerdam
+ * Avoid const casting warning.
+ *
+ * Revision 1.54  2005/12/21 15:07:56  fwarmerdam
+ * better error
+ *
+ * Revision 1.53  2005/09/26 21:09:31  fwarmerdam
+ * Avoid problems in deletion of the iostream.
+ *
+ * Revision 1.52  2005/07/30 03:05:56  fwarmerdam
+ * Added LARGE_OK, frmt_jp2ecw.html link
+ *
+ * Revision 1.51  2005/07/05 22:09:23  fwarmerdam
+ * added ParseMSIG() call
+ *
+ * Revision 1.50  2005/06/28 14:17:06  fwarmerdam
+ * Added worldfile support (on read).
+ *
+ * Revision 1.49  2005/06/24 20:54:00  fwarmerdam
+ * added explicit init/shutdown calls
+ *
+ * Revision 1.48  2005/05/23 06:56:10  fwarmerdam
+ * disable pam support for subfile streams
+ *
+ * Revision 1.47  2005/05/03 21:12:14  fwarmerdam
+ * use GDALJP2Metadata facilities for GML and GeoTIFF
+ *
+ * Revision 1.46  2005/04/25 18:40:16  fwarmerdam
+ * It seems dynamic cast is bad karma on VC6 with /Ox
+ *
+ * Revision 1.45  2005/04/25 04:32:23  fwarmerdam
+ * dynamic cast seems to throw exception sometimes
+ *
+ * Revision 1.44  2005/04/12 03:58:34  fwarmerdam
+ * turn ownership of fpVSIL over to vsiiostream
+ *
+ * Revision 1.43  2005/04/11 13:56:27  fwarmerdam
+ * Added a bit of reformatting.
+ *
+ * Revision 1.42  2005/04/11 13:52:50  fwarmerdam
+ * added proper handling of iostream cleanup from David Carter
+ *
+ * Revision 1.41  2005/04/02 22:02:12  fwarmerdam
+ * initialize variables
+ *
+ * Revision 1.40  2005/04/02 21:22:21  fwarmerdam
+ * added GML and GeoTIFF box direct reading
+ *
+ * Revision 1.39  2005/03/03 17:05:40  fwarmerdam
+ * fixed up serious problems with non-8bit data in ECWDataset::IRasterIO
+ *
+ * Revision 1.38  2005/02/25 15:17:00  fwarmerdam
+ * Try to ensure that we do a multi-band adviseread in IRasterIO()
+ * for line by line access.  This should dramatically improve pixel
+ * interleaved line by line access speed.
+ *
+ * Revision 1.37  2005/02/25 14:54:37  fwarmerdam
+ * Removed extra newline in debug statement.
+ *
+ * Revision 1.36  2005/02/24 15:12:04  fwarmerdam
+ * Ensure IOStream and fpVSIL are properly cleaned up
+ *
+ * Revision 1.35  2005/02/10 04:49:53  fwarmerdam
+ * added SetColorInterpretation
+ *
+ * Revision 1.34  2005/02/10 04:31:55  fwarmerdam
+ * added support to preserve YCbCr images, added GetColorInterpretation()
+ *
+ * Revision 1.33  2005/02/08 04:51:21  fwarmerdam
+ * fixed J2K_SUBFILE parsing error, add ECWCreateJPEG2000
+ *
+ * Revision 1.32  2005/01/26 20:04:44  fwarmerdam
+ * Fixed more non8bit issues.
+ *
+ * Revision 1.31  2005/01/26 18:29:02  fwarmerdam
+ * fixed broken non-8bit cases
+ *
+ * Revision 1.30  2004/12/20 22:17:15  fwarmerdam
+ * last change also include split into ECW and JP2ECW drivers
+ *
+ * Revision 1.29  2004/12/20 22:15:41  fwarmerdam
+ * avoid ECWRasterBand::AdviseRead() now calls on poGDS, limit debug msgs
+ *
+ * Revision 1.28  2004/12/20 06:40:32  fwarmerdam
+ * fixed up for reading non-byte data
+ *
+ * Revision 1.27  2004/12/20 05:44:46  fwarmerdam
+ * stripped out old full res mode - use TryWin and AdviseRead
+ *
+ * Revision 1.26  2004/12/20 05:02:43  fwarmerdam
+ * moved list reader into ECWGetCSList()
+ *
+ * Revision 1.25  2004/12/17 14:50:30  fwarmerdam
+ * implement AdviseRead API
+ *
+ * Revision 1.24  2004/12/10 19:18:05  fwarmerdam
+ * Overhauled to use C++ ECW SDK classes.
+ * Moved creation code into ecwcreatecopy.cpp
+ * Support J2K_SUBFILE for reading "virtually".
+ *
+ * Revision 1.23  2004/06/24 04:53:40  warmerda
+ * remove assert in IRasterIO(), seems to be unneeded
+ *
+ * Revision 1.22  2004/04/15 13:45:03  warmerda
+ * Don't require the file to be open in ::Open() for .ecw files.  Large
+ * files (>2GB) will generally not be open.
+ *
+ * Revision 1.21  2004/04/05 21:29:40  warmerda
+ * improved logic to recognise jp2/jpc
+ *
+ * Revision 1.20  2004/04/05 20:49:34  warmerda
+ * allow jpeg2000 files to be handled
+ *
+ * Revision 1.19  2004/03/30 17:33:51  warmerda
+ * fixed last fix for ecwp:
+ *
+ * Revision 1.18  2004/03/30 17:00:07  warmerda
+ * Allow "ecwp:" urls to be opened.
+ */
+
+#include "gdal_pam.h"
+#include "gdaljp2metadata.h"
+#include "ogr_spatialref.h"
+#include "cpl_string.h"
+#include "cpl_conv.h"
+#include "vsiiostream.h"
+#include "cpl_multiproc.h"
+#include "cpl_minixml.h"
+#include "ogr_api.h"
+#include "ogr_geometry.h"
+
+CPL_CVSID("$Id: ecwdataset.cpp,v 1.62 2006/04/28 04:22:32 fwarmerdam Exp $");
+
+#ifdef FRMT_ecw
+
+static unsigned char jpc_header[] = {0xff,0x4f};
+static unsigned char jp2_header[] = 
+{0x00,0x00,0x00,0x0c,0x6a,0x50,0x20,0x20,0x0d,0x0a,0x87,0x0a};
+
+static int    gnTriedCSFile = FALSE;
+static char **gpapszCSLookup = NULL;
+static void *hECWDatasetMutex = NULL;
+static int    bNCSInitialized = FALSE;
+
+CPL_C_START
+CPLErr CPL_DLL GTIFMemBufFromWkt( const char *pszWKT, 
+                                  const double *padfGeoTransform,
+                                  int nGCPCount, const GDAL_GCP *pasGCPList,
+                                  int *pnSize, unsigned char **ppabyBuffer );
+CPLErr CPL_DLL GTIFWktFromMemBuf( int nSize, unsigned char *pabyBuffer, 
+                          char **ppszWKT, double *padfGeoTransform,
+                          int *pnGCPCount, GDAL_GCP **ppasGCPList );
+CPL_C_END
+
+void ECWInitialize( void );
+
+/************************************************************************/
+/* ==================================================================== */
+/*				ECWDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class ECWRasterBand;
+
+class CPL_DLL ECWDataset : public GDALPamDataset
+{
+    friend class ECWRasterBand;
+
+    CNCSJP2FileView *poFileView;
+    NCSFileViewFileInfoEx *psFileInfo;
+
+    GDALDataType eRasterDataType;
+    NCSEcwCellType eNCSRequestDataType;
+
+    int         bUsingCustomStream;
+
+    // Current view window. 
+    int         bWinActive;
+    int         nWinXOff, nWinYOff, nWinXSize, nWinYSize;
+    int         nWinBufXSize, nWinBufYSize;
+    int         nWinBandCount;
+    int         *panWinBandList;
+    int         nWinBufLoaded;
+    void        **papCurLineBuf;
+
+    int         bGeoTransformValid;
+    double      adfGeoTransform[6];
+    char        *pszProjection;
+    int         nGCPCount;
+    GDAL_GCP    *pasGCPList;
+
+    char        **papszGMLMetadata;
+
+    void        ECW2WKTProjection();
+
+    void        CleanupWindow();
+    int         TryWinRasterIO( GDALRWFlag, int, int, int, int,
+                                GByte *, int, int, GDALDataType,
+                                int, int *, int, int, int );
+    CPLErr      LoadNextLine();
+
+  public:
+    		ECWDataset();
+    		~ECWDataset();
+                
+    static GDALDataset *Open( GDALOpenInfo * );
+    static GDALDataset *OpenJPEG2000( GDALOpenInfo * );
+    static GDALDataset *OpenECW( GDALOpenInfo * );
+
+    virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
+                              void *, int, int, GDALDataType,
+                              int, int *, int, int, int );
+
+    virtual CPLErr GetGeoTransform( double * );
+    virtual const char *GetProjectionRef();
+
+    virtual int    GetGCPCount();
+    virtual const char *GetGCPProjection();
+    virtual const GDAL_GCP *GetGCPs();
+
+    virtual char      **GetMetadata( const char * pszDomain = "" );
+
+    virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize,
+                               int nBufXSize, int nBufYSize, 
+                               GDALDataType eDT, 
+                               int nBandCount, int *panBandList,
+                               char **papszOptions );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            ECWRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class ECWRasterBand : public GDALPamRasterBand
+{
+    friend class ECWDataset;
+    
+    // NOTE: poDS may be altered for NITF/JPEG2000 files!
+    ECWDataset     *poGDS;
+
+    GDALColorInterp         eBandInterp;
+
+    virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
+                              void *, int, int, GDALDataType,
+                              int, int );
+
+  public:
+
+                   ECWRasterBand( ECWDataset *, int );
+                   ~ECWRasterBand();
+
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual int    HasArbitraryOverviews() { return TRUE; }
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual CPLErr SetColorInterpretation( GDALColorInterp );
+
+    virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize,
+                               int nBufXSize, int nBufYSize, 
+                               GDALDataType eDT, char **papszOptions );
+};
+
+/************************************************************************/
+/*                           ECWRasterBand()                            */
+/************************************************************************/
+
+ECWRasterBand::ECWRasterBand( ECWDataset *poDS, int nBand )
+
+{
+    this->poDS = poDS;
+    poGDS = poDS;
+
+    this->nBand = nBand;
+    eDataType = poDS->eRasterDataType;
+    nBlockXSize = poDS->GetRasterXSize();
+    nBlockYSize = 1;
+
+/* -------------------------------------------------------------------- */
+/*      Work out band color interpretation.                             */
+/* -------------------------------------------------------------------- */
+    if( poDS->psFileInfo->eColorSpace == NCSCS_NONE )
+        eBandInterp = GCI_Undefined;
+    else if( poDS->psFileInfo->eColorSpace == NCSCS_GREYSCALE )
+        eBandInterp = GCI_GrayIndex;
+    else if( poDS->psFileInfo->eColorSpace == NCSCS_MULTIBAND )
+        eBandInterp = GCI_Undefined;
+    else if( poDS->psFileInfo->eColorSpace == NCSCS_sRGB )
+    {
+        if( nBand == 1 )
+            eBandInterp = GCI_RedBand;
+        else if( nBand == 2 )
+            eBandInterp = GCI_GreenBand;
+        else if( nBand == 3 )
+            eBandInterp = GCI_BlueBand;
+        else
+            eBandInterp = GCI_Undefined;
+    }
+    else if( poDS->psFileInfo->eColorSpace == NCSCS_YCbCr )
+    {
+        if( CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB","YES") ))
+        {
+            if( nBand == 1 )
+                eBandInterp = GCI_RedBand;
+            else if( nBand == 2 )
+                eBandInterp = GCI_GreenBand;
+            else if( nBand == 3 )
+                eBandInterp = GCI_BlueBand;
+            else
+                eBandInterp = GCI_Undefined;
+        }
+        else
+        {
+            if( nBand == 1 )
+                eBandInterp = GCI_YCbCr_YBand;
+            else if( nBand == 2 )
+                eBandInterp = GCI_YCbCr_CbBand;
+            else if( nBand == 3 )
+                eBandInterp = GCI_YCbCr_CrBand;
+            else
+                eBandInterp = GCI_Undefined;
+        }
+    }
+    else
+        eBandInterp = GCI_Undefined;
+}
+
+/************************************************************************/
+/*                          ~ECWRasterBand()                           */
+/************************************************************************/
+
+ECWRasterBand::~ECWRasterBand()
+
+{
+    FlushCache();
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp ECWRasterBand::GetColorInterpretation()
+
+{
+    return eBandInterp;
+}
+
+/************************************************************************/
+/*                       SetColorInterpretation()                       */
+/*                                                                      */
+/*      This would normally just be used by folks using the ECW code    */
+/*      to read JP2 streams in other formats (such as NITF) and         */
+/*      providing their own color interpretation regardless of what     */
+/*      ECW might think the stream itself says.                         */
+/************************************************************************/
+
+CPLErr ECWRasterBand::SetColorInterpretation( GDALColorInterp eNewInterp )
+
+{
+    eBandInterp = eNewInterp;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                             AdviseRead()                             */
+/************************************************************************/
+
+CPLErr ECWRasterBand::AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize,
+                                  int nBufXSize, int nBufYSize, 
+                                  GDALDataType eDT, 
+                                  char **papszOptions )
+{
+    return poGDS->AdviseRead( nXOff, nYOff, nXSize, nYSize, 
+                              nBufXSize, nBufYSize, eDT, 
+                              1, &nBand, papszOptions );
+}
+
+/************************************************************************/
+/*                             IRasterIO()                              */
+/************************************************************************/
+
+CPLErr ECWRasterBand::IRasterIO( GDALRWFlag eRWFlag,
+                                 int nXOff, int nYOff, int nXSize, int nYSize,
+                                 void * pData, int nBufXSize, int nBufYSize,
+                                 GDALDataType eBufType,
+                                 int nPixelSpace, int nLineSpace )
+    
+{
+    int          iBand, bDirect;
+    int          nNewXSize = nBufXSize, nNewYSize = nBufYSize;
+    GByte        *pabyWorkBuffer = NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Try to do it based on existing "advised" access.                */
+/* -------------------------------------------------------------------- */
+    if( poGDS->TryWinRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, 
+                               (GByte *) pData, nBufXSize, nBufYSize, 
+                               eBufType, 1, &nBand, 
+                               nPixelSpace, nLineSpace, 0 ) )
+        return CE_None;
+
+/* -------------------------------------------------------------------- */
+/*      We will drop down to the block oriented API if only a single    */
+/*      scanline was requested. This is based on the assumption that    */
+/*      doing lots of single scanline windows is expensive.             */
+/* -------------------------------------------------------------------- */
+    if( nYSize == 1 )
+    {
+#ifdef notdef
+        CPLDebug( "ECWRasterBand", 
+                  "RasterIO(%d,%d,%d,%d -> %dx%d) - redirected.", 
+                  nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize );
+#endif
+        return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
+                                         pData, nBufXSize, nBufYSize, 
+                                         eBufType, nPixelSpace, nLineSpace );
+    }
+
+    CPLDebug( "ECWRasterBand", 
+              "RasterIO(nXOff=%d,nYOff=%d,nXSize=%d,nYSize=%d -> %dx%d)", 
+              nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize );
+
+
+    if ( nXSize < nBufXSize )
+            nNewXSize = nXSize;
+
+    if ( nYSize < nBufYSize )
+            nNewYSize = nYSize;
+
+/* -------------------------------------------------------------------- */
+/*      Default line and pixel spacing if needed.                       */
+/* -------------------------------------------------------------------- */
+    if( nLineSpace == 0 )
+        nLineSpace = nBufXSize;
+
+    if( nPixelSpace == 0 )
+        nPixelSpace = 1;
+
+/* -------------------------------------------------------------------- */
+/*      Can we perform direct loads, or must we load into a working     */
+/*      buffer, and transform?                                          */
+/* -------------------------------------------------------------------- */
+    int      	nRawPixelSize = GDALGetDataTypeSize(poGDS->eRasterDataType) / 8;
+
+    bDirect = nPixelSpace == 1 && eBufType == GDT_Byte
+	    && nNewXSize == nBufXSize && nNewYSize == nBufYSize;
+    if( !bDirect )
+        pabyWorkBuffer = (GByte *) CPLMalloc(nNewXSize * nRawPixelSize);
+
+/* -------------------------------------------------------------------- */
+/*      Establish access at the desired resolution.                     */
+/* -------------------------------------------------------------------- */
+    CNCSError oErr;
+
+    poGDS->CleanupWindow();
+
+    iBand = nBand-1;
+    oErr = poGDS->poFileView->SetView( 1, (unsigned int *) (&iBand),
+                                       nXOff, nYOff, 
+                                       nXOff + nXSize - 1, 
+                                       nYOff + nYSize - 1,
+                                       nNewXSize, nNewYSize );
+    if( oErr.GetErrorNumber() != NCS_SUCCESS )
+    {
+        CPLFree( pabyWorkBuffer );
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "%s", oErr.GetErrorMessage() );
+        
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read back one scanline at a time, till request is satisfied.    */
+/*      Supersampling is not supported by the ECW API, so we will do    */
+/*      it ourselves.                                                   */
+/* -------------------------------------------------------------------- */
+    double  	dfSrcYInc = (double)nNewYSize / nBufYSize;
+    double  	dfSrcXInc = (double)nNewXSize / nBufXSize;
+    int      	iSrcLine, iDstLine;
+
+    for( iSrcLine = 0, iDstLine = 0; iDstLine < nBufYSize; iDstLine++ )
+    {
+        NCSEcwReadStatus eRStatus;
+        int     	iDstLineOff = iDstLine * nLineSpace;
+        unsigned char 	*pabySrcBuf;
+
+        if( bDirect )
+            pabySrcBuf = ((GByte *)pData) + iDstLineOff;
+        else
+            pabySrcBuf = pabyWorkBuffer;
+
+	if ( nNewYSize == nBufYSize || iSrcLine == (int)(iDstLine * dfSrcYInc) )
+	{
+            eRStatus = poGDS->poFileView->ReadLineBIL( 
+                poGDS->eNCSRequestDataType, 1, (void **) &pabySrcBuf );
+
+	    if( eRStatus != NCSECW_READ_OK )
+	    {
+	        CPLFree( pabyWorkBuffer );
+	        CPLError( CE_Failure, CPLE_AppDefined,
+			  "NCScbmReadViewLineBIL failed." );
+		return CE_Failure;
+	    }
+
+            if( !bDirect )
+            {
+                if ( nNewXSize == nBufXSize )
+                {
+                    GDALCopyWords( pabyWorkBuffer, poGDS->eRasterDataType, 
+                                   nRawPixelSize, 
+                                   ((GByte *)pData) + iDstLine * nLineSpace, 
+                                   eBufType, nPixelSpace, nBufXSize );
+                }
+		else
+		{
+	            int 	iPixel;
+
+                    for ( iPixel = 0; iPixel < nBufXSize; iPixel++ )
+                    {
+                        GDALCopyWords( pabyWorkBuffer 
+                                       + nRawPixelSize*((int)(iPixel*dfSrcXInc)),
+                                       poGDS->eRasterDataType, nRawPixelSize,
+                                       (GByte *)pData + iDstLineOff
+				       + iPixel * nPixelSpace,
+                                       eBufType, nPixelSpace, 1 );
+                    }
+		}
+            }
+
+            iSrcLine++;
+	}
+	else
+	{
+	    // Just copy the previous line in this case
+	    memcpy( (GByte *)pData + iDstLineOff,
+		    (GByte *)pData + (iDstLineOff - nLineSpace), nLineSpace );
+	}
+    }
+
+    CPLFree( pabyWorkBuffer );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr ECWRasterBand::IReadBlock( int, int nBlockYOff, void * pImage )
+
+{
+    CPLErr eErr = CE_None;
+
+    if( poGDS->TryWinRasterIO( GF_Read, 0, nBlockYOff, nBlockXSize, 1, 
+                               (GByte *) pImage, nBlockXSize, 1, 
+                               eDataType, 1, &nBand, 0, 0, 0 ) )
+        return CE_None;
+
+    eErr = AdviseRead( 0, nBlockYOff, nRasterXSize, nRasterYSize - nBlockYOff,
+                       nRasterXSize, nRasterYSize - nBlockYOff, 
+                       eDataType, NULL );
+    if( eErr != CE_None )
+        return eErr;
+
+    if( poGDS->TryWinRasterIO( GF_Read, 0, nBlockYOff, nBlockXSize, 1, 
+                               (GByte *) pImage, nBlockXSize, 1, 
+                               eDataType, 1, &nBand, 0, 0, 0 ) )
+        return CE_None;
+
+    CPLError( CE_Failure, CPLE_AppDefined, 
+              "TryWinRasterIO() failed for blocked scanline %d of band %d.",
+              nBlockYOff, nBand );
+    return CE_Failure;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            ECWDataset                               */
+/* ==================================================================== */
+/************************************************************************/
+
+
+/************************************************************************/
+/*                            ECWDataset()                              */
+/************************************************************************/
+
+ECWDataset::ECWDataset()
+
+{
+    bUsingCustomStream = FALSE;
+    pszProjection = NULL;
+    poFileView = NULL;
+    bWinActive = FALSE;
+    panWinBandList = NULL;
+    eRasterDataType = GDT_Byte;
+    nGCPCount = 0;
+    pasGCPList = NULL;
+    papszGMLMetadata = NULL;
+    
+    bGeoTransformValid = FALSE;
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+}
+
+/************************************************************************/
+/*                           ~ECWDataset()                              */
+/************************************************************************/
+
+ECWDataset::~ECWDataset()
+
+{
+    FlushCache();
+    CleanupWindow();
+    CPLFree( pszProjection );
+    CSLDestroy( papszGMLMetadata );
+
+    if( nGCPCount > 0 )
+    {
+        GDALDeinitGCPs( nGCPCount, pasGCPList );
+        CPLFree( pasGCPList );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Release / dereference iostream.                                 */
+/* -------------------------------------------------------------------- */
+    // The underlying iostream of the CNCSJP2FileView (poFileView) object may 
+    // also be the underlying iostream of other CNCSJP2FileView (poFileView) 
+    // objects.  Consequently, when we delete the CNCSJP2FileView (poFileView) 
+    // object, we must decrement the nFileViewCount attribute of the underlying
+    // VSIIOStream object, and only delete the VSIIOStream object when 
+    // nFileViewCount is equal to zero.
+
+    CPLMutexHolder oHolder( &hECWDatasetMutex );
+
+    if( poFileView != NULL )
+    {
+        VSIIOStream *poUnderlyingIOStream = (VSIIOStream *)NULL;
+
+        poUnderlyingIOStream = ((VSIIOStream *)(poFileView->GetStream()));
+        delete poFileView;
+
+        if( bUsingCustomStream )
+        {
+            if( --poUnderlyingIOStream->nFileViewCount == 0 )
+                delete poUnderlyingIOStream;
+        }
+    }
+}
+
+/************************************************************************/
+/*                             AdviseRead()                             */
+/************************************************************************/
+
+CPLErr ECWDataset::AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize,
+                               int nBufXSize, int nBufYSize, 
+                               GDALDataType eDT, 
+                               int nBandCount, int *panBandList,
+                               char **papszOptions )
+
+{
+    int *panAdjustedBandList = NULL;
+
+    CPLDebug( "ECW",
+              "ECWDataset::AdviseRead(%d,%d,%d,%d->%d,%d)",
+              nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize );
+
+    if( nBufXSize > nXSize || nBufYSize > nYSize )
+    {
+        CPLError( CE_Warning, CPLE_AppDefined, 
+                  "Supersampling not directly supported by ECW toolkit,\n"
+                  "ignoring AdviseRead() request." );
+        return CE_Warning; 
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Adjust band numbers to be zero based.                           */
+/* -------------------------------------------------------------------- */
+    panAdjustedBandList = (int *) 
+        CPLMalloc(sizeof(int) * nBandCount );
+    for( int ii= 0; ii < nBandCount; ii++ )
+        panAdjustedBandList[ii] = panBandList[ii] - 1;
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup old window cache information.                           */
+/* -------------------------------------------------------------------- */
+    CleanupWindow();
+
+/* -------------------------------------------------------------------- */
+/*      Set the new requested window.                                   */
+/* -------------------------------------------------------------------- */
+    CNCSError oErr;
+    
+    oErr = poFileView->SetView( nBandCount, (UINT32 *) panAdjustedBandList, 
+                                nXOff, nYOff, 
+                                nXOff + nXSize-1, nYOff + nYSize-1,
+                                nBufXSize, nBufYSize );
+
+    CPLFree( panAdjustedBandList );
+    if( oErr.GetErrorNumber() != NCS_SUCCESS )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "%s", oErr.GetErrorMessage() );
+        bWinActive = FALSE;
+        return CE_Failure;
+    }
+
+    bWinActive = TRUE;
+
+/* -------------------------------------------------------------------- */
+/*      Record selected window.                                         */
+/* -------------------------------------------------------------------- */
+    nWinXOff = nXOff;
+    nWinYOff = nYOff;
+    nWinXSize = nXSize;
+    nWinYSize = nYSize;
+    nWinBufXSize = nBufXSize;
+    nWinBufYSize = nBufYSize;
+
+    panWinBandList = (int *) CPLMalloc(sizeof(int)*nBandCount);
+    memcpy( panWinBandList, panBandList, sizeof(int)* nBandCount);
+    nWinBandCount = nBandCount;
+
+    nWinBufLoaded = -1;
+
+/* -------------------------------------------------------------------- */
+/*      Allocate current scanline buffer.                               */
+/* -------------------------------------------------------------------- */
+    papCurLineBuf = (void **) CPLMalloc(sizeof(void*) * nWinBandCount );
+    for( int iBand = 0; iBand < nWinBandCount; iBand++ )
+        papCurLineBuf[iBand] = 
+            CPLMalloc(nBufXSize * (GDALGetDataTypeSize(eRasterDataType)/8) );
+        
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           TryWinRasterIO()                           */
+/*                                                                      */
+/*      Try to satisfy the given request based on the currently         */
+/*      defined window.  Return TRUE on success or FALSE on             */
+/*      failure.  On failure, the caller should satisfy the request     */
+/*      another way (not report an error).                              */
+/************************************************************************/
+
+int ECWDataset::TryWinRasterIO( GDALRWFlag eFlag, 
+                                int nXOff, int nYOff, int nXSize, int nYSize,
+                                GByte *pabyData, int nBufXSize, int nBufYSize, 
+                                GDALDataType eDT,
+                                int nBandCount, int *panBandList, 
+                                int nPixelSpace, int nLineSpace, 
+                                int nBandSpace )
+
+{
+    int iBand, i;
+
+/* -------------------------------------------------------------------- */
+/*      Provide default buffer organization.                            */
+/* -------------------------------------------------------------------- */
+    if( nPixelSpace == 0 )
+        nPixelSpace = GDALGetDataTypeSize( eDT ) / 8;
+    if( nLineSpace == 0 )
+        nLineSpace = nPixelSpace * nBufXSize;
+    if( nBandSpace == 0 )
+        nBandSpace = nLineSpace * nBufYSize;
+
+/* -------------------------------------------------------------------- */
+/*      Do some simple tests to see if the current window can           */
+/*      satisfy our requirement.                                        */
+/* -------------------------------------------------------------------- */
+    if( !bWinActive )
+        return FALSE;
+    
+    if( nXOff != nWinXOff || nXSize != nWinXSize )
+        return FALSE;
+
+    if( nBufXSize != nWinBufXSize )
+        return FALSE;
+
+    for( iBand = 0; iBand < nBandCount; iBand++ )
+    {
+        for( i = 0; i < nWinBandCount; i++ )
+        {
+            if( panWinBandList[iBand] == panBandList[iBand] )
+                break;
+        }
+
+        if( i == nWinBandCount )
+            return FALSE;
+    }
+
+    if( nYOff < nWinYOff || nYOff + nYSize > nWinYOff + nWinYSize )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Now we try more subtle tests.                                   */
+/* -------------------------------------------------------------------- */
+    {
+        static int nDebugCount = 0;
+
+        if( nDebugCount < 30 )
+            CPLDebug( "ECWDataset", 
+                      "TryWinRasterIO(%d,%d,%d,%d -> %dx%d) - doing advised read.", 
+                      nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize );
+
+        if( nDebugCount == 29 )
+            CPLDebug( "ECWDataset", "No more TryWinRasterIO messages will be reported" );
+        
+        nDebugCount++;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Actually load data one buffer line at a time.                   */
+/* -------------------------------------------------------------------- */
+    int iBufLine;
+
+    for( iBufLine = 0; iBufLine < nBufYSize; iBufLine++ )
+    {
+        float fFileLine = ((iBufLine+0.5) / nBufYSize) * nYSize + nYOff;
+        int iWinLine = 
+            (int) (((fFileLine - nWinYOff) / nWinYSize) * nWinBufYSize);
+        
+        if( iWinLine == nWinBufLoaded + 1 )
+            LoadNextLine();
+
+        if( iWinLine != nWinBufLoaded )
+            return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Copy out all our target bands.                                  */
+/* -------------------------------------------------------------------- */
+        int iWinBand;
+        for( iBand = 0; iBand < nBandCount; iBand++ )
+        {
+            for( iWinBand = 0; iWinBand < nWinBandCount; iWinBand++ )
+            {
+                if( panWinBandList[iWinBand] == panBandList[iBand] )
+                    break;
+            }
+
+            GDALCopyWords( papCurLineBuf[iWinBand], eRasterDataType,
+                           GDALGetDataTypeSize( eRasterDataType ) / 8, 
+                           pabyData + nBandSpace * iBand 
+                           + iBufLine * nLineSpace, eDT, nPixelSpace,
+                           nBufXSize );
+        }
+    }
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                            LoadNextLine()                            */
+/************************************************************************/
+
+CPLErr ECWDataset::LoadNextLine()
+
+{
+    if( !bWinActive )
+        return CE_Failure;
+
+    if( nWinBufLoaded == nWinBufYSize-1 )
+    {
+        CleanupWindow();
+        return CE_Failure;
+    }
+
+    NCSEcwReadStatus  eRStatus;
+    eRStatus = poFileView->ReadLineBIL( eNCSRequestDataType, nWinBandCount,
+                                        papCurLineBuf );
+    if( eRStatus != NCSECW_READ_OK )
+        return CE_Failure;
+
+    nWinBufLoaded++;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           CleanupWindow()                            */
+/************************************************************************/
+
+void ECWDataset::CleanupWindow()
+
+{
+    if( !bWinActive )
+        return;
+
+    bWinActive = FALSE;
+    CPLFree( panWinBandList );
+    panWinBandList = NULL;
+
+    for( int iBand = 0; iBand < nWinBandCount; iBand++ )
+        CPLFree( papCurLineBuf[iBand] );
+    CPLFree( papCurLineBuf );
+    papCurLineBuf = NULL;
+}
+
+/************************************************************************/
+/*                             IRasterIO()                              */
+/************************************************************************/
+
+CPLErr ECWDataset::IRasterIO( GDALRWFlag eRWFlag,
+                              int nXOff, int nYOff, int nXSize, int nYSize,
+                              void * pData, int nBufXSize, int nBufYSize,
+                              GDALDataType eBufType, 
+                              int nBandCount, int *panBandMap,
+                              int nPixelSpace, int nLineSpace, int nBandSpace)
+    
+{
+/* -------------------------------------------------------------------- */
+/*      Try to do it based on existing "advised" access.                */
+/* -------------------------------------------------------------------- */
+    if( TryWinRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, 
+                        (GByte *) pData, nBufXSize, nBufYSize, 
+                        eBufType, nBandCount, panBandMap,
+                        nPixelSpace, nLineSpace, nBandSpace ) )
+        return CE_None;
+
+/* -------------------------------------------------------------------- */
+/*      If we are requesting a single line at 1:1, we do a multi-band   */
+/*      AdviseRead() and then TryWinRasterIO() again.                   */
+/* -------------------------------------------------------------------- */
+    if( nYSize == 1 && nBufYSize == 1 && nBandCount > 1 )
+    {
+        CPLErr eErr;
+
+        eErr = AdviseRead( nXOff, nYOff, nXSize, GetRasterYSize() - nYOff,
+                           nBufXSize, GetRasterYSize() - nYOff, eBufType, 
+                           nBandCount, panBandMap, NULL );
+        if( eErr == CE_None 
+            && TryWinRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, 
+                               (GByte *) pData, nBufXSize, nBufYSize, 
+                               eBufType, nBandCount, panBandMap,
+                               nPixelSpace, nLineSpace, nBandSpace ) )
+            return CE_None;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If we are supersampling we need to fall into the general        */
+/*      purpose logic.  We also use the general logic if we are in      */
+/*      some cases unlikely to benefit from interleaved access.         */
+/*                                                                      */
+/*      The one case we would like to handle better here is the         */
+/*      nBufYSize == 1 case (requesting a scanline at a time).  We      */
+/*      should eventually have some logic similiar to the band by       */
+/*      band case where we post a big window for the view, and allow    */
+/*      sequential reads.                                               */
+/* -------------------------------------------------------------------- */
+    if( nXSize < nBufXSize || nYSize < nBufYSize || nYSize == 1 
+        || nBandCount > 100 || nBandCount == 1 || nBufYSize == 1 
+        || nBandCount > GetRasterCount() )
+    {
+        return 
+            GDALDataset::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
+                                    pData, nBufXSize, nBufYSize,
+                                    eBufType, 
+                                    nBandCount, panBandMap,
+                                    nPixelSpace, nLineSpace, nBandSpace);
+    }
+
+    CPLDebug( "ECWDataset", 
+              "RasterIO(%d,%d,%d,%d -> %dx%d) - doing interleaved read.", 
+              nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize );
+
+/* -------------------------------------------------------------------- */
+/*      Setup view.                                                     */
+/* -------------------------------------------------------------------- */
+    UINT32 anBandIndices[100];
+    int    i;
+    NCSError     eNCSErr;
+    CNCSError    oErr;
+    
+    for( i = 0; i < nBandCount; i++ )
+        anBandIndices[i] = panBandMap[i] - 1;
+
+    CleanupWindow();
+
+    oErr = poFileView->SetView( nBandCount, anBandIndices,
+                                nXOff, nYOff, 
+                                nXOff + nXSize - 1, 
+                                nYOff + nYSize - 1,
+                                nBufXSize, nBufYSize );
+    eNCSErr = oErr.GetErrorNumber();
+    
+    if( eNCSErr != NCS_SUCCESS )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "%s", NCSGetErrorText(eNCSErr) );
+        
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Setup working scanline, and the pointers into it.               */
+/* -------------------------------------------------------------------- */
+    int nDataTypeSize = (GDALGetDataTypeSize(eRasterDataType) / 8);
+    GByte *pabyBILScanline = (GByte *) CPLMalloc(nBufXSize * nDataTypeSize *
+                                                 nBandCount);
+    GByte **papabyBIL = (GByte **) CPLMalloc(nBandCount * sizeof(void*));
+
+    for( i = 0; i < nBandCount; i++ )
+        papabyBIL[i] = pabyBILScanline + i * nBufXSize * nDataTypeSize;
+
+/* -------------------------------------------------------------------- */
+/*      Read back all the data for the requested view.                  */
+/* -------------------------------------------------------------------- */
+    for( int iScanline = 0; iScanline < nBufYSize; iScanline++ )
+    {
+        NCSEcwReadStatus  eRStatus;
+
+        eRStatus = poFileView->ReadLineBIL( eNCSRequestDataType, nBandCount,
+                                            (void **) papabyBIL );
+        if( eRStatus != NCSECW_READ_OK )
+        {
+            CPLFree( pabyBILScanline );
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "NCScbmReadViewLineBIL failed." );
+            return CE_Failure;
+        }
+
+        for( i = 0; i < nBandCount; i++ )
+        {
+            GDALCopyWords( 
+                pabyBILScanline + i * nDataTypeSize * nBufXSize,
+                eRasterDataType, nDataTypeSize, 
+                ((GByte *) pData) + nLineSpace * iScanline + nBandSpace * i, 
+                eBufType, nPixelSpace, 
+                nBufXSize );
+        }
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                            OpenJPEG2000()                            */
+/************************************************************************/
+
+GDALDataset *ECWDataset::OpenJPEG2000( GDALOpenInfo * poOpenInfo )
+
+{
+    if( EQUALN(poOpenInfo->pszFilename,"J2K_SUBFILE:",12) )
+        return Open( poOpenInfo );
+
+    else if( poOpenInfo->nHeaderBytes >= 16 
+        && (memcmp( poOpenInfo->pabyHeader, jpc_header, 
+                    sizeof(jpc_header) ) == 0
+            || memcmp( poOpenInfo->pabyHeader, jp2_header, 
+                    sizeof(jp2_header) ) == 0) )
+        return Open( poOpenInfo );
+    
+    else
+        return NULL;
+}
+    
+/************************************************************************/
+/*                              OpenECW()                               */
+/*                                                                      */
+/*      Open method that only supports ECW files.                       */
+/************************************************************************/
+
+GDALDataset *ECWDataset::OpenECW( GDALOpenInfo * poOpenInfo )
+
+{
+/* -------------------------------------------------------------------- */
+/*      This has to either be a file on disk ending in .ecw or a        */
+/*      ecwp: protocol url.                                             */
+/* -------------------------------------------------------------------- */
+    if( (!EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"ecw")
+         || poOpenInfo->nHeaderBytes == 0)
+        && !EQUALN(poOpenInfo->pszFilename,"ecwp:",5) )
+        return( NULL );
+
+    return Open( poOpenInfo );
+}
+    
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *ECWDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    CNCSJP2FileView *poFileView = NULL;
+    NCSError         eErr;
+    CNCSError        oErr;
+    int              i;
+    FILE            *fpVSIL = NULL;
+    VSIIOStream *poIOStream = NULL;
+
+    ECWInitialize();
+
+/* -------------------------------------------------------------------- */
+/*      This will disable automatic conversion of YCbCr to RGB by       */
+/*      the toolkit.                                                    */
+/* -------------------------------------------------------------------- */
+    if( !CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB","YES") ) )
+        NCSecwSetConfig(NCSCFG_JP2_MANAGE_ICC, FALSE);
+
+/* -------------------------------------------------------------------- */
+/*      Handle special case of a JPEG2000 data stream in another file.  */
+/* -------------------------------------------------------------------- */
+    if( EQUALN(poOpenInfo->pszFilename,"J2K_SUBFILE:",12) )
+    {
+        int            subfile_offset=-1, subfile_size=-1;
+        char *real_filename = NULL;
+
+        if( sscanf( poOpenInfo->pszFilename, "J2K_SUBFILE:%d,%d", 
+                    &subfile_offset, &subfile_size ) != 2 )
+          {
+              CPLError( CE_Failure, CPLE_OpenFailed, 
+                        "Failed to parse J2K_SUBFILE specification." );
+              return NULL;
+          }
+
+          real_filename = (char *) strstr(poOpenInfo->pszFilename,",");
+          if( real_filename != NULL )
+              real_filename = (char *) strstr(real_filename+1,",");
+          if( real_filename != NULL )
+              real_filename++;
+          else
+          {
+              CPLError( CE_Failure, CPLE_OpenFailed, 
+                        "Failed to parse J2K_SUBFILE specification." );
+              return NULL;
+          }
+
+          fpVSIL = VSIFOpenL( real_filename, "rb" );
+          if( fpVSIL == NULL )
+          {
+              CPLError( CE_Failure, CPLE_OpenFailed, 
+                        "Failed to open %s.",  real_filename );
+              return NULL;
+          }
+
+          if( hECWDatasetMutex == NULL )
+          {
+              hECWDatasetMutex = CPLCreateMutex();
+          }
+          else if( !CPLAcquireMutex( hECWDatasetMutex, 60.0 ) )
+          {
+              CPLDebug( "ECW", "Failed to acquire mutex in 60s." );
+          }
+          else
+          {
+              CPLDebug( "ECW", "Got mutex." );
+          }
+          poIOStream = new VSIIOStream();
+          poIOStream->Access( fpVSIL, FALSE, real_filename,
+                              subfile_offset, subfile_size );
+
+          poFileView = new CNCSJP2FileView();
+          oErr = poFileView->Open( poIOStream, false );
+
+          // The CNCSJP2FileView (poFileView) object may not use the iostream 
+          // (poIOStream) passed to the CNCSJP2FileView::Open() method if an 
+          // iostream is already available to the ECW JPEG 2000 SDK for a given
+          // file.  Consequently, if the iostream passed to 
+          // CNCSJP2FileView::Open() does not become the underlying iostream 
+          // of the CNCSJP2FileView object, then it should be deleted.
+          //
+          // In addition, the underlying iostream of the CNCSJP2FileView object
+          // should not be deleted until all CNCSJP2FileView objects using the 
+          // underlying iostream are deleted. Consequently, each time a 
+          // CNCSJP2FileView object is created, the nFileViewCount attribute 
+          // of the underlying VSIIOStream object must be incremented for use 
+          // in the ECWDataset destructor.
+		  
+          VSIIOStream * poUnderlyingIOStream = 
+              ((VSIIOStream *)(poFileView->GetStream()));
+          poUnderlyingIOStream->nFileViewCount++;
+
+          if ( poIOStream != poUnderlyingIOStream ) 
+          {
+              delete poIOStream;
+          } 		
+
+          CPLReleaseMutex( hECWDatasetMutex );
+
+          if( oErr.GetErrorNumber() != NCS_SUCCESS )
+          {
+              CPLError( CE_Failure, CPLE_AppDefined, "%s",
+                        oErr.GetErrorMessage() );			
+              return NULL;
+          }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      This has to either be a file on disk ending in .ecw or a        */
+/*      ecwp: protocol url.                                             */
+/* -------------------------------------------------------------------- */
+    else if( poOpenInfo->nHeaderBytes >= 16 
+        && (memcmp( poOpenInfo->pabyHeader, jpc_header, 
+                    sizeof(jpc_header) ) == 0
+            || memcmp( poOpenInfo->pabyHeader, jp2_header, 
+                    sizeof(jp2_header) ) == 0) )
+        /* accept JPEG2000 files */;
+    else if( (!EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"ecw")
+              || poOpenInfo->nHeaderBytes == 0)
+             && !EQUALN(poOpenInfo->pszFilename,"ecwp:",5) )
+        return( NULL );
+    
+/* -------------------------------------------------------------------- */
+/*      Open the client interface.                                      */
+/* -------------------------------------------------------------------- */
+    if( poFileView == NULL )
+    {
+        poFileView = new CNCSFile();
+        oErr = poFileView->Open( (char *) poOpenInfo->pszFilename, FALSE );
+        eErr = oErr.GetErrorNumber();
+        CPLDebug( "ECW", "NCScbmOpenFileView(%s): eErr = %d", 
+                  poOpenInfo->pszFilename, (int) eErr );
+        if( eErr != NCS_SUCCESS )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined, 
+                      "%s", NCSGetErrorText(eErr) );
+            return NULL;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    ECWDataset 	*poDS;
+
+    poDS = new ECWDataset();
+
+    poDS->poFileView = poFileView;
+
+    if( fpVSIL != NULL )
+        poDS->nPamFlags |= GPF_DISABLED;
+
+/* -------------------------------------------------------------------- */
+/*      Fetch general file information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->psFileInfo = poFileView->GetFileInfo();
+
+    CPLDebug( "ECW", "FileInfo: SizeXY=%d,%d Bands=%d\n"
+              "       OriginXY=%g,%g  CellIncrementXY=%g,%g\n",
+              poDS->psFileInfo->nSizeX,
+              poDS->psFileInfo->nSizeY,
+              poDS->psFileInfo->nBands,
+              poDS->psFileInfo->fOriginX,
+              poDS->psFileInfo->fOriginY,
+              poDS->psFileInfo->fCellIncrementX,
+              poDS->psFileInfo->fCellIncrementY );
+
+/* -------------------------------------------------------------------- */
+/*      Establish raster info.                                          */
+/* -------------------------------------------------------------------- */
+    poDS->nRasterXSize = poDS->psFileInfo->nSizeX; 
+    poDS->nRasterYSize = poDS->psFileInfo->nSizeY;
+
+/* -------------------------------------------------------------------- */
+/*      Establish the GDAL data type that corresponds.  A few NCS       */
+/*      data types have no direct corresponding value in GDAL so we     */
+/*      will coerce to something sufficiently similar.                  */
+/* -------------------------------------------------------------------- */
+    poDS->eNCSRequestDataType = poDS->psFileInfo->eCellType;
+    switch( poDS->psFileInfo->eCellType )
+    {
+        case NCSCT_UINT8:
+            poDS->eRasterDataType = GDT_Byte;
+            break;
+
+        case NCSCT_UINT16:
+            poDS->eRasterDataType = GDT_UInt16;
+            break;
+
+        case NCSCT_UINT32:
+        case NCSCT_UINT64:
+            poDS->eRasterDataType = GDT_UInt32;
+            poDS->eNCSRequestDataType = NCSCT_UINT32;
+            break;
+
+        case NCSCT_INT8:
+        case NCSCT_INT16:
+            poDS->eRasterDataType = GDT_Int16;
+            poDS->eNCSRequestDataType = NCSCT_INT16;
+            break;
+
+        case NCSCT_INT32:
+        case NCSCT_INT64:
+            poDS->eRasterDataType = GDT_Int32;
+            poDS->eNCSRequestDataType = NCSCT_INT32;
+            break;
+
+        case NCSCT_IEEE4:
+            poDS->eRasterDataType = GDT_Float32;
+            break;
+
+        case NCSCT_IEEE8:
+            poDS->eRasterDataType = GDT_Float64;
+            break;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    for( i=0; i < poDS->psFileInfo->nBands; i++ )
+        poDS->SetBand( i+1, new ECWRasterBand( poDS, i+1 ) );
+
+/* -------------------------------------------------------------------- */
+/*      Look for supporting coordinate system information.              */
+/* -------------------------------------------------------------------- */
+    if( fpVSIL == NULL )
+    {
+        GDALJP2Metadata oJP2Geo;
+
+        if( oJP2Geo.ReadAndParse( poOpenInfo->pszFilename ) )
+        {
+            poDS->pszProjection = CPLStrdup(oJP2Geo.pszProjection);
+            poDS->bGeoTransformValid = oJP2Geo.bHaveGeoTransform;
+            memcpy( poDS->adfGeoTransform, oJP2Geo.adfGeoTransform, 
+                    sizeof(double) * 6 );
+            poDS->nGCPCount = oJP2Geo.nGCPCount;
+            poDS->pasGCPList = oJP2Geo.pasGCPList;
+            oJP2Geo.pasGCPList = NULL;
+            oJP2Geo.nGCPCount = 0;
+        }
+        else
+        {
+            poDS->ECW2WKTProjection();
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for world file for ecw files.                             */
+/* -------------------------------------------------------------------- */
+    if( !poDS->bGeoTransformValid 
+        && EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"ecw") )
+    {
+        poDS->bGeoTransformValid |= 
+            GDALReadWorldFile( poOpenInfo->pszFilename, ".eww", 
+                               poDS->adfGeoTransform )
+            || GDALReadWorldFile( poOpenInfo->pszFilename, ".ecww", 
+                                  poDS->adfGeoTransform )
+            || GDALReadWorldFile( poOpenInfo->pszFilename, ".wld", 
+                                  poDS->adfGeoTransform );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                            GetGCPCount()                             */
+/************************************************************************/
+
+int ECWDataset::GetGCPCount()
+
+{
+    if( nGCPCount != 0 )
+        return nGCPCount;
+    else
+        return GDALPamDataset::GetGCPCount();
+}
+
+/************************************************************************/
+/*                          GetGCPProjection()                          */
+/************************************************************************/
+
+const char *ECWDataset::GetGCPProjection()
+
+{
+    if( nGCPCount > 0 )
+        return pszProjection;
+    else
+        return GDALPamDataset::GetGCPProjection();
+}
+
+/************************************************************************/
+/*                               GetGCP()                               */
+/************************************************************************/
+
+const GDAL_GCP *ECWDataset::GetGCPs()
+
+{
+    if( nGCPCount != 0 )
+        return pasGCPList;
+    else
+        return GDALPamDataset::GetGCPs();
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *ECWDataset::GetProjectionRef() 
+
+{
+    if( pszProjection == NULL )
+        return GDALPamDataset::GetProjectionRef();
+    else
+        return pszProjection;
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr ECWDataset::GetGeoTransform( double * padfTransform )
+
+{
+    if( bGeoTransformValid )
+    {
+        memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
+        return( CE_None );
+    }
+    else
+        return GDALPamDataset::GetGeoTransform( padfTransform );
+}
+
+/************************************************************************/
+/*                            ECWGetCSList()                            */
+/************************************************************************/
+
+char **ECWGetCSList()
+
+{
+/* -------------------------------------------------------------------- */
+/*      Load the supporting data file with the coordinate system        */
+/*      translations if we don't already have it loaded.  Note,         */
+/*      currently we never unload the file, even if the driver is       */
+/*      destroyed ... but we should.                                    */
+/* -------------------------------------------------------------------- */
+    if( !gnTriedCSFile )
+    {
+        const char *pszFilename = CPLFindFile( "data", "ecw_cs.dat" );
+        
+        gnTriedCSFile = TRUE;
+        if( pszFilename != NULL )
+            gpapszCSLookup = CSLLoad( pszFilename );
+    }
+
+    return gpapszCSLookup;
+}
+
+/************************************************************************/
+/*                            GetMetadata()                             */
+/************************************************************************/
+
+char **ECWDataset::GetMetadata( const char *pszDomain )
+
+{
+    if( pszDomain == NULL || !EQUAL(pszDomain,"GML") )
+        return GDALPamDataset::GetMetadata( pszDomain );
+    else
+        return papszGMLMetadata;
+}
+
+/************************************************************************/
+/*                         ECW2WKTProjection()                          */
+/*                                                                      */
+/*      Set the dataset pszProjection string in OGC WKT format by       */
+/*      looking up the ECW (GDT) coordinate system info in              */
+/*      ecw_cs.dat support data file.                                   */
+/*                                                                      */
+/*      This code is likely still broken in some circumstances.  For    */
+/*      instance, I haven't been careful about changing the linear      */
+/*      projection parameters (false easting/northing) if the units     */
+/*      is feet.  Lots of cases missing here, and in ecw_cs.dat.        */
+/************************************************************************/
+
+void ECWDataset::ECW2WKTProjection()
+
+{
+    if( psFileInfo == NULL )
+        return;
+
+/* -------------------------------------------------------------------- */
+/*      Capture Geotransform.                                           */
+/* -------------------------------------------------------------------- */
+
+    if( psFileInfo->fOriginX != 0.0 
+        || psFileInfo->fOriginY != 0.0 
+        && (psFileInfo->fCellIncrementX != 0.0 && 
+            psFileInfo->fCellIncrementX != 1.0)
+        && (psFileInfo->fCellIncrementY != 0.0 && 
+            psFileInfo->fCellIncrementY != 1.0) )
+    {
+        bGeoTransformValid = TRUE;
+        
+        adfGeoTransform[0] = psFileInfo->fOriginX;
+        adfGeoTransform[1] = psFileInfo->fCellIncrementX;
+        adfGeoTransform[2] = 0.0;
+        
+        adfGeoTransform[3] = psFileInfo->fOriginY;
+        adfGeoTransform[4] = 0.0;
+        adfGeoTransform[5] = psFileInfo->fCellIncrementY;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      do we have projection and datum?                                */
+/* -------------------------------------------------------------------- */
+    CPLDebug( "ECW", "projection=%s, datum=%s",
+              psFileInfo->szProjection, psFileInfo->szDatum );
+
+    if( EQUAL(psFileInfo->szProjection,"RAW") )
+        return;
+
+    if( ECWGetCSList() == NULL )
+        return;
+
+/* -------------------------------------------------------------------- */
+/*      Set projection if we have it.                                   */
+/* -------------------------------------------------------------------- */
+    OGRSpatialReference oSRS;
+    if( EQUAL(psFileInfo->szProjection,"GEODETIC") )
+    {
+    }
+    else
+    {
+        const char *pszProjWKT;
+
+        pszProjWKT = CSLFetchNameValue( gpapszCSLookup, 
+                                        psFileInfo->szProjection );
+
+        if( pszProjWKT == NULL 
+            || EQUAL(pszProjWKT,"unsupported")
+            || oSRS.importFromWkt( (char **) &pszProjWKT ) != OGRERR_NONE )
+        {
+            oSRS.SetLocalCS( psFileInfo->szProjection );
+        }
+
+        if( psFileInfo->eCellSizeUnits == ECW_CELL_UNITS_FEET )
+            oSRS.SetLinearUnits( SRS_UL_US_FOOT, atof(SRS_UL_US_FOOT_CONV));
+        else
+            oSRS.SetLinearUnits( SRS_UL_METER, 1.0 );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Set the geogcs.                                                 */
+/* -------------------------------------------------------------------- */
+    OGRSpatialReference oGeogCS;
+    const char *pszGeogWKT;
+
+    pszGeogWKT = CSLFetchNameValue( gpapszCSLookup, 
+                                    psFileInfo->szDatum );
+
+    if( pszGeogWKT == NULL )
+        oGeogCS.SetWellKnownGeogCS( "WGS84" );
+    else
+    {
+        oGeogCS.importFromWkt( (char **) &pszGeogWKT );
+    }
+
+    if( !oSRS.IsLocal() )
+        oSRS.CopyGeogCSFrom( &oGeogCS );
+
+/* -------------------------------------------------------------------- */
+/*      Capture the resulting composite coordiante system.              */
+/* -------------------------------------------------------------------- */
+    if( pszProjection != NULL )
+    {
+        CPLFree( pszProjection );
+        pszProjection = NULL;
+    }
+    oSRS.exportToWkt( &pszProjection );
+}
+
+#endif /* def FRMT_ecw */
+
+/************************************************************************/
+/*                           ECWInitialize()                            */
+/*                                                                      */
+/*      Initialize NCS library.  We try to defer this as late as        */
+/*      possible since de-initializing it seems to be expensive/slow    */
+/*      on some system.                                                 */
+/************************************************************************/
+
+void ECWInitialize()
+
+{
+    CPLMutexHolder oHolder( &hECWDatasetMutex );
+
+    if( bNCSInitialized )
+        return;
+
+    NCSecwInit();
+    bNCSInitialized = TRUE;
+}
+
+/************************************************************************/
+/*                         GDALDeregister_ECW()                         */
+/************************************************************************/
+
+void GDALDeregister_ECW( GDALDriver * )
+
+{
+    if( gpapszCSLookup )
+    {
+        CSLDestroy( gpapszCSLookup );
+        gpapszCSLookup = NULL;
+        gnTriedCSFile = FALSE;
+    }
+
+    if( bNCSInitialized )
+    {
+        bNCSInitialized = FALSE;
+        NCSecwShutdown();
+    }
+
+    if( hECWDatasetMutex != NULL )
+    {
+        CPLDestroyMutex( hECWDatasetMutex );
+        hECWDatasetMutex = NULL;
+    }
+}
+
+/************************************************************************/
+/*                          GDALRegister_ECW()                        */
+/************************************************************************/
+
+void GDALRegister_ECW()
+
+{
+#ifdef FRMT_ecw 
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "ECW" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "ECW" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "ERMapper Compressed Wavelets" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_ecw.html" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "ecw" );
+        
+        poDriver->pfnOpen = ECWDataset::OpenECW;
+        poDriver->pfnUnloadDriver = GDALDeregister_ECW;
+#ifdef HAVE_COMPRESS
+// The create method seems not to work properly.
+//        poDriver->pfnCreate = ECWCreateECW;  
+        poDriver->pfnCreateCopy = ECWCreateCopyECW;
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, 
+"<CreationOptionList>"
+"   <Option name='TARGET' type='float' description='Compression Percentage' />"
+"   <Option name='PROJ' type='string' description='ERMapper Projection Name'/>"
+"   <Option name='DATUM' type='string' description='ERMapper Datum Name' />"
+"   <Option name='LARGE_OK' type='boolean' description='Enable compressing 500+MB files'/>"
+"</CreationOptionList>" );
+#endif
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+#endif /* def FRMT_ecw */
+}
+
+/************************************************************************/
+/*                        GDALRegister_JP2ECW()                         */
+/************************************************************************/
+void GDALRegister_JP2ECW()
+
+{
+#ifdef FRMT_ecw 
+    GDALDriver	*poDriver;
+
+
+    if( GDALGetDriverByName( "JP2ECW" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "JP2ECW" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "ERMapper JPEG2000" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_jp2ecw.html" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "jp2" );
+        
+        poDriver->pfnOpen = ECWDataset::OpenJPEG2000;
+#ifdef HAVE_COMPRESS
+        poDriver->pfnCreate = ECWCreateJPEG2000;
+        poDriver->pfnCreateCopy = ECWCreateCopyJPEG2000;
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte UInt16 Int16 UInt32 Int32 Float32 Float64" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, 
+"<CreationOptionList>"
+"   <Option name='TARGET' type='float' description='Compression Percentage' />"
+"   <Option name='PROJ' type='string' description='ERMapper Projection Name'/>"
+"   <Option name='DATUM' type='string' description='ERMapper Datum Name' />"
+"   <Option name='LARGE_OK' type='boolean' description='Enable compressing 500+MB files'/>"
+"   <Option name='PROFILE' type='string-select'>"
+"       <Value>BASELINE_0</Value>"
+"       <Value>BASELINE_1</Value>"
+"       <Value>BASELINE_2</Value>"
+"       <Value>NPJE</Value>"
+"       <Value>EPJE</Value>"
+"   </Option>"
+"   <Option name='PROGRESSION' type='string-select'>"
+"       <Value>LRCP</Value>"
+"       <Value>RLCP</Value>"
+"       <Value>RPCL</Value>"
+"   </Option>"
+"   <Option name='CODESTREAM_ONLY' type='boolean' description='No JP2 wrapper'/>"
+"   <Option name='LEVELS' type='int'/>"
+"   <Option name='LAYERS' type='int'/>"
+"   <Option name='PRECINCT_WIDTH' type='int'/>"
+"   <Option name='PRECINCT_HEIGHT' type='int'/>"
+"   <Option name='TILE_WIDTH' type='int'/>"
+"   <Option name='TILE_HEIGHT' type='int'/>"
+"   <Option name='INCLUDE_SOP' type='boolean'/>"
+"   <Option name='INCLUDE_EPH' type='boolean'/>"
+"   <Option name='DECOMPRESS_LAYERS' type='int'/>"
+"   <Option name='DECOMPRESS_RECONSTRUCTION_PARAMETER' type='float'/>"
+"</CreationOptionList>" );
+#endif
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+#endif /* def FRMT_ecw */
+}
+
+
+
diff --git a/Utilities/GDAL/frmts/ecw/frmt_ecw.html b/Utilities/GDAL/frmts/ecw/frmt_ecw.html
new file mode 100644
index 0000000000..7097e1c096
--- /dev/null
+++ b/Utilities/GDAL/frmts/ecw/frmt_ecw.html
@@ -0,0 +1,66 @@
+<html>
+<head>
+<title>ECW -- ERMapper Compress Wavelets (.ecw)</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>ECW -- ERMapper Compress Wavelets (.ecw)</h1>
+
+GDAL supports .ecw format for read access and write.  The current 
+implementation reads any number of bands but returns only as eight bit
+image data.  Coordinate system and georeferencing transformations are read,
+but in some cases coordinate systems may not translate.<p>
+
+Support for the ECW driver in GDAL is optional, and requires linking
+in external ECW SDK libraries provided by ERMapper.<p>
+
+<h2>Creation Issues</h2>
+
+The free ECW compression kit only supports compression of images up to 
+500MB.  To compress very large images it is necessary to license ECW
+technology from ERMapper.  Files to be compressed into ECW format must also
+be at least 128x128.  Non-eight bit source data will be rescaled by
+the ECW SDK in some manner that I don't understand ... the resulting image
+is effectively eight bit.<p>
+
+When writing coordinate system information to ECW files, many less
+common coordinate systems are not mapped properly.  If you know the 
+ERMapper name for the coordinate system you can force it to be set at
+creation time with the PROJ and DATUM creation options. <p>
+
+Creation Options:<p>
+
+<ul>
+
+<li> <b>TARGET=percent</b>: Set the target size reduction as a percentage of 
+the original.  If not provided defaults to 75 for an 75% reduction.<p>
+
+<li> <b>PROJ=name</b>: Name of the ERMapper projection string to use. 
+Common examples are NUTM11, or GEODETIC.<p>
+
+<li> <b>DATUM=name</b>: Name of the ERMapper datum string to use. 
+Common examples are WGS84 or NAD83.<p>
+
+<li> <b>LARGE_OK=YES</b>: Under some circumstances this option can be set
+to allow compressing files larger than 500MB.  It is the users responsibility
+to ensure that ERMapper licensing requirments for large file compression
+are being adhered to.<p>
+
+</ul>
+
+ECW format does not support creation of overviews since the ECW format 
+is already considered to be optimized for "arbitrary overviews". <P>
+
+See Also:<p>
+
+<ul>
+<li> Implemented as <tt>gdal/frmts/ecw/ecwdataset.cpp</tt>.<p>
+
+<li> <a href="http://www.ermapper.com/products/ecw/ecw_body.htm">ECW Page</a>
+at <a href="http://www.ermapper.com/">www.ermapper.com.</a><p>
+
+</ul>
+
+</body>
+</html>
diff --git a/Utilities/GDAL/frmts/ecw/frmt_jp2ecw.html b/Utilities/GDAL/frmts/ecw/frmt_jp2ecw.html
new file mode 100644
index 0000000000..410ed530d2
--- /dev/null
+++ b/Utilities/GDAL/frmts/ecw/frmt_jp2ecw.html
@@ -0,0 +1,85 @@
+<html>
+<head>
+<title>JP2ECW -- ERMapper JPEG2000 (.jp2)</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>JP2ECW -- ERMapper JPEG2000 (.jp2)</h1>
+
+GDAL supports reading and writing JPEG2000 files using the ECW SDK from 
+ERMapper.<p>
+
+Coordinate system and georeferencing transformations are read, and 
+some degree of support is included for GeoJP2 (tm) (GeoTIFF-in-JPEG2000), 
+ERMapper GML-in-JPEG2000, and the new GML-in-JPEG2000 specification developed
+at OGC. <p>
+
+Support for the JP2ECW driver in GDAL is optional, and requires linking
+in external ECW SDK libraries provided by ERMapper.<p>
+
+<h2>Creation Issues</h2>
+
+The free ECW compression kit only supports compression of images up to 
+500MB.  To compress very large images it is necessary to license ECW
+technology from ERMapper. Creation of 8 bit, and 16bit signed or unsigned
+values is supported.<p>
+
+Creation Options:<p>
+
+<ul>
+
+<li> <b>TARGET=percent</b>: Set the target size reduction as a percentage of 
+the original.  If not provided defaults to 75 for an 75% reduction.<p>
+
+<li> <b>PROJ=name</b>: Name of the ERMapper projection string to use. 
+Common examples are NUTM11, or GEODETIC.<p>
+
+<li> <b>DATUM=name</b>: Name of the ERMapper datum string to use. 
+Common examples are WGS84 or NAD83.<p>
+
+<li> <b>LARGE_OK=YES/NO</b>: Under some circumstances this option can be set
+to allow compressing files larger than 500MB.  It is the users responsibility
+to ensure that ERMapper licensing requirments for large file compression
+are being adhered to.<p>
+
+<li> <b>PROFILE=profile</b>: One of BASELINE_0, BASELINE_1, BASELINE_2, 
+NPJE or EPJE.  Review the ECW SDK documentation for details on profile 
+meanings.<p>
+
+<li> <b>PROGRESSION=LRCP/RLCP/RPCL</b>: Set the progression order with
+which the JPEG2000 codestream is written.<p>
+
+<li> <b>CODESTREAM_ONLY=YES/NO</b>: If set to YES, only the compressed
+imagery code stream will be written.  If NO (the default) a JP2 package
+will be written around the code stream including a variety of meta
+information.<p>
+
+<li> <b>LEVELS=n</b>: See ECW SDK for details.<p>
+<li> <b>LAYERS=n</b>: See ECW SDK for details.<p>
+<li> <b>PRECINCT_WIDTH=n</b>: See ECW SDK for details.<p>
+<li> <b>PRECINCT_HEIGHT=n</b>: See ECW SDK for details.<p>
+<li> <b>TILE_WIDTH=n</b>: See ECW SDK for details.<p>
+<li> <b>TILE_HEIGHT=n</b>: See ECW SDK for details.<p>
+<li> <b>INCLUDE_SOP=YES/NO</b>: See ECW SDK for details.<p>
+<li> <b>INCLUDE_EPH=YES/NO</b>: See ECW SDK for details.<p>
+<li> <b>DECOMPRESS_LAYERS=n</b>: See ECW SDK for details.<p>
+<li> <b>DECOMPRESS_RECONSTRUCTION_PARAMETER=n</b>: See ECW SDK for details.<p>
+
+</ul>
+
+JPEG2000 format does not support creation of overviews since the format 
+is already considered to be optimized for "arbitrary overviews". <P>
+
+See Also:<p>
+
+<ul>
+<li> Implemented as <tt>gdal/frmts/ecw/ecwdataset.cpp</tt>.<p>
+
+<li> <a href="http://www.ermapper.com/products/ecw/ecw_body.htm">ECW Page</a>
+at <a href="http://www.ermapper.com/">www.ermapper.com.</a><p>
+
+</ul>
+
+</body>
+</html>
diff --git a/Utilities/GDAL/frmts/ecw/jp2userbox.cpp b/Utilities/GDAL/frmts/ecw/jp2userbox.cpp
new file mode 100644
index 0000000000..4c8beca358
--- /dev/null
+++ b/Utilities/GDAL/frmts/ecw/jp2userbox.cpp
@@ -0,0 +1,136 @@
+/******************************************************************************
+ * $Id: jp2userbox.cpp,v 1.1 2005/04/02 21:14:57 fwarmerdam Exp $
+ *
+ * Project:  GDAL ECW Driver
+ * Purpose:  JP2UserBox implementation - arbitrary box read/write.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************
+ *
+ * $Log: jp2userbox.cpp,v $
+ * Revision 1.1  2005/04/02 21:14:57  fwarmerdam
+ * New
+ *
+ */
+
+#include "jp2userbox.h"
+
+CPL_CVSID("$Id: jp2userbox.cpp,v 1.1 2005/04/02 21:14:57 fwarmerdam Exp $");
+
+/************************************************************************/
+/*                             JP2UserBox()                             */
+/************************************************************************/
+
+JP2UserBox::JP2UserBox()
+{
+    pabyData = NULL;
+    nDataLength = 0;
+
+    m_nTBox = 0;
+}
+
+/************************************************************************/
+/*                            ~JP2UserBox()                             */
+/************************************************************************/
+
+JP2UserBox::~JP2UserBox()
+
+{
+    if( pabyData != NULL )
+    {
+        CPLFree( pabyData );
+        pabyData = NULL;
+    }
+}
+
+/************************************************************************/
+/*                              SetData()                               */
+/************************************************************************/
+
+void JP2UserBox::SetData( int nLengthIn, unsigned char *pabyDataIn )
+
+{
+    if( pabyData != NULL )
+        CPLFree( pabyData );
+
+    nDataLength = nLengthIn;
+    pabyData = (unsigned char *) CPLMalloc(nDataLength);
+    memcpy( pabyData, pabyDataIn, nDataLength );
+
+    m_bValid = true;
+}
+
+/************************************************************************/
+/*                            UpdateXLBox()                             */
+/************************************************************************/
+
+void JP2UserBox::UpdateXLBox()
+
+{
+    m_nXLBox = 8 + nDataLength;
+    m_nLDBox = nDataLength;
+}
+
+/************************************************************************/
+/*                               Parse()                                */
+/*                                                                      */
+/*      Parse box, and data contents from file into memory.             */
+/************************************************************************/
+
+CNCSError JP2UserBox::Parse( class CNCSJP2File &JP2File, 
+                             CNCSJPCIOStream &Stream )
+
+{
+    CNCSError Error = NCS_SUCCESS;
+    
+    return Error;
+}
+
+/************************************************************************/
+/*                              UnParse()                               */
+/*                                                                      */
+/*      Write box meta information, and data to file.                   */
+/************************************************************************/
+
+CNCSError JP2UserBox::UnParse( class CNCSJP2File &JP2File, 
+                               CNCSJPCIOStream &Stream )
+
+{
+    CNCSError Error = NCS_SUCCESS;
+
+    if( m_nTBox == 0 )
+    {
+        Error = NCS_UNKNOWN_ERROR;
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "No box type set in JP2UserBox::UnParse()" );
+        return Error;
+    }
+
+    Error = CNCSJP2Box::UnParse(JP2File, Stream);
+
+//    NCSJP2_CHECKIO_BEGIN(Error, Stream);
+    Stream.Write(pabyData, nDataLength);
+//    NCSJP2_CHECKIO_END();
+
+    return Error;
+}
diff --git a/Utilities/GDAL/frmts/ecw/jp2userbox.h b/Utilities/GDAL/frmts/ecw/jp2userbox.h
new file mode 100644
index 0000000000..0835449983
--- /dev/null
+++ b/Utilities/GDAL/frmts/ecw/jp2userbox.h
@@ -0,0 +1,67 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  GDAL
+ * Purpose:  ECW Driver: user defined data box.  Simple one to read/write
+ *           user defined data.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************
+ *
+ * $Log: jp2userbox.h,v $
+ * Revision 1.1  2005/04/02 21:14:57  fwarmerdam
+ * New
+ *
+ */
+
+#ifndef JP2USERBOX_H_INCLUDED
+#define JP2USERBOX_H_INCLUDED
+
+#include "vsiiostream.h"
+//#include "NCSJP2Box.h"
+
+class NCSJPC_EXPORT_ALL JP2UserBox : public CNCSJP2Box {
+
+private:
+    int           nDataLength;
+    unsigned char *pabyData;
+
+public:
+    JP2UserBox();
+
+    virtual ~JP2UserBox();
+
+    virtual CNCSError Parse(class CNCSJP2File &JP2File, 
+                            CNCSJPCIOStream &Stream);
+    virtual CNCSError UnParse(class CNCSJP2File &JP2File, 
+                              CNCSJPCIOStream &Stream);
+    virtual void UpdateXLBox(void);
+
+    void    SetData( int nDataLength, unsigned char *pabyDataIn );
+    
+    int     GetDataLength() { return nDataLength; }
+    unsigned char *GetData() { return pabyData; }
+};
+
+#endif /* ndef JP2USERBOX_H_INCLUDED */
+
diff --git a/Utilities/GDAL/frmts/ecw/lookup.py b/Utilities/GDAL/frmts/ecw/lookup.py
new file mode 100644
index 0000000000..7e14c5b592
--- /dev/null
+++ b/Utilities/GDAL/frmts/ecw/lookup.py
@@ -0,0 +1,196 @@
+#******************************************************************************
+#  $Id: lookup.py,v 1.2 2006/04/03 01:46:18 fwarmerdam Exp $
+# 
+#  Project:  GDAL ECW Driver
+#  Purpose:  Script to lookup ECW (GDT) coordinate systems and translate
+#            into OGC WKT for storage in $GDAL_HOME/data/ecw_cs.dat.
+#  Author:   Frank Warmerdam, warmerdam@pobox.com
+# 
+#******************************************************************************
+#  Copyright (c) 2003, Frank Warmerdam
+# 
+#  Permission is hereby granted, free of charge, to any person obtaining a
+#  copy of this software and associated documentation files (the "Software"),
+#  to deal in the Software without restriction, including without limitation
+#  the rights to use, copy, modify, merge, publish, distribute, sublicense,
+#  and/or sell copies of the Software, and to permit persons to whom the
+#  Software is furnished to do so, subject to the following conditions:
+# 
+#  The above copyright notice and this permission notice shall be included
+#  in all copies or substantial portions of the Software.
+# 
+#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+#  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+#  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+#  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+#  DEALINGS IN THE SOFTWARE.
+#******************************************************************************
+# 
+# $Log: lookup.py,v $
+# Revision 1.2  2006/04/03 01:46:18  fwarmerdam
+# Added somenew projections
+#
+# Revision 1.1  2003/06/19 17:53:34  warmerda
+# New
+#
+
+import string
+import sys
+import osr
+
+##############################################################################
+# rtod(): radians to degrees.
+
+def r2d( rad ):
+    return float(rad) / 0.0174532925199433
+
+##############################################################################
+# load_dict()
+
+def load_dict( filename ):
+    lines = open( filename ).readlines()
+
+    this_dict = {}
+    for line in lines:
+        if line[:8] != 'proj_name':
+	    tokens = string.split(line,',')
+	    for i in range(len(tokens)):
+		tokens[i] = string.strip(tokens[i])
+
+            this_dict[tokens[0]] = tokens
+
+    return this_dict
+
+##############################################################################
+# Mainline
+
+#dir = 'M:/software/ER Viewer 2.0c/GDT_Data/'
+dir = '/u/data/ecw/gdt/'
+
+dict_list = [ 'tranmerc', 'lambert1', 'lamcon2', 'utm', 'albersea', 'mercator',
+	      'obmerc_b', 'grinten', 'cassini', 'lambazea', 'datum_sp' ]
+
+dict_dict = {}
+
+for item in dict_list:
+    dict_dict[item] = load_dict( dir + item + '.dat' )
+    #print 'loaded: %s' % item
+
+pfile = open( dir + 'project.dat' )
+pfile.readline()
+
+for line in pfile.readlines():
+    try:
+        tokens = string.split(string.strip(line),',')
+        if len(tokens) < 3:
+            continue
+        
+	for i in range(len(tokens)):
+	    tokens[i] = string.strip(tokens[i])
+
+  	id = tokens[0]
+        type = tokens[1]
+
+	lsize = float(tokens[2])
+	lsize_str = tokens[2]
+
+	dline = dict_dict[type][id]
+
+	srs = osr.SpatialReference()
+
+	# Handle translation of the projection parameters.
+
+	if type != 'utm':
+	    fn = float(dline[1])*lsize
+	    fe = float(dline[2])*lsize
+
+        if type == 'tranmerc':
+	    srs.SetTM( r2d(dline[5]), r2d(dline[4]), float(dline[3]), fe, fn )
+
+	elif type == 'mercator':
+	    srs.SetMercator( r2d(dline[5]), r2d(dline[4]), float(dline[3]), fe, fn )
+
+	elif type == 'grinten':
+	    srs.SetVDG( r2d(dline[3]), fe, fn )
+
+	elif type == 'cassini':
+	    srs.SetCS( r2d(dline[4]), r2d(dline[3]), fe, fn )
+
+	elif type == 'lambazea':
+	    srs.SetLAEA( r2d(dline[5]), r2d(dline[4]),
+                         fe, fn )
+
+	elif type == 'lambert1':
+	    srs.SetLCC1SP( r2d(dline[5]), r2d(dline[4]),
+                           float(dline[3]), fe, fn )
+
+	elif type == 'lamcon2':
+	    srs.SetLCC( r2d(dline[7]), r2d(dline[8]), 
+		        r2d(dline[9]), r2d(dline[6]), fe, fn )
+
+#	elif type == 'lambert2':
+#	    false_en = '+y_0=%.2f +x_0=%.2f' \
+#		% (float(dline[12])*lsize, float(dline[13])*lsize)
+#	    result = '+proj=lcc %s +lat_0=%s +lon_0=%s +lat_1=%s +lat_2=%s' \
+#               % (false_en, r2d(dline[3]), r2d(dline[4]), 
+#			r2d(dline[7]), r2d(dline[8]))
+
+	elif type == 'albersea':
+	    srs.SetACEA( r2d(dline[3]), r2d(dline[4]),
+		         r2d(dline[5]), r2d(dline[6]), fe, fn )
+
+#	elif type == 'obmerc_b':
+#	    result = '+proj=omerc %s +lat_0=%s +lonc=%s +alpha=%s +k=%s' \
+#               % (false_en, r2d(dline[5]), r2d(dline[6]), r2d(dline[4]), dline[3])
+
+	elif type == 'utm':
+	    srs.SetUTM( int(dline[1]), dline[2] != 'S' )
+
+	# Handle Units from projects.dat file.
+	if lsize_str == '0.30480061':
+	    srs.SetLinearUnits( 'US Foot', float(lsize_str) )
+	elif lsize_str != '1.0':
+	    srs.SetLinearUnits( 'unnamed', float(lsize_str) )
+
+        wkt = srs.ExportToWkt()
+        if len(wkt) > 0:
+	    print '%s:%s' % (id, srs.ExportToWkt())
+        else:
+            print '%s:unsupported' % id
+		
+    except KeyError:
+	print '%s:unsupported' % id
+
+    except:
+        print 'cant translate: ', line
+	raise
+
+## Translate datums to their underlying spheroid information.
+
+pfile = open( dir + 'datum.dat' )
+pfile.readline()
+
+for line in pfile.readlines():
+    tokens = string.split(string.strip(line),',')
+    for i in range(len(tokens)):
+        tokens[i] = string.strip(tokens[i])
+
+    id = tokens[0]
+
+    sp_name = tokens[2]
+    dline = dict_dict['datum_sp'][id]
+    srs = osr.SpatialReference()
+
+    if id == 'WGS84':
+        srs.SetWellKnownGeogCS( 'WGS84' )
+    elif id == 'NAD27': 
+        srs.SetWellKnownGeogCS( 'NAD27' )
+    elif id == 'NAD83': 
+        srs.SetWellKnownGeogCS( 'NAD83' )
+    else:
+        srs.SetGeogCS( tokens[1], id, sp_name, float(dline[2]), float(dline[4]) )
+
+    print '%s:%s' % (id, srs.ExportToWkt())
+
diff --git a/Utilities/GDAL/frmts/ecw/makefile.vc b/Utilities/GDAL/frmts/ecw/makefile.vc
new file mode 100644
index 0000000000..35e66ed6aa
--- /dev/null
+++ b/Utilities/GDAL/frmts/ecw/makefile.vc
@@ -0,0 +1,27 @@
+
+EXTRAFLAGS = 	-I$(ECWDIR)\include -I$(ECWDIR)/Source/include \
+		/D_MBCS /D_UNICODE /DUNICODE /D_WINDOWS \
+		/DLIBECWJ2 /DWIN32 /D_WINDLL -DFRMT_ecw -DNO_X86_MMI
+
+OBJ	=	ecwdataset.obj ecwcreatecopy.obj jp2userbox.obj
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+all:	default testecw.exe
+
+clean:
+	-del *.obj
+	-del *.exe
+
+testecw.exe:	testecw.cpp
+	$(CC) /Zi /MD $(EXTRAFLAGS) testecw.cpp $(ECWLIB)
+
+ecw_example1.exe:	ecw_example1.c
+	$(CC) /MD $(EXTRAFLAGS) ecw_example1.c $(ECWLIB)
+	
+
diff --git a/Utilities/GDAL/frmts/ecw/vsiiostream.h b/Utilities/GDAL/frmts/ecw/vsiiostream.h
new file mode 100644
index 0000000000..6cff32b80f
--- /dev/null
+++ b/Utilities/GDAL/frmts/ecw/vsiiostream.h
@@ -0,0 +1,219 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  GDAL 
+ * Purpose:  ECW Driver: virtualized io stream declaration.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************
+ *
+ * $Log: vsiiostream.h,v $
+ * Revision 1.10  2005/04/12 03:58:34  fwarmerdam
+ * turn ownership of fpVSIL over to vsiiostream
+ *
+ * Revision 1.9  2005/04/11 13:52:50  fwarmerdam
+ * added proper handling of iostream cleanup from David Carter
+ *
+ * Revision 1.8  2005/04/04 14:13:54  fwarmerdam
+ * Return true for 0 byte reads.
+ *
+ * Revision 1.7  2005/04/02 21:17:01  fwarmerdam
+ * add extra include files
+ *
+ * Revision 1.6  2005/03/10 14:48:42  fwarmerdam
+ * Return false on read requests of zero bytes, at David's suggestion.
+ *
+ * Revision 1.5  2005/02/24 15:11:42  fwarmerdam
+ * fixed debug arguments in Read()
+ *
+ * Revision 1.4  2005/02/07 22:53:54  fwarmerdam
+ * added preliminary Create support for JP2ECW driver
+ *
+ * Revision 1.3  2004/12/21 16:11:11  fwarmerdam
+ * hacked Read() method error reporting to avoid NPJE issue
+ *
+ * Revision 1.2  2004/12/20 22:17:57  fwarmerdam
+ * adjusted for two create copy entry points
+ *
+ * Revision 1.1  2004/12/10 19:15:34  fwarmerdam
+ * New
+ *
+ */
+
+#ifndef VSIIOSTREAM_H_INCLUDED
+#define VSIIOSTREAM_H_INCLUDED
+
+#include "cpl_vsi.h"
+#include "gdal_priv.h"
+#include "gdal_frmts.h"
+
+#ifdef FRMT_ecw
+
+/* -------------------------------------------------------------------- */
+/*      These definitions aren't really specific to the VSIIOStream,    */
+/*      but are shared amoung the ECW driver modules.                   */
+/* -------------------------------------------------------------------- */
+#include <NCSECWClient.h>
+#include <NCSECWCompressClient.h>
+#include <NCSErrors.h>
+#include <NCSFile.h>
+#include <NCSJP2File.h>
+#include <NCSJP2FileView.h>
+
+/* As of July 2002 only uncompress support is available on Unix */
+#define HAVE_COMPRESS
+
+#ifdef HAVE_COMPRESS
+GDALDataset *
+ECWCreateCopyECW( const char * pszFilename, GDALDataset *poSrcDS, 
+                 int bStrict, char ** papszOptions, 
+                 GDALProgressFunc pfnProgress, void * pProgressData );
+GDALDataset *
+ECWCreateCopyJPEG2000( const char * pszFilename, GDALDataset *poSrcDS, 
+                 int bStrict, char ** papszOptions, 
+                 GDALProgressFunc pfnProgress, void * pProgressData );
+
+GDALDataset *
+ECWCreateECW( const char * pszFilename, int nXSize, int nYSize, int nBands, 
+              GDALDataType eType, char **papszOptions );
+GDALDataset *
+ECWCreateJPEG2000(const char *pszFilename, int nXSize, int nYSize, int nBands, 
+                  GDALDataType eType, char **papszOptions );
+#endif
+
+CPL_C_START
+char **ECWGetCSList(void);
+CPL_C_END
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             VSIIOStream                              */
+/* ==================================================================== */
+/************************************************************************/
+
+class VSIIOStream : public CNCSJPCIOStream
+
+{
+  public:
+    
+    INT64    startOfJPData;
+    INT64    lengthOfJPData;
+    FILE    *fpVSIL;
+    int      bWritable;
+	int      nFileViewCount;
+    char     *pszFilename;
+
+    VSIIOStream() {
+        nFileViewCount = 0;
+        startOfJPData = 0;
+        lengthOfJPData = -1;
+        fpVSIL = NULL;
+    }
+    virtual ~VSIIOStream() {
+        Close();
+        if( fpVSIL != NULL )
+        {
+            VSIFCloseL( fpVSIL );
+            fpVSIL = NULL;
+        }
+    }
+
+    virtual CNCSError Access( FILE *fpVSILIn, BOOLEAN bWrite,
+                              const char *pszFilename, 
+                              INT64 start, INT64 size = -1) {
+
+        fpVSIL = fpVSILIn;
+        startOfJPData = start;
+        lengthOfJPData = size;
+        bWritable = bWrite;
+        VSIFSeekL(fpVSIL, startOfJPData, SEEK_SET);
+
+        return(CNCSJPCIOStream::Open((char *)pszFilename, (bool) bWrite));
+    }
+
+    virtual bool NCS_FASTCALL Seek() {
+        return(true);
+    }
+    
+    virtual bool NCS_FASTCALL Seek(INT64 offset, Origin origin = CURRENT) {
+        switch(origin) {
+            case START:
+                return(0 == VSIFSeekL(fpVSIL, offset+startOfJPData, SEEK_SET));
+
+            case CURRENT:
+                return(0 == VSIFSeekL(fpVSIL, offset, SEEK_CUR));
+                
+            case END:
+                return(0 == VSIFSeekL(fpVSIL, offset, SEEK_END));
+        }
+        
+        return(false);
+    }
+
+    virtual INT64 NCS_FASTCALL Tell() {
+        return VSIFTellL( fpVSIL ) - startOfJPData;
+    }
+
+    virtual INT64 NCS_FASTCALL Size() {
+        if( lengthOfJPData != -1 )
+            return lengthOfJPData;
+        else
+        {
+            INT64 curPos = Tell(), size;
+
+            Seek( 0, END );
+            size = Tell();
+            Seek( curPos, START );
+
+            return size;
+        }
+    }
+
+    virtual bool NCS_FASTCALL Read(void* buffer, UINT32 count) {
+        if( count == 0 )
+            return true;
+
+//        return(1 == VSIFReadL( buffer, count, 1, fpVSIL ) );
+
+        // The following is a hack 
+        if( VSIFReadL( buffer, count, 1, fpVSIL ) != 1 )
+        {
+            CPLDebug( "VSIIOSTREAM",
+                      "Read(%d) failed @ %d, ignoring failure.",
+                      count, (int) (VSIFTellL( fpVSIL ) - startOfJPData) );
+        }
+        
+        return true;
+    }
+
+    virtual bool NCS_FASTCALL Write(void* buffer, UINT32 count) {
+        if( count == 0 )
+            return true;
+        return(1 == VSIFWriteL(buffer, count, 1, fpVSIL));
+    }
+};
+
+#endif /* def FRMT_ecw */
+
+#endif /* ndef VSIIOSTREAM_H_INCLUDED */
+
diff --git a/Utilities/GDAL/frmts/elas/GNUmakefile b/Utilities/GDAL/frmts/elas/GNUmakefile
new file mode 100644
index 0000000000..0ebffe049c
--- /dev/null
+++ b/Utilities/GDAL/frmts/elas/GNUmakefile
@@ -0,0 +1,14 @@
+
+
+include ../../GDALmake.opt
+
+OBJ	=	elasdataset.o
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/elas/elasdataset.cpp b/Utilities/GDAL/frmts/elas/elasdataset.cpp
new file mode 100644
index 0000000000..ee99c50ab5
--- /dev/null
+++ b/Utilities/GDAL/frmts/elas/elasdataset.cpp
@@ -0,0 +1,684 @@
+/******************************************************************************
+ * $Id: elasdataset.cpp,v 1.11 2005/05/05 14:02:58 fwarmerdam Exp $
+ *
+ * Project:  ELAS Translator
+ * Purpose:  Complete implementation of ELAS translator module for GDAL.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: elasdataset.cpp,v $
+ * Revision 1.11  2005/05/05 14:02:58  fwarmerdam
+ * PAM Enable
+ *
+ * Revision 1.10  2003/07/08 15:37:05  warmerda
+ * avoid warnings
+ *
+ * Revision 1.9  2002/11/23 18:54:17  warmerda
+ * added CREATIONDATATYPES metadata for drivers
+ *
+ * Revision 1.8  2002/09/04 06:50:37  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.7  2002/06/12 21:12:25  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.6  2001/11/11 23:50:59  warmerda
+ * added required class keyword to friend declarations
+ *
+ * Revision 1.5  2001/07/18 04:51:56  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.4  2000/02/28 16:32:20  warmerda
+ * use SetBand method
+ *
+ * Revision 1.3  1999/05/17 17:18:14  warmerda
+ * Use CPL_MSBPTR32() instead of CPL_SWAPPTR32().
+ *
+ * Revision 1.2  1999/05/17 01:36:42  warmerda
+ * Added support for georeferencing values ... added ELASHeader.
+ *
+ * Revision 1.1  1999/05/13 19:17:48  warmerda
+ * New
+ *
+ */
+
+#include "gdal_pam.h"
+
+CPL_CVSID("$Id: elasdataset.cpp,v 1.11 2005/05/05 14:02:58 fwarmerdam Exp $");
+
+CPL_C_START
+void	GDALRegister_ELAS(void);
+CPL_C_END
+
+typedef struct {
+    GInt32	NBIH;	/* bytes in header, normaly 1024 */
+    GInt32      NBPR;	/* bytes per data record (all bands of scanline) */
+    GInt32	IL;	/* initial line - normally 1 */
+    GInt32	LL;	/* last line */
+    GInt32	IE;	/* initial element (pixel), normally 1 */
+    GInt32	LE;	/* last element (pixel) */
+    GInt32	NC;	/* number of channels (bands) */
+    GInt32	H4321;	/* header record identifier - always 4321. */
+    char	YLabel[4]; /* Should be "NOR" for UTM */
+    GInt32      YOffset;/* topleft pixel center northing */
+    char	XLabel[4]; /* Should be "EAS" for UTM */
+    GInt32      XOffset;/* topleft pixel center easting */
+    float	YPixSize;/* height of pixel in georef units */
+    float	XPixSize;/* width of pixel in georef units */
+    float	Matrix[4]; /* 2x2 transformation matrix.  Should be
+                              1,0,0,1 for pixel/line, or
+                              1,0,0,-1 for UTM */
+    GByte	IH19[4];/* data type, and size flags */
+    GInt32	IH20;	/* number of secondary headers */
+    char	unused1[8];
+    GInt32	LABL;	/* used by LABL module */
+    char	HEAD;	/* used by HEAD module */
+    char	Comment1[64];
+    char	Comment2[64];
+    char	Comment3[64];
+    char	Comment4[64];
+    char	Comment5[64];
+    char	Comment6[64];
+    GUInt16	ColorTable[256];  /* RGB packed with 4 bits each */
+    char	unused2[32];
+} ELASHeader;
+
+
+/************************************************************************/
+/* ==================================================================== */
+/*				ELASDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class ELASRasterBand;
+
+class ELASDataset : public GDALPamDataset
+{
+    friend class ELASRasterBand;
+
+    FILE	*fp;
+
+    ELASHeader  sHeader;
+    int		bHeaderModified;
+
+    GDALDataType eRasterDataType;
+
+    int		nLineOffset;
+    int		nBandOffset;     // within a line.
+    
+    double	adfGeoTransform[6];
+
+  public:
+                 ELASDataset();
+                 ~ELASDataset();
+
+    virtual CPLErr GetGeoTransform( double * );
+    virtual CPLErr SetGeoTransform( double * );
+
+    static GDALDataset *Open( GDALOpenInfo * );
+    static GDALDataset *Create( const char * pszFilename,
+                                int nXSize, int nYSize, int nBands,
+                                GDALDataType eType, char ** papszParmList );
+
+    virtual void FlushCache( void );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            ELASRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class ELASRasterBand : public GDALPamRasterBand
+{
+    friend class ELASDataset;
+
+  public:
+
+                   ELASRasterBand( ELASDataset *, int );
+
+    // should override RasterIO eventually.
+    
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual CPLErr IWriteBlock( int, int, void * ); 
+};
+
+
+/************************************************************************/
+/*                           ELASRasterBand()                            */
+/************************************************************************/
+
+ELASRasterBand::ELASRasterBand( ELASDataset *poDS, int nBand )
+
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+
+    this->eAccess = poDS->eAccess;
+
+    eDataType = poDS->eRasterDataType;
+
+    nBlockXSize = poDS->GetRasterXSize();
+    nBlockYSize = 1;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr ELASRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                   void * pImage )
+
+{
+    ELASDataset	*poGDS = (ELASDataset *) poDS;
+    CPLErr		eErr = CE_None;
+    long		nOffset;
+    int			nDataSize;
+
+    CPLAssert( nBlockXOff == 0 );
+
+    nDataSize = GDALGetDataTypeSize(eDataType) * poGDS->GetRasterXSize() / 8;
+    nOffset = poGDS->nLineOffset * nBlockYOff + 1024 + (nBand-1) * nDataSize;
+    
+/* -------------------------------------------------------------------- */
+/*      If we can't seek to the data, we will assume this is a newly    */
+/*      created file, and that the file hasn't been extended yet.       */
+/*      Just read as zeros.                                             */
+/* -------------------------------------------------------------------- */
+    if( VSIFSeek( poGDS->fp, nOffset, SEEK_SET ) != 0 
+        || VSIFRead( pImage, 1, nDataSize, poGDS->fp ) != (size_t) nDataSize )
+    {
+        CPLError( CE_Failure, CPLE_FileIO,
+                  "Seek or read of %d bytes at %ld failed.\n",
+                  nDataSize, nOffset );
+        eErr = CE_Failure;
+    }
+
+    return eErr;
+}
+
+/************************************************************************/
+/*                            IWriteBlock()                             */
+/************************************************************************/
+
+CPLErr ELASRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
+                                     void * pImage )
+
+{
+    ELASDataset	*poGDS = (ELASDataset *) poDS;
+    CPLErr		eErr = CE_None;
+    long		nOffset;
+    int			nDataSize;
+
+    CPLAssert( nBlockXOff == 0 );
+    CPLAssert( eAccess == GA_Update );
+
+    nDataSize = GDALGetDataTypeSize(eDataType) * poGDS->GetRasterXSize() / 8;
+    nOffset = poGDS->nLineOffset * nBlockYOff + 1024 + (nBand-1) * nDataSize;
+    
+    if( VSIFSeek( poGDS->fp, nOffset, SEEK_SET ) != 0
+        || VSIFWrite( pImage, 1, nDataSize, poGDS->fp ) != (size_t) nDataSize )
+    {
+        CPLError( CE_Failure, CPLE_FileIO,
+                  "Seek or write of %d bytes at %ld failed.\n",
+                  nDataSize, nOffset );
+        eErr = CE_Failure;
+    }
+
+    return eErr;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*      ELASDataset                                                     */
+/* ==================================================================== */
+/************************************************************************/
+
+
+/************************************************************************/
+/*                            ELASDataset()                             */
+/************************************************************************/
+
+ELASDataset::ELASDataset()
+
+{
+    fp = NULL;
+
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+}
+
+/************************************************************************/
+/*                            ~ELASDataset()                            */
+/************************************************************************/
+
+ELASDataset::~ELASDataset()
+
+{
+    FlushCache();
+
+    VSIFClose( fp );
+    fp = NULL;
+}
+
+/************************************************************************/
+/*                             FlushCache()                             */
+/*                                                                      */
+/*      We also write out the header, if it is modified.                */
+/************************************************************************/
+
+void ELASDataset::FlushCache()
+
+{
+    GDALDataset::FlushCache();
+
+    if( bHeaderModified )
+    {
+        VSIFSeek( fp, 0, SEEK_SET );
+        VSIFWrite( &sHeader, 1024, 1, fp );
+        bHeaderModified = FALSE;
+    }
+}
+
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *ELASDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+/* -------------------------------------------------------------------- */
+/*	First we check to see if the file has the expected header	*/
+/*	bytes.								*/    
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->nHeaderBytes < 256 )
+        return NULL;
+
+    if( CPL_MSBWORD32(*((GInt32 *) (poOpenInfo->pabyHeader+0))) != 1024
+        || CPL_MSBWORD32(*((GInt32 *) (poOpenInfo->pabyHeader+28))) != 4321 )
+    {
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    ELASDataset 	*poDS;
+    const char	 	*pszAccess;
+
+    if( poOpenInfo->eAccess == GA_Update )
+        pszAccess = "r+b";
+    else
+        pszAccess = "rb";
+
+    poDS = new ELASDataset();
+
+    poDS->fp = VSIFOpen( poOpenInfo->pszFilename, pszAccess );
+    if( poDS->fp == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Attempt to open `%s' with acces `%s' failed.\n",
+                  poOpenInfo->pszFilename, pszAccess );
+        return NULL;
+    }
+
+    poDS->eAccess = poOpenInfo->eAccess;
+
+/* -------------------------------------------------------------------- */
+/*      Read the header information.                                    */
+/* -------------------------------------------------------------------- */
+    poDS->bHeaderModified = FALSE;
+    if( VSIFRead( &(poDS->sHeader), 1024, 1, poDS->fp ) != 1 )
+    {
+        CPLError( CE_Failure, CPLE_FileIO,
+                  "Attempt to read 1024 byte header filed on file:\n", 
+                  "%s\n", poOpenInfo->pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Extract information of interest from the header.                */
+/* -------------------------------------------------------------------- */
+    int		nStart, nEnd, nELASDataType, nBytesPerSample;
+    
+    poDS->nLineOffset = CPL_MSBWORD32( poDS->sHeader.NBPR );
+
+    nStart = CPL_MSBWORD32( poDS->sHeader.IL );
+    nEnd = CPL_MSBWORD32( poDS->sHeader.LL );
+    poDS->nRasterYSize = nEnd - nStart + 1;
+
+    nStart = CPL_MSBWORD32( poDS->sHeader.IE );
+    nEnd = CPL_MSBWORD32( poDS->sHeader.LE );
+    poDS->nRasterXSize = nEnd - nStart + 1;
+
+    poDS->nBands = CPL_MSBWORD32( poDS->sHeader.NC );
+
+    nELASDataType = (poDS->sHeader.IH19[2] & 0x7e) >> 2;
+    nBytesPerSample = poDS->sHeader.IH19[3];
+    
+    if( nELASDataType == 0 && nBytesPerSample == 1 )
+        poDS->eRasterDataType = GDT_Byte;
+    else if( nELASDataType == 1 && nBytesPerSample == 1 )
+        poDS->eRasterDataType = GDT_Byte;
+    else if( nELASDataType == 16 && nBytesPerSample == 4 )
+        poDS->eRasterDataType = GDT_Float32;
+    else if( nELASDataType == 17 && nBytesPerSample == 8 )
+        poDS->eRasterDataType = GDT_Float64;
+    else
+    {
+        delete poDS;
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Unrecognised image data type %d, with BytesPerSample=%d.\n",
+                  nELASDataType, nBytesPerSample );
+        return NULL;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*	Band offsets are always multiples of 256 within a multi-band	*/
+/*	scanline of data.						*/
+/* -------------------------------------------------------------------- */
+    poDS->nBandOffset =
+        (poDS->nRasterXSize * GDALGetDataTypeSize(poDS->eRasterDataType)/8);
+
+    if( poDS->nBandOffset % 256 != 0 )
+    {
+        poDS->nBandOffset =
+            poDS->nBandOffset - (poDS->nBandOffset % 256) + 256;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    int		iBand;
+
+    for( iBand = 0; iBand < poDS->nBands; iBand++ )
+    {
+        poDS->SetBand( iBand+1, new ELASRasterBand( poDS, iBand+1 ) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*	Extract the projection coordinates, if present.			*/
+/* -------------------------------------------------------------------- */
+    if( poDS->sHeader.XOffset != 0 )
+    {
+        CPL_MSBPTR32(&(poDS->sHeader.XPixSize));
+        CPL_MSBPTR32(&(poDS->sHeader.YPixSize));
+
+        poDS->adfGeoTransform[0] =
+            (GInt32) CPL_MSBWORD32(poDS->sHeader.XOffset);
+        poDS->adfGeoTransform[1] = poDS->sHeader.XPixSize;
+        poDS->adfGeoTransform[2] = 0.0;
+        poDS->adfGeoTransform[3] =
+            (GInt32) CPL_MSBWORD32(poDS->sHeader.YOffset);
+        poDS->adfGeoTransform[4] = 0.0;
+        poDS->adfGeoTransform[5] = -1.0 * ABS(poDS->sHeader.YPixSize);
+
+        CPL_MSBPTR32(&(poDS->sHeader.XPixSize));
+        CPL_MSBPTR32(&(poDS->sHeader.YPixSize));
+
+        poDS->adfGeoTransform[0] -= poDS->adfGeoTransform[1] * 0.5;
+        poDS->adfGeoTransform[3] -= poDS->adfGeoTransform[5] * 0.5;
+    }
+    else
+    {
+        poDS->adfGeoTransform[0] = 0.0;
+        poDS->adfGeoTransform[1] = 1.0;
+        poDS->adfGeoTransform[2] = 0.0;
+        poDS->adfGeoTransform[3] = 0.0;
+        poDS->adfGeoTransform[4] = 0.0;
+        poDS->adfGeoTransform[5] = 1.0;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                               Create()                               */
+/*                                                                      */
+/*      Create a new GeoTIFF or TIFF file.                              */
+/************************************************************************/
+
+GDALDataset *ELASDataset::Create( const char * pszFilename,
+                                  int nXSize, int nYSize, int nBands,
+                                  GDALDataType eType,
+                                  char ** /* notdef: papszParmList */ )
+
+{
+    int		nBandOffset;
+    
+/* -------------------------------------------------------------------- */
+/*      Verify input options.                                           */
+/* -------------------------------------------------------------------- */
+    if( eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_Float64 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Attempt to create an ELAS dataset with an illegal\n"
+                  "data type (%d).\n",
+                  eType );
+
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try to create the file.                                         */
+/* -------------------------------------------------------------------- */
+    FILE	*fp;
+
+    fp = VSIFOpen( pszFilename, "w" );
+
+    if( fp == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Attempt to create file `%s' failed.\n",
+                  pszFilename );
+        return NULL;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*	How long will each band of a scanline be?			*/
+/* -------------------------------------------------------------------- */
+    nBandOffset = nXSize * GDALGetDataTypeSize(eType)/8;
+
+    if( nBandOffset % 256 != 0 )
+    {
+        nBandOffset = nBandOffset - (nBandOffset % 256) + 256;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Setup header data block.                                        */
+/*                                                                      */
+/*      Note that CPL_MSBWORD32() will swap little endian words to      */
+/*      big endian on little endian platforms.                          */
+/* -------------------------------------------------------------------- */
+    ELASHeader	sHeader;
+
+    memset( &sHeader, 0, 1024 );
+
+    sHeader.NBIH = CPL_MSBWORD32(1024);
+
+    sHeader.NBPR = CPL_MSBWORD32(nBands * nBandOffset);
+    
+    sHeader.IL = CPL_MSBWORD32(1);
+    sHeader.LL = CPL_MSBWORD32(nYSize);
+
+    sHeader.IE = CPL_MSBWORD32(1);
+    sHeader.LE = CPL_MSBWORD32(nXSize);
+
+    sHeader.NC = CPL_MSBWORD32(nBands);
+
+    sHeader.H4321 = CPL_MSBWORD32(4321);
+
+    sHeader.IH19[0] = 0x04;
+    sHeader.IH19[1] = 0xd2;
+    sHeader.IH19[3] = (GByte) (GDALGetDataTypeSize(eType) / 8);
+
+    if( eType == GDT_Byte )
+        sHeader.IH19[2] = 1 << 2;
+    else if( eType == GDT_Float32 )
+        sHeader.IH19[2] = 16 << 2;
+    else if( eType == GDT_Float64 )
+        sHeader.IH19[2] = 17 << 2;
+
+/* -------------------------------------------------------------------- */
+/*      Write the header data.                                          */
+/* -------------------------------------------------------------------- */
+    VSIFWrite( &sHeader, 1024, 1, fp );
+
+/* -------------------------------------------------------------------- */
+/*      Now write out zero data for all the imagery.  This is           */
+/*      inefficient, but simplies the IReadBlock() / IWriteBlock() logic.*/
+/* -------------------------------------------------------------------- */
+    GByte	*pabyLine;
+
+    pabyLine = (GByte *) CPLCalloc(nBandOffset,nBands);
+    for( int iLine = 0; iLine < nYSize; iLine++ )
+    {
+        if( VSIFWrite( pabyLine, 1, nBandOffset, fp ) != (size_t) nBandOffset )
+        {
+            CPLError( CE_Failure, CPLE_FileIO,
+                      "Error writing ELAS image data ... likely insufficient"
+                      " disk space.\n" );
+            VSIFClose( fp );
+            CPLFree( pabyLine );
+            return NULL;
+        }
+    }
+
+    CPLFree( pabyLine );
+    
+    VSIFClose( fp );
+
+/* -------------------------------------------------------------------- */
+/*      Try to return a regular handle on the file.                     */
+/* -------------------------------------------------------------------- */
+    return (GDALDataset *) GDALOpen( pszFilename, GA_Update );
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr ELASDataset::GetGeoTransform( double * padfTransform )
+
+{
+    memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
+
+    return( CE_None );
+}
+
+/************************************************************************/
+/*                          SetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr ELASDataset::SetGeoTransform( double * padfTransform )
+
+{
+/* -------------------------------------------------------------------- */
+/*      I don't think it supports rotation, but perhaps it is possible  */
+/*      for us to use the 2x2 transform matrix to accomplish some       */
+/*      sort of rotation.                                               */
+/* -------------------------------------------------------------------- */
+    if( padfTransform[2] != 0.0 || padfTransform[4] != 0.0 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Attempt to set rotated geotransform on ELAS file.\n"
+                  "ELAS does not support rotation.\n" );
+
+        return CE_Failure;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Remember the new transform, and update the header.              */
+/* -------------------------------------------------------------------- */
+    int		nXOff, nYOff;
+    
+    memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 );
+
+    bHeaderModified = TRUE;
+
+    nXOff = (int) (adfGeoTransform[0] + adfGeoTransform[1]*0.5);
+    nYOff = (int) (adfGeoTransform[3] + adfGeoTransform[5]*0.5);
+
+    sHeader.XOffset = CPL_MSBWORD32(nXOff);
+    sHeader.YOffset = CPL_MSBWORD32(nYOff);
+
+    sHeader.XPixSize = (float) ABS(adfGeoTransform[1]);
+    sHeader.YPixSize = (float) ABS(adfGeoTransform[5]);
+
+    CPL_MSBPTR32(&(sHeader.XPixSize));
+    CPL_MSBPTR32(&(sHeader.YPixSize));
+
+    strncpy( sHeader.YLabel, "NOR ", 4 );
+    strncpy( sHeader.XLabel, "EAS ", 4 );
+
+    sHeader.Matrix[0] = 1.0;
+    sHeader.Matrix[1] = 0.0;
+    sHeader.Matrix[2] = 0.0;
+    sHeader.Matrix[3] = -1.0;
+    
+    CPL_MSBPTR32(&(sHeader.Matrix[0]));
+    CPL_MSBPTR32(&(sHeader.Matrix[1]));
+    CPL_MSBPTR32(&(sHeader.Matrix[2]));
+    CPL_MSBPTR32(&(sHeader.Matrix[3]));
+    
+    return( CE_None );
+}
+
+
+/************************************************************************/
+/*                          GDALRegister_ELAS()                        */
+/************************************************************************/
+
+void GDALRegister_ELAS()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "ELAS" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "ELAS" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "ELAS" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte Float32 Float64" );
+        
+        poDriver->pfnOpen = ELASDataset::Open;
+        poDriver->pfnCreate = ELASDataset::Create;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
diff --git a/Utilities/GDAL/frmts/elas/makefile.vc b/Utilities/GDAL/frmts/elas/makefile.vc
new file mode 100644
index 0000000000..16f0a58109
--- /dev/null
+++ b/Utilities/GDAL/frmts/elas/makefile.vc
@@ -0,0 +1,13 @@
+
+OBJ	=	elasdataset.obj
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/envisat/EnvisatFile.c b/Utilities/GDAL/frmts/envisat/EnvisatFile.c
new file mode 100644
index 0000000000..fc66dfb547
--- /dev/null
+++ b/Utilities/GDAL/frmts/envisat/EnvisatFile.c
@@ -0,0 +1,1925 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  APP ENVISAT Support
+ * Purpose:  Low Level Envisat file access (read/write) API.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Atlantis Scientific, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: EnvisatFile.c,v $
+ * Revision 1.11  2004/03/10 18:18:54  warmerda
+ * Avoid warning.
+ *
+ * Revision 1.10  2004/03/10 18:18:29  warmerda
+ * Avoid warning.
+ *
+ * Revision 1.9  2002/06/07 14:08:35  warmerda
+ * Fixed last fix.
+ *
+ * Revision 1.8  2002/06/07 14:07:49  warmerda
+ * Default to doing a GDAL rather than APP build.
+ *
+ * Revision 1.7  2002/05/24 21:29:26  warmerda
+ * avoid warnings
+ *
+ * Revision 1.6  2002/05/18 22:57:54  warmerda
+ * allow long lines
+ *
+ * Revision 1.5  2001/07/18 04:51:56  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.4  2001/03/26 18:44:03  warmerda
+ * removed debugging statements
+ *
+ * Revision 1.3  2001/03/26 18:43:05  warmerda
+ * fixed bug in EnvisatFile_Close
+ *
+ * Revision 1.2  2001/02/27 22:34:33  warmerda
+ * added EnvisatFile_ReadDatasetRecord
+ *
+ * Revision 1.1  2001/02/24 14:20:48  warmerda
+ * New
+ *
+ */
+
+#ifndef APP_BUILD
+#  define GDAL_BUILD
+#  include "cpl_conv.h"
+#  include "EnvisatFile.h"
+
+CPL_CVSID("$Id$");
+
+#else
+#  include "APP/app.h"
+#  include "util/Files/EnvisatFile.h"
+#endif
+
+typedef struct 
+{
+    char	*ds_name;
+    char	*ds_type;
+    char	*filename;
+    int		ds_offset;
+    int		ds_size;
+    int		num_dsr;
+    int		dsr_size;
+} EnvisatDatasetInfo;
+
+typedef struct
+{
+    char	*key;
+    char	*value;
+    char	*units;
+    char	*literal_line;
+    int         value_offset;
+} EnvisatNameValue;
+
+struct EnvisatFile_tag
+{
+    FILE	*fp;
+    char        *filename;
+    int		updatable;
+    int         header_dirty;
+    int		dsd_offset;
+
+    int		mph_count;
+    EnvisatNameValue **mph_entries;
+
+    int		sph_count;
+    EnvisatNameValue **sph_entries;
+
+    int		ds_count;
+    EnvisatDatasetInfo **ds_info;
+    
+};
+
+#ifdef GDAL_BUILD
+#  define SUCCESS 0
+#  define FAILURE 1
+#  define SendError( text )   CPLError( CE_Failure, CPLE_AppDefined, "%s", text )
+#endif
+
+#define MPH_SIZE 1247
+
+/*
+ * API For handling name/value lists.
+ */
+int S_NameValueList_Parse( const char *text, int text_offset, 
+                           int *entry_count, 
+                           EnvisatNameValue ***entries );
+void S_NameValueList_Destroy( int *entry_count, 
+                             EnvisatNameValue ***entries );
+int S_NameValueList_FindKey( const char *key,
+                             int entry_count, 
+                             EnvisatNameValue **entries );
+const char *S_NameValueList_FindValue( const char *key,
+                                       int entry_count, 
+                                       EnvisatNameValue **entries,
+                                       const char * default_value );
+
+int S_NameValueList_Rewrite( FILE *fp, int entry_count, 
+                             EnvisatNameValue **entries );
+
+EnvisatNameValue *
+    S_EnivsatFile_FindNameValue( EnvisatFile *self,
+                                 EnvisatFile_HeaderFlag mph_or_sph,
+                                 const char * key );
+
+
+
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    Envisat_SetupLevel0
+
+Purpose:
+    Patch up missing information about SPH, and datasets for incomplete 
+    level 0 signal datasets.
+
+Description:
+
+Inputs:
+    self -- Envisat file handle.
+
+Outputs:
+
+Returns:
+    SUCCESS or FAILURE
+
+-----------------------------------------------------------------------------*/
+
+static int EnvisatFile_SetupLevel0( EnvisatFile *self )
+
+{
+    int	file_length;
+    unsigned char header[68];
+    EnvisatDatasetInfo *ds_info;
+
+    self->dsd_offset = 0;
+    self->ds_count = 1;
+    self->ds_info = (EnvisatDatasetInfo **) 
+        calloc(sizeof(EnvisatDatasetInfo*),self->ds_count);
+
+    if( self->ds_info == NULL )
+        return FAILURE;
+
+    /*
+     * Figure out how long the file is. 
+     */
+
+    fseek( self->fp, 0, SEEK_END );
+    file_length = (int) ftell( self->fp );
+    
+    /* 
+     * Read the first record header, and verify the well known values.
+     */
+    fseek( self->fp, 3203, SEEK_SET );
+    fread( header, 68, 1, self->fp );
+
+    if( header[38] != 0 || header[39] != 0x1d
+        || header[40] != 0 || header[41] != 0x54 )
+    {
+        SendError( "Didn't get expected Data Field Header Length, or Mode ID\n"
+                   "values for the first data record." );
+        return FAILURE;
+    }
+
+    /* 
+     * Then build the dataset into structure from that. 
+     */
+    ds_info = (EnvisatDatasetInfo *) calloc(sizeof(EnvisatDatasetInfo),1);
+    
+    ds_info->ds_name = strdup( "ASAR SOURCE PACKETS         " );
+    ds_info->ds_type = strdup( "M" );
+    ds_info->filename = strdup( "                                                              " );
+    ds_info->ds_offset = 3203;
+    ds_info->dsr_size = -1;
+    ds_info->num_dsr = 0;
+    ds_info->ds_size = file_length - ds_info->ds_offset;
+    
+    self->ds_info[0] = ds_info;
+
+    return SUCCESS;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    Envisat_Open
+
+Purpose:
+    Open an ENVISAT formatted file, and read all headers.
+
+Description:
+
+Inputs:
+    filename -- name of Envisat file.
+    mode -- either "r" for read access, or "r+" for read/write access.
+
+Outputs:
+    self -- file handle, NULL on FAILURE.
+
+Returns:
+    SUCCESS or FAILURE
+
+-----------------------------------------------------------------------------*/
+
+int EnvisatFile_Open( EnvisatFile **self_ptr, 
+                      const char *filename, 
+                      const char *mode )
+
+{
+    FILE	*fp;
+    EnvisatFile	*self;
+    char	mph_data[1248];
+    char	*sph_data, *ds_data;
+    int		sph_size, num_dsd, dsd_size, i;
+
+    *self_ptr = NULL;
+
+    /*
+     * Check for legal mode argument.  Force to be binary for correct
+     * operation on DOS file systems.
+     */
+    if( strcmp(mode,"r") == 0 )
+        mode = "rb";
+    else if( strcmp(mode,"r+") == 0 )
+        mode = "rb+";
+    else
+    {
+        SendError( "Illegal mode value used in EnvisatFile_Open(), only "
+                   "\"r\" and \"r+\" are supported." );
+        return FAILURE;
+    }
+
+    /*
+     * Try to open the file, and report failure. 
+     */
+
+    fp = fopen( filename, mode );
+
+    if( fp == NULL )
+    {
+        char	error_buf[2048];
+
+        sprintf( error_buf, 
+                 "Unable to open file \"%s\" in EnvisatFile_Open().", 
+                 filename );
+
+        SendError( error_buf );
+        return FAILURE;
+    }
+
+    /*
+     * Create, and initialize the EnvisatFile structure. 
+     */
+    self = (EnvisatFile *) calloc(sizeof(EnvisatFile),1);
+    if( self == NULL )
+        return FAILURE;
+
+    self->fp = fp;
+    self->filename = strdup( filename );
+    self->header_dirty = 0;
+    self->updatable = (strcmp(mode,"rb+") == 0);
+
+    /*
+     * Read the MPH, and process it as a group of name/value pairs. 
+     */
+
+    if( fread( mph_data, 1, MPH_SIZE, fp ) != MPH_SIZE )
+    {
+        free( self );
+        SendError( "fread() for mph failed." );
+        return FAILURE;
+    }
+
+    mph_data[MPH_SIZE] = '\0';
+    if( S_NameValueList_Parse( mph_data, 0, 
+                               &(self->mph_count), 
+                               &(self->mph_entries) ) == FAILURE )
+        return FAILURE;
+
+    /*
+     * Is this an incomplete level 0 file?
+     */
+    if( EnvisatFile_GetKeyValueAsInt( self, MPH, "SPH_SIZE", -1 ) == 0 
+        && strncmp(EnvisatFile_GetKeyValueAsString( self, MPH, "PRODUCT", ""),
+                   "ASA_IM__0P", 10) == 0 )
+    {
+
+        if( EnvisatFile_SetupLevel0( self ) == FAILURE )
+        {
+            EnvisatFile_Close( self );
+            return FAILURE;
+        }
+        else
+        {
+            *self_ptr = self;
+            return SUCCESS;
+        }
+    }
+
+    /*
+     * Read the SPH, and process it as a group of name/value pairs.  
+     */
+    sph_size = EnvisatFile_GetKeyValueAsInt( self, MPH, "SPH_SIZE", 0 );
+
+    if( sph_size == 0 )							
+    {
+        SendError( "File does not appear to have SPH,"
+                   " SPH_SIZE not set, or zero." );
+        return FAILURE;
+    }
+
+    sph_data = (char *) malloc(sph_size + 1 );
+    if( sph_data == NULL )
+        return FAILURE;
+
+    if( (int) fread( sph_data, 1, sph_size, fp ) != sph_size )
+    {
+        free( self );
+        SendError( "fread() for sph failed." );
+        return FAILURE;
+    }
+
+    sph_data[sph_size] = '\0';
+    ds_data = strstr(sph_data,"DS_NAME");
+    if( ds_data != NULL )
+    {
+        self->dsd_offset = (int) (ds_data - sph_data) + MPH_SIZE;
+        *(ds_data-1) = '\0';
+    }
+
+    if( S_NameValueList_Parse( sph_data, MPH_SIZE,
+                               &(self->sph_count), 
+                               &(self->sph_entries) ) == FAILURE )
+        return FAILURE;
+
+    /*
+     * Parse the Dataset Definitions.
+     */
+    num_dsd = EnvisatFile_GetKeyValueAsInt( self, MPH, "NUM_DSD", 0 );
+    dsd_size = EnvisatFile_GetKeyValueAsInt( self, MPH, "DSD_SIZE", 0 );
+    
+    if( num_dsd > 0 && ds_data == NULL )
+    {
+        SendError( "DSDs indicated in MPH, but not found in SPH." );
+        return FAILURE;
+    }
+
+    self->ds_info = (EnvisatDatasetInfo **) 
+        calloc(sizeof(EnvisatDatasetInfo*),num_dsd);
+    if( self->ds_info == NULL )
+        return FAILURE;
+
+    for( i = 0; i < num_dsd; i++ )
+    {
+        int	dsdh_count = 0;
+        EnvisatNameValue **dsdh_entries = NULL;
+        char	*dsd_data;
+        EnvisatDatasetInfo *ds_info;
+
+        /*
+         * We parse each DSD grouping into a name/value list. 
+         */
+        dsd_data = ds_data + i * dsd_size;
+        dsd_data[dsd_size-1] = '\0';
+        
+        if( S_NameValueList_Parse( dsd_data, 0, 
+                                   &dsdh_count, &dsdh_entries ) == FAILURE )
+            return FAILURE;
+
+        /* 
+         * Then build the dataset into structure from that. 
+         */
+        ds_info = (EnvisatDatasetInfo *) calloc(sizeof(EnvisatDatasetInfo),1);
+
+        ds_info->ds_name = strdup( 
+            S_NameValueList_FindValue( "DS_NAME", 
+                                       dsdh_count, dsdh_entries, "" ));
+        ds_info->ds_type = strdup( 
+            S_NameValueList_FindValue( "DS_TYPE", 
+                                       dsdh_count, dsdh_entries, "" ));
+        ds_info->filename = strdup( 
+            S_NameValueList_FindValue( "FILENAME", 
+                                       dsdh_count, dsdh_entries, "" ));
+        ds_info->ds_offset = atoi(
+            S_NameValueList_FindValue( "DS_OFFSET", 
+                                       dsdh_count, dsdh_entries, "0" ));
+        ds_info->ds_size = atoi(
+            S_NameValueList_FindValue( "DS_SIZE", 
+                                       dsdh_count, dsdh_entries, "0" ));
+        ds_info->num_dsr = atoi(
+            S_NameValueList_FindValue( "NUM_DSR", 
+                                       dsdh_count, dsdh_entries, "0" ));
+        ds_info->dsr_size = atoi(
+            S_NameValueList_FindValue( "DSR_SIZE", 
+                                       dsdh_count, dsdh_entries, "0" ));
+
+        S_NameValueList_Destroy( &dsdh_count, &dsdh_entries );
+
+        self->ds_info[i] = ds_info;
+        self->ds_count++;
+    }
+    
+    free( sph_data );
+
+    /*
+     * Return successfully.
+     */
+    *self_ptr = self;
+
+    return SUCCESS;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_Create
+
+Purpose:
+    Create a new ENVISAT formatted file based on a template file.
+
+Description:
+
+Inputs:
+    filename -- name of Envisat file.
+    template_file -- name of envisat file header to utilize as template. 
+
+Outputs:
+    self -- file handle, NULL on FAILURE.
+
+Returns:
+    SUCCESS or FAILURE
+
+-----------------------------------------------------------------------------*/
+
+int EnvisatFile_Create( EnvisatFile **self_ptr, 
+                        const char *filename, 
+                        const char *template_file )
+
+{
+    int		template_size;
+    char	*template_data;
+    FILE	*fp;
+
+    /*
+     * Try to open the template file, and read it into memory.
+     */
+
+    fp = fopen( template_file, "rb" );
+
+    if( fp == NULL )
+    {
+        char	error_buf[2048];
+
+        sprintf( error_buf, 
+                 "Unable to open file \"%s\" in EnvisatFile_Create().", 
+                 template_file );
+
+        SendError( error_buf );
+        return FAILURE;
+    }
+
+    fseek( fp, 0, SEEK_END );
+    template_size = (int) ftell( fp );
+
+    template_data = (char *) malloc(template_size);
+    
+    fseek( fp, 0, SEEK_SET );
+    fread( template_data, template_size, 1, fp );
+    fclose( fp );
+
+    /*
+     * Try to write the template out to the new filename. 
+     */
+    
+    fp = fopen( filename, "wb" );
+    if( fp == NULL )
+    {
+        char	error_buf[2048];
+
+        sprintf( error_buf, 
+                 "Unable to open file \"%s\" in EnvisatFile_Create().", 
+                 filename );
+
+        SendError( error_buf );
+        return FAILURE;
+    }
+
+    fwrite( template_data, template_size, 1, fp );
+    fclose( fp );
+
+    free( template_data );
+
+    /*
+     * Now just open the file normally. 
+     */
+    
+    return EnvisatFile_Open( self_ptr, filename, "r+" );
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_GetCurrentLength
+
+Purpose:
+    Fetch the current file length.
+
+Description:
+    The length is computed by scanning the dataset definitions, not the
+    physical file length.  
+
+Inputs:
+    self -- the file to operate on. 
+
+Outputs:
+
+Returns:
+    Returns length or -1 on failure.
+
+-----------------------------------------------------------------------------*/
+
+int EnvisatFile_GetCurrentLength( EnvisatFile *self )
+
+{
+    int		length;
+    int		ds;
+    int		ds_offset;
+    int         ds_size;
+
+    length = MPH_SIZE 
+        + EnvisatFile_GetKeyValueAsInt( self, MPH, "SPH_SIZE", 0 );
+
+    for( ds = 0; 
+         EnvisatFile_GetDatasetInfo( self, ds, NULL, NULL, NULL, 
+                                     &ds_offset, &ds_size, NULL, NULL )
+             != FAILURE; 
+         ds++ )
+    {
+        if( ds_offset != 0 && (ds_offset+ds_size) > length )
+            length = ds_offset + ds_size;
+    }
+
+    return length;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_RewriteHeader
+
+Purpose:
+    Update the envisat file header on disk to match the in-memory image.
+
+Description:
+
+Inputs:
+    self -- handle for file to close.
+
+Outputs:
+
+Returns:
+    SUCCESS or FAILURE.
+
+-----------------------------------------------------------------------------*/
+
+static int EnvisatFile_RewriteHeader( EnvisatFile *self )
+
+{
+    int		dsd, dsd_size;
+
+    /* 
+     * Rewrite MPH and SPH headers.
+     */
+    if( S_NameValueList_Rewrite( self->fp, 
+                        self->mph_count, self->mph_entries ) == FAILURE )
+        return FAILURE;
+
+    if( S_NameValueList_Rewrite( self->fp, 
+                        self->sph_count, self->sph_entries ) == FAILURE )
+        return FAILURE;
+
+    /*
+     * Rewrite DSDs.  We actually have to read each, and reparse to set
+     * the individual parameters properly.
+     */
+    dsd_size = EnvisatFile_GetKeyValueAsInt( self, MPH, "DSD_SIZE", 0 );
+    if( dsd_size == 0 )
+        return FAILURE;
+
+    for( dsd = 0; dsd < self->ds_count; dsd++ )
+    {
+        char	*dsd_text;
+        int	dsdh_count = 0, key_index;
+        EnvisatNameValue **dsdh_entries = NULL;
+
+        dsd_text = (char *) calloc(1,dsd_size+1);
+        if( fseek( self->fp, self->dsd_offset + dsd * dsd_size, 
+                   SEEK_SET ) != 0 )
+        {
+            SendError( "fseek() failed in EnvisatFile_RewriteHeader()" );
+            return FAILURE;
+        }
+        
+        if( (int) fread( dsd_text, 1, dsd_size, self->fp ) != dsd_size )
+        {
+            SendError( "fread() failed in EnvisatFile_RewriteHeader()" );
+            return FAILURE;
+        }
+
+        if( S_NameValueList_Parse( dsd_text, self->dsd_offset + dsd*dsd_size, 
+                                   &dsdh_count, &dsdh_entries ) == FAILURE )
+            return FAILURE;
+
+        free( dsd_text );
+
+        key_index = S_NameValueList_FindKey( "DS_OFFSET", 
+                                             dsdh_count, dsdh_entries );
+        if( key_index == -1 )
+            continue;
+
+        sprintf( dsdh_entries[key_index]->value, "%+021d", 
+                 self->ds_info[dsd]->ds_offset );
+
+        key_index = S_NameValueList_FindKey( "DS_SIZE", 
+                                             dsdh_count, dsdh_entries );
+        sprintf( dsdh_entries[key_index]->value, "%+021d", 
+                 self->ds_info[dsd]->ds_size );
+
+        key_index = S_NameValueList_FindKey( "NUM_DSR", 
+                                             dsdh_count, dsdh_entries );
+        sprintf( dsdh_entries[key_index]->value, "%+011d", 
+                 self->ds_info[dsd]->num_dsr );
+
+        key_index = S_NameValueList_FindKey( "DSR_SIZE", 
+                                             dsdh_count, dsdh_entries );
+        sprintf( dsdh_entries[key_index]->value, "%+011d", 
+                 self->ds_info[dsd]->dsr_size );
+
+        if( S_NameValueList_Rewrite( self->fp, dsdh_count, dsdh_entries )
+            == FAILURE )
+            return FAILURE;
+
+        S_NameValueList_Destroy( &dsdh_count, &dsdh_entries );
+    }
+
+    self->header_dirty = 0;
+
+    return SUCCESS;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_Close
+
+Purpose:
+    Close an ENVISAT formatted file, releasing all associated resources.
+
+Description:
+
+Inputs:
+    self -- handle for file to close.
+
+Outputs:
+
+Returns:
+
+
+-----------------------------------------------------------------------------*/
+
+void EnvisatFile_Close( EnvisatFile *self )
+
+{
+    int		i;
+
+    /*
+     * Do we need to write out the header information?
+     */
+    if( self->header_dirty )
+        EnvisatFile_RewriteHeader( self );
+
+    /*
+     * Close file. 
+     */
+    if( self->fp != NULL )
+        fclose( self->fp );
+
+    /*
+     * Clean up data structures. 
+     */
+    S_NameValueList_Destroy( &(self->mph_count), &(self->mph_entries) );
+    S_NameValueList_Destroy( &(self->sph_count), &(self->sph_entries) );
+
+    for( i = 0; i < self->ds_count; i++ )
+    {
+        if( self->ds_info != NULL && self->ds_info[i] != NULL )
+        {
+            free( self->ds_info[i]->ds_name );
+            free( self->ds_info[i]->ds_type );
+            free( self->ds_info[i]->filename );
+            free( self->ds_info[i] );
+        }
+    }
+    if( self->ds_info != NULL )
+        free( self->ds_info );
+    if( self->filename != NULL )
+        free( self->filename );
+
+    free( self );
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_GetFilename
+
+Purpose:
+    Fetch name of this Envisat file.
+
+Description:
+
+Inputs:
+    self -- handle for file to get name of.
+
+Outputs:
+
+Returns:
+    const pointer to internal copy of the filename.  Do not alter or free.
+
+
+-----------------------------------------------------------------------------*/
+
+const char *EnvisatFile_GetFilename( EnvisatFile *self )
+
+{
+    return self->filename;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_GetKeyByIndex()
+
+Purpose:
+    Fetch the key with the indicated index.
+
+Description:
+    This function can be used to "discover" the set of available keys by
+    by scanning with index values starting at zero and ending when a NULL
+    is returned.
+
+Inputs:
+    self -- the file to be searched.
+    mph_or_sph -- Either MPH or SPH depending on the header to be searched.
+    key_index -- key index, from zero to number of keys-1.
+
+Outputs:
+
+Returns:
+    pointer to key name or NULL on failure.
+
+-----------------------------------------------------------------------------*/
+
+const char *EnvisatFile_GetKeyByIndex( EnvisatFile *self, 
+                                       EnvisatFile_HeaderFlag mph_or_sph,
+                                       int key_index )
+
+{
+    int	entry_count;
+    EnvisatNameValue **entries;
+
+    /*
+     * Select source list. 
+     */
+    if( mph_or_sph == MPH )
+    {
+        entry_count = self->mph_count;
+        entries = self->mph_entries;
+    }
+    else
+    {
+        entry_count = self->sph_count;
+        entries = self->sph_entries;
+    }
+
+    if( key_index < 0 || key_index >= entry_count )
+        return NULL;
+    else
+        return entries[key_index]->key;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_GetKeyValueAsString()
+
+Purpose:
+    Fetch the value associated with the indicated key as a string.
+
+Description:
+
+Inputs:
+    self -- the file to be searched.
+    mph_or_sph -- Either MPH or SPH depending on the header to be searched.
+    key -- the key (name) to be searched for.
+    default_value -- the value to return if the key is not found.
+
+Outputs:
+
+Returns:
+    pointer to value string, or default_value if not found.
+
+-----------------------------------------------------------------------------*/
+
+const char *EnvisatFile_GetKeyValueAsString( EnvisatFile *self, 
+                                             EnvisatFile_HeaderFlag mph_or_sph,
+                                             const char *key,
+                                             const char *default_value )
+
+{
+    int	entry_count, key_index;
+    EnvisatNameValue **entries;
+
+    /*
+     * Select source list. 
+     */
+    if( mph_or_sph == MPH )
+    {
+        entry_count = self->mph_count;
+        entries = self->mph_entries;
+    }
+    else
+    {
+        entry_count = self->sph_count;
+        entries = self->sph_entries;
+    }
+
+    /*
+     * Find and return the value.
+     */
+    key_index = S_NameValueList_FindKey( key, entry_count, entries );
+    if( key_index == -1 )
+        return default_value;
+    else
+        return entries[key_index]->value;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_SetKeyValueAsString()
+
+Purpose:
+    Set the value associated with the indicated key as a string.
+
+Description:
+
+Inputs:
+    self -- the file to be searched.
+    mph_or_sph -- Either MPH or SPH depending on the header to be searched.
+    key -- the key (name) to be searched for.
+    value -- the value to assign.
+
+Outputs:
+
+Returns:
+    SUCCESS or FAILURE.
+
+-----------------------------------------------------------------------------*/
+
+int EnvisatFile_SetKeyValueAsString( EnvisatFile *self, 
+                                     EnvisatFile_HeaderFlag mph_or_sph,
+                                     const char *key,
+                                     const char *value )
+
+{
+    int	entry_count, key_index;
+    EnvisatNameValue **entries;
+
+    if( !self->updatable )
+    {
+        SendError( "File not opened for update access." );
+        return FAILURE;
+    }
+
+    /*
+     * Select source list. 
+     */
+    if( mph_or_sph == MPH )
+    {
+        entry_count = self->mph_count;
+        entries = self->mph_entries;
+    }
+    else
+    {
+        entry_count = self->sph_count;
+        entries = self->sph_entries;
+    }
+
+    /*
+     * Find and return the value.
+     */
+    key_index = S_NameValueList_FindKey( key, entry_count, entries );
+    if( key_index == -1 )
+    {
+        char	error_buf[2048];
+
+        sprintf( error_buf, 
+                 "Unable to set header field \"%s\", field not found.", 
+                 key );
+
+        SendError( error_buf );
+        return FAILURE;
+    }
+
+    self->header_dirty = 1;
+    if( strlen(value) > strlen(entries[key_index]->value) )
+    {
+        strncpy( entries[key_index]->value, value, 
+                 strlen(entries[key_index]->value) );
+    }
+    else
+    {
+        memset( entries[key_index]->value, ' ', 
+                strlen(entries[key_index]->value) );
+        strncpy( entries[key_index]->value, value, strlen(value) );
+    }
+
+    return SUCCESS;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_GetKeyValueAsInt()
+
+Purpose:
+    Fetch the value associated with the indicated key as an integer.
+
+Description:
+
+Inputs:
+    self -- the file to be searched.
+    mph_or_sph -- Either MPH or SPH depending on the header to be searched.
+    key -- the key (name) to be searched for.
+    default_value -- the value to return if the key is not found.
+
+Outputs:
+
+Returns:
+    key value, or default_value if key not found.
+
+-----------------------------------------------------------------------------*/
+
+int EnvisatFile_GetKeyValueAsInt( EnvisatFile *self, 
+                                  EnvisatFile_HeaderFlag mph_or_sph,
+                                  const char *key,
+                                  int default_value )
+
+{
+    int	entry_count, key_index;
+    EnvisatNameValue **entries;
+
+    /*
+     * Select source list. 
+     */
+    if( mph_or_sph == MPH )
+    {
+        entry_count = self->mph_count;
+        entries = self->mph_entries;
+    }
+    else
+    {
+        entry_count = self->sph_count;
+        entries = self->sph_entries;
+    }
+
+    /*
+     * Find and return the value.
+     */
+    key_index = S_NameValueList_FindKey( key, entry_count, entries );
+    if( key_index == -1 )
+        return default_value;
+    else
+        return atoi(entries[key_index]->value);
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_SetKeyValueAsInt()
+
+Purpose:
+    Set the value associated with the indicated key as an integer.
+
+Description:
+
+Inputs:
+    self -- the file to be searched.
+    mph_or_sph -- Either MPH or SPH depending on the header to be searched.
+    key -- the key (name) to be searched for.
+    value -- the value to assign.
+
+Outputs:
+
+Returns:
+    SUCCESS or FAILURE.
+
+-----------------------------------------------------------------------------*/
+
+int EnvisatFile_SetKeyValueAsInt( EnvisatFile *self, 
+                                  EnvisatFile_HeaderFlag mph_or_sph,
+                                  const char *key,
+                                  int value )
+
+{
+    char format[32], string_value[128];
+    const char *prototype_value;
+
+
+    prototype_value = EnvisatFile_GetKeyValueAsString( self, mph_or_sph, key, NULL);
+    if( prototype_value == NULL )
+    {
+        char	error_buf[2048];
+
+        sprintf( error_buf, 
+                 "Unable to set header field \"%s\", field not found.", 
+                 key );
+
+        SendError( error_buf );
+        return FAILURE;
+    }
+
+    sprintf( format, "%%+0%dd", strlen(prototype_value) );
+    sprintf( string_value, format, value );
+
+    return EnvisatFile_SetKeyValueAsString( self, mph_or_sph, key, string_value );
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_GetKeyValueAsDouble()
+
+Purpose:
+    Fetch the value associated with the indicated key as a double.
+
+Description:
+
+Inputs:
+    self -- the file to be searched.
+    mph_or_sph -- Either MPH or SPH depending on the header to be searched.
+    key -- the key (name) to be searched for.
+    default_value -- the value to return if the key is not found.
+
+Outputs:
+
+Returns:
+    key value, or default_value if key not found.
+
+-----------------------------------------------------------------------------*/
+
+double EnvisatFile_GetKeyValueAsDouble( EnvisatFile *self, 
+                                        EnvisatFile_HeaderFlag mph_or_sph,
+                                        const char *key,
+                                        double default_value )
+
+{
+    int	entry_count, key_index;
+    EnvisatNameValue **entries;
+
+    /*
+     * Select source list. 
+     */
+    if( mph_or_sph == MPH )
+    {
+        entry_count = self->mph_count;
+        entries = self->mph_entries;
+    }
+    else
+    {
+        entry_count = self->sph_count;
+        entries = self->sph_entries;
+    }
+
+    /*
+     * Find and return the value.
+     */
+    key_index = S_NameValueList_FindKey( key, entry_count, entries );
+    if( key_index == -1 )
+        return default_value;
+    else
+        return atof(entries[key_index]->value);
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_SetKeyValueAsDouble()
+
+Purpose:
+    Set the value associated with the indicated key as a double.
+
+Description:
+    Note that this function attempts to format the new value similarly to
+    the previous value.  In some cases (expecially exponential values) this 
+    may not work out well.  In case of problems the caller is encourage to
+    format the value themselves, and use the EnvisatFile_SetKeyValueAsString
+    function, but taking extreme care about the string length.
+
+Inputs:
+    self -- the file to be searched.
+    mph_or_sph -- Either MPH or SPH depending on the header to be searched.
+    key -- the key (name) to be searched for.
+    value -- the value to assign.
+
+Outputs:
+
+Returns:
+    SUCCESS or FAILURE.
+
+-----------------------------------------------------------------------------*/
+
+int EnvisatFile_SetKeyValueAsDouble( EnvisatFile *self, 
+                                     EnvisatFile_HeaderFlag mph_or_sph,
+                                     const char *key,
+                                     double value )
+
+{
+    char format[32], string_value[128];
+    const char *prototype_value;
+    int		length;
+
+    prototype_value = EnvisatFile_GetKeyValueAsString( self, mph_or_sph, key, NULL);
+    if( prototype_value == NULL )
+    {
+        char	error_buf[2048];
+
+        sprintf( error_buf, 
+                 "Unable to set header field \"%s\", field not found.", 
+                 key );
+
+        SendError( error_buf );
+        return FAILURE;
+    }
+
+    length = strlen(prototype_value);
+    if( prototype_value[length-4] == 'E' )
+    {
+        sprintf( format, "%%+%dE", length-4 );
+        sprintf( string_value, format, value );
+    }
+    else
+    {
+        int	decimals = 0, i;
+        for( i = length-1; i > 0; i-- )
+        {
+            if( prototype_value[i] == '.' )
+                break;
+            
+            decimals++;
+        }
+
+        sprintf( format, "%%+0%d.%df", length, decimals );
+        sprintf( string_value, format, value );
+
+        if( (int)strlen(string_value) > length )
+            string_value[length] = '\0';
+    }
+
+    return EnvisatFile_SetKeyValueAsString( self, mph_or_sph, key, string_value );
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_GetDatasetIndex()
+
+Purpose:
+    Fetch the datasat index give a dataset name.
+
+Description:
+    The provided name is extended with spaces, so it isn't necessary for the
+    application to pass all the passing spaces.
+
+Inputs:
+    self -- the file to be searched.
+    ds_name -- the name (DS_NAME) of the dataset to find.
+
+Outputs:
+
+Returns:
+    Dataset index that matches, or -1 if none found.
+
+-----------------------------------------------------------------------------*/
+
+int EnvisatFile_GetDatasetIndex( EnvisatFile *self, const char *ds_name )
+
+{
+    int		i;
+    char	padded_ds_name[100];
+
+    /* 
+     * Padd the name.  While the normal product spec says the DS_NAME will
+     * be 28 characters, I try to pad more than this incase the specification
+     * is changed. 
+     */
+    strcpy( padded_ds_name, ds_name );
+    for( i = strlen(padded_ds_name); i < sizeof(padded_ds_name)-1; i++ )
+    {
+        padded_ds_name[i] = ' ';
+    }
+    padded_ds_name[i] = '\0';
+
+    /* 
+     * Compare only for the full length of DS_NAME we have saved.
+     */
+    for( i = 0; i < self->ds_count; i++ )
+    {
+        if( strncmp( padded_ds_name, self->ds_info[i]->ds_name, 
+                     strlen(self->ds_info[i]->ds_name) ) == 0 )
+        {
+            return i;
+        }
+    }
+
+    return -1;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_GetDatasetInfo()
+
+Purpose:
+    Fetch the information associated with a dataset definition.
+
+Description:
+    The returned strings are pointers to internal copies, and should not be
+    modified, or freed.  Note, any of the "output" parameters can safely be
+    NULL if it is not needed.
+
+Inputs:
+    self -- the file to be searched.
+    ds_index -- the dataset index to fetch
+
+Outputs:
+    ds_name -- the dataset symbolic name, ie 'MDS1 SQ ADS              '.
+    ds_type -- the dataset type, ie. 'A', not sure of valid values.
+    filename -- dataset filename, normally spaces, or 'NOT USED          '.
+    ds_offset -- the byte offset in the whole file to the first byte of 
+                 dataset data.  This is 0 for unused datasets.
+    ds_size -- the size, in bytes, of the whole dataset.
+    num_dsr -- the number of records in the dataset.
+    dsr_size -- the size of one record in the dataset in bytes, -1 if
+                records are variable sized.
+
+Returns:
+    SUCCESS if dataset exists, or FAILURE if ds_index is out of range.
+
+-----------------------------------------------------------------------------*/
+
+int EnvisatFile_GetDatasetInfo( EnvisatFile *self, 
+                                int ds_index, 
+                                char **ds_name, 
+                                char **ds_type,
+                                char **filename,
+                                int  *ds_offset,
+                                int  *ds_size,
+                                int  *num_dsr,
+                                int  *dsr_size )
+
+{
+    if( ds_index < 0 || ds_index >= self->ds_count )
+        return FAILURE;
+
+    if( ds_name != NULL )
+        *ds_name = self->ds_info[ds_index]->ds_name;
+    if( ds_type != NULL )
+        *ds_type = self->ds_info[ds_index]->ds_type;
+    if( filename != NULL )
+        *filename = self->ds_info[ds_index]->filename;
+    if( ds_offset != NULL )
+        *ds_offset = self->ds_info[ds_index]->ds_offset;
+    if( ds_size != NULL )
+        *ds_size = self->ds_info[ds_index]->ds_size;
+    if( num_dsr != NULL )
+        *num_dsr = self->ds_info[ds_index]->num_dsr;
+    if( dsr_size != NULL )
+        *dsr_size = self->ds_info[ds_index]->dsr_size;
+
+    return SUCCESS;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_SetDatasetInfo()
+
+Purpose:
+    Update the information associated with a dataset definition.
+
+Description:
+
+Inputs:
+    self -- the file to be searched.
+    ds_index -- the dataset index to fetch
+    ds_offset -- the byte offset in the whole file to the first byte of 
+                 dataset data.  This is 0 for unused datasets.
+    ds_size -- the size, in bytes, of the whole dataset.
+    num_dsr -- the number of records in the dataset.
+    dsr_size -- the size of one record in the dataset in bytes, -1 if
+                records are variable sized.
+
+Outputs:
+
+Returns:
+    SUCCESS or FAILURE.
+
+-----------------------------------------------------------------------------*/
+
+int EnvisatFile_SetDatasetInfo( EnvisatFile *self, 
+                                int ds_index, 
+                                int ds_offset,
+                                int ds_size,
+                                int num_dsr,
+                                int dsr_size )
+
+{
+    if( ds_index < 0 || ds_index >= self->ds_count )
+        return FAILURE;
+
+    self->ds_info[ds_index]->ds_offset = ds_offset;
+    self->ds_info[ds_index]->ds_size = ds_size;
+    self->ds_info[ds_index]->num_dsr = num_dsr;
+    self->ds_info[ds_index]->dsr_size = dsr_size;
+    self->header_dirty = 1;
+
+    return SUCCESS;
+}
+
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_ReadDatasetChunk()
+
+Purpose:
+    Read an arbitrary chunk of a dataset.
+
+Description:
+    Note that no range checking is made on offset and size, and data may be
+    read from outside the dataset if they are inappropriate.
+
+Inputs:
+    self -- the file to be searched.
+    ds_index -- the index of dataset to access.
+    offset -- byte offset within database to read.
+    size -- size of buffer to fill in bytes.
+    buffer -- buffer to load data into
+
+Outputs:
+    buffer is updated on SUCCESS.
+
+Returns:
+    SUCCESS or FAILURE
+
+-----------------------------------------------------------------------------*/
+
+int EnvisatFile_ReadDatasetChunk( EnvisatFile *self, 
+                                  int ds_index,
+                                  int offset,
+                                  int size,
+                                  void * buffer )
+
+{
+    if( ds_index < 0 || ds_index >= self->ds_count )
+    {
+        SendError( "Attempt to read non-existant dataset in "
+                   "EnvisatFile_ReadDatasetChunk()" );
+        return FAILURE;
+    }
+
+    if( offset < 0 
+        || offset + size > self->ds_info[ds_index]->ds_size )
+    {
+        SendError( "Attempt to read beyond end of dataset in "
+                   "EnvisatFile_ReadDatasetChunk()" );
+        return FAILURE;
+    }
+
+    if( fseek( self->fp, self->ds_info[ds_index]->ds_offset+offset, SEEK_SET )
+        != 0 )
+    {
+        SendError( "seek failed in EnvisatFile_ReadChunk()" );
+        return FAILURE;
+    }
+
+    if( (int) fread( buffer, 1, size, self->fp ) != size )
+    {
+        SendError( "read failed in EnvisatFile_ReadChunk()" );
+        return FAILURE;
+    }
+
+    return SUCCESS;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_WriteDatasetRecord()
+
+Purpose:
+    Write an arbitrary dataset record.
+
+Description:
+    Note that no range checking is made on offset and size, and data may be
+    read from outside the dataset if they are inappropriate.
+
+Inputs:
+    self -- the file to be searched.
+    ds_index -- the index of dataset to access.
+    record_index -- the record to write.
+    record_buffer -- buffer to load data into
+
+Outputs:
+
+Returns:
+    SUCCESS or FAILURE
+
+-----------------------------------------------------------------------------*/
+
+int EnvisatFile_WriteDatasetRecord( EnvisatFile *self, 
+                                    int ds_index,
+                                    int record_index,
+                                    void *buffer )
+
+{
+    int		absolute_offset;
+    int         result;
+
+    if( ds_index < 0 || ds_index >= self->ds_count )
+    {
+        SendError( "Attempt to write non-existant dataset in "
+                   "EnvisatFile_WriteDatasetRecord()" );
+        return FAILURE;
+    }
+
+    if( record_index < 0
+        || record_index >=  self->ds_info[ds_index]->num_dsr )
+    {
+        SendError( "Attempt to write beyond end of dataset in "
+                   "EnvisatFile_WriteDatasetRecord()" );
+        return FAILURE;
+    }
+
+    absolute_offset = self->ds_info[ds_index]->ds_offset
+        + record_index * self->ds_info[ds_index]->dsr_size;
+
+    if( fseek( self->fp, absolute_offset, SEEK_SET ) != 0 )
+    {
+        SendError( "seek failed in EnvisatFile_WriteDatasetRecord()" );
+        return FAILURE;
+    }
+
+    result = fwrite( buffer, 1, self->ds_info[ds_index]->dsr_size, self->fp );
+    if( result != self->ds_info[ds_index]->dsr_size )
+    {
+        SendError( "write failed in EnvisatFile_WriteDatasetRecord()" );
+        return FAILURE;
+    }
+
+    return SUCCESS;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    EnvisatFile_ReadDatasetRecord()
+
+Purpose:
+    Read an arbitrary dataset record.
+
+Description:
+    Note that no range checking is made on offset and size, and data may be
+    read from outside the dataset if they are inappropriate.
+
+Inputs:
+    self -- the file to be searched.
+    ds_index -- the index of dataset to access.
+    record_index -- the record to write.
+    record_buffer -- buffer to load data into
+
+Outputs:
+
+Returns:
+    SUCCESS or FAILURE
+
+-----------------------------------------------------------------------------*/
+
+int EnvisatFile_ReadDatasetRecord( EnvisatFile *self, 
+                                    int ds_index,
+                                    int record_index,
+                                    void *buffer )
+
+{
+    int		absolute_offset;
+    int         result;
+
+    if( ds_index < 0 || ds_index >= self->ds_count )
+    {
+        SendError( "Attempt to write non-existant dataset in "
+                   "EnvisatFile_WriteDatasetRecord()" );
+        return FAILURE;
+    }
+
+    if( record_index < 0
+        || record_index >=  self->ds_info[ds_index]->num_dsr )
+    {
+        SendError( "Attempt to write beyond end of dataset in "
+                   "EnvisatFile_WriteDatasetRecord()" );
+        return FAILURE;
+    }
+
+    absolute_offset = self->ds_info[ds_index]->ds_offset
+        + record_index * self->ds_info[ds_index]->dsr_size;
+
+    if( fseek( self->fp, absolute_offset, SEEK_SET ) != 0 )
+    {
+        SendError( "seek failed in EnvisatFile_WriteDatasetRecord()" );
+        return FAILURE;
+    }
+
+    result = fread( buffer, 1, self->ds_info[ds_index]->dsr_size, self->fp );
+    if( result != self->ds_info[ds_index]->dsr_size )
+    {
+        SendError( "read failed in EnvisatFile_ReadDatasetRecord()" );
+        return FAILURE;
+    }
+
+    return SUCCESS;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    S_NameValueList_FindKey()
+
+Purpose:
+    Search for given key in list of name/value pairs. 
+
+Description:
+    Scans list looking for index of EnvisatNameValue where the key matches
+    (case sensitive) the passed in name. 
+
+Inputs:
+    key -- the key, such as "SLICE_POSITION" being searched for. 
+    entry_count -- the number of items in the entries array.
+    entries -- array of name/value structures to search. 
+
+Outputs:
+
+Returns:
+    array index into entries, or -1 on failure. 
+
+-----------------------------------------------------------------------------*/
+
+int S_NameValueList_FindKey( const char *key, 
+                             int entry_count, 
+                             EnvisatNameValue **entries )
+
+{
+    int		i;
+
+    for( i = 0; i < entry_count; i++ )
+    {
+        if( strcmp(entries[i]->key,key) == 0 )
+            return i;
+    }
+    
+    return -1;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    S_NameValueList_FindValue()
+
+Purpose:
+    Search for given key in list of name/value pairs, and return value.
+
+Description:
+    Returns value string or default if key not found.
+
+Inputs:
+    key -- the key, such as "SLICE_POSITION" being searched for. 
+    entry_count -- the number of items in the entries array.
+    entries -- array of name/value structures to search. 
+    default_value -- value to use if key not found.
+
+Outputs:
+
+Returns:
+    value string, or default if key not found.
+
+-----------------------------------------------------------------------------*/
+
+const char *S_NameValueList_FindValue( const char *key, 
+                                       int entry_count, 
+                                       EnvisatNameValue **entries,
+                                       const char *default_value )
+
+{
+    int		i;
+
+    i = S_NameValueList_FindKey( key, entry_count, entries );
+    if( i == -1 )
+        return default_value;
+    else
+        return entries[i]->value;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    S_NameValueList_Parse()
+
+Purpose:
+    Parse a block of envisat style name/value pairs into an
+    EnvisatNameValue structure list. 
+
+Description:
+    The passed in text block should be zero terminated.  The entry_count, 
+    and entries should be pre-initialized (normally to 0 and NULL).
+
+Inputs:
+    text -- the block of text, multiple lines, to be processed.
+
+Outputs:
+    entry_count -- returns with the updated number of entries in the
+                   entries array.
+    entries -- returns with updated array info structures. 
+
+Returns:
+    SUCCESS or FAILURE
+
+-----------------------------------------------------------------------------*/
+
+int S_NameValueList_Parse( const char *text, int text_offset, 
+                           int *entry_count, 
+                           EnvisatNameValue ***entries )
+
+{
+    const char  *next_text = text;
+
+    /*
+     * Loop over each input line in the text block.
+     */
+    while( *next_text != '\0' )
+    {
+        char	line[1024];
+        int     line_len = 0, equal_index, src_char, line_offset;
+        EnvisatNameValue *entry;
+
+        /*
+         * Extract one line of text into the "line" buffer, and remove the
+         * newline character.  Eat leading spaces.
+         */
+        while( *next_text == ' ' ) 
+        {
+            next_text++;
+        }
+        line_offset = (int) (next_text - text) + text_offset;
+        while( *next_text != '\0' && *next_text != '\n' )
+        {
+            if( line_len > sizeof(line)-1 )
+            {
+                SendError( "S_NameValueList_Parse(): "
+                           "Corrupt line, longer than 1024 characters." );
+                return FAILURE;
+            }
+
+            line[line_len++] = *(next_text++);
+        }
+
+        line[line_len] = '\0';
+        if( *next_text == '\n' )
+            next_text++;
+
+        /*
+         * Blank lines are permitted.  We will skip processing of any line
+         * that doesn't have an equal sign, under the assumption it is
+         * white space.
+         */
+        if( strstr( line, "=") == NULL )
+            continue;
+
+        /*
+         * Create the name/value info structure. 
+         */
+        entry = (EnvisatNameValue *) calloc(sizeof(EnvisatNameValue),1);
+        entry->literal_line = strdup(line);
+
+        /*
+         * Capture the key.  We take everything up to the equal sign.  There
+         * shouldn't be any white space, but if so, we take it as part of the
+         * key.
+         */
+        equal_index = strstr(line, "=") - line;
+        entry->key = (char *) malloc(equal_index+1);
+        strncpy( entry->key, line, equal_index );
+        entry->key[equal_index] = '\0';
+        entry->value_offset = line_offset + equal_index + 1;
+
+        /*
+         * If the next character after the equal sign is a double quote, then
+         * the value is a string.  Suck out the text between the double quotes.
+         */
+        if( line[equal_index+1] == '"' )
+        {
+            for( src_char = equal_index + 2;
+                 line[src_char] != '\0' && line[src_char] != '"';
+                 src_char++ ) {}
+
+            line[src_char] = '\0';
+            entry->value = strdup( line + equal_index + 2 );
+            entry->value_offset += 1;
+        }
+
+        /*
+         * The value is numeric, and may include a units field.
+         */
+        else
+        {
+            for( src_char = equal_index + 1; 
+                 line[src_char] != '\0' && line[src_char] != '<' 
+                     && line[src_char] != ' ';
+                 src_char++ ) {}
+
+            /* capture units */
+            if( line[src_char] == '<' )
+            {
+                int dst_char;
+
+                for( dst_char = src_char+1; 
+                     line[dst_char] != '>' && line[dst_char] != '\0';
+                     dst_char++ ) {}
+
+                line[dst_char] = '\0';
+                entry->units = strdup( line + src_char + 1 );
+            }
+
+            line[src_char] = '\0';
+            entry->value = strdup( line + equal_index + 1 );
+        }
+
+        /*
+         * Add the entry to the name/value list. 
+         */
+        if( entries == NULL )
+        {
+            *entry_count = 1;
+            *entries = (EnvisatNameValue **) 
+                calloc( 1, sizeof(EnvisatNameValue) );
+        }
+        else
+        {
+            (*entry_count)++;
+            *entries = (EnvisatNameValue **) 
+                realloc( *entries, *entry_count * sizeof(EnvisatNameValue*) );
+        }
+
+        if( *entries == NULL )
+        {
+            *entry_count = 0;
+            return FAILURE;
+        }
+
+        (*entries)[*entry_count-1] = entry;
+    }
+    
+    return SUCCESS;
+}
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    S_NameValueList_Rewrite()
+
+Purpose:
+    Rewrite the values of a name/value list in the file.
+
+Description:
+
+Inputs:
+    fp -- the FILE to operate on.
+    entry_count -- number of entries to write.
+    entries -- array of entry descriptions.
+
+Returns:
+    SUCCESS or FAILURE
+
+
+-----------------------------------------------------------------------------*/
+
+int S_NameValueList_Rewrite( FILE * fp, int entry_count, 
+                              EnvisatNameValue **entries )	      
+
+{
+    int		i;
+
+    for( i = 0; i < entry_count; i++ )
+    {
+        EnvisatNameValue	*entry = entries[i];
+
+        if( fseek( fp, entry->value_offset, SEEK_SET ) != 0 )
+        {
+            SendError( "fseek() failed writing name/value list." );
+            return FAILURE;
+        }
+
+        if( fwrite( entry->value, 1, strlen(entry->value), fp ) != 
+            strlen(entry->value) )
+        {
+            SendError( "fwrite() failed writing name/value list." );
+            return FAILURE;
+        }
+    }
+
+    return SUCCESS;
+}
+
+
+/*-----------------------------------------------------------------------------
+
+Name:
+    S_NameValueList_Destroy()
+
+Purpose:
+    Free resources associated with a name/value list.
+
+Description:
+    The count, and name/value list pointers are set to 0/NULL on completion.
+
+Inputs:
+    entry_count -- returns with the updated number of entries in the
+                   entries array.
+    entries -- returns with updated array info structures. 
+
+Outputs:
+    entry_count -- Set to zero.
+    entries -- Sett o NULL.
+
+Returns:
+
+
+-----------------------------------------------------------------------------*/
+
+void S_NameValueList_Destroy( int *entry_count, 
+                              EnvisatNameValue ***entries )	      
+
+{
+    int		i;
+
+    for( i = 0; i < *entry_count; i++ )
+    {
+        free( (*entries)[i]->key );
+        free( (*entries)[i]->value );
+        free( (*entries)[i]->units );
+        free( (*entries)[i]->literal_line );
+        free( (*entries)[i] );
+    }
+
+    free( *entries );
+    
+    *entry_count = 0;
+    *entries = NULL;
+}
+
+/* EOF */
+
diff --git a/Utilities/GDAL/frmts/envisat/EnvisatFile.h b/Utilities/GDAL/frmts/envisat/EnvisatFile.h
new file mode 100644
index 0000000000..8180413398
--- /dev/null
+++ b/Utilities/GDAL/frmts/envisat/EnvisatFile.h
@@ -0,0 +1,137 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  APP ENVISAT Support
+ * Purpose:  Low Level Envisat file access (read/write) API.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Atlantis Scientific, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: EnvisatFile.h,v $
+ * Revision 1.1  2001/02/24 14:19:40  warmerda
+ * New
+ *
+ */
+
+#ifndef __ENVISAT_FILE_H__
+#define __ENVISAT_FILE_H__
+
+typedef struct EnvisatFile_tag EnvisatFile;
+
+typedef enum 
+{
+    MPH = 0,
+    SPH = 1
+} EnvisatFile_HeaderFlag;
+
+int EnvisatFile_Open( EnvisatFile **self, const char *filename, 
+                      const char *mode );
+void EnvisatFile_Close( EnvisatFile *self );
+const char *EnvisatFile_GetFilename( EnvisatFile *self );
+int EnvisatFile_Create( EnvisatFile **self, const char *filename, 
+                        const char *template_file );
+int EnvisatFile_GetCurrentLength( EnvisatFile *self );
+
+const char* EnvisatFile_GetKeyByIndex(  EnvisatFile *self, 
+                                        EnvisatFile_HeaderFlag mph_or_sph,
+                                        int key_index );
+
+int EnvisatFile_TestKey(  EnvisatFile *self, 
+                          EnvisatFile_HeaderFlag mph_or_sph,
+                          const char *key );
+
+const char *EnvisatFile_GetKeyValueAsString( EnvisatFile *self, 
+                                             EnvisatFile_HeaderFlag mph_or_sph,
+                                             const char *key,
+                                             const char *default_value );
+
+int EnvisatFile_SetKeyValueAsString( EnvisatFile *self, 
+                                     EnvisatFile_HeaderFlag mph_or_sph,
+                                     const char *key,
+                                     const char *value );
+
+int EnvisatFile_GetKeyValueAsInt( EnvisatFile *self, 
+                                  EnvisatFile_HeaderFlag mph_or_sph,
+                                  const char *key, 
+                                  int default_value );
+
+int EnvisatFile_SetKeyValueAsInt( EnvisatFile *self, 
+                                  EnvisatFile_HeaderFlag mph_or_sph,
+                                  const char *key, 
+                                  int value );
+
+double EnvisatFile_GetKeyValueAsDouble( EnvisatFile *self, 
+                                        EnvisatFile_HeaderFlag mph_or_sph,
+                                        const char *key, 
+                                        double default_value );
+int EnvisatFile_SetKeyValueAsDouble( EnvisatFile *self, 
+                                     EnvisatFile_HeaderFlag mph_or_sph,
+                                     const char *key, 
+                                     double value );
+
+int EnvisatFile_GetDatasetIndex( EnvisatFile *self, const char *ds_name );
+
+int EnvisatFile_GetDatasetInfo( EnvisatFile *self, 
+                                int ds_index,
+                                char **ds_name, 
+                                char **ds_type,
+                                char **filename,
+                                int  *ds_offset,
+                                int  *ds_size,
+                                int  *num_dsr,
+                                int  *dsr_size );
+int EnvisatFile_SetDatasetInfo( EnvisatFile *self, 
+                                int ds_index,
+                                int ds_offset,
+                                int ds_size,
+                                int num_dsr,
+                                int dsr_size );
+                              
+int EnvisatFile_ReadDatasetRecord( EnvisatFile *self, 
+                                   int ds_index,
+                                   int record_index, 
+                                   void *record_buffer );
+int EnvisatFile_WriteDatasetRecord( EnvisatFile *self, 
+                                    int ds_index,
+                                    int record_index, 
+                                    void *record_buffer );
+int EnvisatFile_ReadDatasetChunk( EnvisatFile *self, 
+                                  int ds_index,
+                                  int offset, 
+                                  int size,
+                                  void *buffer );
+                                
+
+#ifndef FAILURE
+#  define FAILURE 1
+#endif
+#ifndef SUCCESS 
+#  define SUCCESS 0
+#endif
+
+#endif /* __ENVISAT_FILE_H__ */
+
+/* EOF */
+
+
+
diff --git a/Utilities/GDAL/frmts/envisat/GNUmakefile b/Utilities/GDAL/frmts/envisat/GNUmakefile
new file mode 100644
index 0000000000..f7cb068299
--- /dev/null
+++ b/Utilities/GDAL/frmts/envisat/GNUmakefile
@@ -0,0 +1,24 @@
+
+OBJ	=	EnvisatFile.o envisatdataset.o
+
+include ../../GDALmake.opt
+
+XTRA_OPT =	-I../raw
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(XTRA_OPT) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o
+
+all:	$(OBJ)
+
+install-obj:	$(O_OBJ)
+
+envisat_dump:	envisat_dump.o EnvisatFile.o
+	$(CC) $(CFLAGS) envisat_dump.o EnvisatFile.o $(GDAL_LIB) -ldl -lm -o envisat_dump
+
+dumpgeo:	dumpgeo.o EnvisatFile.o
+	$(CC) $(CFLAGS) dumpgeo.o EnvisatFile.o $(GDAL_LIB) -ldl -lm -o dumpgeo
+
diff --git a/Utilities/GDAL/frmts/envisat/dumpgeo.c b/Utilities/GDAL/frmts/envisat/dumpgeo.c
new file mode 100644
index 0000000000..ee1ca9a49a
--- /dev/null
+++ b/Utilities/GDAL/frmts/envisat/dumpgeo.c
@@ -0,0 +1,171 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  APP ENVISAT Support
+ * Purpose:  Test mainline for dumping ENVISAT format files.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Atlantis Scientific, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#include "cpl_conv.h"
+#include "EnvisatFile.h"
+
+int main( int argc, char ** argv )
+
+{
+    EnvisatFile	*es_file;
+    int	        ds_index;
+    int	ds_offset, ds_size, num_dsr, dsr_size, i_record;
+
+    if( argc != 2 )
+    {
+        printf( "Usage: dumpgeo filename\n" );
+        exit( 1 );
+    }
+
+    
+    if( EnvisatFile_Open( &es_file, argv[1], "r" ) != 0 )
+    {
+        printf( "EnvisatFile_Open(%s) failed.\n", argv[1] );
+        exit( 2 );
+    }
+
+    ds_index = EnvisatFile_GetDatasetIndex( es_file, "GEOLOCATION GRID ADS" );
+    if( ds_index == -1 )
+    {
+        printf( "Can't find geolocation grid ads.\n" );
+        exit( 3 );
+    }        
+
+    EnvisatFile_GetDatasetInfo( es_file, 
+                                ds_index, NULL, NULL, NULL,
+                                &ds_offset, &ds_size,
+                                &num_dsr, &dsr_size );
+    if( ds_offset == 0 )
+    {
+        printf( "No data for geolocation grid ads.\n" );
+        exit( 4 );
+    }
+
+    CPLAssert( dsr_size == 521 );
+
+    for( i_record = 0; i_record < num_dsr; i_record++ )
+    {
+        GByte	abyRecord[521];
+        GUInt32	unValue;
+        float   fValue;
+        int	sample;
+
+        EnvisatFile_ReadDatasetRecord( es_file, ds_index, i_record, 
+                                       abyRecord );
+
+        printf( "<====================== Record %d ==================>\n", 
+                i_record );
+
+        /* field 1 */
+        CPL_SWAP32PTR( abyRecord + 0 );
+        CPL_SWAP32PTR( abyRecord + 4 );
+        CPL_SWAP32PTR( abyRecord + 8 );
+
+        printf( "start line: mjd_days = %d, sec = %d, msec = %d\n", 
+                ((int *) abyRecord)[0],
+                ((unsigned int *) abyRecord)[1],
+                ((unsigned int *) abyRecord)[2] );
+
+        /* field 2 */
+        printf( "Attachment flag = %d\n", abyRecord[12] );
+
+        /* field 3 */
+        memcpy( &unValue, abyRecord + 13, 4 );
+        printf( "range line (first in granule) = %d\n", 
+                CPL_SWAP32( unValue ) );
+
+        /* field 4 */
+        memcpy( &unValue, abyRecord + 17, 4 );
+        printf( "lines in granule = %d\n", CPL_SWAP32( unValue ) );
+
+        /* field 5 */
+        memcpy( &fValue, abyRecord + 21, 4 );
+        CPL_SWAP32PTR( &fValue );
+        printf( "track heading (first line) = %f\n", fValue );
+
+        /* field 6 */
+
+        printf( "first line of granule:\n" );
+        for( sample = 0; sample < 11; sample++ )
+        {
+            memcpy( &unValue, abyRecord + 25 + sample*4, 4 );
+            printf( "  sample=%d ", CPL_SWAP32(unValue) );
+
+            memcpy( &fValue, abyRecord + 25 + 44 + sample * 4, 4 );
+            CPL_SWAP32PTR( &fValue );
+            printf( "time=%g ", fValue );
+
+            memcpy( &fValue, abyRecord + 25 + 88 + sample * 4, 4 );
+            CPL_SWAP32PTR( &fValue );
+            printf( "angle=%g ", fValue );
+
+            memcpy( &unValue, abyRecord + 25 + 132 + sample*4, 4 );
+            printf( "(%.9f,", ((int) CPL_SWAP32(unValue)) * 0.000001 );
+
+            memcpy( &unValue, abyRecord + 25 + 176 + sample*4, 4 );
+            printf( "%.9f)\n", ((int) CPL_SWAP32(unValue)) * 0.000001 );
+        }
+
+        /* field 8 */
+        CPL_SWAP32PTR( abyRecord + 267 );
+        CPL_SWAP32PTR( abyRecord + 271 );
+        CPL_SWAP32PTR( abyRecord + 275 );
+
+        printf( "end line: mjd_days = %d, sec = %d, msec = %d\n", 
+                ((int *) (abyRecord + 267))[0],
+                ((unsigned int *) (abyRecord + 267))[1],
+                ((unsigned int *) (abyRecord + 267))[2] );
+
+        /* field 9 */
+        printf( "final line of granule:\n" );
+        for( sample = 0; sample < 11; sample++ )
+        {
+            memcpy( &unValue, abyRecord + 279 + sample*4, 4 );
+            printf( "  sample=%d ", CPL_SWAP32(unValue) );
+
+            memcpy( &fValue, abyRecord + 279 + 44 + sample * 4, 4 );
+            CPL_SWAP32PTR( &fValue );
+            printf( "time=%g ", fValue );
+
+            memcpy( &fValue, abyRecord + 279 + 88 + sample * 4, 4 );
+            CPL_SWAP32PTR( &fValue );
+            printf( "angle=%g ", fValue );
+
+            memcpy( &unValue, abyRecord + 279 + 132 + sample*4, 4 );
+            printf( "(%.9f,", ((int) CPL_SWAP32(unValue)) * 0.000001 );
+
+            memcpy( &unValue, abyRecord + 279 + 176 + sample*4, 4 );
+            printf( "%.9f)\n", ((int) CPL_SWAP32(unValue)) * 0.000001 );
+        }
+    }
+
+    EnvisatFile_Close( es_file );
+
+    exit( 0 );
+}
diff --git a/Utilities/GDAL/frmts/envisat/envisat_dump.c b/Utilities/GDAL/frmts/envisat/envisat_dump.c
new file mode 100644
index 0000000000..801fb1f3ca
--- /dev/null
+++ b/Utilities/GDAL/frmts/envisat/envisat_dump.c
@@ -0,0 +1,118 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  APP ENVISAT Support
+ * Purpose:  Test mainline for dumping ENVISAT format files.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Atlantis Scientific, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#include "cpl_conv.h"
+#include "EnvisatFile.h"
+
+int main( int argc, char ** argv )
+
+{
+    EnvisatFile	*es_file;
+    int		i;
+    const char  *key;
+
+    if( argc != 2 )
+    {
+        printf( "Usage: envisatdump filename\n" );
+        exit( 1 );
+    }
+
+    
+    if( EnvisatFile_Open( &es_file, argv[1], "r" ) != 0 )
+    {
+        printf( "EnvisatFile_Open(%s) failed.\n", argv[1] );
+        exit( 2 );
+    }
+    
+    printf( "MPH\n" );
+    printf( "===\n" );
+
+    for( i = 0; 
+         (key = EnvisatFile_GetKeyByIndex( es_file, MPH, i )) != NULL;
+         i++ )
+    {
+        const char	*value = EnvisatFile_GetKeyValueAsString( es_file, 
+                                                                  MPH, 
+                                                                  key,
+                                                                  "" );
+
+        printf( "%s = [%s]\n", key, value );
+    }
+    
+    printf( "\n" );
+    printf( "SPH\n" );
+    printf( "===\n" );
+
+    for( i = 0; 
+         (key = EnvisatFile_GetKeyByIndex( es_file, SPH, i )) != NULL;
+         i++ )
+    {
+        const char	*value = EnvisatFile_GetKeyValueAsString( es_file, 
+                                                                  SPH, 
+                                                                  key,
+                                                                  "" );
+
+        printf( "%s = [%s]\n", key, value );
+    }
+    
+    printf( "\n" );
+    printf( "Datasets\n" );
+    printf( "========\n" );
+
+    for( i = 0; TRUE; i++ )
+    {
+        char	*ds_name, *ds_type, *filename;
+        int	ds_offset, ds_size, num_dsr, dsr_size;
+
+        if( EnvisatFile_GetDatasetInfo( es_file, 
+                                        i, 
+                                        &ds_name, 
+                                        &ds_type, 
+                                        &filename, 
+                                        &ds_offset, 
+                                        &ds_size,
+                                        &num_dsr,
+                                        &dsr_size ) == 1 )
+            break;
+
+        printf( "\nDatset %d\n", i );
+
+        printf( "ds_name = %s\n", ds_name );
+        printf( "ds_type = %s\n", ds_type );
+        printf( "filename = %s\n", filename );
+        printf( "ds_offset = %d\n", ds_offset );
+        printf( "ds_size = %d\n", ds_size );
+        printf( "num_dsr = %d\n", num_dsr );
+        printf( "dsr_size = %d\n", dsr_size );
+    }
+
+    EnvisatFile_Close( es_file );
+
+    exit( 0 );
+}
diff --git a/Utilities/GDAL/frmts/envisat/envisatdataset.cpp b/Utilities/GDAL/frmts/envisat/envisatdataset.cpp
new file mode 100644
index 0000000000..b5779fd87b
--- /dev/null
+++ b/Utilities/GDAL/frmts/envisat/envisatdataset.cpp
@@ -0,0 +1,840 @@
+/******************************************************************************
+ * $Id: envisatdataset.cpp,v 1.19 2005/05/05 14:02:58 fwarmerdam Exp $
+ *
+ * Project:  APP ENVISAT Support
+ * Purpose:  Reader for ENVISAT format image data.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Atlantis Scientific, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: envisatdataset.cpp,v $
+ * Revision 1.19  2005/05/05 14:02:58  fwarmerdam
+ * PAM Enable
+ *
+ * Revision 1.18  2003/03/24 14:16:38  warmerda
+ * added fallback case for unrecognised products
+ *
+ * Revision 1.17  2002/12/04 21:15:19  warmerda
+ * Hacked to support AATSR TOA Level 1 data.
+ *
+ * Revision 1.16  2002/09/04 06:50:37  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.15  2002/06/12 21:12:25  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.14  2002/06/07 14:21:55  warmerda
+ * avoid paths in include directives
+ *
+ * Revision 1.13  2002/05/29 16:38:35  warmerda
+ * dont use variable sized buffer on stack
+ *
+ * Revision 1.12  2002/05/21 21:29:58  warmerda
+ * added support for MERIS tiepoints, and capture DS name as band desc
+ *
+ * Revision 1.11  2002/05/16 03:29:50  warmerda
+ * ensure fpImage is closed on cleanup
+ *
+ * Revision 1.10  2002/04/26 14:53:22  warmerda
+ * added EscapedRecord for metadata
+ *
+ * Revision 1.9  2002/04/16 17:53:33  warmerda
+ * Initialize variables.
+ *
+ * Revision 1.8  2002/04/08 17:33:25  warmerda
+ * now you can get dataset records via metadata api
+ *
+ * Revision 1.7  2001/09/26 19:23:27  warmerda
+ * added CollectDSDMetadata
+ *
+ * Revision 1.6  2001/09/24 18:40:17  warmerda
+ * Added MDS2 support, and metadata support
+ *
+ * Revision 1.5  2001/07/18 04:51:56  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.4  2001/06/15 17:15:29  warmerda
+ * use CPL_MSBWORD32 instead of CPL_SWAP32 to be cross platform
+ *
+ * Revision 1.3  2001/02/28 21:58:45  warmerda
+ * added GCP collection
+ *
+ * Revision 1.2  2001/02/15 22:32:03  warmerda
+ * Added FLT32 support.
+ *
+ * Revision 1.1  2001/02/13 18:29:04  warmerda
+ * New
+ *
+ */
+
+#include "rawdataset.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: envisatdataset.cpp,v 1.19 2005/05/05 14:02:58 fwarmerdam Exp $");
+
+CPL_C_START
+#include "EnvisatFile.h"
+CPL_C_END
+
+CPL_C_START
+void	GDALRegister_Envisat(void);
+CPL_C_END
+
+/************************************************************************/
+/* ==================================================================== */
+/*				EnvisatDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class EnvisatDataset : public RawDataset
+{
+    EnvisatFile *hEnvisatFile;
+    FILE	*fpImage;
+
+    int         nGCPCount;
+    GDAL_GCP    *pasGCPList;
+
+    char        **papszTempMD;
+    
+    void        ScanForGCPs_ASAR();
+    void        ScanForGCPs_MERIS();
+
+    void	CollectMetadata( EnvisatFile_HeaderFlag );
+    void        CollectDSDMetadata();
+
+  public:
+    		EnvisatDataset();
+    	        ~EnvisatDataset();
+    
+    virtual int    GetGCPCount();
+    virtual const char *GetGCPProjection();
+    virtual const GDAL_GCP *GetGCPs();
+    virtual char **GetMetadata( const char * pszDomain );
+
+
+    static GDALDataset *Open( GDALOpenInfo * );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*				EnvisatDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                            EnvisatDataset()                             */
+/************************************************************************/
+
+EnvisatDataset::EnvisatDataset()
+{
+    hEnvisatFile = NULL;
+    fpImage = NULL;
+    nGCPCount = 0;
+    pasGCPList = NULL;
+    papszTempMD = NULL;
+}
+
+/************************************************************************/
+/*                            ~EnvisatDataset()                            */
+/************************************************************************/
+
+EnvisatDataset::~EnvisatDataset()
+
+{
+    FlushCache();
+
+    if( hEnvisatFile != NULL )
+        EnvisatFile_Close( hEnvisatFile );
+
+    if( fpImage != NULL )
+        VSIFClose( fpImage );
+
+    if( nGCPCount > 0 )
+    {
+        GDALDeinitGCPs( nGCPCount, pasGCPList );
+        CPLFree( pasGCPList );
+    }
+
+    CSLDestroy( papszTempMD );
+}
+
+/************************************************************************/
+/*                            GetGCPCount()                             */
+/************************************************************************/
+
+int EnvisatDataset::GetGCPCount()
+
+{
+    return nGCPCount;
+}
+
+/************************************************************************/
+/*                          GetGCPProjection()                          */
+/************************************************************************/
+
+const char *EnvisatDataset::GetGCPProjection()
+
+{
+    if( nGCPCount > 0 )
+        return "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",7030]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",6326]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"DMSH\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",4326]]";
+    else
+        return "";
+}
+
+/************************************************************************/
+/*                               GetGCP()                               */
+/************************************************************************/
+
+const GDAL_GCP *EnvisatDataset::GetGCPs()
+
+{
+    return pasGCPList;
+}
+
+/************************************************************************/
+/*                          ScanForGCPs_ASAR()                          */
+/************************************************************************/
+
+void EnvisatDataset::ScanForGCPs_ASAR()
+
+{
+    int		nDatasetIndex, nNumDSR, nDSRSize, iRecord;
+
+/* -------------------------------------------------------------------- */
+/*      Do we have a meaningful geolocation grid?                       */
+/* -------------------------------------------------------------------- */
+    nDatasetIndex = EnvisatFile_GetDatasetIndex( hEnvisatFile, 
+                                                 "GEOLOCATION GRID ADS" );
+    if( nDatasetIndex == -1 )
+        return;
+
+    if( EnvisatFile_GetDatasetInfo( hEnvisatFile, nDatasetIndex, 
+                                    NULL, NULL, NULL, NULL, NULL, 
+                                    &nNumDSR, &nDSRSize ) != SUCCESS )
+        return;
+
+    if( nNumDSR == 0 || nDSRSize != 521 )
+        return;
+
+/* -------------------------------------------------------------------- */
+/*      Collect the first GCP set from each record.			*/
+/* -------------------------------------------------------------------- */
+    GByte	abyRecord[521];
+    int  	nRange=0, nSample, iGCP;
+    GUInt32 	unValue;
+
+    nGCPCount = 0;
+    pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),(nNumDSR+1) * 11);
+
+    for( iRecord = 0; iRecord < nNumDSR; iRecord++ )
+    {
+        if( EnvisatFile_ReadDatasetRecord( hEnvisatFile, nDatasetIndex, 
+                                           iRecord, abyRecord ) != SUCCESS )
+            continue;
+
+        memcpy( &unValue, abyRecord + 13, 4 );
+        nRange = CPL_MSBWORD32( unValue );
+
+        for( iGCP = 0; iGCP < 11; iGCP++ )
+        {
+            char	szId[128];
+
+            GDALInitGCPs( 1, pasGCPList + nGCPCount );
+
+            CPLFree( pasGCPList[nGCPCount].pszId );
+            
+            sprintf( szId, "%d", nGCPCount+1 );
+            pasGCPList[nGCPCount].pszId = CPLStrdup( szId );
+
+            memcpy( &unValue, abyRecord + 25 + iGCP*4, 4 );
+            nSample = CPL_MSBWORD32(unValue);
+
+            memcpy( &unValue, abyRecord + 25 + 176 + iGCP*4, 4 );
+            pasGCPList[nGCPCount].dfGCPX = ((int)CPL_MSBWORD32(unValue))*0.000001;
+
+            memcpy( &unValue, abyRecord + 25 + 132 + iGCP*4, 4 );
+            pasGCPList[nGCPCount].dfGCPY = ((int)CPL_MSBWORD32(unValue))*0.000001;
+
+            pasGCPList[nGCPCount].dfGCPZ = 0.0;
+
+            pasGCPList[nGCPCount].dfGCPLine = nRange - 0.5;
+            pasGCPList[nGCPCount].dfGCPPixel = nSample - 0.5;
+            
+            nGCPCount++;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      We also collect the bottom GCPs from the last granule.          */
+/* -------------------------------------------------------------------- */
+    memcpy( &unValue, abyRecord + 17, 4 );
+    nRange = nRange + CPL_MSBWORD32( unValue ) - 1;
+
+    for( iGCP = 0; iGCP < 11; iGCP++ )
+    {
+        char	szId[128];
+
+        GDALInitGCPs( 1, pasGCPList + nGCPCount );
+
+        CPLFree( pasGCPList[nGCPCount].pszId );
+            
+        sprintf( szId, "%d", nGCPCount+1 );
+        pasGCPList[nGCPCount].pszId = CPLStrdup( szId );
+
+        memcpy( &unValue, abyRecord + 279 + iGCP*4, 4 );
+        nSample = CPL_MSBWORD32(unValue);
+
+        memcpy( &unValue, abyRecord + 279 + 176 + iGCP*4, 4 );
+        pasGCPList[nGCPCount].dfGCPX = ((int)CPL_MSBWORD32(unValue))*0.000001;
+
+        memcpy( &unValue, abyRecord + 279 + 132 + iGCP*4, 4 );
+        pasGCPList[nGCPCount].dfGCPY = ((int)CPL_MSBWORD32(unValue))*0.000001;
+
+        pasGCPList[nGCPCount].dfGCPZ = 0.0;
+
+        pasGCPList[nGCPCount].dfGCPLine = nRange - 0.5;
+        pasGCPList[nGCPCount].dfGCPPixel = nSample - 0.5;
+            
+        nGCPCount++;
+    }
+}
+
+/************************************************************************/
+/*                         ScanForGCPs_MERIS()                          */
+/************************************************************************/
+
+void EnvisatDataset::ScanForGCPs_MERIS()
+
+{
+    int		nDatasetIndex, nNumDSR, nDSRSize, iRecord;
+
+/* -------------------------------------------------------------------- */
+/*      Do we have a meaningful geolocation grid?  Seach for a          */
+/*      DS_TYPE=A and a name containing "geolocation" or "tie           */
+/*      points".                                                        */
+/* -------------------------------------------------------------------- */
+    nDatasetIndex = EnvisatFile_GetDatasetIndex( hEnvisatFile, 
+                                                 "Tie points ADS" );
+    if( nDatasetIndex == -1 )
+        return;
+
+    if( EnvisatFile_GetDatasetInfo( hEnvisatFile, nDatasetIndex, 
+                                    NULL, NULL, NULL, NULL, NULL, 
+                                    &nNumDSR, &nDSRSize ) != SUCCESS )
+        return;
+
+    if( nNumDSR == 0 )
+        return;
+
+/* -------------------------------------------------------------------- */
+/*      Figure out the tiepoint space, and how many we have.            */
+/* -------------------------------------------------------------------- */
+    int  nLinesPerTiePoint, nSamplesPerTiePoint;
+    int  nTPPerLine, nTPPerColumn = nNumDSR;
+
+    if( nNumDSR == 0 )
+        return;
+
+    nLinesPerTiePoint = 
+        EnvisatFile_GetKeyValueAsInt( hEnvisatFile, SPH, 
+                                      "LINES_PER_TIE_PT", 0 );
+    nSamplesPerTiePoint =
+        EnvisatFile_GetKeyValueAsInt( hEnvisatFile, SPH, 
+                                      "SAMPLES_PER_TIE_PT", 0 );
+
+    if( nLinesPerTiePoint == 0 || nSamplesPerTiePoint == 0 )
+        return;
+
+    nTPPerLine = (GetRasterXSize() + nSamplesPerTiePoint - 1) 
+        / nSamplesPerTiePoint;
+
+    if( (GetRasterXSize() + nSamplesPerTiePoint - 1) 
+        / nSamplesPerTiePoint  != nTPPerColumn )
+    {
+        CPLDebug( "EnvisatDataset", "Got %d instead of %d nTPPerColumn.", 
+                  (GetRasterXSize()+nSamplesPerTiePoint-1)/nSamplesPerTiePoint,
+                  nTPPerColumn );
+        return;
+    }
+
+    if( 50*nTPPerLine + 13 != nDSRSize )
+    {
+        CPLDebug( "EnvisatDataset", 
+                  "DSRSize=%d instead of expected %d for tiepoints ADS.", 
+                  nDSRSize, 50*nTPPerLine + 13 );
+        return;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Collect the first GCP set from each record.			*/
+/* -------------------------------------------------------------------- */
+    GByte	*pabyRecord = (GByte *) CPLMalloc(nDSRSize);
+    int  	iGCP;
+    GUInt32 	unValue;
+
+    nGCPCount = 0;
+    pasGCPList = (GDAL_GCP *) 
+        CPLCalloc(sizeof(GDAL_GCP),nNumDSR * nTPPerLine);
+
+    for( iRecord = 0; iRecord < nNumDSR; iRecord++ )
+    {
+        if( EnvisatFile_ReadDatasetRecord( hEnvisatFile, nDatasetIndex, 
+                                           iRecord, pabyRecord ) != SUCCESS )
+            continue;
+
+        memcpy( &unValue, pabyRecord + 13, 4 );
+
+        for( iGCP = 0; iGCP < nTPPerLine; iGCP++ )
+        {
+            char	szId[128];
+
+            GDALInitGCPs( 1, pasGCPList + nGCPCount );
+
+            CPLFree( pasGCPList[nGCPCount].pszId );
+            
+            sprintf( szId, "%d", nGCPCount+1 );
+            pasGCPList[nGCPCount].pszId = CPLStrdup( szId );
+
+            memcpy( &unValue, pabyRecord + 13 + nTPPerLine*4 + iGCP*4, 4 );
+            pasGCPList[nGCPCount].dfGCPX = 
+                ((int)CPL_MSBWORD32(unValue))*0.000001;
+
+            memcpy( &unValue, pabyRecord + 13 + iGCP*4, 4 );
+            pasGCPList[nGCPCount].dfGCPY = 
+                ((int)CPL_MSBWORD32(unValue))*0.000001;
+
+            pasGCPList[nGCPCount].dfGCPZ = 0.0;
+
+            pasGCPList[nGCPCount].dfGCPLine = iRecord*nLinesPerTiePoint + 0.5;
+            pasGCPList[nGCPCount].dfGCPPixel = iGCP*nSamplesPerTiePoint + 0.5;
+            
+            nGCPCount++;
+        }
+    }
+    CPLFree( pabyRecord );
+}
+
+/************************************************************************/
+/*                            GetMetadata()                             */
+/************************************************************************/
+
+char **EnvisatDataset::GetMetadata( const char * pszDomain )
+
+{
+    if( pszDomain == NULL || !EQUALN(pszDomain,"envisat-ds-",11) )
+        return GDALDataset::GetMetadata( pszDomain );
+
+/* -------------------------------------------------------------------- */
+/*      Get the dataset name and record number.                         */
+/* -------------------------------------------------------------------- */
+    char	szDSName[128];
+    int         i, nRecord = -1;
+
+    strncpy( szDSName, pszDomain+11, sizeof(szDSName) );
+    for( i = 0; i < (int) sizeof(szDSName)-1; i++ )
+    {
+        if( szDSName[i] == '-' )
+        {
+            szDSName[i] = '\0';
+            nRecord = atoi(szDSName+1);
+            break;
+        }
+    }
+
+    if( nRecord == -1 )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Get the dataset index and info.                                 */
+/* -------------------------------------------------------------------- */
+    int nDSIndex = EnvisatFile_GetDatasetIndex( hEnvisatFile, szDSName );
+    int nDSRSize, nNumDSR;
+
+    if( nDSIndex == -1 )
+        return NULL;
+
+    EnvisatFile_GetDatasetInfo( hEnvisatFile, nDSIndex, NULL, NULL, NULL,
+                                NULL, NULL, &nNumDSR, &nDSRSize );
+
+    if( nDSRSize == -1 || nRecord < 0 || nRecord >= nNumDSR )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Read the requested record.                                      */
+/* -------------------------------------------------------------------- */
+    char  *pszRecord;
+
+    pszRecord = (char *) CPLMalloc(nDSRSize+1);
+    
+    if( EnvisatFile_ReadDatasetRecord( hEnvisatFile, nDSIndex, nRecord, 
+                                       pszRecord ) == FAILURE )
+    {
+        CPLFree( pszRecord );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Massage the data into a safe textual format.  For now we        */
+/*      just turn zero bytes into spaces.                               */
+/* -------------------------------------------------------------------- */
+    char *pszEscapedRecord;
+
+    CSLDestroy( papszTempMD );
+
+    pszEscapedRecord = CPLEscapeString( pszRecord, nDSRSize, 
+                                        CPLES_BackslashQuotable );
+    papszTempMD = CSLSetNameValue( NULL, "EscapedRecord", pszEscapedRecord );
+    CPLFree( pszEscapedRecord );
+
+    for( i = 0; i < nDSRSize; i++ )
+        if( pszRecord[i] == '\0' )
+            pszRecord[i] = ' ';
+
+    papszTempMD = CSLSetNameValue( papszTempMD, "RawRecord", pszRecord );
+
+    CPLFree( pszRecord );
+
+    return papszTempMD;
+}
+
+/************************************************************************/
+/*                         CollectDSDMetadata()                         */
+/*                                                                      */
+/*      Collect metadata based on any DSD entries with filenames        */
+/*      associated.                                                     */
+/************************************************************************/
+
+void EnvisatDataset::CollectDSDMetadata()
+
+{
+    char	*pszDSName, *pszFilename;
+    int		iDSD;
+
+    for( iDSD = 0; 
+         EnvisatFile_GetDatasetInfo( hEnvisatFile, iDSD, &pszDSName, NULL, 
+                             &pszFilename, NULL, NULL, NULL, NULL ) == SUCCESS;
+         iDSD++ )
+    {
+        if( pszFilename == NULL 
+            || strlen(pszFilename) == 0 
+            || EQUALN(pszFilename,"NOT USED",8)
+            || EQUALN(pszFilename,"        ",8))
+            continue;
+
+        char	szKey[128], szTrimmedName[128];
+        int	i;
+        
+        strcpy( szKey, "DS_" );
+        strcat( szKey, pszDSName );
+
+        // strip trailing spaces. 
+        for( i = strlen(szKey)-1; i && szKey[i] == ' '; i-- )
+            szKey[i] = '\0';
+
+        // convert spaces into underscores.
+        for( i = 0; szKey[i] != '\0'; i++ )
+        {
+            if( szKey[i] == ' ' )
+                szKey[i] = '_';
+        }
+
+        strcat( szKey, "_NAME" );
+
+        strcpy( szTrimmedName, pszFilename );
+        for( i = strlen(szTrimmedName)-1; i && szTrimmedName[i] == ' '; i--)
+            szTrimmedName[i] = '\0';
+
+        SetMetadataItem( szKey, szTrimmedName );
+    }
+}
+
+/************************************************************************/
+/*                          CollectMetadata()                           */
+/*                                                                      */
+/*      Collect metadata from the SPH or MPH header fields.             */
+/************************************************************************/
+
+void EnvisatDataset::CollectMetadata( EnvisatFile_HeaderFlag  eMPHOrSPH )
+
+{
+    int		iKey;
+    
+    for( iKey = 0; TRUE; iKey++ )
+    {
+        const char *pszValue, *pszKey;
+        char  szHeaderKey[128];
+
+        pszKey = EnvisatFile_GetKeyByIndex(hEnvisatFile, eMPHOrSPH, iKey);
+        if( pszKey == NULL )
+            break;
+
+        pszValue = EnvisatFile_GetKeyValueAsString( hEnvisatFile, eMPHOrSPH,
+                                                    pszKey, NULL );
+
+        if( pszValue == NULL )
+            continue;
+
+        // skip some uninteresting structural information.
+        if( EQUAL(pszKey,"TOT_SIZE")
+            || EQUAL(pszKey,"SPH_SIZE")
+            || EQUAL(pszKey,"NUM_DSD")
+            || EQUAL(pszKey,"DSD_SIZE")
+            || EQUAL(pszKey,"NUM_DATA_SETS") )
+            continue;
+
+        if( eMPHOrSPH == MPH )
+            sprintf( szHeaderKey, "MPH_%s", pszKey );
+        else
+            sprintf( szHeaderKey, "SPH_%s", pszKey );
+
+        SetMetadataItem( szHeaderKey, pszValue );
+    }
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *EnvisatDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    EnvisatFile	*hEnvisatFile;
+    
+/* -------------------------------------------------------------------- */
+/*      Check the header.                                               */
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->nHeaderBytes < 8 || poOpenInfo->fp == NULL )
+        return NULL;
+
+    if( !EQUALN((const char *) poOpenInfo->pabyHeader, "PRODUCT=",8) )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Try opening the dataset.                                        */
+/* -------------------------------------------------------------------- */
+    int		ds_index;
+
+    if( EnvisatFile_Open( &hEnvisatFile, poOpenInfo->pszFilename, "r" ) 
+        == FAILURE )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Find a Mesurement type dataset to use as our reference          */
+/*      raster band.                                                    */
+/* -------------------------------------------------------------------- */
+    int		dsr_size, num_dsr, ds_offset, bNative;
+    char        *pszDSType;
+
+    for( ds_index = 0; TRUE; ds_index++ )
+    {
+        if( EnvisatFile_GetDatasetInfo( hEnvisatFile, ds_index, 
+                                        NULL, &pszDSType, NULL, 
+                                        &ds_offset, NULL, 
+                                        &num_dsr, &dsr_size ) == FAILURE )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined, 
+                      "Unable to find \"MDS1\" measurement datatset in Envisat file." );
+            EnvisatFile_Close( hEnvisatFile );
+            return NULL;
+        }
+
+        /* Have we found what we are looking for?  A Measurement ds. */
+        if( EQUAL(pszDSType,"M") )
+            break;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    EnvisatDataset 	*poDS;
+
+    poDS = new EnvisatDataset();
+
+    poDS->hEnvisatFile = hEnvisatFile;
+
+/* -------------------------------------------------------------------- */
+/*      Setup image definition.                                         */
+/* -------------------------------------------------------------------- */
+    const char  *pszDataType, *pszSampleType, *pszProduct;
+    GDALDataType eDataType;
+    int          nPrefixBytes;
+
+    EnvisatFile_GetDatasetInfo( hEnvisatFile, ds_index, 
+                                NULL, NULL, NULL, &ds_offset, NULL, 
+                                &num_dsr, &dsr_size );
+
+    poDS->nRasterXSize = EnvisatFile_GetKeyValueAsInt( hEnvisatFile, SPH,
+                                                       "LINE_LENGTH", 0 );
+    poDS->nRasterYSize = num_dsr;
+    poDS->eAccess = GA_ReadOnly;
+
+    pszProduct = EnvisatFile_GetKeyValueAsString( hEnvisatFile, MPH,
+                                                  "PRODUCT", "" );
+    pszDataType = EnvisatFile_GetKeyValueAsString( hEnvisatFile, SPH, 
+                                                   "DATA_TYPE", "" );
+    pszSampleType = EnvisatFile_GetKeyValueAsString( hEnvisatFile, SPH, 
+                                                     "SAMPLE_TYPE", "" );
+    if( EQUAL(pszDataType,"FLT32") && EQUALN(pszSampleType,"COMPLEX",7))
+        eDataType = GDT_CFloat32;
+    else if( EQUAL(pszDataType,"FLT32") )
+        eDataType = GDT_Float32;
+    else if( EQUAL(pszDataType,"UWORD") )
+        eDataType = GDT_UInt16;
+    else if( EQUAL(pszDataType,"SWORD") && EQUALN(pszSampleType,"COMPLEX",7) )
+        eDataType = GDT_CInt16;
+    else if( EQUAL(pszDataType,"SWORD") )
+        eDataType = GDT_Int16;
+    else if( EQUALN(pszProduct,"ATS_TOA_1",8) )
+    {
+        /* all 16bit data, no line length provided */
+        eDataType = GDT_Int16;
+        poDS->nRasterXSize = (dsr_size - 20) / 2;
+    }
+    else if( poDS->nRasterXSize == 0 )
+    {
+        CPLError( CE_Warning, CPLE_AppDefined,
+                  "Envisat product format not recognised.  Assuming 8bit\n"
+                  "with no per-record prefix data.  Results may be useless!" );
+        eDataType = GDT_Byte;
+        poDS->nRasterXSize = dsr_size;
+    }
+    else
+    {
+        if( dsr_size >= 2 * poDS->nRasterXSize )
+            eDataType = GDT_UInt16;
+        else
+            eDataType = GDT_Byte;
+    }
+
+#ifdef CPL_LSB 
+    bNative = FALSE;
+#else
+    bNative = TRUE;
+#endif
+
+    nPrefixBytes = dsr_size - 
+        ((GDALGetDataTypeSize(eDataType) / 8) * poDS->nRasterXSize);
+
+/* -------------------------------------------------------------------- */
+/*      Fail out if we didn't get non-zero sizes.                       */
+/* -------------------------------------------------------------------- */
+    if( poDS->nRasterXSize < 1 || poDS->nRasterYSize < 1 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Unable to determine organization of dataset.  It would\n"
+                  "appear this is an Envisat dataset, but an unsupported\n"
+                  "data product.  Unable to utilize." );
+        delete poDS;
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Assume ownership of the file handled from the GDALOpenInfo.     */
+/* -------------------------------------------------------------------- */
+    poDS->fpImage = poOpenInfo->fp;
+    poOpenInfo->fp = NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Try to collect GCPs.                                            */
+/* -------------------------------------------------------------------- */
+
+/* -------------------------------------------------------------------- */
+/*      Scan for all datasets matching the reference dataset.           */
+/* -------------------------------------------------------------------- */
+    int	num_dsr2, dsr_size2, iBand = 0;
+    const char *pszDSName;
+
+    for( ds_index = 0; 
+         EnvisatFile_GetDatasetInfo( hEnvisatFile, ds_index, 
+                                     (char **) &pszDSName, NULL, NULL, 
+                                     &ds_offset, NULL, 
+                                     &num_dsr2, &dsr_size2 ) == SUCCESS;
+         ds_index++ )
+    {
+        if( EQUAL(pszDSType,"M") 
+            && num_dsr2 == num_dsr && dsr_size2 == dsr_size )
+        {
+            poDS->SetBand( iBand+1,
+                       new RawRasterBand( poDS, iBand+1, poDS->fpImage,
+                                          ds_offset + nPrefixBytes,
+                                          GDALGetDataTypeSize(eDataType) / 8, 
+                                          dsr_size, 
+                                          eDataType, bNative ) );
+            iBand++;
+
+            poDS->GetRasterBand(iBand)->SetDescription( pszDSName );
+        }
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Collect metadata.                                               */
+/* -------------------------------------------------------------------- */
+    poDS->CollectMetadata( MPH );
+    poDS->CollectMetadata( SPH );
+    poDS->CollectDSDMetadata();
+
+    if( EQUALN(pszProduct,"MER",3) )
+        poDS->ScanForGCPs_MERIS();
+    else
+        poDS->ScanForGCPs_ASAR();
+
+/* -------------------------------------------------------------------- */
+/*      Check for overviews.                                            */
+/* -------------------------------------------------------------------- */
+    poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                         GDALRegister_Envisat()                       */
+/************************************************************************/
+
+void GDALRegister_Envisat()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "ESAT" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "ESAT" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "Envisat Image Format" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_various.html#Envisat" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "n1" );
+
+        poDriver->pfnOpen = EnvisatDataset::Open;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/envisat/makefile.vc b/Utilities/GDAL/frmts/envisat/makefile.vc
new file mode 100644
index 0000000000..85cb720fab
--- /dev/null
+++ b/Utilities/GDAL/frmts/envisat/makefile.vc
@@ -0,0 +1,15 @@
+
+OBJ	=	envisatdataset.obj EnvisatFile.obj
+
+GDAL_ROOT	=	..\..
+
+EXTRAFLAGS = 	-I..\raw
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/fit/GNUmakefile b/Utilities/GDAL/frmts/fit/GNUmakefile
new file mode 100644
index 0000000000..e354abce3b
--- /dev/null
+++ b/Utilities/GDAL/frmts/fit/GNUmakefile
@@ -0,0 +1,15 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	fitdataset.o fit.o
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(XTRA_OPT) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o
+
+all:	$(OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/fit/fit.cpp b/Utilities/GDAL/frmts/fit/fit.cpp
new file mode 100644
index 0000000000..0671eb02b8
--- /dev/null
+++ b/Utilities/GDAL/frmts/fit/fit.cpp
@@ -0,0 +1,210 @@
+/******************************************************************************
+ * $Id: fit.cpp,v 1.4 2001/07/18 04:51:56 warmerda Exp $
+ *
+ * Project:  FIT Driver
+ * Purpose:  Implement FIT Support - not using the SGI iflFIT library.
+ * Author:   Philip Nemec, nemec@keyholecorp.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Keyhole, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: fit.cpp,v $
+ * Revision 1.4  2001/07/18 04:51:56  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.3  2001/07/12 00:49:18  nemec
+ * Convert unknown colorInterp based on number of bands for happier .fit files
+ *
+ * Revision 1.2  2001/07/06 18:46:25  nemec
+ * Cleanup files - improve Windows build, make proper copyright notice
+ *
+ *
+ */
+
+#include <limits.h>
+#include "fit.h"
+
+CPL_CVSID("$Id: fit.cpp,v 1.4 2001/07/18 04:51:56 warmerda Exp $");
+
+GDALDataType fitDataType(int dtype) {
+    switch (dtype) {
+    case 1: // iflBit   /* single-bit */
+        fprintf(stderr, 
+                "GDAL unsupported data type (single-bit) in fitDataType\n");
+        return GDT_Unknown;
+    case 2: // iflUChar    /* unsigned character (byte) */
+        return GDT_Byte;
+    case 4: // iflChar     /* signed character (byte) */
+        fprintf(stderr, 
+                "GDAL unsupported data type (signed char) in fitDataType\n");
+        return GDT_Unknown;
+//         return Byte;
+    case 8: // iflUShort   /* unsigned short integer (nominally 16 bits) */
+        return GDT_UInt16;
+    case 16: // iflShort   /* signed short integer */
+        return GDT_Int16;
+    case 32: // iflUInt    /* unsigned integer (nominally 32 bits) */
+//     case 32: // iflULong   /* deprecated, same as iflUInt */
+        return GDT_UInt32;
+        break;
+    case 64: // iflInt     /* integer */
+//     case 64: // iflLong    /* deprecated, same as iflULong */
+        return GDT_Int32;
+    case 128: // iflFloat  /* floating point */
+        return GDT_Float32;
+    case 256: // iflDouble /* double precision floating point */
+        return GDT_Float64;
+    default:
+        CPLError(CE_Failure, CPLE_NotSupported, 
+                 "FIT - unknown data type %i in fitDataType", dtype);
+        return GDT_Unknown;
+    } // switch
+}
+
+int fitGetDataType(GDALDataType eDataType) {
+    switch (eDataType) {
+    case GDT_Byte:
+        return 2; // iflUChar - unsigned character (byte)
+    case GDT_UInt16:
+        return 8; // iflUShort - unsigned short integer (nominally 16 bits)
+    case GDT_Int16:
+        return 16; // iflShort - signed short integer
+    case GDT_UInt32:
+        return 32; // iflUInt - unsigned integer (nominally 32 bits)
+    case GDT_Int32:
+        return 64; // iflInt - integer
+    case GDT_Float32:
+        return 128; // iflFloat - floating point
+    case GDT_Float64:
+        return 256; // iflDouble - double precision floating point
+    default:
+        CPLError(CE_Failure, CPLE_NotSupported, 
+                 "FIT - unsupported GDALDataType %i in fitGetDataType",
+                 eDataType);
+        return 0;
+    } // switch
+}
+
+#define UNSUPPORTED_COMBO() \
+            CPLError(CE_Failure, CPLE_NotSupported, \
+                     "FIT write - unsupported combination (band 1 = %s " \
+                     "and %i bands) - ignoring color model", \
+                     GDALGetColorInterpretationName(colorInterp), nBands); \
+            return 0
+
+
+int fitGetColorModel(GDALColorInterp colorInterp, int nBands) {
+    // XXX - shoould check colorInterp for all bands, not just first one
+
+    switch(colorInterp) {
+    case GCI_GrayIndex:
+        switch (nBands) {
+        case 1:
+            return 2; // iflLuminance - luminance
+        case 2:
+            return 13; // iflLuminanceAlpha - Luminance plus alpha
+        default:
+            UNSUPPORTED_COMBO();
+        } // switch
+
+    case GCI_PaletteIndex:
+        CPLError(CE_Failure, CPLE_NotSupported, 
+                 "FIT write - unsupported ColorInterp PaletteIndex\n");
+        return 0;
+
+    case GCI_RedBand:
+        switch (nBands) {
+        case 3:
+            return 3; // iflRGB - full color (Red, Green, Blue triplets)
+        case 4:
+            return 5; // iflRGBA - full color with transparency (alpha channel)
+        default:
+            UNSUPPORTED_COMBO();
+        } // switch
+
+    case GCI_BlueBand:
+        switch (nBands) {
+        case 3:
+            return 9; // iflBGR - full color (ordered Blue, Green, Red)
+        default:
+            UNSUPPORTED_COMBO();
+        } // switch
+
+    case GCI_AlphaBand:
+        switch (nBands) {
+        case 4:
+            return 10; // iflABGR - Alpha, Blue, Green, Red (SGI frame buffers)
+        default:
+            UNSUPPORTED_COMBO();
+        } // switch
+
+    case GCI_HueBand:
+        switch (nBands) {
+        case 3:
+            return 6; // iflHSV - Hue, Saturation, Value
+        default:
+            UNSUPPORTED_COMBO();
+        } // switch
+
+    case GCI_CyanBand:
+        switch (nBands) {
+        case 3:
+            return 7; // iflCMY - Cyan, Magenta, Yellow
+        case 4:
+            return 8; // iflCMYK - Cyan, Magenta, Yellow, Black
+        default:
+            UNSUPPORTED_COMBO();
+        } // switch
+        
+    case GCI_GreenBand:
+    case GCI_SaturationBand:
+    case GCI_LightnessBand:
+    case GCI_MagentaBand:
+    case GCI_YellowBand:
+    case GCI_BlackBand:
+        CPLError(CE_Failure, CPLE_NotSupported, 
+                 "FIT write - unsupported combination (band 1 = %s) "
+                 "- ignoring color model",
+                 GDALGetColorInterpretationName(colorInterp), nBands);
+        return 0;
+
+    default:
+        CPLDebug("FIT write", "unrecognized colorInterp %i - deriving from "
+                 "number of bands (%i)", colorInterp, nBands);
+        switch (nBands) {
+        case 1:
+            return 2; // iflLuminance - luminance
+        case 2:
+            return 13; // iflLuminanceAlpha - Luminance plus alpha
+        case 3:
+            return 3; // iflRGB - full color (Red, Green, Blue triplets)
+        case 4:
+            return 5; // iflRGBA - full color with transparency (alpha channel)
+        } // switch
+
+        CPLError(CE_Failure, CPLE_NotSupported, 
+                 "FIT write - unrecognized colorInterp %i and "
+                 "unrecognized number of bands (%i)", colorInterp, nBands);
+
+        return 0;
+    } // switch
+}
diff --git a/Utilities/GDAL/frmts/fit/fit.h b/Utilities/GDAL/frmts/fit/fit.h
new file mode 100644
index 0000000000..bd08aceff7
--- /dev/null
+++ b/Utilities/GDAL/frmts/fit/fit.h
@@ -0,0 +1,122 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  FIT Driver
+ * Purpose:  Implement FIT Support - not using the SGI iflFIT library.
+ * Author:   Philip Nemec, nemec@keyholecorp.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Keyhole, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: fit.h,v $
+ * Revision 1.2  2001/07/06 18:46:25  nemec
+ * Cleanup files - improve Windows build, make proper copyright notice
+ *
+ *
+ */
+
+#ifndef __FIT_H__
+#define __FIT_H__
+
+#include "gdal.h"
+
+struct FITinfo {
+    unsigned short magic;	// file ident
+    unsigned short version;	// file version
+    unsigned int xSize;		// image size
+    unsigned int ySize;
+    unsigned int zSize;
+    unsigned int cSize;
+    int dtype;			// data type
+    int order;			// RGBRGB.. or RR..GG..BB..
+    int space;			// coordinate space
+    int cm;			// color model
+    unsigned int xPageSize;	// page size
+    unsigned int yPageSize;
+    unsigned int zPageSize;
+    unsigned int cPageSize;
+				// NOTE: a word of padding is inserted here
+				//       due to struct alignment rules
+    double minValue;		// min/max pixel values
+    double maxValue;
+    unsigned int dataOffset;	// offset to first page of data
+
+    // non-header values
+    unsigned int userOffset;	// offset to area of user data
+};
+
+struct FIThead02 {		// file header for version 02
+    unsigned short magic;	// file ident
+    unsigned short version;	// file version
+    unsigned int xSize;		// image size
+    unsigned int ySize;
+    unsigned int zSize;
+    unsigned int cSize;
+    int dtype;			// data type
+    int order;			// RGBRGB.. or RR..GG..BB..
+    int space;			// coordinate space
+    int cm;			// color model
+    unsigned int xPageSize;	// page size
+    unsigned int yPageSize;
+    unsigned int zPageSize;
+    unsigned int cPageSize;
+    short _padding;		// NOTE: a word of padding is inserted here
+				//       due to struct alignment rules
+    double minValue;		// min/max pixel values
+    double maxValue;
+    unsigned int dataOffset;	// offset to first page of data
+    // user extensible area...
+};
+
+
+struct FIThead01 {		// file header for version 01
+    unsigned short magic;	// file ident
+    unsigned short version;	// file version
+    unsigned int xSize;		// image size
+    unsigned int ySize;
+    unsigned int zSize;
+    unsigned int cSize;
+    int dtype;			// data type
+    int order;			// RGBRGB.. or RR..GG..BB..
+    int space;			// coordinate space
+    int cm;			// color model
+    unsigned int xPageSize;	// page size
+    unsigned int yPageSize;
+    unsigned int zPageSize;
+    unsigned int cPageSize;
+    unsigned int dataOffset;	// offset to first page of data
+    // user extensible area...
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+GDALDataType fitDataType(int dtype);
+int fitGetDataType(GDALDataType eDataType);
+int fitGetColorModel(GDALColorInterp colorInterp, int nBands);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __FIT_H__
diff --git a/Utilities/GDAL/frmts/fit/fitdataset.cpp b/Utilities/GDAL/frmts/fit/fitdataset.cpp
new file mode 100644
index 0000000000..13f61c86de
--- /dev/null
+++ b/Utilities/GDAL/frmts/fit/fitdataset.cpp
@@ -0,0 +1,1445 @@
+/******************************************************************************
+ * $Id: fitdataset.cpp,v 1.23 2006/02/17 17:37:41 fwarmerdam Exp $
+ *
+ * Project:  FIT Driver
+ * Purpose:  Implement FIT Support - not using the SGI iflFIT library.
+ * Author:   Philip Nemec, nemec@keyholecorp.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Keyhole, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: fitdataset.cpp,v $
+ * Revision 1.23  2006/02/17 17:37:41  fwarmerdam
+ * Removed class qualifier on constructor of FITRasterBand.
+ *
+ * Revision 1.22  2005/07/05 17:10:52  fwarmerdam
+ * give swap funcs unique names
+ *
+ * Revision 1.21  2005/05/05 14:02:57  fwarmerdam
+ * PAM Enable
+ *
+ * Revision 1.20  2004/03/22 23:45:41  aubin
+ * wrap endian swapping functions in namespace to limit scope
+ *
+ * Revision 1.19  2004/02/25 19:04:19  warmerda
+ * Modified a few calls in the create code to use VSI*L API as well.
+ * Untested as of yet.
+ *
+ * Revision 1.18  2004/02/24 23:50:14  aubin
+ * replace previous bad checkin due to windows vs. unix cr/lf differences
+ *
+ * Revision 1.17  2004/02/24 23:16:11  aubin
+ * Fixed Win32 file I/O.  Re-open file with VSIFOpenL(), 
+ * and use VSIFReadL() instead of fread().  
+ * Added some memory guard objects to catch a few memory leaks.
+ *
+ * Revision 1.16  2004/02/24 22:00:50  warmerda
+ * test commit
+ *
+ * Revision 1.15  2002/11/23 18:54:17  warmerda
+ * added CREATIONDATATYPES metadata for drivers
+ *
+ * Revision 1.14  2002/09/04 06:50:37  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.13  2002/06/15 00:07:23  aubin
+ * mods to enable 64bit file i/o
+ *
+ * Revision 1.12  2002/06/12 21:12:25  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.11  2001/11/11 23:50:59  warmerda
+ * added required class keyword to friend declarations
+ *
+ * Revision 1.10  2001/07/25 17:21:56  nemec
+ * Fixed another bug with partial tiles (relating to different image origins)
+ * Added more debugging info for simpler bug reports
+ *
+ * Revision 1.9  2001/07/25 01:25:47  nemec
+ * Changes with values used for padding - now all zeros but without overhead of
+ * zeroing memory all the time.
+ *
+ * Revision 1.8  2001/07/18 04:51:56  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.7  2001/07/12 22:08:13  nemec
+ * Change check if 64-bit seek is needed.  Should work on all systems (no longer
+ * comparing to a 64 bit constant).
+ * Also improved calculation to account for files whose dimensions aren't exact
+ * multiples of the page size.
+ *
+ * Revision 1.6  2001/07/12 18:59:36  warmerda
+ * Avoid strings.h and bzero() -- not on NT.  Use memset() instead.
+ * Some odd problem with initializing i in a for loop in a case statement avoided.
+ * Don't use LL suffix to indicate long long constants ... doesn't work on NT.
+ *
+ * Revision 1.5  2001/07/10 02:12:40  nemec
+ * Fixed writing of files whose dimensions aren't exact multiples of the page size
+ *
+ * Revision 1.4  2001/07/07 01:03:35  nemec
+ * Fixed math on check if 64 bit seek is needed
+ *
+ * Revision 1.3  2001/07/06 22:02:55  nemec
+ * Fixed some bugs related to large files (fseek64 when available, trap otherwise)
+ * Better support for different file layouts (upper left, lower left, etc.)
+ * Fixed files with dimensinos that aren't exact multiples of the page size
+ *
+ * Revision 1.2  2001/07/06 18:46:25  nemec
+ * Cleanup files - improve Windows build, make proper copyright notice
+ *
+ *
+ */
+
+#include "fit.h"
+#include "gstEndian.h"
+#include "gdal_pam.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: fitdataset.cpp,v 1.23 2006/02/17 17:37:41 fwarmerdam Exp $");
+
+CPL_C_START
+ 
+void	GDALRegister_FIT(void);
+CPL_C_END
+
+#define FIT_WRITE
+
+#define FIT_PAGE_SIZE 128
+
+using namespace gstEndian;
+
+/************************************************************************/
+/* ==================================================================== */
+/*				FITDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class FITRasterBand;
+
+class FITDataset : public GDALPamDataset
+{
+    friend class FITRasterBand;
+    
+    FILE	*fp;
+    FITinfo	*info;
+    double      adfGeoTransform[6];
+    
+  public:
+    FITDataset();
+    ~FITDataset();
+    static GDALDataset *Open( GDALOpenInfo * );
+//     virtual CPLErr GetGeoTransform( double * );
+};
+
+#ifdef FIT_WRITE
+static GDALDataset *FITCreateCopy(const char * pszFilename,
+                                  GDALDataset *poSrcDS, 
+                                  int bStrict, char ** papszOptions, 
+                                  GDALProgressFunc pfnProgress,
+                                  void * pProgressData );
+#endif // FIT_WRITE
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            FITRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class FITRasterBand : public GDALPamRasterBand
+{
+    friend class FITDataset;
+    
+    unsigned long recordSize; // number of bytes of a single page/block/record
+    unsigned long numXBlocks; // number of pages in the X direction
+    unsigned long numYBlocks; // number of pages in the Y direction
+    unsigned long bytesPerComponent;
+    unsigned long bytesPerPixel;
+    char *tmpImage;
+
+public:
+
+    FITRasterBand( FITDataset *, int );
+    ~FITRasterBand();
+    
+    // should override RasterIO eventually.
+    
+    virtual CPLErr IReadBlock( int, int, void * );
+//     virtual CPLErr WriteBlock( int, int, void * ); 
+    virtual double GetMinimum( int *pbSuccess );
+    virtual double GetMaximum( int *pbSuccess );
+    virtual GDALColorInterp GetColorInterpretation();
+};
+
+
+/************************************************************************/
+/*                           FITRasterBand()                            */
+/************************************************************************/
+
+FITRasterBand::FITRasterBand( FITDataset *poDS, int nBand ) : tmpImage( NULL )
+
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+    
+/* -------------------------------------------------------------------- */
+/*      Get the GDAL data type.                                         */
+/* -------------------------------------------------------------------- */
+    eDataType = fitDataType(poDS->info->dtype);
+
+/* -------------------------------------------------------------------- */
+/*      Get the page sizes.                                             */
+/* -------------------------------------------------------------------- */
+    nBlockXSize = poDS->info->xPageSize;
+    nBlockYSize = poDS->info->yPageSize;
+
+/* -------------------------------------------------------------------- */
+/*      Caculate the values for record offset calculations.             */
+/* -------------------------------------------------------------------- */
+    bytesPerComponent = (GDALGetDataTypeSize(eDataType) / 8);
+    bytesPerPixel = poDS->nBands * bytesPerComponent;
+    recordSize = bytesPerPixel * nBlockXSize * nBlockYSize;
+    numXBlocks =
+        (unsigned long) ceil((double) poDS->info->xSize / nBlockXSize);
+    numYBlocks =
+        (unsigned long) ceil((double) poDS->info->ySize / nBlockYSize);
+
+    tmpImage = (char *) malloc(recordSize);
+    if (! tmpImage)
+        CPLError(CE_Fatal, CPLE_NotSupported, 
+                 "FITRasterBand couldn't allocate %lu bytes", recordSize);
+
+/* -------------------------------------------------------------------- */
+/*      Set the access flag.  For now we set it the same as the         */
+/*      whole dataset, but eventually this should take account of       */
+/*      locked channels, or read-only secondary data files.             */
+/* -------------------------------------------------------------------- */
+    /* ... */
+}
+
+
+FITRasterBand::~FITRasterBand()
+{
+    if ( tmpImage )
+        free ( tmpImage );
+}
+
+
+/************************************************************************/
+/*                            IReadBlock()                              */
+/************************************************************************/
+
+#define COPY_XFIRST(t) { \
+                t *dstp = (t *) pImage; \
+                t *srcp = (t *) tmpImage; \
+                srcp += nBand-1; \
+                long i = 0; \
+                for(long y=ystart; y != ystop; y+= yinc) \
+                    for(long x=xstart; x != xstop; x+= xinc, i++) { \
+                        dstp[i] = srcp[(y * nBlockXSize + x) * \
+                                       poFIT_DS->nBands]; \
+                    } \
+    }
+
+
+#define COPY_YFIRST(t) { \
+                t *dstp = (t *) pImage; \
+                t *srcp = (t *) tmpImage; \
+                srcp += nBand-1; \
+                long i = 0; \
+                for(long x=xstart; x != xstop; x+= xinc, i++) \
+                    for(long y=ystart; y != ystop; y+= yinc) { \
+                        dstp[i] = srcp[(x * nBlockYSize + y) * \
+                                       poFIT_DS->nBands]; \
+                    } \
+    }
+
+CPLErr FITRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+
+{
+    FITDataset	*poFIT_DS = (FITDataset *) poDS;
+
+    uint64 tilenum = 0;
+
+    switch (poFIT_DS->info->space) {
+    case 1:
+        // iflUpperLeftOrigin - from upper left corner
+        // scan right then down
+        tilenum = nBlockYOff * numXBlocks + nBlockXOff;
+        break;
+    case 2:
+        // iflUpperRightOrigin - from upper right corner
+        // scan left then down
+        tilenum = numYBlocks * numXBlocks + (numXBlocks-1-nBlockXOff);
+        break;
+    case 3:
+        // iflLowerRightOrigin - from lower right corner
+        // scan left then up
+        tilenum = (numYBlocks-1-nBlockYOff) * numXBlocks +
+            (numXBlocks-1-nBlockXOff);
+        break;
+    case 4:
+        // iflLowerLeftOrigin - from lower left corner
+        // scan right then up
+        tilenum = (numYBlocks-1-nBlockYOff) * numXBlocks + nBlockXOff;
+        break;
+    case 5:
+        // iflLeftUpperOrigin -* from upper left corner
+        // scan down then right
+        tilenum = nBlockXOff * numYBlocks + nBlockYOff;
+        break;
+    case 6:
+        // iflRightUpperOrigin - from upper right corner
+        // scan down then left
+        tilenum = (numXBlocks-1-nBlockXOff) * numYBlocks + nBlockYOff;
+        break;
+    case 7:
+        // iflRightLowerOrigin - from lower right corner
+        // scan up then left
+        tilenum = nBlockXOff * numYBlocks + (numYBlocks-1-nBlockYOff);
+        break;
+    case 8:
+        // iflLeftLowerOrigin -* from lower left corner
+        // scan up then right
+        tilenum = (numXBlocks-1-nBlockXOff) * numYBlocks +
+            (numYBlocks-1-nBlockYOff);
+        break;
+    default:
+        CPLError(CE_Failure, CPLE_NotSupported,
+                 "FIT - unrecognized image space %i",
+                 poFIT_DS->info->space);
+        tilenum = 0;
+    } // switch
+
+    uint64 offset = poFIT_DS->info->dataOffset + recordSize * tilenum;
+//     CPLDebug("FIT", "%i RasterBand::IReadBlock %i %i (out of %i %i) -- %i",
+//              poFIT_DS->info->space,
+//              nBlockXOff, nBlockYOff, numXBlocks, numYBlocks, tilenum);
+
+    if ( VSIFSeekL( poFIT_DS->fp, offset, SEEK_SET ) == -1 ) {
+        CPLError(CE_Failure, CPLE_NotSupported,
+                 "FIT - 64bit file seek failure, handle=%p", poFIT_DS->fp );
+	    return CE_Failure;
+    }
+
+    // XXX - should handle status
+    // fast path is single component (ll?) - no copy needed
+    char *p;
+    int fastpath = FALSE;
+
+    if ((poFIT_DS->nBands == 1) && (poFIT_DS->info->space == 1)) // upper left
+        fastpath = TRUE;
+
+    if (! fastpath) {
+        VSIFReadL( tmpImage, recordSize, 1, poFIT_DS->fp );
+        // offset to correct component to swap
+        p = (char *) tmpImage + nBand-1;
+    }
+    else {
+        VSIFReadL( pImage, recordSize, 1, poFIT_DS->fp );
+        p = (char *) pImage;
+    }
+
+
+#ifdef swapping
+    unsigned long i = 0;
+
+    switch(bytesPerComponent) {
+    case 1:
+        // do nothing
+        break;
+    case 2:
+        for(i=0; i < recordSize; i+= bytesPerPixel)
+            gst_swap16(p + i);
+        break;
+    case 4:
+        for(i=0; i < recordSize; i+= bytesPerPixel)
+            gst_swap32(p + i);
+        break;
+    case 8:
+        for(i=0; i < recordSize; i+= bytesPerPixel)
+            gst_swap64(p + i);
+        break;
+    default:
+        CPLError(CE_Failure, CPLE_NotSupported, 
+                 "FITRasterBand::IReadBlock unsupported bytesPerPixel %lu",
+                 bytesPerComponent);
+    } // switch
+#endif // swapping
+
+    if (! fastpath) {
+        long xinc, yinc, xstart, ystart, xstop, ystop;
+        if (poFIT_DS->info->space <= 4) {
+            // scan left/right first
+
+            switch (poFIT_DS->info->space) {
+            case 1:
+                // iflUpperLeftOrigin - from upper left corner
+                // scan right then down
+                xinc = 1;
+                yinc = 1;
+                break;
+            case 2:
+                // iflUpperRightOrigin - from upper right corner
+                // scan left then down
+                xinc = -1;
+                yinc = 1;
+                break;
+            case 3:
+                // iflLowerRightOrigin - from lower right corner
+                // scan left then up
+                xinc = -1;
+                yinc = -1;
+                break;
+            case 4:
+                // iflLowerLeftOrigin - from lower left corner
+                // scan right then up
+                xinc = 1;
+                yinc = -1;
+               break;
+            default:
+                CPLError(CE_Failure, CPLE_NotSupported,
+                         "FIT - unrecognized image space %i",
+                         poFIT_DS->info->space);
+                xinc = 1;
+                yinc = 1;
+            } // switch
+
+
+            if (xinc == 1) {
+                xstart = 0;
+                xstop = nBlockXSize;
+            }
+            else {
+                xstart = nBlockXSize-1;
+                xstop = -1;
+            }
+            if (yinc == 1) {
+                ystart = 0;
+                ystop = nBlockYSize;
+            }
+            else {
+                int localBlockYSize = nBlockYSize;
+                long maxy_full =
+                    (long) floor(poFIT_DS->info->ySize / (double) nBlockYSize);
+                if (nBlockYOff >= maxy_full)
+                    localBlockYSize = poFIT_DS->info->ySize % nBlockYSize;
+                ystart = localBlockYSize-1;
+                ystop = -1;
+            }
+
+            switch(bytesPerComponent) {
+            case 1:
+                COPY_XFIRST(char);
+                break;
+            case 2:
+                COPY_XFIRST(uint16);
+                break;
+            case 4:
+                COPY_XFIRST(uint32);
+                break;
+            case 8:
+                COPY_XFIRST(uint64);
+                break;
+            default:
+                CPLError(CE_Failure, CPLE_NotSupported, 
+                         "FITRasterBand::IReadBlock unsupported "
+                         "bytesPerComponent % %lu", bytesPerComponent);
+            } // switch
+
+        } // scan left/right first
+        else {
+            // scan up/down first
+
+            switch (poFIT_DS->info->space) {
+            case 5:
+                // iflLeftUpperOrigin -* from upper left corner
+                // scan down then right
+                xinc = 1;
+                yinc = 1;
+                break;
+            case 6:
+                // iflRightUpperOrigin - from upper right corner
+                // scan down then left
+                xinc = -1;
+                yinc = 1;
+                break;
+            case 7:
+                // iflRightLowerOrigin - from lower right corner
+                // scan up then left
+                xinc = -1;
+                yinc = -1;
+                break;
+            case 8:
+                // iflLeftLowerOrigin -* from lower left corner
+                // scan up then right
+                xinc = 1;
+                yinc = -1;
+                break;
+            default:
+                CPLError(CE_Failure, CPLE_NotSupported,
+                         "FIT - unrecognized image space %i",
+                         poFIT_DS->info->space);
+                xinc = 1;
+                yinc = 1;
+            } // switch
+
+            if (xinc == 1) {
+                xstart = 0;
+                xstop = nBlockXSize;
+            }
+            else {
+                int localBlockXSize = nBlockXSize;
+                long maxx_full =
+                    (long) floor(poFIT_DS->info->xSize / (double) nBlockXSize);
+                if (nBlockXOff >= maxx_full)
+                    localBlockXSize = poFIT_DS->info->xSize % nBlockXSize;
+                xstart = localBlockXSize-1;
+                xstop = -1;
+            }
+            if (yinc == 1) {
+                ystart = 0;
+                ystop = nBlockYSize;
+            }
+            else {
+                ystart = nBlockYSize-1;
+                ystop = -1;
+            }
+
+            switch(bytesPerComponent) {
+            case 1:
+                COPY_YFIRST(char);
+                break;
+            case 2:
+                COPY_YFIRST(uint16);
+                break;
+            case 4:
+                COPY_YFIRST(uint32);
+                break;
+            case 8:
+                COPY_YFIRST(uint64);
+                break;
+            default:
+                CPLError(CE_Failure, CPLE_NotSupported, 
+                         "FITRasterBand::IReadBlock unsupported "
+                         "bytesPerComponent % %lu", bytesPerComponent);
+            } // switch
+
+        } // scan up/down first
+
+    } // ! fastpath
+    return CE_None;
+}
+
+#if 0
+/************************************************************************/
+/*                             ReadBlock()                              */
+/************************************************************************/
+
+CPLErr FITRasterBand::ReadBlock( int nBlockXOff, int nBlockYOff,
+                                 void * pImage )
+
+{
+    FITDataset	*poFIT_DS = (FITDataset *) poDS;
+
+    
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                             WriteBlock()                             */
+/************************************************************************/
+
+CPLErr FITRasterBand::WriteBlock( int nBlockXOff, int nBlockYOff,
+                                 void * pImage )
+
+{
+    FITDataset	*poFIT_DS = (FITDataset *) poDS;
+
+    
+
+    return CE_None;
+}
+#endif
+
+/************************************************************************/
+/*                             GetMinimum()                             */
+/************************************************************************/
+
+double FITRasterBand::GetMinimum( int *pbSuccess )
+{
+    FITDataset *poFIT_DS = (FITDataset *) poDS;
+
+    if ((! poFIT_DS) || (! poFIT_DS->info))
+        return GDALRasterBand::GetMinimum( pbSuccess );
+
+    if (pbSuccess)
+        *pbSuccess = TRUE;
+
+    if (poFIT_DS->info->version &&
+        EQUALN((const char *) &(poFIT_DS->info->version), "02", 2)) {
+        return poFIT_DS->info->minValue;
+    }
+    else {
+        return GDALRasterBand::GetMinimum( pbSuccess );
+    }
+}
+
+/************************************************************************/
+/*                             GetMaximum()                             */
+/************************************************************************/
+
+double FITRasterBand::GetMaximum( int *pbSuccess )
+{
+    FITDataset *poFIT_DS = (FITDataset *) poDS;
+
+    if ((! poFIT_DS) || (! poFIT_DS->info))
+        return GDALRasterBand::GetMaximum( pbSuccess );
+
+    if (pbSuccess)
+        *pbSuccess = TRUE;
+
+    if (EQUALN((const char *) &poFIT_DS->info->version, "02", 2)) {
+        return poFIT_DS->info->maxValue;
+    }
+    else {
+        return GDALRasterBand::GetMaximum( pbSuccess );
+    }
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp FITRasterBand::GetColorInterpretation()
+{
+    FITDataset	*poFIT_DS = (FITDataset *) poDS;
+
+    if ((! poFIT_DS) || (! poFIT_DS->info))
+        return GCI_Undefined;
+
+    switch(poFIT_DS->info->cm) {
+    case 1: // iflNegative - inverted luminance (min value is white)
+        CPLError( CE_Warning, CPLE_NotSupported, 
+                  "FIT - color model Negative not supported - ignoring model",
+                  poFIT_DS->info->cm);
+            return GCI_Undefined;
+
+    case 2: // iflLuminance - luminance
+        if (poFIT_DS->nBands != 1) {
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model Luminance mismatch with %i bands",
+                      poFIT_DS->nBands);
+            return GCI_Undefined;
+        }
+        switch (nBand) {
+        case 1:
+            return GCI_GrayIndex;
+        default:
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model Luminance unknown band %i", nBand);
+            return GCI_Undefined;
+        } // switch nBand
+
+    case 3: // iflRGB - full color (Red, Green, Blue triplets)
+        if (poFIT_DS->nBands != 3) {
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model RGB mismatch with %i bands",
+                      poFIT_DS->nBands);
+            return GCI_Undefined;
+        }
+        switch (nBand) {
+        case 1:
+            return GCI_RedBand;
+        case 2:
+            return GCI_GreenBand;
+        case 3:
+            return GCI_BlueBand;
+        default:
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model RGB unknown band %i", nBand);
+            return GCI_Undefined;
+        } // switch nBand
+
+    case 4: // iflRGBPalette - color mapped values
+        CPLError( CE_Warning, CPLE_NotSupported, 
+                  "FIT - color model RGBPalette not supported - "
+                  "ignoring model",
+                  poFIT_DS->info->cm);
+            return GCI_Undefined;
+
+    case 5: // iflRGBA - full color with transparency (alpha channel)
+        if (poFIT_DS->nBands != 4) {
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model RGBA mismatch with %i bands",
+                      poFIT_DS->nBands);
+            return GCI_Undefined;
+        }
+        switch (nBand) {
+        case 1:
+            return GCI_RedBand;
+        case 2:
+            return GCI_GreenBand;
+        case 3:
+            return GCI_BlueBand;
+        case 4:
+            return GCI_AlphaBand;
+        default:
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model RGBA unknown band %i", nBand);
+            return GCI_Undefined;
+        } // switch nBand
+
+    case 6: // iflHSV - Hue, Saturation, Value
+        if (poFIT_DS->nBands != 3) {
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model HSV mismatch with %i bands",
+                      poFIT_DS->nBands);
+            return GCI_Undefined;
+        }
+        switch (nBand) {
+        case 1:
+            return GCI_HueBand;
+        case 2:
+            return GCI_SaturationBand;
+        case 3:
+            return GCI_LightnessBand;
+        default:
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model HSV unknown band %i", nBand);
+            return GCI_Undefined;
+        } // switch nBand
+
+    case 7: // iflCMY - Cyan, Magenta, Yellow
+        if (poFIT_DS->nBands != 3) {
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model CMY mismatch with %i bands",
+                      poFIT_DS->nBands);
+            return GCI_Undefined;
+        }
+        switch (nBand) {
+        case 1:
+            return GCI_CyanBand;
+        case 2:
+            return GCI_MagentaBand;
+        case 3:
+            return GCI_YellowBand;
+        default:
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model CMY unknown band %i", nBand);
+            return GCI_Undefined;
+        } // switch nBand
+
+    case 8: // iflCMYK - Cyan, Magenta, Yellow, Black
+        if (poFIT_DS->nBands != 4) {
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model CMYK mismatch with %i bands",
+                      poFIT_DS->nBands);
+            return GCI_Undefined;
+        }
+        switch (nBand) {
+        case 1:
+            return GCI_CyanBand;
+        case 2:
+            return GCI_MagentaBand;
+        case 3:
+            return GCI_YellowBand;
+        case 4:
+            return GCI_BlackBand;
+        default:
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model CMYK unknown band %i", nBand);
+            return GCI_Undefined;
+        } // switch nBand
+
+    case 9: // iflBGR - full color (ordered Blue, Green, Red)
+        if (poFIT_DS->nBands != 3) {
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model BGR mismatch with %i bands",
+                      poFIT_DS->nBands);
+            return GCI_Undefined;
+        }
+        switch (nBand) {
+        case 1:
+            return GCI_BlueBand;
+        case 2:
+            return GCI_GreenBand;
+        case 3:
+            return GCI_RedBand;
+        default:
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model BGR unknown band %i", nBand);
+            return GCI_Undefined;
+        } // switch nBand
+
+    case 10: // iflABGR - Alpha, Blue, Green, Red (SGI frame buffers)
+        if (poFIT_DS->nBands != 4) {
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model ABGR mismatch with %i bands",
+                      poFIT_DS->nBands);
+            return GCI_Undefined;
+        }
+        switch (nBand) {
+        case 1:
+            return GCI_AlphaBand;
+        case 2:
+            return GCI_BlueBand;
+        case 3:
+            return GCI_GreenBand;
+        case 4:
+            return GCI_RedBand;
+        default:
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model ABGR unknown band %i", nBand);
+            return GCI_Undefined;
+        } // switch nBand
+
+    case 11: // iflMultiSpectral - multi-spectral data, arbitrary number of
+        // chans
+        return GCI_Undefined;
+
+    case 12: // iflYCC PhotoCD color model (Luminance, Chrominance)
+        CPLError( CE_Warning, CPLE_NotSupported, 
+                  "FIT - color model YCC not supported - ignoring model",
+                  poFIT_DS->info->cm);
+            return GCI_Undefined;
+
+    case 13: // iflLuminanceAlpha - Luminance plus alpha
+        if (poFIT_DS->nBands != 2) {
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model LuminanceAlpha mismatch with "
+                      "%i bands",
+                      poFIT_DS->nBands);
+            return GCI_Undefined;
+        }
+        switch (nBand) {
+        case 1:
+            return GCI_GrayIndex;
+        case 2:
+            return GCI_AlphaBand;
+        default:
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "FIT - color model LuminanceAlpha unknown band %i",
+                      nBand);
+            return GCI_Undefined;
+        } // switch nBand
+
+    default:
+        CPLError( CE_Warning, CPLE_NotSupported, 
+                  "FIT - unrecognized color model %i - ignoring model",
+                  poFIT_DS->info->cm);
+        return GCI_Undefined;
+    } // switch
+}
+
+/************************************************************************/
+/*                             FITDataset()                             */
+/************************************************************************/
+
+FITDataset::FITDataset() : fp( NULL ), info( NULL )
+{
+
+    adfGeoTransform[0] = 0.0; // x origin (top left corner)
+    adfGeoTransform[1] = 1.0; // x pixel size
+    adfGeoTransform[2] = 0.0;
+
+    adfGeoTransform[3] = 0.0; // y origin (top left corner)
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0; // y pixel size
+}
+
+/************************************************************************/
+/*                             ~FITDataset()                             */
+/************************************************************************/
+
+FITDataset::~FITDataset()
+{
+    FlushCache();
+    if (info)
+        delete(info);
+}
+
+// simple guard object to delete memory 
+// when the guard goes out of scope
+template< class T >
+class DeleteGuard
+{
+public:
+    DeleteGuard( T *p ) : _ptr( p ) { }
+    ~DeleteGuard()
+    {
+	    delete _ptr;
+    }
+
+    T *take()
+    {
+        T *tmp = _ptr;
+	    _ptr = 0;
+	    return tmp;
+    }
+
+private:
+    T *_ptr;
+	// prevent default copy constructor and assignment operator
+    DeleteGuard( const DeleteGuard & );  
+    DeleteGuard &operator=( const DeleteGuard & );
+};
+
+// simple guard object to free memory 
+// when the guard goes out of scope
+template< class T >
+class FreeGuard
+{
+public:
+    FreeGuard( T *p ) : _ptr( p ) { }
+    ~FreeGuard()
+    {
+	    if ( _ptr )
+			free( _ptr );
+    }
+
+    T *take()
+    {
+        T *tmp = _ptr;
+	    _ptr = 0;
+	    return tmp;
+    }
+
+private:
+    T *_ptr;
+	// prevent default copy constructor and assignment operator
+    FreeGuard( const FreeGuard & );  
+    FreeGuard &operator=( const FreeGuard & );
+};
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *FITDataset::Open( GDALOpenInfo * poOpenInfo )
+{
+/* -------------------------------------------------------------------- */
+/*	First we check to see if the file has the expected header	*/
+/*	bytes.								*/    
+/* -------------------------------------------------------------------- */
+
+    if( poOpenInfo->nHeaderBytes < 5 )
+        return NULL;
+
+
+    if( !EQUALN((const char *) poOpenInfo->pabyHeader, "IT01", 4) &&
+        !EQUALN((const char *) poOpenInfo->pabyHeader, "IT02", 4) )
+        return NULL;
+
+    if( poOpenInfo->eAccess == GA_Update )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "The FIT driver does not support update access to existing"
+                  " files.\n" );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    FITDataset 	*poDS;
+
+    poDS = new FITDataset();
+    DeleteGuard<FITDataset> guard( poDS );
+
+    // close FILE* as it will not handle large files
+    VSIFClose( poOpenInfo->fp );
+    poOpenInfo->fp = NULL;
+
+	// re-open file for large file (64bit) access
+    if ( poOpenInfo->eAccess == GA_ReadOnly )
+	poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
+    else
+	poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" );
+
+    if ( !poDS->fp ) {
+	    CPLError( CE_Failure, CPLE_OpenFailed,
+		"Failed to re-open %s with FIT driver.\n",
+		poOpenInfo->pszFilename );
+	    return NULL;
+    }
+    poDS->eAccess = poOpenInfo->eAccess;
+
+
+    poDS->info = new FITinfo;
+    FITinfo *info = poDS->info;
+
+/* -------------------------------------------------------------------- */
+/*      Read other header values.                                       */
+/* -------------------------------------------------------------------- */
+    FIThead02 *head = (FIThead02 *) poOpenInfo->pabyHeader;
+
+    // extract the image attributes from the file header
+    if (EQUALN((const char *) &head->version, "02", 2)) {
+        // incomplete header
+        if( poOpenInfo->nHeaderBytes < (signed) sizeof(FIThead02) )
+            return NULL;
+
+        CPLDebug("FIT", "Loading file with header version 02");
+
+        gst_swapb(head->minValue);
+	info->minValue = head->minValue;
+        gst_swapb(head->maxValue);
+	info->maxValue = head->maxValue;
+        gst_swapb(head->dataOffset);
+	info->dataOffset = head->dataOffset;
+
+        info->userOffset = sizeof(FIThead02);
+    }
+    else if (EQUALN((const char *) &head->version, "01", 2)) {
+        // incomplete header
+        if( poOpenInfo->nHeaderBytes < (signed) sizeof(FIThead01) )
+            return NULL;
+
+        CPLDebug("FIT", "Loading file with header version 01");
+
+        // map old style header into new header structure
+	FIThead01* head01 = (FIThead01*)&head;
+        gst_swapb(head->dataOffset);
+	info->dataOffset = head01->dataOffset;
+
+        info->userOffset = sizeof(FIThead01);
+    }
+    else {
+        // unrecognized header version
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "FIT - unsupported header version %.2s\n",
+                  &head->version);
+        return NULL;
+    }
+
+    CPLDebug("FIT", "userOffset %i, dataOffset %i",
+             info->userOffset, info->dataOffset);
+
+    info->magic = head->magic;
+    info->version = head->version;
+
+    gst_swapb(head->xSize);
+    info->xSize = head->xSize;
+    gst_swapb(head->ySize);
+    info->ySize = head->ySize;
+    gst_swapb(head->zSize);
+    info->zSize = head->zSize;
+    gst_swapb(head->cSize);
+    info->cSize = head->cSize;
+    gst_swapb(head->dtype);
+    info->dtype = head->dtype;
+    gst_swapb(head->order);
+    info->order = head->order;
+    gst_swapb(head->space);
+    info->space = head->space;
+    gst_swapb(head->cm);
+    info->cm = head->cm;
+    gst_swapb(head->xPageSize);
+    info->xPageSize = head->xPageSize;
+    gst_swapb(head->yPageSize);
+    info->yPageSize = head->yPageSize;
+    gst_swapb(head->zPageSize);
+    info->zPageSize = head->zPageSize;
+    gst_swapb(head->cPageSize);
+    info->cPageSize = head->cPageSize;
+
+    CPLDebug("FIT", "size %i %i %i %i, pageSize %i %i %i %i",
+             info->xSize, info->ySize, info->zSize, info->cSize, 
+             info->xPageSize, info->yPageSize, info->zPageSize,
+             info->cPageSize);
+
+    CPLDebug("FIT", "dtype %i order %i space %i cm %i",
+             info->dtype, info->order, info->space, info->cm);
+
+    /**************************/
+
+    poDS->nRasterXSize = head->xSize;
+    poDS->nRasterYSize = head->ySize;
+    poDS->nBands = head->cSize;
+
+/* -------------------------------------------------------------------- */
+/*      Check if 64 bit seek is needed.                                 */
+/* -------------------------------------------------------------------- */
+    uint64 bytesPerComponent =
+        (GDALGetDataTypeSize(fitDataType(poDS->info->dtype)) / 8);
+    uint64 bytesPerPixel = head->cSize * bytesPerComponent;
+    uint64 recordSize = bytesPerPixel * head->xPageSize *
+        head->yPageSize;
+    uint64 numXBlocks =
+        (uint64) ceil((double) head->xSize / head->xPageSize);
+    uint64 numYBlocks =
+        (uint64) ceil((double) head->ySize / head->yPageSize);
+
+    uint64 maxseek = recordSize * numXBlocks * numYBlocks;
+
+//     CPLDebug("FIT", "(sizeof %i) max seek %llx ==> %llx\n", sizeof(uint64),
+//              maxseek, maxseek >> 31);
+    if (maxseek >> 31) // signed long
+#ifdef VSI_LARGE_API_SUPPORTED
+        CPLDebug("FIT", "Using 64 bit version of fseek");
+#else
+        CPLError(CE_Fatal, CPLE_NotSupported, 
+                 "FIT - need 64 bit version of fseek");
+#endif
+
+/* -------------------------------------------------------------------- */
+/*      Verify all "unused" header values.                              */
+/* -------------------------------------------------------------------- */
+
+    if( info->zSize != 1 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "FIT driver - unsupported zSize %i\n", info->zSize);
+        return NULL;
+    }
+
+    if( info->order != 1 ) // interleaved - RGBRGB
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "FIT driver - unsupported order %i\n", info->order);
+        return NULL;
+    }
+
+    if( info->zPageSize != 1 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "FIT driver - unsupported zPageSize %i\n", info->zPageSize);
+        return NULL;
+    }
+
+    if( info->cPageSize != info->cSize )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "FIT driver - unsupported cPageSize %i (!= %i)\n",
+                  info->cPageSize, info->cSize);
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    for( int i = 0; i < poDS->nBands; i++ )
+    {
+        poDS->SetBand( i+1,  new FITRasterBand( poDS, i+1 ) ) ;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return guard.take();
+}
+
+/************************************************************************/
+/*                           FITCreateCopy()                            */
+/************************************************************************/
+
+#ifdef FIT_WRITE
+static GDALDataset *FITCreateCopy(const char * pszFilename,
+                                  GDALDataset *poSrcDS, 
+                                  int bStrict, char ** papszOptions, 
+                                  GDALProgressFunc pfnProgress,
+                                  void * pProgressData )
+{
+    CPLDebug("FIT", "CreateCopy %s - %i", pszFilename, bStrict);
+
+/* -------------------------------------------------------------------- */
+/*      Create the dataset.                                             */
+/* -------------------------------------------------------------------- */
+    FILE	*fpImage;
+
+    if( !pfnProgress( 0.0, NULL, pProgressData ) )
+    {
+        CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
+        return NULL;
+    }
+
+    fpImage = VSIFOpenL( pszFilename, "wb" );
+    if( fpImage == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "FIT - unable to create file %s.\n", 
+                  pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Generate header.                                                */
+/* -------------------------------------------------------------------- */
+    // XXX - should FIT_PAGE_SIZE be based on file page size ??
+    int size = MAX(sizeof(FIThead02), FIT_PAGE_SIZE);
+    FIThead02 *head = (FIThead02 *) malloc(size);
+    FreeGuard<FIThead02> guardHead( head );
+
+    // clean header so padding (past real header) is all zeros
+    memset( head, 0, size );
+
+    strncpy((char *) &head->magic, "IT", 2);
+    strncpy((char *) &head->version, "02", 2);
+
+    head->xSize = poSrcDS->GetRasterXSize();
+    gst_swapb(head->xSize);
+    head->ySize = poSrcDS->GetRasterYSize();
+    gst_swapb(head->ySize);
+    head->zSize = 1;
+    gst_swapb(head->zSize);
+    int nBands = poSrcDS->GetRasterCount();
+    head->cSize = nBands;
+    gst_swapb(head->cSize);
+
+    GDALRasterBand *firstBand = poSrcDS->GetRasterBand(1);
+    if (! firstBand) {
+        //free(head);
+        return NULL;
+    }
+
+    head->dtype = fitGetDataType(firstBand->GetRasterDataType());
+    if (! head->dtype) {
+        //free(head);
+        return NULL;
+    }
+    gst_swapb(head->dtype);
+    head->order = 1; // interleaved - RGBRGB
+    gst_swapb(head->order);
+    head->space = 1; // upper left
+    gst_swapb(head->space);
+
+    // XXX - need to check all bands
+    head->cm = fitGetColorModel(firstBand->GetColorInterpretation(), nBands);
+    gst_swapb(head->cm);
+
+    int blockX, blockY;
+    firstBand->GetBlockSize(&blockX, &blockY);
+    CPLDebug("FIT write", "inherited block size %ix%i", blockX, blockY);
+
+    if( CSLFetchNameValue(papszOptions,"PAGESIZE") != NULL )
+    {
+        const char *str = CSLFetchNameValue(papszOptions,"PAGESIZE");
+        int newBlockX, newBlockY;
+        sscanf(str, "%i,%i", &newBlockX, &newBlockY);
+        if (newBlockX && newBlockY) {
+            blockX = newBlockX;
+            blockY = newBlockY;
+        }
+        else {
+            CPLError(CE_Failure, CPLE_OpenFailed, 
+                     "FIT - Unable to parse option PAGESIZE values [%s]", str);
+        }
+    }
+
+    // XXX - need to do lots of checking of block size
+    // * provide ability to override block size with options
+    // * handle non-square block size (like scanline)
+    //   - probably default from non-tiled image - have default block size
+    // * handle block size bigger than image size
+    // * undesirable block size (non power of 2, others?)
+    // * mismatched block sizes for different bands
+    // * image that isn't even pages (ie. partially empty pages at edge)
+    CPLDebug("FIT write", "using block size %ix%i", blockX, blockY);
+
+    head->xPageSize = blockX;
+    gst_swapb(head->xPageSize);
+    head->yPageSize = blockY;
+    gst_swapb(head->yPageSize);
+    head->zPageSize = 1;
+    gst_swapb(head->zPageSize);
+    head->cPageSize = nBands;
+    gst_swapb(head->cPageSize);
+
+    // XXX - need to check all bands
+    head->minValue = firstBand->GetMinimum();
+    gst_swapb(head->minValue);
+    // XXX - need to check all bands
+    head->maxValue = firstBand->GetMaximum();
+    gst_swapb(head->maxValue);
+    head->dataOffset = size;
+    gst_swapb(head->dataOffset);
+
+    VSIFWriteL(head, size, 1, fpImage);
+
+/* -------------------------------------------------------------------- */
+/*      Loop over image, copying image data.                            */
+/* -------------------------------------------------------------------- */
+    unsigned long bytesPerComponent =
+        (GDALGetDataTypeSize(firstBand->GetRasterDataType()) / 8);
+    unsigned long bytesPerPixel = nBands * bytesPerComponent;
+
+    unsigned long pageBytes = blockX * blockY * bytesPerPixel;
+    char *output = (char *) malloc(pageBytes);
+    if (! output)
+        CPLError(CE_Fatal, CPLE_NotSupported, 
+                 "FITRasterBand couldn't allocate %lu bytes", pageBytes);
+    FreeGuard<char> guardOutput( output );
+
+    long maxx = (long) ceil(poSrcDS->GetRasterXSize() / (double) blockX);
+    long maxy = (long) ceil(poSrcDS->GetRasterYSize() / (double) blockY);
+    long maxx_full = (long) floor(poSrcDS->GetRasterXSize() / (double) blockX);
+    long maxy_full = (long) floor(poSrcDS->GetRasterYSize() / (double) blockY);
+
+    CPLDebug("FIT", "about to write %ix%i blocks", maxx, maxy);
+
+    for(long y=0; y < maxy; y++)
+        for(long x=0; x < maxx; x++) {
+            long readX = blockX;
+            long readY = blockY;
+            int do_clean = FALSE;
+
+            // handle cases where image size isn't an exact multiple
+            // of page size
+            if (x >= maxx_full) {
+                readX = poSrcDS->GetRasterXSize() % blockX;
+                do_clean = TRUE;
+            }
+            if (y >= maxy_full) {
+                readY = poSrcDS->GetRasterYSize() % blockY;
+                do_clean = TRUE;
+            }
+
+            // clean out image if only doing partial reads
+            if (do_clean)
+                memset( output, 0, pageBytes );
+
+            for( int iBand = 0; iBand < nBands; iBand++ ) {
+                GDALRasterBand * poBand = poSrcDS->GetRasterBand( iBand+1 );
+                CPLErr eErr =
+                    poBand->RasterIO( GF_Read, // eRWFlag
+                                      x * blockX, // nXOff
+                                      y * blockY, // nYOff
+                                      readX, // nXSize
+                                      readY, // nYSize
+                                      output + iBand * bytesPerComponent,
+                                      // pData
+                                      blockX, // nBufXSize
+                                      blockY, // nBufYSize
+                                      firstBand->GetRasterDataType(),
+                                      // eBufType
+                                      bytesPerPixel, // nPixelSpace
+                                      bytesPerPixel * blockX); // nLineSpace
+                if (eErr != CE_None)
+                    CPLError(CE_Failure, CPLE_FileIO, 
+                             "FIT write - CreateCopy got read error %i", eErr);
+            } // for iBand
+
+#ifdef swapping
+            char *p = output;
+            unsigned long i;
+            switch(bytesPerComponent) {
+            case 1:
+                // do nothing
+                break;
+            case 2:
+                for(i=0; i < pageBytes; i+= bytesPerComponent)
+                    gst_swap16(p + i);
+                break;
+            case 4:
+                for(i=0; i < pageBytes; i+= bytesPerComponent)
+                    gst_swap32(p + i);
+                break;
+            case 8:
+                for(i=0; i < pageBytes; i+= bytesPerComponent)
+                    gst_swap64(p + i);
+                break;
+            default:
+                CPLError(CE_Failure, CPLE_NotSupported, 
+                         "FIT write - unsupported bytesPerPixel %lu",
+                         bytesPerComponent);
+            } // switch
+#endif // swapping
+            
+            VSIFWriteL(output, pageBytes, 1, fpImage);
+
+            double perc = ((double) (y * maxx + x)) / (maxx * maxy);
+//             printf("progress %f\n", perc);
+            if( !pfnProgress( perc, NULL, pProgressData ) )
+            {
+                CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
+                //free(output);
+                VSIFCloseL( fpImage );
+                VSIUnlink( pszFilename );
+                return NULL;
+            }
+        } // for x
+
+    //free(output);
+
+    VSIFCloseL( fpImage );
+
+    pfnProgress( 1.0, NULL, pProgressData );
+
+/* -------------------------------------------------------------------- */
+/*      Re-open dataset, and copy any auxilary pam information.         */
+/* -------------------------------------------------------------------- */
+    GDALPamDataset *poDS = (GDALPamDataset *) 
+        GDALOpen( pszFilename, GA_ReadOnly );
+
+    if( poDS )
+        poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
+
+    return poDS;
+}
+#endif // FIT_WRITE
+
+/************************************************************************/
+/*                           GetGeoTransform()                          */
+/************************************************************************/
+
+// CPLErr FITDataset::GetGeoTransform( double * padfTransform )
+// {
+//     CPLDebug("FIT", "FITDataset::GetGeoTransform");
+//     memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
+//     return( CE_None );
+// }
+
+/************************************************************************/
+/*                          GDALRegister_FIT()                          */
+/************************************************************************/
+
+void GDALRegister_FIT()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "FIT" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "FIT" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "FIT Image" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_various.html#" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "" );
+
+        poDriver->pfnOpen = FITDataset::Open;
+#ifdef FIT_WRITE
+        poDriver->pfnCreateCopy = FITCreateCopy;
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte UInt16 Int16 UInt32 Int32 Float32 Float64" );
+#endif // FIT_WRITE
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
diff --git a/Utilities/GDAL/frmts/fit/gstEndian.h b/Utilities/GDAL/frmts/fit/gstEndian.h
new file mode 100644
index 0000000000..3abac82f66
--- /dev/null
+++ b/Utilities/GDAL/frmts/fit/gstEndian.h
@@ -0,0 +1,140 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  FIT Driver
+ * Purpose:  Implement FIT Support - not using the SGI iflFIT library.
+ * Author:   Philip Nemec, nemec@keyholecorp.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Keyhole, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: gstEndian.h,v $
+ * Revision 1.4  2005/07/05 17:10:52  fwarmerdam
+ * give swap funcs unique names
+ *
+ * Revision 1.3  2004/03/22 23:45:41  aubin
+ * wrap endian swapping functions in namespace to limit scope
+ *
+ * Revision 1.2  2001/07/06 18:46:25  nemec
+ * Cleanup files - improve Windows build, make proper copyright notice
+ *
+ *
+ */
+
+#ifndef _gstEndian_h_
+#define _gstEndian_h_
+
+// endian swapping tools
+
+#include <stdio.h>
+#include <cpl_port.h>
+
+#include "gstTypes.h"
+
+namespace gstEndian {
+
+// have to do swapping on Linux and Windows
+#ifdef CPL_LSB
+#define swapping
+#else
+#endif
+
+#ifdef swapping
+size_t swapped_fread(void *ptr, size_t size, size_t nitems, FILE *stream);
+size_t swapped_fwrite(const void *ptr, size_t size, size_t nitems, FILE
+                      *stream);
+
+static inline void gst_swap64(void * value)
+{
+    // 0x1122334455667788 --> 0x8877665544332211
+
+	*(uint64 *)(value) =
+		   ( ((*(uint64 *)(value) & 0x00000000000000ff) << 56) |
+        	 ((*(uint64 *)(value) & 0x000000000000ff00) << 40)  |
+        	 ((*(uint64 *)(value) & 0x0000000000ff0000) << 24)  |
+        	 ((*(uint64 *)(value) & 0x00000000ff000000) << 8)  |
+        	 ((*(uint64 *)(value) >> 8) & 0x00000000ff000000)  |
+        	 ((*(uint64 *)(value) >> 24) & 0x0000000000ff0000)  |
+        	 ((*(uint64 *)(value) >> 40) & 0x000000000000ff00)  |
+        	 ((*(uint64 *)(value) >> 56) & 0x00000000000000ff) );
+}
+
+static inline void gst_swap32(void * value)
+{
+    // 0x12 34 56 78 --> 0x78 56 34 12
+
+	*(uint32 *)(value) =
+	       ( ((*(uint32 *)(value) & 0x000000ff) << 24) |
+        	 ((*(uint32 *)(value) & 0x0000ff00) << 8)  |
+        	 ((*(uint32 *)(value) >> 8) & 0x0000ff00)  |
+        	 ((*(uint32 *)(value) >> 24) & 0x000000ff) );
+}
+
+static inline void gst_swap16(void * value)
+{
+    *(uint16 *)(value) = 
+		   ( ((*(uint16 *)(value) & 0x00ff) << 8) |
+             ((*(uint16 *)(value) >> 8) & 0x00ff) );
+}
+
+static inline void gst_swapbytes(void * value, int size)
+{
+    switch (size) {
+    case 1:
+        // do nothing
+        break;
+    case 2:
+        gst_swap16(value);
+        break;
+    case 4:
+        gst_swap32(value);
+        break;
+    case 8:
+        gst_swap64(value);
+        break;
+    default:
+        fprintf(stderr, "gst_swapbytes unsupported size %i - not swapping\n",
+                size);
+        break;
+    } // switch
+}
+
+#define gst_swapb( value )  gst_swapbytes( &value, sizeof(value))
+
+#else // swapping
+
+#define swapped_fread(ptr, size, nitems, stream) \
+	fread(ptr, size, nitems, stream)
+#define swapped_fwrite(ptr, size, nitems, stream) \
+	fwrite(ptr, size, nitems, stream)
+
+#define gst_swap64( vlaue )
+#define gst_swap32( vlaue )
+#define gst_swap16( vlaue )
+#define gst_swapbytes( value, size )
+#define gst_swapb( value )
+
+#endif // swapping
+
+} // gstEndian namespace
+
+#endif // ! _gstEndian_h_
diff --git a/Utilities/GDAL/frmts/fit/gstTypes.h b/Utilities/GDAL/frmts/fit/gstTypes.h
new file mode 100644
index 0000000000..ccb87e7cb7
--- /dev/null
+++ b/Utilities/GDAL/frmts/fit/gstTypes.h
@@ -0,0 +1,57 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  FIT Driver
+ * Purpose:  Implement FIT Support - not using the SGI iflFIT library.
+ * Author:   Philip Nemec, nemec@keyholecorp.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Keyhole, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: gstTypes.h,v $
+ * Revision 1.3  2001/07/12 16:41:41  warmerda
+ * use CPL types for portability (ie. to Solaris)
+ *
+ * Revision 1.2  2001/07/06 18:46:25  nemec
+ * Cleanup files - improve Windows build, make proper copyright notice
+ *
+ *
+ */
+
+#ifndef _gstTypes_h_
+#define _gstTypes_h_
+
+#include <stdarg.h>
+#include "cpl_conv.h"
+
+typedef int (*gstItemGetFunc)(void *data, int tag, ...);
+
+typedef GUInt16				uint16;
+typedef GInt16				int16;
+typedef GUInt32				uint32;
+typedef GInt32				int32;
+typedef GUIntBig			uint64;
+typedef GIntBig				int64;
+
+typedef unsigned char                   uchar;
+
+#endif // !_gstTypes_h_
diff --git a/Utilities/GDAL/frmts/fit/makefile.vc b/Utilities/GDAL/frmts/fit/makefile.vc
new file mode 100644
index 0000000000..2ef481c0d3
--- /dev/null
+++ b/Utilities/GDAL/frmts/fit/makefile.vc
@@ -0,0 +1,14 @@
+
+OBJ	=	\
+	fit.obj fitdataset.obj
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/fits/GNUmakefile b/Utilities/GDAL/frmts/fits/GNUmakefile
new file mode 100644
index 0000000000..bad3bb0ad7
--- /dev/null
+++ b/Utilities/GDAL/frmts/fits/GNUmakefile
@@ -0,0 +1,15 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	fitsdataset.o
+
+FITS_OPTS	=	
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(FITS_OPTS) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/fits/fitsdataset.cpp b/Utilities/GDAL/frmts/fits/fitsdataset.cpp
new file mode 100644
index 0000000000..9853051fce
--- /dev/null
+++ b/Utilities/GDAL/frmts/fits/fitsdataset.cpp
@@ -0,0 +1,684 @@
+/******************************************************************************
+ * $Id: fitsdataset.cpp,v 1.20 2005/05/06 14:03:37 fwarmerdam Exp $
+ *
+ * Project:  FITS Driver
+ * Purpose:  Implement FITS raster read/write support
+ * Author:   Simon Perkins, s.perkins@lanl.gov
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Simon Perkins
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: fitsdataset.cpp,v $
+ * Revision 1.20  2005/05/06 14:03:37  fwarmerdam
+ * Fixed gdal_pam include.
+ *
+ * Revision 1.19  2005/05/05 14:02:58  fwarmerdam
+ * PAM Enable
+ *
+ * Revision 1.18  2003/05/06 05:20:38  sperkins
+ * cleaned up comments
+ *
+ * Revision 1.17  2003/05/06 05:14:11  sperkins
+ * Minor tidying up.
+ *
+ * Revision 1.16  2003/05/05 23:31:53  sperkins
+ * Removed dodgy "value type guessing" code from FITS metadata write routine.
+ *
+ * Revision 1.15  2002/11/23 18:54:17  warmerda
+ * added CREATIONDATATYPES metadata for drivers
+ *
+ * Revision 1.14  2002/09/04 06:50:37  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.13  2002/07/02 22:40:46  sperkins
+ * Backpedalling... Special creation options for b-scaled FITS files
+ * eliminated to reduce weirdness.
+ *
+ * Revision 1.12  2002/07/02 01:20:24  sperkins
+ * * frmts/fits/fitsdataset.cpp: Modified creation options for FITS
+ * driver and handling of "b-scaled" files. See docs for details.
+ *
+ * Revision 1.11  2002/06/12 21:12:25  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.10  2002/01/22 02:12:17  sperkins
+ * Minor formatting change.
+ *
+ * Revision 1.9  2002/01/22 01:36:54  sperkins
+ * Silently truncate when writing values to FITS files outside valid range.
+ *
+ * Revision 1.8  2001/12/06 19:25:12  warmerda
+ * updated as per submission by Diana Esch-Mosher
+ *
+ * Revision 1.7  2001/11/11 23:50:59  warmerda
+ * added required class keyword to friend declarations
+ *
+ * Revision 1.6  2001/09/14 17:05:39  warmerda
+ * Used strrchr() instead of rindex().
+ *
+ * Revision 1.5  2001/07/18 04:51:56  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.4  2001/03/11 22:31:03  sperkins
+ * Added FITS support for "byte scaled" files.
+ *
+ * Revision 1.3  2001/03/09 01:57:48  sperkins
+ * Added support for reading and writing metadata to FITS files.
+ *
+ * Revision 1.2  2001/03/06 23:06:01  sperkins
+ * Fixed bug in situation where we attempt to read from a newly created band.
+ *
+ * Revision 1.1  2001/03/06 03:53:44  sperkins
+ * Added FITS format support.
+ *
+ */
+
+
+
+#include "gdal_pam.h"
+#include "cpl_string.h"
+#include <string.h>
+
+CPL_CVSID("$Id: fitsdataset.cpp,v 1.20 2005/05/06 14:03:37 fwarmerdam Exp $");
+
+CPL_C_START
+#include <fitsio.h>
+void	GDALRegister_FITS(void);
+CPL_C_END
+
+/************************************************************************/
+/* ==================================================================== */
+/*				FITSDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class FITSRasterBand;
+
+class FITSDataset : public GDALPamDataset {
+
+  friend class FITSRasterBand;
+  
+  fitsfile* hFITS;
+
+  GDALDataType gdalDataType;   // GDAL code for the image type
+  int fitsDataType;   // FITS code for the image type
+
+  bool isExistingFile;
+  long highestOffsetWritten;  // How much of image has been written
+
+  FITSDataset();     // Others shouldn't call this constructor explicitly
+
+  CPLErr Init(fitsfile* hFITS_, bool isExistingFile_);
+
+public:
+  ~FITSDataset();
+
+  static GDALDataset* Open(GDALOpenInfo* );
+  static GDALDataset* Create(const char* pszFilename,
+			     int nXSize, int nYSize, int nBands,
+			     GDALDataType eType,
+			     char** papszParmList);
+  
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            FITSRasterBand                           */
+/* ==================================================================== */
+/************************************************************************/
+
+class FITSRasterBand : public GDALPamRasterBand {
+
+  friend class	FITSDataset;
+  
+public:
+
+  FITSRasterBand(FITSDataset*, int);
+  ~FITSRasterBand();
+
+  virtual CPLErr IReadBlock( int, int, void * );
+  virtual CPLErr IWriteBlock( int, int, void * ); 
+};
+
+
+/************************************************************************/
+/*                          FITSRasterBand()                           */
+/************************************************************************/
+
+FITSRasterBand::FITSRasterBand(FITSDataset *poDS, int nBand) {
+
+  this->poDS = poDS;
+  this->nBand = nBand;
+  eDataType = poDS->gdalDataType;
+  nBlockXSize = poDS->nRasterXSize;;
+  nBlockYSize = 1;
+}
+
+/************************************************************************/
+/*                          ~FITSRasterBand()                           */
+/************************************************************************/
+
+FITSRasterBand::~FITSRasterBand() {
+    FlushCache();
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr FITSRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
+                                  void* pImage ) {
+ 
+  // A FITS block is one row (we assume BSQ formatted data)
+  FITSDataset* dataset = (FITSDataset*) poDS;
+  fitsfile* hFITS = dataset->hFITS;
+  int status = 0;
+
+  // Since a FITS block is a whole row, nBlockXOff must be zero
+  // and the row number equals the y block offset. Also, nBlockYOff
+  // cannot be greater than the number of rows
+  CPLAssert(nBlockXOff == 0);
+  CPLAssert(nBlockYOff < nRasterYSize);
+
+  // Calculate offsets and read in the data. Note that FITS array offsets
+  // start at 1...
+  long offset = (nBand - 1) * nRasterXSize * nRasterYSize +
+    nBlockYOff * nRasterXSize + 1;
+  long nElements = nRasterXSize;
+
+  // If we haven't written this block to the file yet, then attempting
+  // to read causes an error, so in this case, just return zeros.
+  if (!dataset->isExistingFile && offset > dataset->highestOffsetWritten) {
+    memset(pImage, 0, nBlockXSize * nBlockYSize
+	   * GDALGetDataTypeSize(eDataType) / 8);
+    return CE_None;
+  }
+
+  // Otherwise read in the image data
+  fits_read_img(hFITS, dataset->fitsDataType, offset, nElements,
+		0, pImage, 0, &status);
+  if (status) {
+    CPLError(CE_Failure, CPLE_AppDefined,
+	     "Couldn't read image data from FITS file (%d).", status);
+    return CE_Failure;
+  }
+  
+  return CE_None;
+}
+
+/************************************************************************/
+/*                            IWriteBlock()                             */
+/*                                                                      */
+/************************************************************************/
+
+CPLErr FITSRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
+				   void* pImage) {
+
+  FITSDataset* dataset = (FITSDataset*) poDS;
+  fitsfile* hFITS = dataset->hFITS;
+  int status = 0;
+
+  // Since a FITS block is a whole row, nBlockXOff must be zero
+  // and the row number equals the y block offset. Also, nBlockYOff
+  // cannot be greater than the number of rows
+
+  // Calculate offsets and read in the data. Note that FITS array offsets
+  // start at 1...
+  long offset = (nBand - 1) * nRasterXSize * nRasterYSize +
+    nBlockYOff * nRasterXSize + 1;
+  long nElements = nRasterXSize;
+  fits_write_img(hFITS, dataset->fitsDataType, offset, nElements,
+		 pImage, &status);
+
+  // Capture special case of non-zero status due to data range
+  // overflow Standard GDAL policy is to silently truncate, which is
+  // what CFITSIO does, in addition to returning NUM_OVERFLOW (412) as
+  // the status.
+  if (status == NUM_OVERFLOW)
+    status = 0;
+
+  // Check for other errors
+  if (status) {
+    CPLError(CE_Failure, CPLE_AppDefined,
+	     "Error writing image data to FITS file (%d).", status);
+    return CE_Failure;
+  }
+
+  // When we write a block, update the offset counter that we've written
+  if (offset > dataset->highestOffsetWritten)
+    dataset->highestOffsetWritten = offset;
+
+  return CE_None;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             FITSDataset                             */
+/* ==================================================================== */
+/************************************************************************/
+
+// Some useful utility functions
+
+// Simple static function to determine if FITS header keyword should
+// be saved in meta data.
+static const int ignorableHeaderCount = 15;
+static char* ignorableFITSHeaders[ignorableHeaderCount] = {
+  "SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "END",
+  "XTENSION", "PCOUNT", "GCOUNT", "EXTEND", "CONTINUE",
+  "COMMENT", "", "LONGSTRN"
+};
+static bool isIgnorableFITSHeader(const char* name) {
+  for (int i = 0; i < ignorableHeaderCount; ++i) {
+    if (strcmp(name, ignorableFITSHeaders[i]) == 0)
+      return true;
+  }
+  return false;
+}
+
+
+/************************************************************************/
+/*                            FITSDataset()                            */
+/************************************************************************/
+
+FITSDataset::FITSDataset() {
+  hFITS = 0;
+}
+
+/************************************************************************/
+/*                           ~FITSDataset()                            */
+/************************************************************************/
+
+FITSDataset::~FITSDataset() {
+
+  int status;
+  if (hFITS) {
+    if(eAccess == GA_Update) {   // Only do this if we've successfully opened the file and  update capability
+      // Write any meta data to the file that's compatible with FITS
+      status = 0;
+      fits_movabs_hdu(hFITS, 1, 0, &status);
+      fits_write_key_longwarn(hFITS, &status);
+      if (status) {
+        CPLError(CE_Warning, CPLE_AppDefined,
+	         "Couldn't move to first HDU in FITS file %s (%d).\n", 
+	         GetDescription(), status);
+      }
+      char** metaData = GetMetadata();
+      int count = CSLCount(metaData);
+      for (int i = 0; i < count; ++i) {
+        const char* field = CSLGetField(metaData, i);
+        if (strlen(field) == 0)
+	  continue;
+        else {
+	  char* key;
+	  const char* value = CPLParseNameValue(field, &key);
+	  // FITS keys must be less than 8 chars
+	  if (strlen(key) <= 8 && !isIgnorableFITSHeader(key)) {
+	    // Although FITS provides support for different value
+	    // types, the GDAL Metadata mechanism works only with
+	    // string values. Prior to about 2003-05-02, this driver
+	    // would attempt to guess the value type from the metadata
+	    // value string amd then would use the appropriate
+	    // type-specific FITS keyword update routine. This was
+	    // found to be troublesome (e.g. a numeric version string
+	    // with leading zeros would be interpreted as a number
+	    // and might get those leading zeros stripped), and so now
+	    // the driver writes every value as a string. In practice
+	    // this is not a problem since most FITS reading routines
+	    // will convert from strings to numbers automatically, but
+	    // if you want finer control, use the underlying FITS
+	    // handle. Note: to avoid a compiler warning we copy the
+	    // const value string to a non const one...
+	    char* valueCpy = strdup(value);
+	    fits_update_key_longstr(hFITS, key, valueCpy, 0, &status);
+	    free(valueCpy);
+
+	    // Check for errors
+	    if (status) {
+	      CPLError(CE_Warning, CPLE_AppDefined,
+		       "Couldn't update key %s in FITS file %s (%d).", 
+		       key, GetDescription(), status);
+	      return;
+	    }
+	  }
+	  // Must free up key
+	  CPLFree(key);
+        }
+      }
+
+      // Make sure we flush the raster cache before we close the file!
+      FlushCache();
+    }
+
+    // Close the FITS handle - ignore the error status
+    fits_close_file(hFITS, &status);
+
+  }
+}
+
+
+/************************************************************************/
+/*                           Init()                                     */
+/************************************************************************/
+
+CPLErr FITSDataset::Init(fitsfile* hFITS_, bool isExistingFile_) {
+
+  hFITS = hFITS_;
+  isExistingFile = isExistingFile_;
+  highestOffsetWritten = 0;
+  int status = 0;
+
+  // Move to the primary HDU
+  fits_movabs_hdu(hFITS, 1, 0, &status);
+  if (status) {
+    CPLError(CE_Failure, CPLE_AppDefined,
+	     "Couldn't move to first HDU in FITS file %s (%d).\n", 
+	     GetDescription(), status);
+    return CE_Failure;
+  }
+
+  // The cftsio driver automatically rescales data on read and write
+  // if BSCALE and BZERO are defined as header keywords. This behavior
+  // causes overflows with GDAL and is slightly mysterious, so we
+  // disable this rescaling here.
+  fits_set_bscale(hFITS, 1.0, 0.0, &status);
+
+  // Get the image info for this dataset (note that all bands in a FITS dataset
+  // have the same type)
+  int bitpix;
+  int naxis;
+  const int maxdim = 3;
+  long naxes[maxdim];
+  fits_get_img_param(hFITS, maxdim, &bitpix, &naxis, naxes, &status);
+  if (status) {
+    CPLError(CE_Failure, CPLE_AppDefined,
+	     "Couldn't determine image parameters of FITS file %s (%d).", 
+	     GetDescription(), status);
+    return CE_Failure;
+  }
+
+  // Determine data type
+  if (bitpix == BYTE_IMG) {
+     gdalDataType = GDT_Byte;
+     fitsDataType = TBYTE;
+  }
+  else if (bitpix == SHORT_IMG) {
+    gdalDataType = GDT_Int16;
+    fitsDataType = TSHORT;
+  }
+  else if (bitpix == LONG_IMG) {
+    gdalDataType = GDT_Int32;
+    fitsDataType = TLONG;
+  }
+  else if (bitpix == FLOAT_IMG) {
+    gdalDataType = GDT_Float32;
+    fitsDataType = TFLOAT;
+  }
+  else if (bitpix == DOUBLE_IMG) {
+    gdalDataType = GDT_Float64;
+    fitsDataType = TDOUBLE;
+  }
+  else {
+    CPLError(CE_Failure, CPLE_AppDefined,
+	     "FITS file %s has unknown data type: %d.", GetDescription(), 
+	     bitpix);
+    return CE_Failure;
+  }
+
+  // Determine image dimensions - we assume BSQ ordering
+  if (naxis == 2) {
+    nRasterXSize = naxes[0];
+    nRasterYSize = naxes[1];
+    nBands = 1;
+  }
+  else if (naxis == 3) {
+    nRasterXSize = naxes[0];
+    nRasterYSize = naxes[1];
+    nBands = naxes[2];
+  }
+  else {
+    CPLError(CE_Failure, CPLE_AppDefined,
+	     "FITS file %s does not have 2 or 3 dimensions.", 
+	     GetDescription());
+    return CE_Failure;
+  }
+
+  // Read header information from file and use it to set metadata
+  // This process understands the CONTINUE standard for long strings.
+  // We don't bother to capture header names that duplicate information
+  // already captured elsewhere (e.g. image dimensions and type)
+  int keyNum = 1;
+  char key[100];
+  char value[100];
+  bool endReached = false;
+  do {
+    fits_read_keyn(hFITS, keyNum, key, value, 0, &status);
+    if (status) {
+      CPLError(CE_Failure, CPLE_AppDefined,
+	       "Error while reading key %d from FITS file %s (%d)", 
+	       keyNum, GetDescription(), status);
+      return CE_Failure;
+    }
+    // What we do next depends upon the key and the value
+    if (strcmp(key, "END") == 0) {
+      endReached = true;
+    }
+    else if (isIgnorableFITSHeader(key)) {
+      // Ignore it!
+    }
+    else {   // Going to store something, but check for long strings etc
+      // Strip off leading and trailing quote if present
+      char* newValue = value;
+      if (value[0] == '\'' && value[strlen(value) - 1] == '\'') {
+	newValue = value + 1;
+	value[strlen(value) - 1] = '\0';
+      }
+      // Check for long string
+      if (strrchr(newValue, '&') == newValue + strlen(newValue) - 1) {
+	// Value string ends in "&", so use long string conventions
+	char* longString = 0;
+	fits_read_key_longstr(hFITS, key, &longString, 0, &status);
+	// Note that read_key_longst already strips quotes
+	if (status) {
+	  CPLError(CE_Failure, CPLE_AppDefined,
+		   "Error while reading long string for key %s from "
+		   "FITS file %s (%d)", key, GetDescription(), status);
+	  return CE_Failure;
+	}
+	SetMetadataItem(key, longString);
+	free(longString);
+      }
+      else {  // Normal keyword
+	SetMetadataItem(key, newValue);
+      }
+    }
+    ++keyNum;
+  } while (!endReached);
+  
+  // Create the bands
+  for (int i = 0; i < nBands; ++i)
+    SetBand(i+1, new FITSRasterBand(this, i+1));
+  
+  return CE_None;
+}
+
+
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset* FITSDataset::Open(GDALOpenInfo* poOpenInfo) {
+
+  const char* fitsID = "SIMPLE  =                    T";  // Spaces important!
+  size_t fitsIDLen = strlen(fitsID);  // Should be 30 chars long
+
+  if ((size_t)poOpenInfo->nHeaderBytes < fitsIDLen)
+    return NULL;
+  if (memcmp(poOpenInfo->pabyHeader, fitsID, fitsIDLen))
+    return NULL;
+  
+  // Get access mode and attempt to open the file
+  int status = 0;
+  fitsfile* hFITS = 0;
+  if (poOpenInfo->eAccess == GA_ReadOnly) 
+    fits_open_file(&hFITS, poOpenInfo->pszFilename, READONLY, &status);
+  else
+    fits_open_file(&hFITS, poOpenInfo->pszFilename, READWRITE, &status);
+  if (status) {
+    CPLError(CE_Failure, CPLE_AppDefined,
+	     "Error while opening FITS file %s (%d).\n", 
+	     poOpenInfo->pszFilename, status);
+    fits_close_file(hFITS, &status);
+    return NULL;
+  }
+
+  // Create a FITSDataset object and initialize it from the FITS handle
+  FITSDataset* dataset = new FITSDataset();
+  dataset->eAccess = poOpenInfo->eAccess;
+
+  // Set up the description and initialize the dataset
+  dataset->SetDescription(poOpenInfo->pszFilename);
+  if (dataset->Init(hFITS, true) != CE_None) {
+    delete dataset;
+    return NULL;
+  }
+  else
+  {
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+      dataset->SetDescription( poOpenInfo->pszFilename );
+      dataset->TryLoadXML();
+
+      return dataset;
+  }
+}
+
+
+/************************************************************************/
+/*                               Create()                               */
+/*                                                                      */
+/*      Create a new FITS file.                                         */
+/************************************************************************/
+
+GDALDataset *FITSDataset::Create(const char* pszFilename,
+				 int nXSize, int nYSize, 
+				 int nBands, GDALDataType eType,
+				 char** papszParmList) {
+
+
+  FITSDataset* dataset;
+  fitsfile* hFITS;
+  int status = 0;
+
+  // No creation options are defined. The BSCALE/BZERO options were
+  // removed on 2002-07-02 by Simon Perkins because they introduced
+  // excessive complications and didn't really fit into the GDAL
+  // paradigm.
+
+  // Create the file - to force creation, we prepend the name with '!'
+  char* extFilename = new char[strlen(pszFilename) + 10];  // 10 for margin!
+  sprintf(extFilename, "!%s", pszFilename);
+  fits_create_file(&hFITS, extFilename, &status);
+  delete extFilename;
+  if (status) {
+    CPLError(CE_Failure, CPLE_AppDefined,
+	     "Couldn't create FITS file %s (%d).\n", pszFilename, status);
+    return NULL;
+  }
+
+  // Determine FITS type of image
+  int bitpix;
+  if (eType == GDT_Byte)
+    bitpix = BYTE_IMG;
+  else if (eType == GDT_Int16)
+    bitpix = SHORT_IMG;
+  else if (eType == GDT_Int32)
+    bitpix = LONG_IMG;
+  else if (eType == GDT_Float32)
+    bitpix = FLOAT_IMG;
+  else if (eType == GDT_Float64)
+    bitpix = DOUBLE_IMG;
+  else {
+    CPLError(CE_Failure, CPLE_AppDefined,
+	     "GDALDataType (%d) unsupported for FITS", eType);
+    fits_close_file(hFITS, &status);
+    return NULL;
+  }
+
+  // Now create an image of appropriate size and type
+  long naxes[3] = {nXSize, nYSize, nBands};
+  int naxis = (nBands == 1) ? 2 : 3;
+  fits_create_img(hFITS, bitpix, naxis, naxes, &status);
+
+  // Check the status
+  if (status) {
+    CPLError(CE_Failure, CPLE_AppDefined,
+	     "Couldn't create image within FITS file %s (%d).", 
+	     pszFilename, status);
+    fits_close_file(hFITS, &status);
+    return NULL;
+  }
+
+  dataset = new FITSDataset();
+  dataset->nRasterXSize = nXSize;
+  dataset->nRasterYSize = nYSize;
+  dataset->eAccess = GA_Update;
+  dataset->SetDescription(pszFilename);
+
+  // Init recalculates a lot of stuff we already know, but...
+  if (dataset->Init(hFITS, false) != CE_None) { 
+    delete dataset;
+    return NULL;
+  }
+  else {
+    return dataset;
+  }
+}
+
+
+/************************************************************************/
+/*                          GDALRegister_FITS()                        */
+/************************************************************************/
+
+void GDALRegister_FITS() {
+
+    GDALDriver* poDriver;
+
+    if( GDALGetDriverByName( "FITS" ) == NULL) {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "FITS" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "Flexible Image Transport System" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_various.html#FITS" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte Int16 Int32 Float32 Float64" );
+        
+        poDriver->pfnOpen = FITSDataset::Open;
+        poDriver->pfnCreate = FITSDataset::Create;
+        poDriver->pfnCreateCopy = NULL;
+
+        GetGDALDriverManager()->RegisterDriver(poDriver);
+    }
+}
diff --git a/Utilities/GDAL/frmts/fits/makefile.vc b/Utilities/GDAL/frmts/fits/makefile.vc
new file mode 100755
index 0000000000..2c651f3134
--- /dev/null
+++ b/Utilities/GDAL/frmts/fits/makefile.vc
@@ -0,0 +1,15 @@
+
+OBJ	=	fitsdataset.obj
+
+EXTRAFLAGS = 	-I$(FITS_DIR) -DFRMT_fits 
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/frmt_various.html b/Utilities/GDAL/frmts/frmt_various.html
new file mode 100644
index 0000000000..0741a116ac
--- /dev/null
+++ b/Utilities/GDAL/frmts/frmt_various.html
@@ -0,0 +1,725 @@
+<html>
+<head>
+<title>Various Supported GDAL Raster Formats</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>Various Support GDAL Raster Formats</h1>
+
+
+<h2><a name="AAIGrid">AAIGrid -- Arc/Info ASCII Grid</a></h2>
+
+Supported for read and write access, including reading of an affine
+georeferencing transform and some projections.  
+This format is the ASCII interchange format for Arc/Info Grid, 
+and takes the form of an ASCII file, plus sometimes an associated .prj file.
+It is normally produced with the Arc/Info ASCIIGRID command.<p>
+
+The projections support (read if a *.prj file is available) is quite
+limited.  Additional sample .prj files may be sent to the maintainer,
+<a href="mailto:warmerdam@pobox.com">warmerdam@pobox.com</a>.<p>
+
+The NODATA value for the grid read is also preserved when available.
+Grids are treated as signed 16bit integer unless the first scanline contains
+decimal values in which case the image is treated as 32bit floating point.
+<p>
+
+The <a href="#AIG">AIG</a> driver is also available
+for Arc/Info Binary Grid format.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/aaigrid/aaigriddataset.cpp</tt>.<p>
+
+<h2><a name="AIG">AIG -- Arc/Info Binary Grid</a></h2>
+
+Supported by GDAL for read access.  
+This format is the internal binary format for Arc/Info Grid, 
+and takes the form of a coverage level directory in an Arc/Info database. 
+To open the coverage select the coverage directory, or an .adf file (such as
+hdr.adf) from within it.<p>
+
+Support includes reading of an affine 
+georeferencing transform, some projections, and a color
+table (.clr) if available.<p>
+
+This driver is implemented based on a reverse engineering of the format.  See
+the <a href="http://home.gdal.org/projects/aigrid/index.html">format 
+description</a> for more details.<p>
+
+The projections support (read if a prj.adf file is available) is quite
+limited.  Additional sample prj.adf files may be sent to the maintainer,
+<a href="mailto:warmerdam@pobox.com">warmerdam@pobox.com</a>.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/aigrid/aigdataset.cpp</tt>.<p>
+
+<h2><a name="BSB">BSB -- Maptech/NOAA BSB Nautical Chart Format</a></h2>
+
+BSB Nautical Chart format is supported for read access, including reading 
+the colour table and the reference points (as GCPs).  Note that the .BSB 
+files cannot be selected directly.  Instead select the .KAP files.  Versions
+1.1, 2.0 and 3.0 have been tested successfully.<p>
+
+This driver should also support GEO/NOS format as supplied by Softchart.
+These files normally have the extension .nos with associated .geo files
+containing georeferencing ... the .geo files are currently ignored. <P>
+
+This driver is based on work by Mike Higgins.  See the frmts/bsb/bsb_read.c
+files for details on patents affecting BSB format.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/bsb/bsbdataset.cpp</tt>.<p>
+
+<h2><a name="BT">BT -- VTP .bt Binary Terrain Format</a></h2>
+
+The .bt format is used for elevation data in the VTP software.  The
+driver includes support for reading and writing .bt 1.3 format including
+support for Int16, Int32 and Float32 pixel data types.<p>
+
+ The driver does
+<b>not</b> support reading or writting gzipped (.bt.gz) .bt files even
+though this is supported by the VTP software.  Please unpack the files
+before using with GDAL using the "gzip -d file.bt.gz".<p>
+
+Projections in external .prj files are read and written, and support for
+most internally defined coordinate systems is also available.<p>
+
+Read/write imagery access with the GDAL .bt driver is terribly slow due to
+a very inefficient access strategy to this column oriented data.  This could
+be corrected, but it would be a fair effort.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/raw/btdataset.cpp</tt>.<p>
+
+See Also: The <a href="http://www.vterrain.org/Implementation/Formats/BT.html">
+BT file format</a> is defined on the <a href="http://www.vterrain.org/">
+VTP</a> web site.<p>
+
+<h2><a name="CEOS">CEOS -- CEOS Image</a></h2>
+
+This is a simple, read-only reader for ceos image files.  To use, select
+the main imagery file.  This driver reads only the image data, and does
+not capture any metadata, or georeferencing.<p>
+
+This driver is known to work with CEOS data produced by Spot Image, but
+will have problems with many other data sources.  In particular, it will
+only work with eight bit unsigned data.<p>
+
+See the separate <a href="#SAR_CEOS">SAR_CEOS</a> driver for access to 
+SAR CEOS data products.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/ceos/ceosdataset.cpp</tt>.<p>
+
+<h2><a name="DODS"><a name="OPeNDAP">
+DODS/OPeNDAP -- Read rasters from DODS/OPeNDAP servers</a></a></h2>
+
+Support for read access to DODS/OPeNDAP servers. Pass the DODS/OPeNDAP URL to
+the driver as you would when accessing a local file. The URL specifies the
+remote server, data set and raster within the data set. In addition, you must
+tell the driver which dimensions are to be interpreted as distinct bands as
+well as which correspond to Latitude and Longitude. See the file README.DODS
+for more detailed information.<p>
+
+<h2><a name="DOQ1">DOQ1 -- First Generation USGS DOQ</a></h2>
+
+Support for read access, including reading of an affine georeferencing 
+transform, and capture of the projection string.   This format is the old,
+unlabelled DOQ (Digital Ortho Quad) format from the USGS.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/raw/doq1dataset.cpp</tt>.<p>
+
+<h2><a name="DOQ2">DOQ2 -- New Labelled USGS DOQ</a></h2>
+
+Support for read access, including reading of an affine georeferencing 
+transform, capture of the projection string and reading of other auxilary
+fields as metadata.   This format is the new,
+labelled DOQ (Digital Ortho Quad) format from the USGS.<p>
+
+This driver was implemented by Derrick J Brashear.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/raw/doq2dataset.cpp</tt>.<p>
+
+See Also: <a href="http://rockyweb.cr.usgs.gov/nmpstds/doqstds.html">
+USGS DOQ Standards</a><p>
+
+<h2><a name="EHdr">EHdr -- ESRI .hdr Labelled</h2>
+
+GDAL supports reading and writing the ESRI .hdr labelling format,
+often referred to as ESRI BIL format.  Eight, sixteen
+and thirty-two bit integer raster data types are supported as well as 32
+bit floating point.  Coordinate systems (from a .prj file), and georeferencing
+are supported.  Unrecognised options in the .hdr file are ignored.  To 
+open a dataset select the file with the image file (often with the extension
+.bil).  If present .clr color table files are read, but not written.<p>
+
+This driver may be sufficient to read GTOPO30, SRTM30 + SRTM90 data and Geospot products.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/raw/ehdrdataset.cpp</tt>.<p>
+
+See Also: <p>
+<ul>
+
+   <li> <a href="http://downloads.esri.com/support/whitepapers/other_/eximgav.pdf">ESRI whitepaper: + Extendable Image Formats for ArcView GIS 3.1 and 3.2</a>  (BIL, see p. 5)<p>
+   <li> <a href="http://edcdaac.usgs.gov/gtopo30/gtopo30.html">GTOPO30 - Global Topographic Data</a><p>
+   <li> <a href="http://edcdaac.usgs.gov/gtopo30/README.html">GTOPO30 Documentation</a><p>
+</ul>
+
+<h2><a name="ENVI">ENVI - ENVI .hdr Labelled Raster</h2>
+
+GDAL supports some variations of raw raster files with associated ENVI
+style .hdr files describing the format.  To select an existing ENVI raster
+file select the binary file containing the data (as opposed to the .hdr 
+file), and GDAL will find the .hdr file by replacing the dataset extension
+with .hdr.<p>
+
+GDAL should support reading bil, bip and bsq interleaved formats, and
+most pixel types are supported, including 8, 16 and 32bit signed and unsigned
+integers, 32bit and 64 bit floating point, and 32bit and 64bit complex floating
+point.  There is limited support for recognising map_info keywords with the 
+coordinate system and georeferencing.  In particular, UTM and State Plane 
+should work.<p>
+
+Creation Options:<p>
+
+<ul>
+
+	<li> <b>INTERLEAVE=BSQ/BIP/BIL</b>: Force the generation specified
+	type of interleaving. <b>BSQ</b> --- band sequental (default),
+	<b>BIP</b> --- data interleaved by pixel,
+	<b>BIL</b> --- data interleaved by line.<p>
+	
+	<li> <b>SUFFIX=REPLACE/ADD</b>: Force adding ".hdr" suffix to supplied
+	filename, e.g. if user selects "file.bin" name for output dataset,
+	"file.bin.hdr" header file will be created. By default header file
+	suffix replaces the binary file suffix, e.g. for "file.bin" name
+	"file.hdr" header file will be created.
+
+</ul>
+
+
+NOTE: Implemented as <tt>gdal/frmts/raw/envidataset.cpp</tt>.<p>
+
+<h2><a name="Envisat">Envisat -- Envisat Image Product</h2>
+
+GDAL supports the Envisat product format for read access.  All sample
+types are supported.  Files with two matching measurement datasets (MDS)
+are represented as having two bands.  Currently all ASAR Level 1 and above
+products, and some MERIS and AATSR products are supported.<p>
+
+The control points of the GEOLOCATION GRID ADS dataset are read if available,
+generally giving a good coverage of the dataset.  The GCPs are in WGS84.<p>
+
+Virtually all key/value pairs from the MPH and SPH (primary and secondary
+headers) are copied through as dataset level metadata. <p>
+
+NOTE: Implemented as <tt>gdal/frmts/envisat/envisatdataset.cpp</tt>.<p>
+
+See Also: <a href="http://envisat.esa.int/dataproducts/">
+Envisat Data Products</a> at ESA.<p>
+
+<h2><a name="FITS">FITS -- Flexible Image Transport System</h2>
+
+<p>FITS is a format used mainly by astronomers, but it is a relatively
+simple format that supports arbitrary image types and multi-spectral
+images, and so has found its way into GDAL. FITS support is
+implemented in terms of the standard <a
+href="http://heasarc.gsfc.nasa.gov/docs/software/fitsio/fitsio.html">CFITSIO
+library</a>, which you must have on your system in order for FITS
+support to be enabled. Both reading and writing of FITS files is
+supported. At the current time, no support for a georeferencing system
+is implemented, but WCS (World Coordinate System) support is possible
+in the future.
+
+<p>Non-standard header keywords that are present in the FITS file will
+be copied to the dataset's metadata when the file is opened, for
+access via GDAL methods. Similarly, non-standard header keywords that
+the user defines in the dataset's metadata will be written to the FITS
+file when the GDAL handle is closed.
+
+<p>Note to those familiar with the CFITSIO library: The automatic
+rescaling of data values, triggered by the presence of the BSCALE and
+BZERO header keywords in a FITS file, is disabled in GDAL. Those
+header keywords are accessible and updatable via dataset metadata, in
+the same was as any other header keywords, but they do not affect
+reading/writing of data values from/to the file.
+
+<p>NOTE: Implemented as <tt>gdal/frmts/fits/fitsdataset.cpp</tt>.<p>
+
+<h2><a name="GXF">GXF -- Grid eXchange File</a></h2>
+
+This is a raster exchange format propagated by Geosoft, and made a standard
+in the gravity/magnetics field.  The format is supported for read, and write
+and includes support for georeferencing information, and projections. <p>
+
+Details on the supporting code, and format can be found on the
+<a href="http://gdal.velocet.ca/projects/gxf/index.html">GXF-3</a> page.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/gxf/gxfdataset.cpp</tt>.<p>
+
+<h2><a name="IDA">IDA -- Image Display and Analysis</h2>
+
+GDAL supports reading and writing IDA images with some limitations.  IDA 
+images are the image format of WinDisp 4.  The files are always one band only
+of 8bit data.  IDA files often have the extension .img though that is not
+required.<p>
+
+Projection and georeferencing information is read though some projections
+(ie. Meteosat, and Hammer-Aitoff) are not supported.  When writing IDA files
+the projection must have a false easting and false northing of zero. The 
+support coordinate systems in IDA are Geographic, Lambert Conformal Conic,
+Lambert Azimuth Equal Area, Albers Equal-Area Conic and Goodes Homolosine.<p>
+
+IDA files typically contain values scaled to 8bit via a slope and offset.  
+These are returned as the slope and offset values of the bands and they must
+be used if the data is to be rescaled to original raw values for analysis. <p>
+
+NOTE: Implemented as <tt>gdal/frmts/raw/idadataset.cpp</tt>. <p>
+
+See Also: <A href="http://www.fao.org/giews/english/windisp/windisp.htm">WinDisp</a><p>
+
+<h2><a name="ILWIS">ILWIS -- Raster Map</h2>
+
+This driver implements reading and writing of ILWIS raster maps and map lists. Select the raster files with the.mpr (for raster map) or .mpl (for maplist) extensions
+<p>
+Features:
+<ul>
+<li>Support for Byte, Int16, Int32 and Float64 pixel data types.</li>
+<li>Supports map lists with an associated set of ILWIS raster maps.</li>
+<li>Read and write geo-reference (.grf). Support for geo-referencing transform is limited to north-oriented GeoRefCorner only. If possible the affine transform is computed from the corner coordinates.</li>
+<li>Read and write coordinate files (.csy). Support is limited to: Projection type of Projection and Lat/Lon type that are defined in .csy file, the rest of pre-defined projection types are ignored.</li>
+</ul>
+
+Limitations:
+<ul>
+<li>Map lists with internal raster map storage (such as produced through Import General Raster) are not supported.</li>
+<li>ILWIS domain (.dom) and representation (.rpr) files are currently ignored.</li>
+</ul>
+
+<h2><a name="JDEM">JDEM -- Japanese DEM (.mem)</a></h2>
+
+GDAL includes read support for Japanese DEM files, normally having the
+extension .mem.  These files are a product of the Japanese Geographic Survey 
+Institute.<p>
+
+These files are represented as having one 32bit floating band with elevation
+data.  The georeferencing of the files is returned as well as the coordinate
+system (always lat/long on the Tokyo datum). <p>
+
+There is no update or creation support for this format. <p>
+
+NOTE: Implemented as <tt>gdal/frmts/jdem/jdemdataset.cpp</tt>.<p>
+
+See Also: <a href="http://www.gsi.go.jp/ENGLISH/">Geographic Survey 
+Institute (GSI) Web Site.</a><p>
+
+<h2><a name="LAN">LAN -- Erdas 7.x .LAN and .GIS</a></h2>
+
+GDAL supports reading Erdas 7.x .LAN and .GIS raster files.  Currently 
+4bit, 8bit and 16bit pixel data types are supported.  <p>
+
+GDAL does read the map extents (geotransform) from LAN/GIS files, and 
+attempts to read the coordinate system informaton.  However, this format
+of file does not include complete coordinate system information, so for
+state plane and UTM coordinate systems a LOCAL_CS definition is returned
+with valid linear units but no other meaningful information.<p>
+
+The .TRL, .PRO and worldfiles are ignored at this time.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/raw/landataset.cpp</tt><p>
+
+Development of this driver was financially supported by Kevin Flanders of
+(<a href="http://www.peoplegis.com">PeopleGIS</a>). <p>
+
+<h2><a name="MFF">MFF -- Vexcel MFF Raster</a></h2>
+
+GDAL includes read, update, and creation support for Vexcel's
+MFF raster format.  MFF dataset consist of a header file (typically with
+the extension .hdr) and a set of data files with extensions like .x00, .b00
+and so on.   To open a dataset select the .hdr file.<p>
+
+Reading lat/long GCPs (TOP_LEFT_CORNER, ...) is supported but there is no
+support for reading affine georeferencing or projection information.<p>
+
+Unrecognised keywords from the .hdr file are preserved as metadata. <p>
+
+All data types with GDAL equivelents are supported, including 8, 16, 32 and
+64 bit data precisions in integer, real and complex data types.  In addition
+tile organized files (as produced by the Vexcel SAR Processor - APP) are
+supported for reading. <p>
+
+On creation (with a format code of MFF) a simple, ungeoreferenced raster
+file is created.<p>
+
+MFF files are not normally portable between systems with different byte orders.
+However GDAL honours the new BYTE_ORDER keyword which can take a value of
+LSB (Integer -- little endian), and MSB (Motorola -- big endian).  This may
+be manually added to the .hdr file if required.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/raw/mffdataset.cpp</tt>.<p>
+
+
+<h2><a name="NDF">NDF -- NLAPS Data Format</a></h2>
+
+GDAL has limited support for reading NLAPS Data Format files.  This is a
+format primarily used by the Eros Data Center for distribution of Landsat 
+data.  NDF datasets consist of a header file (often with the extension .H1)
+and one or more associated raw data files (often .I1, .I2, ...).  To open
+a dataset select the header file, often with the extension .H1, .H2 or
+.HD. <p>
+
+The NDF driver only supports 8bit data.  The only supported projection is
+UTM.  NDF version 1 (NDF_VERSION=0.00) and NDF version 2 are both supported.
+<p>
+
+NOTE: Implemented as <tt>gdal/frmts/raw/ndfdataset.cpp</tt>.<p>
+
+See Also: <a href="http://landsat.usgs.gov/documents/NLAPSII.pdf">NLAPS
+Data Format Specification</a>.<p>					
+
+<h2><a name="GMT">GMT -- GMT Compatible netCDF</a></h2>
+
+GDAL has limited support for reading and writing netCDF <i>grid</i> files. 
+NetCDF files that are not recognised as grids (they lack variables called
+dimension, and z) will be silently ignored by this driver.  This driver is
+primarily intended to provide a mechanism for grid interchange with the 
+<a href="http://gmt.soest.hawaii.edu/">GMT</a> package.  The 
+netCDF driver should be used for more general netCDF datasets.<p>
+
+The units information in the file will be ignored, but x_range, and
+y_range information will be read to get georeferenced extents of the raster.
+All netCDF data types should be supported for reading.
+
+Newly created files (with a type of <tt>GMT</tt>) will always have
+units of "meters" for x, y and z but the x_range, y_range and z_range should 
+be correct.  Note that netCDF does not have an unsigned byte data type, so 
+8bit rasters will generally need to be converted to Int16 for export to 
+GMT.<p>
+
+NetCDF support in GDAL is optional, and not compiled in by default.  <p>
+
+NOTE: Implemented as <tt>gdal/frmts/netcdf/gmtdataset.cpp</tt>.<p>
+
+See Also: <a href="http://www.unidata.ucar.edu/packages/netcdf/">Unidata NetCDF Page</a><p>
+
+<h2><a name="netCDF">netCDF -- Network Common Data Format</a></h2>
+
+GDAL has limited support for reading general netCDF files.<p>
+
+NetCDF support in GDAL is optional, and not compiled in by default.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/netcdf/netcdfdataset.cpp</tt>.<p>
+
+See Also: <a href="http://www.unidata.ucar.edu/packages/netcdf/">Unidata NetCDF Page</a><p>
+
+<h2><a name="PAux">PAux -- PCI .aux Labelled Raw Format</a></h2>
+
+GDAL includes a partial implementation of the PCI .aux labelled raw raster 
+file for read, write and creation.  To open a PCI labelled file, select the
+raw data file itself.  The .aux file (which must have a common base name) will
+be checked for automatically. <p>
+
+The format type for creating new files is <tt>PAux</tt>.  All PCI data types
+(8U, 16U, 16S, and 32R) are supported.  Currently georeferencing, projections, 
+and other metadata is ignored.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/raw/pauxdataset.cpp</tt>.<p>
+
+See Also: <a href="http://www.pcigeomatics.com/cgi-bin/pcihlp/GDB|Supported+File+Formats|Raw+Binary+Image+Format+(RAW)|Raw+.aux+Format">PCI's .aux Format
+Description</a><p>
+
+<h2><a name="PCRaster">PCRaster raster file format</a></h2>
+<p>GDAL includes support for reading and writing PCRaster raster files. PCRaster is a dynamic modelling system for distributed simulation models. The main applications of PCRaster are found in environmental modelling: geography, hydrology, ecology to name a few. Examples include rainfall-runoff models, vegetation competition models and slope stability models.</p>
+
+<p>The driver reads all types of PCRaster maps: booleans, nominal, ordinals, scalar, directional and ldd. The same cell representation used to store values in the file is used to store the values in memory.</p>
+
+<p>The driver detects whether the source of the GDAL raster is a PCRaster file. When such a raster is written to a file the value scale of the original raster will be used. The driver <b>always</b> writes values using UINT1, INT4 or REAL4 cell representations, depending on the value scale:</p>
+
+<table>
+  <thead>
+    <tr>
+      <th>Value scale</th>
+      <th>Cell representation</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>VS_BOOLEAN</td>
+      <td>CR_UINT1</td>
+    </tr>
+    <tr>
+      <td>VS_NOMINAL</td>
+      <td>CR_INT4</td>
+    </tr>
+    <tr>
+      <td>VS_ORDINAL</td>
+      <td>CR_INT4</td>
+    </tr>
+    <tr>
+      <td>VS_SCALAR</td>
+      <td>CR_REAL4</td>
+    </tr>
+    <tr>
+      <td>VS_DIRECTION</td>
+      <td>CR_REAL4</td>
+    </tr>
+    <tr>
+      <td>VS_LDD</td>
+      <td>CR_UINT1</td>
+    </tr>
+  </tbody>
+</table>
+
+<p>For rasters from other sources than a PCRaster raster file a value scale and cell representation is determined according to the folowing rules:</p>
+
+<table>
+  <thead>
+    <tr>
+      <th>Source type</th>
+      <th>Target value scale</th>
+      <th>Target cell representation</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>GDT_Byte</td>
+      <td>VS_BOOLEAN</td>
+      <td>CR_UINT1</td>
+    </tr>
+    <!--
+    <tr>
+      <td>GDT_UInt16</td>
+      <td>VS_NOMINAL</td>
+      <td>CR_UINT1</td>
+    </tr>
+    <tr>
+      <td>GDT_UInt32</td>
+      <td>VS_NOMINAL</td>
+      <td>CR_UINT1</td>
+    </tr>
+    <tr>
+      <td>GDT_Int16</td>
+      <td>VS_NOMINAL</td>
+      <td>CR_INT4</td>
+    </tr>
+    -->
+
+    <tr>
+      <td>GDT_Int32</td>
+      <td>VS_NOMINAL</td>
+      <td>CR_INT4</td>
+    </tr>
+    <tr>
+      <td>GDT_Float32</td>
+      <td>VS_SCALAR</td>
+      <td>CR_REAL4</td>
+    </tr>
+    <tr>
+      <td>GDT_Float64</td>
+      <td>VS_SCALAR</td>
+      <td>CR_REAL4</td>
+    </tr>
+  </tbody>
+</table>
+
+<p>The driver can convert values from one supported cell representation to another. It cannot convert to unsupported cell representations. For example, it is not possible to write a PCRaster raster file from values which are used as CR_INT2 (GDT_Int16).
+
+<p>NOTE: Implemented as <tt>gdal/frmts/pcraster/pcrasterdataset.cpp</tt>.</p>
+
+See also: <a href="http://pcraster.geog.uu.nl">PCRaster website at Utrecht University</a> and <a href="http://www.pcraster.nl">PCRaster Environmental Software company website</a>.
+
+<h2><a name="PNG">PNG -- Portable Network Graphics</a></h2>
+
+GDAL includes support for reading, and creating .png files.  Greyscale, 
+pseudo-colored, Paletted, RGB and RGBA PNG files are supported as well as 
+precisions of eight and sixteen bits per sample.<p>
+
+PNG files are linearly compressed, so random reading of large PNG files can
+be very inefficient (resulting in many restarts of decompression from the
+start of the file).<p>
+
+Text chunks are translated into metadata, typically with multiple lines per
+item.  <a href="#WLD">World files</a> with the extensions of .wld, .tfw or
+.tifw will be read.  Single transparency values in greyscale files will be
+recognised as a nodata value in GDAL.  Transparent index in paletted images
+are preserved when the color table is read. <p>
+
+PNG files can be created with a type of PNG, using the CreateCopy() method, 
+requiring a prototype to read from.  Writing includes support for the
+various image types, and will preserve transparency/nodata values.  
+Georeferencing .wld files are written if option WORLFFILE setted.  All
+pixel types other than 16bit unsigned will be written as eight bit.<p>
+
+Creation Options:<p>
+
+<ul>
+
+<li> <b>WORLDFILE=YES</b>: Force the generation of an associated ESRI world
+file (.wld). See <a href="#WLD">World File</a> section for details.<p>
+</ul>
+
+NOTE: Implemented as <tt>gdal/frmts/png/pngdataset.cpp</tt>.<p> 
+
+PNG support is implemented based on the libpng reference library.  
+More information is available at <a href="http://www.libpng.org/pub/png">
+http://www.libpng.org/pub/png</a>.<p>
+
+<h2><a name="PNM">PNM -- Netpbm (.pgm, .ppm)</a></h2>
+
+GDAL includes support for reading, and creating .pgm (greyscale), and
+.ppm (RGB color) files compatible with the Netpbm tools.  Only the binary
+(raw) formats are supported.<p>
+
+Netpbm files can be created with a type of PNM.<p>
+
+Creation Options:<p>
+
+<ul>
+
+<li> <b>MAXVAL=n</b>: Force setting the maximum color value to <B>n</B>
+in the output PNM file. May be useful if you plannig use the output files with
+software which is not liberal to this value.<p>
+</ul>
+
+NOTE: Implemented as <tt>gdal/frmts/raw/pnmdataset.cpp</tt>. 
+
+<h2><a name="SAR_CEOS">SAR_CEOS -- CEOS SAR Image</a></h2>
+
+This is a read-only reader for CEOS SAR image files.  To use, select
+the main imagery file.<p>
+
+This driver works with most Radarsat and ERS data
+products, including single look complex products; however, it is unlikely
+to work for non-Radar CEOS products.  The simpler <a href="#CEOS">CEOS</a>
+driver is often appropriate for these.<p>
+
+This driver will attempt to read 15 lat/long GCPS by sampling the per-scanline
+CEOS superstructure information.  It also captures various pieces of metadata
+from various header files, including:<p>
+
+<pre>
+  CEOS_LOGICAL_VOLUME_ID=EERS-1-SAR-MLD  
+  CEOS_PROCESSING_FACILITY=APP         
+  CEOS_PROCESSING_AGENCY=CCRS    
+  CEOS_PROCESSING_COUNTRY=CANADA      
+  CEOS_SOFTWARE_ID=APP 1.62    
+  CEOS_ACQUISITION_TIME=19911029162818919               
+  CEOS_SENSOR_CLOCK_ANGLE=  90.000
+  CEOS_ELLIPSOID=IUGG_75         
+  CEOS_SEMI_MAJOR=    6378.1400000
+  CEOS_SEMI_MINOR=    6356.7550000
+</pre>
+
+The SAR_CEOS driver also includes some support for SIR-C and PALSAR 
+polarimetric data.  The SIR-C format contains an image in compressed
+scattering matrix form, described 
+<a href="http://southport.jpl.nasa.gov/software/dcomp/dcomp.html">here</a>.  
+GDAL decompresses the data as it is read in.  The PALSAR format
+contains bands that correspond almost exactly to elements of the 3x3
+Hermitian covariance matrix-
+see the <a href="http://www.ersdac.or.jp/palsar/palsar_E.html">
+ERSDAC-VX-CEOS-004A.pdf</a> document for a complete description (pixel 
+storage is described on page 193). GDAL converts these to complex floating
+point covariance matrix bands as they are read in.  The convention used
+to represent the covariance matrix in terms of the scattering matrix
+elements HH, HV (=VH), and VV is indicated below. Note that the 
+non-diagonal elements of the matrix are complex values, while the diagonal
+values are real (though represented as complex bands).<p> 
+
+<ul>
+<li> Band 1: Covariance_11 (Float32) = HH*conj(HH)
+<li> Band 2: Covariance_12 (CFloat32) = sqrt(2)*HH*conj(HV)
+<li> Band 3: Covariance_13 (CFloat32) = HH*conj(VV)
+<li> Band 4: Covariance_22 (Float32) = 2*HV*conj(HV)
+<li> Band 5: Covariance_23 (CFloat32) = sqrt(2)*HV*conj(VV)
+<li> Band 6: Covariance_33 (Float32) = VV*conj(VV)
+</ul>
+
+
+The identities of the bands are also reflected in the metadata.  <p>
+
+NOTE: Implemented as <tt>gdal/frmts/ceos2/sar_ceosdataset.cpp</tt>.<p>
+
+<h2><a name="SDTS">SDTS -- USGS SDTS DEM</a></h2>
+
+GDAL includes support for reading USGS SDTS formatted DEMs.  USGS DEMs are
+always returned with a data type of signed sixteen bit integer, or 32bit float.
+Projection and georeferencing information is also returned.<p>
+
+SDTS datasets consist of a number of files.  Each DEM should have one file
+with a name like XXXCATD.DDF.  This should be selected to open
+the dataset.<p>
+
+The elevation units of DEMs may be feet or meters.  The GetType() method on a 
+band will attempt to return if the units are Feet ("ft") or Meters ("m").
+
+NOTE: Implemented as <tt>gdal/frmts/sdts/sdtsdataset.cpp</tt>.<p>
+
+<h2><a name="SGI">SGI - SGI Image Format</a></h2>
+
+The SGI driver currently supports the reading of SGI Image files.<p>
+
+The driver currently supports 1, 2, 3, and 4 band images. The driver 
+currently supports "8 bit per channel value" images. The driver supports 
+both uncompressed and run-length encoded (RLE) images.<p>
+
+The GDAL SGI Driver was based on Paul Bourke's SGI image read code.<p>
+
+The driver does not currently have any image creation capabilities.<p>
+
+See Also:<p>
+
+<ul>
+<li> <a href="http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/">Paul Bourke's SGI Image Read Code</a>
+<li> <a href="ftp://ftp.sgi.com/graphics/SGIIMAGESPEC">SGI Image File Format Document </a>
+</ul>
+
+NOTE: Implemented as <tt>gdal/frmts/sgi/sgidataset.cpp</tt>.<p>
+
+<h2><a name="WLD">WLD -- ESRI World File</a></h2>
+
+A world file file is a plain ASCII text file consisting of six values separated
+by newlines.  The format is: <br>
+
+<pre>
+ pixel X size
+ rotation about the Y axis (usually 0.0)
+ rotation about the X axis (usually 0.0)
+ negative pixel Y size
+ X coordinate of upper left pixel center
+ Y coordinate of upper left pixel center
+</pre>
+
+For example: <br>
+
+<pre>
+60.0000000000
+0.0000000000
+0.0000000000
+-60.0000000000
+440750.0000000000
+3751290.0000000000
+</pre>
+
+You can construct that file simply by using your favorite text editor.<p>
+
+World file usually has suffix .wld, but sometimes it may has .tfw, tifw, .jgw
+or other suffixes depending on the image file it comes with.<p>
+
+<h2><a name="XPM">XPM - X11 Pixmap</a></h2>
+
+GDAL includes support for reading and writing XPM (X11 Pixmap Format) 
+image files.  These are colormapped one band images primarily used for 
+simple graphics purposes in X11 applications.  It has been incorporated in
+GDAL primarily to ease translation of GDAL images into a form useable with
+the GTK toolkit.<p>
+
+The XPM support does not support georeferecing (not available from XPM files)
+nor does it support XPM files with more than one character per pixel.  New
+XPM files must be colormapped or greyscale, and colortables will be reduced
+to about 70 colors automatically.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/xpm/xpmdataset.cpp</tt>.<p>
+
+<hr>
+<a href="formats_list.html">Full list of GDAL Raster Formats</a>
+</body>
+</html>
+
+
diff --git a/Utilities/GDAL/frmts/gdalallregister.cpp b/Utilities/GDAL/frmts/gdalallregister.cpp
new file mode 100644
index 0000000000..de7376f6fe
--- /dev/null
+++ b/Utilities/GDAL/frmts/gdalallregister.cpp
@@ -0,0 +1,384 @@
+/******************************************************************************
+ * $Id: gdalallregister.cpp,v 1.87 2006/04/20 13:51:51 fwarmerdam Exp $
+ *
+ * Project:  GDAL Core
+ * Purpose:  Implementation of GDALAllRegister(), primary format registration.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1998, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: gdalallregister.cpp,v $
+ * Revision 1.87  2006/04/20 13:51:51  fwarmerdam
+ * added terragen support
+ *
+ * Revision 1.86  2006/04/03 23:25:10  fwarmerdam
+ * Fixup header formatting.
+ *
+ * Revision 1.85  2006/03/31 02:59:57  fwarmerdam
+ * added idrisi
+ *
+ * Revision 1.84  2006/02/07 21:44:43  fwarmerdam
+ * DIPX->DIPEx
+ *
+ * Revision 1.83  2006/02/01 17:22:19  fwarmerdam
+ * added DIPX driver
+ *
+ * Revision 1.82  2005/12/20 16:16:57  fwarmerdam
+ * added sgi format
+ *
+ * Revision 1.81  2005/10/20 13:46:07  fwarmerdam
+ * added Leveller
+ *
+ * Revision 1.80  2005/08/17 15:34:23  fwarmerdam
+ * added RIK driver
+ *
+ * Revision 1.79  2005/07/12 16:56:56  denad21
+ * added hdf5 support
+ *
+ * Revision 1.78  2005/07/07 23:35:44  fwarmerdam
+ * added msgn support
+ *
+ * Revision 1.77  2005/05/19 20:41:14  dron
+ * Added RMF format.
+ *
+ * Revision 1.76  2005/04/04 15:25:26  fwarmerdam
+ * GDALAllRegister() now CPL_STDCALL
+ *
+ * Revision 1.75  2005/01/15 07:46:53  fwarmerdam
+ * added GDALRegister_JP2ECW
+ *
+ * Revision 1.74  2005/01/06 20:27:35  fwarmerdam
+ * added NDF driver
+ *
+ * Revision 1.73  2004/12/26 16:17:31  fwarmerdam
+ * added ida format
+ *
+ * Revision 1.72  2004/12/09 16:37:18  fwarmerdam
+ * re-enable ILWIS
+ *
+ * Revision 1.71  2004/12/02 15:58:58  fwarmerdam
+ * temporarily disable ilwis driver
+ *
+ * Revision 1.70  2004/11/30 17:02:37  lichun
+ * Added ILWIS driver support
+ *
+ * Revision 1.69  2004/10/22 14:15:23  fwarmerdam
+ * Added PCRaster support.
+ *
+ * Revision 1.68  2004/10/21 20:04:46  fwarmerdam
+ * added Register_RS2
+ *
+ * Revision 1.67  2004/10/16 14:58:01  fwarmerdam
+ * added GMT
+ *
+ * Revision 1.66  2004/09/16 18:24:13  fwarmerdam
+ * added airsar
+ *
+ * Revision 1.65  2004/09/03 19:06:50  warmerda
+ * added CPG driver
+ *
+ * Revision 1.64  2004/05/26 17:45:39  warmerda
+ * added LAN format
+ *
+ * Revision 1.63  2004/04/05 21:30:44  warmerda
+ * moved ECW down so other jpeg2000 drivers used in preference
+ *
+ * Revision 1.62  2004/01/07 20:06:34  warmerda
+ * Added netcdf support
+ */
+
+#include "gdal_priv.h"
+#include "gdal_frmts.h"
+
+CPL_CVSID("$Id: gdalallregister.cpp,v 1.87 2006/04/20 13:51:51 fwarmerdam Exp $");
+
+#ifdef notdef
+// we may have a use for this some day
+static char *szConfiguredFormats = "GDAL_FORMATS";
+#endif
+
+/************************************************************************/
+/*                          GDALAllRegister()                           */
+/*                                                                      */
+/*      Register all identifiably supported formats.                    */
+/************************************************************************/
+
+/**
+ * Register all known configured GDAL drivers.
+ *
+ * This function will drive any of the following that are configured into
+ * GDAL.  Possible others as well that haven't been updated in this
+ * documentation:
+ *
+ * <ul>
+ * <li> GeoTIFF (GTiff)
+ * <li> Geosoft GXF (GXF)
+ * <li> Erdas Imagine (HFA)
+ * <li> CEOS (CEOS)
+ * <li> ELAS (ELAS)
+ * <li> Arc/Info Binary Grid (AIGrid)
+ * <li> SDTS Raster DEM (SDTS)
+ * <li> OGDI (OGDI)
+ * <li> ESRI Labelled BIL (EHdr)
+ * <li> PCI .aux Labelled Raw Raster (PAux)
+ * <li> HDF4 Hierachal Data Format Release 4
+ * <li> HDF5 Hierachal Data Format Release 5
+ * </ul>
+ *
+ */
+
+void CPL_STDCALL GDALAllRegister()
+
+{
+    GetGDALDriverManager()->AutoLoadDrivers();
+
+#ifdef FRMT_vrt
+    GDALRegister_VRT();
+#endif    
+
+#ifdef FRMT_gdb    
+    GDALRegister_GDB();
+#endif    
+
+#ifdef FRMT_gtiff    
+    GDALRegister_GTiff();
+#endif    
+
+#ifdef FRMT_nitf
+    GDALRegister_NITF();
+#endif
+
+#ifdef FRMT_hfa
+    GDALRegister_HFA();
+#endif
+    
+#ifdef FRMT_ceos2
+    GDALRegister_SAR_CEOS();
+#endif
+    
+#ifdef FRMT_ceos
+    GDALRegister_CEOS();
+#endif
+    
+#ifdef FRMT_elas
+    GDALRegister_ELAS();
+#endif
+    
+#ifdef FRMT_aigrid
+//    GDALRegister_AIGrid2();
+    GDALRegister_AIGrid();
+#endif
+
+#ifdef FRMT_aaigrid
+    GDALRegister_AAIGrid();
+#endif
+
+#ifdef FRMT_sdts
+    GDALRegister_SDTS();
+#endif
+
+#ifdef FRMT_ogdi
+    GDALRegister_OGDI();
+#endif
+
+#ifdef FRMT_dted
+    GDALRegister_DTED();
+#endif
+
+#ifdef FRMT_png
+    GDALRegister_PNG();
+#endif
+
+#ifdef FRMT_jpeg
+    GDALRegister_JPEG();
+#endif
+
+#ifdef FRMT_mem
+    GDALRegister_MEM();
+#endif
+
+#ifdef FRMT_jdem
+    GDALRegister_JDEM();
+#endif
+
+#ifdef FRMT_gif
+    GDALRegister_GIF();
+#endif
+
+#ifdef FRMT_envisat
+    GDALRegister_Envisat();
+#endif
+
+#ifdef FRMT_fits
+    GDALRegister_FITS();
+#endif
+
+#ifdef FRMT_bsb
+    GDALRegister_BSB();
+#endif
+
+#ifdef FRMT_xpm
+    GDALRegister_XPM();
+#endif
+
+#ifdef FRMT_bmp
+    GDALRegister_BMP();
+#endif
+
+#ifdef FRMT_airsar
+    GDALRegister_AirSAR();
+#endif
+
+#ifdef FRMT_rs2
+    GDALRegister_RS2();
+#endif
+
+#ifdef FRMT_pcidsk
+    GDALRegister_PCIDSK();
+#endif
+
+#ifdef FRMT_pcraster
+    GDALRegister_PCRaster();
+#endif
+
+#ifdef FRMT_ilwis
+    GDALRegister_ILWIS();
+#endif
+
+#ifdef FRMT_rik
+    GDALRegister_RIK();
+#endif
+
+#ifdef FRMT_sgi
+    GDALRegister_SGI();
+#endif
+
+#ifdef FRMT_leveller
+    GDALRegister_Leveller();
+#endif
+
+#ifdef FRMT_terragen
+    GDALRegister_Terragen();
+#endif
+
+#ifdef FRMT_netcdf
+    GDALRegister_GMT();
+    GDALRegister_netCDF();
+#endif
+
+#ifdef FRMT_hdf4
+    GDALRegister_HDF4();
+    GDALRegister_HDF4Image();
+#endif
+
+#ifdef FRMT_raw
+    GDALRegister_PNM();
+    GDALRegister_DOQ1();
+    GDALRegister_DOQ2();
+    GDALRegister_ENVI();
+    GDALRegister_EHdr();
+    GDALRegister_PAux();
+    GDALRegister_MFF();
+    GDALRegister_HKV();
+    GDALRegister_FujiBAS();
+    GDALRegister_GSC();
+    GDALRegister_FAST();
+    GDALRegister_BT();
+    GDALRegister_LAN();
+    GDALRegister_CPG();
+    GDALRegister_IDA();
+    GDALRegister_NDF();
+    GDALRegister_DIPEx();
+    GDALRegister_ISIS2();
+#endif
+
+#ifdef FRMT_jp2kak
+// JPEG2000 support using Kakadu toolkit
+    GDALRegister_JP2KAK();
+#endif
+
+#ifdef FRMT_jpeg2000
+// JPEG2000 support using JasPer toolkit
+// This one should always be placed after other JasPer supported formats,
+// such as BMP or PNM. In other case we will get bad side effects.
+    GDALRegister_JPEG2000();
+#endif
+
+#ifdef FRMT_ecw
+    GDALRegister_ECW();
+    GDALRegister_JP2ECW();
+#endif
+
+#ifdef FRMT_l1b
+    GDALRegister_L1B();
+#endif
+
+#ifdef FRMT_fit
+    GDALRegister_FIT();
+#endif
+
+#ifdef FRMT_mrsid
+    GDALRegister_MrSID();
+#endif
+
+#ifdef FRMT_rmf
+    GDALRegister_RMF();
+#endif
+
+#ifdef FRMT_msgn
+    GDALRegister_MSGN();
+#endif
+
+#ifdef FRMT_idrisi
+    GDALRegister_IDRISI();
+#endif
+
+/* -------------------------------------------------------------------- */
+/*      Our test for the following is weak or expensive so we try       */
+/*      them last.                                                      */
+/* -------------------------------------------------------------------- */
+#ifdef FRMT_usgsdem
+    GDALRegister_USGSDEM();
+#endif
+
+#ifdef FRMT_gxf
+    GDALRegister_GXF();
+#endif    
+
+#ifdef FRMT_grass
+    GDALRegister_GRASS();
+#endif
+
+#ifdef FRMT_dods
+    GDALRegister_DODS();
+#endif
+#ifdef FRMT_hdf5
+    GDALRegister_HDF5();
+    GDALRegister_HDF5Image();
+#endif
+/* -------------------------------------------------------------------- */
+/*      Deregister any drivers explicitly marked as supressed by the    */
+/*      GDAL_SKIP environment variable.                                 */
+/* -------------------------------------------------------------------- */
+    GetGDALDriverManager()->AutoSkipDrivers();
+}
diff --git a/Utilities/GDAL/frmts/gif/GNUmakefile b/Utilities/GDAL/frmts/gif/GNUmakefile
new file mode 100644
index 0000000000..ccddda8f4f
--- /dev/null
+++ b/Utilities/GDAL/frmts/gif/GNUmakefile
@@ -0,0 +1,26 @@
+
+include ../../GDALmake.opt
+
+ifeq ($(GIF_SETTING),internal)
+XTRA_OPT =	-Ilibungif
+OBJ	=	egif_lib.o dgif_lib.o gifalloc.o gif_err.o \
+		\
+		gifdataset.o
+else
+OBJ	=	gifdataset.o
+endif
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(CPPFLAGS) $(XTRA_OPT)
+
+default:	$(O_OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+../o/%.o:	libungif/%.c
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@
+
+
+all:	$(OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/gif/frmt_gif.html b/Utilities/GDAL/frmts/gif/frmt_gif.html
new file mode 100644
index 0000000000..32a43300dc
--- /dev/null
+++ b/Utilities/GDAL/frmts/gif/frmt_gif.html
@@ -0,0 +1,52 @@
+<html>
+<head>
+<title>GIF -- Graphics Interchange Format</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>GIF -- Graphics Interchange Format</h1>
+
+GDAL supports reading and writing of normal, and interlaced GIF files.  Gif
+files always appear as having one colormapped eight bit band. GIF files have
+no support for georeferencing.<p>
+
+A GIF image with transparency will have that entry marked as having
+an alpha value of 0.0 (transparent).  Also, the transparent value will
+be returned as the NoData value for the band.<p>
+
+If an ESRI world file exists with the .wld extension, it will be read and
+used to establish the geotransform for the image.<p>
+
+<h2>Creation Issues</h2>
+
+GIF files can only be created as 1 8bit band using the "CreateCopy" mechanism.
+If written from a file that is not colormapped, a default greyscale colormap
+is generated.  Tranparent GIFs are not currently supported on creation.<p>
+
+<b>WORLFILE=ON</b>: Force the generation of an associated ESRI world
+file (.wld).<p>
+
+Interlaced (progressive) GIF files can be generated by supplying the 
+<b>INTERLACING=ON</b> option on creation. <p>
+
+By default GDAL does not support the creation of LZW compressed GIF files
+due to the usual LZW patent issues; however, it is possible to build GDAL
+from source with LZW compression support if the LZW supporting version of
+<b>giflib</b> is installed.<p>
+
+GDAL's GIF support is implemented based on source from the libungif 4.0 library
+written by Gershon Elbor, Eric Raymond and Toshio Kuratomi.  It was written
+with the financial support of the <a href="http://www.dmsolutions.ca/">DM
+Solutions Group</a>, and <a href="http://www.ciet.org/">CIET 
+International</a>.<p>
+
+See Also:<p>
+
+<ul>
+<li> <a href="http://prtr-13.ucsc.edu/~badger/software/libungif/">Libungif Home
+Page</a><p>
+</ul>
+
+</body>
+</html>
diff --git a/Utilities/GDAL/frmts/gif/gifdataset.cpp b/Utilities/GDAL/frmts/gif/gifdataset.cpp
new file mode 100644
index 0000000000..990a4c362b
--- /dev/null
+++ b/Utilities/GDAL/frmts/gif/gifdataset.cpp
@@ -0,0 +1,795 @@
+/******************************************************************************
+ * $Id: gifdataset.cpp,v 1.25 2005/10/11 18:59:35 fwarmerdam Exp $
+ *
+ * Project:  GIF Driver
+ * Purpose:  Implement GDAL GIF Support using libungif code.  
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: gifdataset.cpp,v $
+ * Revision 1.25  2005/10/11 18:59:35  fwarmerdam
+ * remove CPL_DLL attribute on EGifOpen
+ *
+ * Revision 1.24  2005/10/07 20:58:43  fwarmerdam
+ * Fall through to pam for getgeotransform if we don't have a world file.
+ * Use VSI*L virtual file io layer allowing memory buffer access.
+ *
+ * Revision 1.23  2005/08/04 18:44:33  fwarmerdam
+ * Report background as metadata rather than NODATA.  It was causing too
+ * many problems.
+ *
+ * Revision 1.22  2005/05/05 14:05:03  fwarmerdam
+ * PAM Enable
+ *
+ * Revision 1.21  2004/08/20 17:33:06  warmerda
+ * Support overviews.
+ *
+ * Revision 1.20  2003/12/09 16:47:17  warmerda
+ * Make sure GetGeoTransform() still initializes the geotransform array.
+ *
+ * Revision 1.19  2003/11/07 18:33:10  warmerda
+ * additional transparent hack for AUG
+ *
+ * Revision 1.18  2003/10/15 14:02:07  warmerda
+ * treat background color as NODATA
+ *
+ * Revision 1.17  2003/07/08 21:14:16  warmerda
+ * avoid warnings
+ *
+ * Revision 1.16  2003/03/13 16:39:53  warmerda
+ * Added support for .gfw.
+ *
+ * Revision 1.15  2002/11/26 02:36:38  warmerda
+ * Use first saved image to set the width and height.  One sample image
+ * (wmt.cgi.gif) has a disagreement between this value and the "screen" size.
+ * See http://mapserver.gis.umn.edu/bugs/show_bug.cgi?id=235
+ *
+ * Revision 1.14  2002/11/23 18:54:17  warmerda
+ * added CREATIONDATATYPES metadata for drivers
+ *
+ * Revision 1.13  2002/10/28 19:25:00  warmerda
+ * Ensure that MakeMapObject is called with sizes that are powers of 2.
+ *
+ * Revision 1.12  2002/09/04 06:50:37  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.11  2002/07/13 04:16:39  warmerda
+ * added WORLDFILE support
+ *
+ * Revision 1.10  2002/06/12 21:12:25  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.9  2001/11/11 23:50:59  warmerda
+ * added required class keyword to friend declarations
+ *
+ * Revision 1.8  2001/08/22 20:53:01  warmerda
+ * added support for reading transparent value
+ *
+ * Revision 1.7  2001/08/22 17:12:49  warmerda
+ * added world file support
+ *
+ * Revision 1.6  2001/07/18 04:51:56  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.5  2001/01/10 15:37:58  warmerda
+ * Fixed help topic name.
+ *
+ * Revision 1.4  2001/01/10 15:36:44  warmerda
+ * implement interlacing support
+ *
+ * Revision 1.3  2001/01/10 05:32:17  warmerda
+ * Added GIFCreateCopy() implementation.
+ *
+ * Revision 1.2  2001/01/10 05:00:32  warmerda
+ * Only check against GIF87/GIF89, not GIF87a/GIF89a.
+ *
+ * Revision 1.1  2001/01/10 04:40:15  warmerda
+ * New
+ *
+ */
+
+#include "gdal_pam.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: gifdataset.cpp,v 1.25 2005/10/11 18:59:35 fwarmerdam Exp $");
+
+CPL_C_START
+#include "gif_lib.h"
+CPL_C_END
+
+CPL_C_START
+void	GDALRegister_GIF(void);
+
+// This prototype seems to have been messed up!
+GifFileType * EGifOpen(void* userData, OutputFunc writeFunc);
+CPL_C_END
+
+static int InterlacedOffset[] = { 0, 4, 2, 1 }; 
+static int InterlacedJumps[] = { 8, 8, 4, 2 };  
+
+static int VSIGIFReadFunc( GifFileType *, GifByteType *, int);
+static int VSIGIFWriteFunc( GifFileType *, const GifByteType *, int );
+
+/************************************************************************/
+/* ==================================================================== */
+/*				GIFDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class GIFRasterBand;
+
+class GIFDataset : public GDALPamDataset
+{
+    friend class GIFRasterBand;
+
+    FILE *fp;
+
+    GifFileType *hGifFile;
+
+    int	   bGeoTransformValid;
+    double adfGeoTransform[6];
+
+  public:
+                 GIFDataset();
+                 ~GIFDataset();
+
+    virtual CPLErr GetGeoTransform( double * );
+    static GDALDataset *Open( GDALOpenInfo * );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            GIFRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class GIFRasterBand : public GDALPamRasterBand
+{
+    friend class GIFDataset;
+
+    SavedImage	*psImage;
+
+    int		*panInterlaceMap;
+    
+    GDALColorTable *poColorTable;
+
+    int		nTransparentColor;
+
+  public:
+
+                   GIFRasterBand( GIFDataset *, int, SavedImage *, int );
+    virtual       ~GIFRasterBand();
+
+    virtual CPLErr IReadBlock( int, int, void * );
+
+    virtual double GetNoDataValue( int *pbSuccess = NULL );
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual GDALColorTable *GetColorTable();
+};
+
+/************************************************************************/
+/*                           GIFRasterBand()                            */
+/************************************************************************/
+
+GIFRasterBand::GIFRasterBand( GIFDataset *poDS, int nBand, 
+                              SavedImage *psSavedImage, int nBackground )
+
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+
+    eDataType = GDT_Byte;
+
+    nBlockXSize = poDS->nRasterXSize;
+    nBlockYSize = 1;
+
+    psImage = psSavedImage;
+
+/* -------------------------------------------------------------------- */
+/*      Setup interlacing map if required.                              */
+/* -------------------------------------------------------------------- */
+    panInterlaceMap = NULL;
+    if( psImage->ImageDesc.Interlace )
+    {
+        int	i, j, iLine = 0;
+
+        panInterlaceMap = (int *) CPLCalloc(poDS->nRasterYSize,sizeof(int));
+
+	for (i = 0; i < 4; i++)
+        {
+	    for (j = InterlacedOffset[i]; 
+                 j < poDS->nRasterYSize;
+                 j += InterlacedJumps[i]) 
+                panInterlaceMap[j] = iLine++;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for transparency.  We just take the first graphic         */
+/*      control extension block we find, if any.                        */
+/* -------------------------------------------------------------------- */
+    int	iExtBlock;
+
+    nTransparentColor = -1;
+    for( iExtBlock = 0; iExtBlock < psImage->ExtensionBlockCount; iExtBlock++ )
+    {
+        unsigned char *pExtData;
+
+        if( psImage->ExtensionBlocks[iExtBlock].Function != 0xf9 )
+            continue;
+
+        pExtData = (unsigned char *) psImage->ExtensionBlocks[iExtBlock].Bytes;
+
+        /* check if transparent color flag is set */
+        if( !(pExtData[0] & 0x1) )
+            continue;
+
+        nTransparentColor = pExtData[3];
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Setup colormap.                                                 */
+/* -------------------------------------------------------------------- */
+    ColorMapObject 	*psGifCT = psImage->ImageDesc.ColorMap;
+    if( psGifCT == NULL )
+        psGifCT = poDS->hGifFile->SColorMap;
+
+    poColorTable = new GDALColorTable();
+    for( int iColor = 0; iColor < psGifCT->ColorCount; iColor++ )
+    {
+        GDALColorEntry	oEntry;
+
+        oEntry.c1 = psGifCT->Colors[iColor].Red;
+        oEntry.c2 = psGifCT->Colors[iColor].Green;
+        oEntry.c3 = psGifCT->Colors[iColor].Blue;
+
+        if( iColor == nTransparentColor )
+            oEntry.c4 = 0;
+        else
+            oEntry.c4 = 255;
+
+        poColorTable->SetColorEntry( iColor, &oEntry );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If we have a background value, return it here.  Some            */
+/*      applications might want to treat this as transparent, but in    */
+/*      many uses this is inappropriate so we don't return it as        */
+/*      nodata or transparent.                                          */
+/* -------------------------------------------------------------------- */
+    if( nBackground != 255 )
+    {
+        char szBackground[10];
+        
+        sprintf( szBackground, "%d", nBackground );
+        SetMetadataItem( "GIF_BACKGROUND", szBackground );
+    }
+}
+
+/************************************************************************/
+/*                           ~GIFRasterBand()                           */
+/************************************************************************/
+
+GIFRasterBand::~GIFRasterBand()
+
+{
+    if( poColorTable != NULL )
+        delete poColorTable;
+
+    CPLFree( panInterlaceMap );
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr GIFRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+
+{
+    CPLAssert( nBlockXOff == 0 );
+
+    if( panInterlaceMap != NULL )
+        nBlockYOff = panInterlaceMap[nBlockYOff];
+
+    memcpy( pImage, psImage->RasterBits + nBlockYOff * nBlockXSize, 
+            nBlockXSize );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp GIFRasterBand::GetColorInterpretation()
+
+{
+    return GCI_PaletteIndex;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *GIFRasterBand::GetColorTable()
+
+{
+    return poColorTable;
+}
+
+/************************************************************************/
+/*                           GetNoDataValue()                           */
+/************************************************************************/
+
+double GIFRasterBand::GetNoDataValue( int *pbSuccess )
+
+{
+    if( pbSuccess != NULL )
+        *pbSuccess = nTransparentColor != -1;
+
+    return nTransparentColor;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             GIFDataset                               */
+/* ==================================================================== */
+/************************************************************************/
+
+
+/************************************************************************/
+/*                            GIFDataset()                            */
+/************************************************************************/
+
+GIFDataset::GIFDataset()
+
+{
+    hGifFile = NULL;
+    fp = NULL;
+    bGeoTransformValid = FALSE;
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+}
+
+/************************************************************************/
+/*                           ~GIFDataset()                            */
+/************************************************************************/
+
+GIFDataset::~GIFDataset()
+
+{
+    FlushCache();
+    if( hGifFile )
+        DGifCloseFile( hGifFile );
+    if( fp != NULL )
+        VSIFCloseL( fp );
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr GIFDataset::GetGeoTransform( double * padfTransform )
+
+{
+    if( bGeoTransformValid )
+    {
+        memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
+        return CE_None;
+    }
+    else
+        return GDALPamDataset::GetGeoTransform( padfTransform );
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *GIFDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+/* -------------------------------------------------------------------- */
+/*	First we check to see if the file has the expected header	*/
+/*	bytes.								*/    
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->nHeaderBytes < 8 )
+        return NULL;
+
+    if( !EQUALN((const char *) poOpenInfo->pabyHeader, "GIF87a",5)
+        && !EQUALN((const char *) poOpenInfo->pabyHeader, "GIF89a",5) )
+        return NULL;
+
+    if( poOpenInfo->eAccess == GA_Update )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "The GIF driver does not support update access to existing"
+                  " files.\n" );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Open the file and ingest.                                       */
+/* -------------------------------------------------------------------- */
+    GifFileType 	*hGifFile;
+    FILE                *fp;
+
+    fp = VSIFOpenL( poOpenInfo->pszFilename, "r" );
+    if( fp == NULL )
+        return NULL;
+
+    hGifFile = DGifOpen( fp, VSIGIFReadFunc );
+    if( hGifFile == NULL )
+    {
+        VSIFCloseL( fp );
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "DGifOpen() failed for %s.\n"
+                  "Perhaps the gif file is corrupt?\n",
+                  poOpenInfo->pszFilename );
+
+        return NULL;
+    }
+
+    if( DGifSlurp( hGifFile ) != GIF_OK )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "DGifSlurp() failed for %s.\n"
+                  "Perhaps the gif file is corrupt?\n",
+                  poOpenInfo->pszFilename );
+
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    GIFDataset 	*poDS;
+
+    poDS = new GIFDataset();
+
+    poDS->fp = fp;
+    poDS->eAccess = GA_ReadOnly;
+    poDS->hGifFile = hGifFile;
+
+/* -------------------------------------------------------------------- */
+/*      Capture some information from the file that is of interest.     */
+/* -------------------------------------------------------------------- */
+    poDS->nRasterXSize = hGifFile->SavedImages[0].ImageDesc.Width;
+    poDS->nRasterYSize = hGifFile->SavedImages[0].ImageDesc.Height;
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    for( int iImage = 0; iImage < hGifFile->ImageCount; iImage++ )
+    {
+        SavedImage	*psImage = hGifFile->SavedImages + iImage;
+
+        if( psImage->ImageDesc.Width != poDS->nRasterXSize
+            || psImage->ImageDesc.Height != poDS->nRasterYSize )
+            continue;
+
+        poDS->SetBand( poDS->nBands+1, 
+                       new GIFRasterBand( poDS, poDS->nBands+1, psImage,
+                                          hGifFile->SBackGroundColor ));
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for world file.                                           */
+/* -------------------------------------------------------------------- */
+    poDS->bGeoTransformValid = 
+        GDALReadWorldFile( poOpenInfo->pszFilename, ".wld", 
+                           poDS->adfGeoTransform )
+        || GDALReadWorldFile( poOpenInfo->pszFilename, ".gfw", 
+                              poDS->adfGeoTransform );
+
+/* -------------------------------------------------------------------- */
+/*      Support overviews.                                              */
+/* -------------------------------------------------------------------- */
+    poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                           GIFCreateCopy()                            */
+/************************************************************************/
+
+static GDALDataset *
+GIFCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, 
+               int bStrict, char ** papszOptions, 
+               GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    int  nBands = poSrcDS->GetRasterCount();
+    int  nXSize = poSrcDS->GetRasterXSize();
+    int  nYSize = poSrcDS->GetRasterYSize();
+    int	 bInterlace = FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Check for interlaced option.                                    */
+/* -------------------------------------------------------------------- */
+    if( CSLFetchNameValue( papszOptions, "INTERLACING" ) != NULL )
+        bInterlace = TRUE;
+
+/* -------------------------------------------------------------------- */
+/*      Some some rudimentary checks                                    */
+/* -------------------------------------------------------------------- */
+    if( nBands != 1 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "GIF driver only supports one band images.\n" );
+
+        return NULL;
+    }
+
+    if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte 
+        && bStrict )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "GIF driver doesn't support data type %s. "
+                  "Only eight bit bands supported.\n", 
+                  GDALGetDataTypeName( 
+                      poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
+
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Open the output file.                                           */
+/* -------------------------------------------------------------------- */
+    GifFileType *hGifFile;
+    FILE *fp;
+
+    fp = VSIFOpenL( pszFilename, "w" );
+    if( fp == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "Failed to create %s:\n%s", 
+                  pszFilename, VSIStrerror( errno ) );
+        return NULL;
+    }
+
+    hGifFile = EGifOpen( fp, VSIGIFWriteFunc );
+    if( hGifFile == NULL )
+    {
+        VSIFCloseL( fp );
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "EGifOpenFilename(%s) failed.  Does file already exist?",
+                  pszFilename );
+
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Prepare colortable.                                             */
+/* -------------------------------------------------------------------- */
+    GDALRasterBand	*poBand = poSrcDS->GetRasterBand(1);
+    ColorMapObject	*psGifCT;
+    int			iColor;
+
+    if( poBand->GetColorTable() == NULL )
+    {
+        psGifCT = MakeMapObject( 256, NULL );
+        for( iColor = 0; iColor < 256; iColor++ )
+        {
+            psGifCT->Colors[iColor].Red = (GifByteType) iColor;
+            psGifCT->Colors[iColor].Green = (GifByteType) iColor;
+            psGifCT->Colors[iColor].Blue = (GifByteType) iColor;
+        }
+    }
+    else
+    {
+        GDALColorTable	*poCT = poBand->GetColorTable();
+        int nFullCount = 1;
+
+        while( nFullCount < poCT->GetColorEntryCount() )
+            nFullCount = nFullCount * 2;
+
+        psGifCT = MakeMapObject( nFullCount, NULL );
+        for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ )
+        {
+            GDALColorEntry	sEntry;
+
+            poCT->GetColorEntryAsRGB( iColor, &sEntry );
+            psGifCT->Colors[iColor].Red = (GifByteType) sEntry.c1;
+            psGifCT->Colors[iColor].Green = (GifByteType) sEntry.c2;
+            psGifCT->Colors[iColor].Blue = (GifByteType) sEntry.c3;
+        }
+        for( ; iColor < nFullCount; iColor++ )
+        {
+            psGifCT->Colors[iColor].Red = 0;
+            psGifCT->Colors[iColor].Green = 0;
+            psGifCT->Colors[iColor].Blue = 0;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Setup parameters.                                               */
+/* -------------------------------------------------------------------- */
+    if (EGifPutScreenDesc(hGifFile, nXSize, nYSize, 
+                          psGifCT->ColorCount, 0,
+			  psGifCT) == GIF_ERROR ||
+	EGifPutImageDesc(hGifFile,
+			 0, 0, nXSize, nYSize, bInterlace, NULL) == GIF_ERROR )
+    {
+        PrintGifError();
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Error writing gif file." );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Loop over image, copying image data.                            */
+/* -------------------------------------------------------------------- */
+    GByte 	*pabyScanline;
+    CPLErr      eErr;
+
+    pabyScanline = (GByte *) CPLMalloc( nXSize );
+
+    if( !bInterlace )
+    {
+        for( int iLine = 0; iLine < nYSize; iLine++ )
+        {
+            eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, 
+                                     pabyScanline, nXSize, 1, GDT_Byte,
+                                     nBands, nBands * nXSize );
+            
+            if( EGifPutLine( hGifFile, pabyScanline, nXSize ) == GIF_ERROR )
+            {
+                CPLError( CE_Failure, CPLE_AppDefined, 
+                          "Error writing gif file." );
+                return NULL;
+            }
+        }
+    }
+    else
+    {
+        int 	i, j;
+
+	/* Need to perform 4 passes on the images: */
+	for ( i = 0; i < 4; i++)
+        {
+	    for (j = InterlacedOffset[i]; j < nYSize; j += InterlacedJumps[i]) 
+            {
+                poBand->RasterIO( GF_Read, 0, j, nXSize, 1, 
+                                  pabyScanline, nXSize, 1, GDT_Byte,
+                                  1, nXSize );
+            
+		if (EGifPutLine(hGifFile, pabyScanline, nXSize)
+		    == GIF_ERROR) return GIF_ERROR;
+	    }
+        }
+    }
+
+    CPLFree( pabyScanline );
+
+/* -------------------------------------------------------------------- */
+/*      cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    if (EGifCloseFile(hGifFile) == GIF_ERROR)
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "EGifCloseFile() failed.\n" );
+        return NULL;
+    }
+    
+    VSIFCloseL( fp );
+
+/* -------------------------------------------------------------------- */
+/*      Do we need a world file?                                          */
+/* -------------------------------------------------------------------- */
+    if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) )
+    {
+    	double      adfGeoTransform[6];
+	
+	poSrcDS->GetGeoTransform( adfGeoTransform );
+	GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Re-open dataset, and copy any auxilary pam information.         */
+/* -------------------------------------------------------------------- */
+    GDALPamDataset *poDS = (GDALPamDataset *) 
+        GDALOpen( pszFilename, GA_ReadOnly );
+
+    if( poDS )
+        poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                           VSIGIFReadFunc()                           */
+/*                                                                      */
+/*      Proxy function for reading from GIF file.                       */
+/************************************************************************/
+
+static int VSIGIFReadFunc( GifFileType *psGFile, GifByteType *pabyBuffer, 
+                           int nBytesToRead )
+
+{
+    return VSIFReadL( pabyBuffer, 1, nBytesToRead, 
+                      (FILE *) psGFile->UserData );
+}
+
+/************************************************************************/
+/*                          VSIGIFWriteFunc()                           */
+/*                                                                      */
+/*      Proxy write function.                                           */
+/************************************************************************/
+
+static int VSIGIFWriteFunc( GifFileType *psGFile, 
+                            const GifByteType *pabyBuffer, int nBytesToWrite )
+
+{
+    return VSIFWriteL( (void *) pabyBuffer, 1, nBytesToWrite, 
+                       (FILE *) psGFile->UserData );
+}
+
+/************************************************************************/
+/*                          GDALRegister_GIF()                        */
+/************************************************************************/
+
+void GDALRegister_GIF()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "GIF" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "GIF" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "Graphics Interchange Format (.gif)" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_gif.html" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gif" );
+        poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/gif" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte" );
+
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, 
+"<CreationOptionList>\n"
+"   <Option name='INTERLACING' type='boolean'/>\n"
+"   <Option name='WORLDFILE' type='boolean'/>\n"
+"</CreationOptionList>\n" );
+
+        poDriver->pfnOpen = GIFDataset::Open;
+        poDriver->pfnCreateCopy = GIFCreateCopy;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/gif/libungif/README b/Utilities/GDAL/frmts/gif/libungif/README
new file mode 100644
index 0000000000..fb5492b4fb
--- /dev/null
+++ b/Utilities/GDAL/frmts/gif/libungif/README
@@ -0,0 +1,14 @@
+This code is from libungif 4.1.0
+
+In theory it comes from:
+  http://prtr-13.ucsc.edu/~badger/software/libungif.shtml
+
+But I don't think this works any more. I generally dig up 
+source RPMs to dig up the code.  
+
+Changes:
+ o Select only lib files needed.
+ o Fix a few warnings about unused variables. 
+ o Hack private include file to avoid VersionString warnings.
+ o Hacked in O_BINARY support whereever O_BINARY is defined.
+
diff --git a/Utilities/GDAL/frmts/gif/libungif/dgif_lib.c b/Utilities/GDAL/frmts/gif/libungif/dgif_lib.c
new file mode 100644
index 0000000000..aa73114be7
--- /dev/null
+++ b/Utilities/GDAL/frmts/gif/libungif/dgif_lib.c
@@ -0,0 +1,1001 @@
+/******************************************************************************
+*   "Gif-Lib" - Yet another gif library.				      *
+*									      *
+* Written by:  Gershon Elber			IBM PC Ver 1.1,	Aug. 1990     *
+*******************************************************************************
+* The kernel of the GIF Decoding process can be found here.		      *
+*******************************************************************************
+* History:								      *
+* 16 Jun 89 - Version 1.0 by Gershon Elber.				      *
+*  3 Sep 90 - Version 1.1 by Gershon Elber (Support for Gif89, Unique names). *
+******************************************************************************/
+
+
+#if defined (__MSDOS__) && !defined(__DJGPP__) && !defined(__GNUC__)
+#include <io.h>
+#include <alloc.h>
+#include <stdlib.h>
+#include <sys\stat.h>
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif /* __MSDOS__ */
+
+#ifdef unix
+#include <unistd.h>
+#endif
+
+#ifndef __MSDOS__
+#include <stdlib.h>
+#endif
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include "gif_lib.h"
+#include "gif_lib_private.h"
+
+#define COMMENT_EXT_FUNC_CODE	0xfe /* Extension function code for comment. */
+#define GIF_STAMP	"GIFVER"	 /* First chars in file - GIF stamp. */
+#define GIF_STAMP_LEN	sizeof(GIF_STAMP) - 1
+#define GIF_VERSION_POS	3		/* Version first character in stamp. */
+
+/* avoid extra function call in case we use fread (TVT) */
+#define READ(_gif,_buf,_len)                                     \
+  (((GifFilePrivateType*)_gif->Private)->Read ?                   \
+    ((GifFilePrivateType*)_gif->Private)->Read(_gif,_buf,_len) : \
+    fread(_buf,1,_len,((GifFilePrivateType*)_gif->Private)->File))
+
+static int DGifGetWord(GifFileType *GifFile, int *Word);
+static int DGifSetupDecompress(GifFileType *GifFile);
+static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line,
+								int LineLen);
+static int DGifGetPrefixChar(unsigned int *Prefix, int Code, int ClearCode);
+static int DGifDecompressInput(GifFileType *GifFile, int *Code);
+static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf,
+						     GifByteType *NextByte);
+
+/******************************************************************************
+*   Open a new gif file for read, given by its name.			      *
+*   Returns GifFileType pointer dynamically allocated which serves as the gif *
+* info record. _GifError is cleared if succesfull.			      *
+******************************************************************************/
+GifFileType *DGifOpenFileName(const char *FileName)
+{
+    int FileHandle;
+    GifFileType *GifFile;
+
+    if ((FileHandle = open(FileName, O_RDONLY
+#ifdef O_BINARY
+			           | O_BINARY
+#endif /* __MSDOS__ */
+			                     )) == -1) {
+	_GifError = D_GIF_ERR_OPEN_FAILED;
+	return NULL;
+    }
+
+    GifFile = DGifOpenFileHandle(FileHandle);
+    if (GifFile == (GifFileType *)NULL)
+        close(FileHandle);
+    return GifFile;
+}
+
+/******************************************************************************
+*   Update a new gif file, given its file handle.                             *
+*   Returns GifFileType pointer dynamically allocated which serves as the gif *
+*   info record. _GifError is cleared if succesfull.                          *
+******************************************************************************/
+GifFileType *DGifOpenFileHandle(int FileHandle)
+{
+    char Buf[GIF_STAMP_LEN+1];
+    GifFileType *GifFile;
+    GifFilePrivateType *Private;
+    FILE *f;
+
+    if ((GifFile = (GifFileType *) malloc(sizeof(GifFileType))) == NULL) {
+        _GifError = D_GIF_ERR_NOT_ENOUGH_MEM;
+        return NULL;
+    }
+
+    memset(GifFile, '\0', sizeof(GifFileType));
+
+    if ((Private = (GifFilePrivateType *) malloc(sizeof(GifFilePrivateType)))
+    == NULL) {
+        _GifError = D_GIF_ERR_NOT_ENOUGH_MEM;
+        free((char *) GifFile);
+        return NULL;
+    }
+
+#if defined(__MSDOS__) || defined(WIN32)
+    setmode(FileHandle, O_BINARY);      /* Make sure it is in binary mode. */
+    f = fdopen(FileHandle, "rb");           /* Make it into a stream: */
+    setvbuf(f, NULL, _IOFBF, GIF_FILE_BUFFER_SIZE);/* And inc. stream buffer.*/
+#else
+    f = fdopen(FileHandle, "rb");           /* Make it into a stream: */
+#endif /* __MSDOS__ */
+
+    GifFile->Private = (VoidPtr) Private;
+    Private->FileHandle = FileHandle;
+    Private->File = f;
+    Private->FileState = FILE_STATE_READ;
+    Private->Read     = 0;  /* don't use alternate input method (TVT) */
+    GifFile->UserData = 0;  /* TVT */
+
+    /* Lets see if this is a GIF file: */
+    if (READ(GifFile,Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) {
+        _GifError = D_GIF_ERR_READ_FAILED;
+        fclose(f);
+        free((char *) Private);
+        free((char *) GifFile);
+        return NULL;
+    }
+
+    /* The GIF Version number is ignored at this time. Maybe we should do    */
+    /* something more useful with it.                         */
+    Buf[GIF_STAMP_LEN] = 0;
+    if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) {
+        _GifError = D_GIF_ERR_NOT_GIF_FILE;
+        fclose(f);
+        free((char *) Private);
+        free((char *) GifFile);
+        return NULL;
+    }
+
+    if (DGifGetScreenDesc(GifFile) == GIF_ERROR) {
+        fclose(f);
+        free((char *) Private);
+        free((char *) GifFile);
+        return NULL;
+    }
+
+    _GifError = 0;
+
+    return GifFile;
+}
+
+/******************************************************************************
+* GifFileType constructor with user supplied input function (TVT)             *
+*                                                                             *
+******************************************************************************/
+GifFileType *DGifOpen( void* userData, InputFunc readFunc )
+{
+    char Buf[GIF_STAMP_LEN+1];
+    GifFileType *GifFile;
+    GifFilePrivateType *Private;
+
+
+    if ((GifFile = (GifFileType *) malloc(sizeof(GifFileType))) == NULL) {
+	  _GifError = D_GIF_ERR_NOT_ENOUGH_MEM;
+	  return NULL;
+    }
+
+    memset(GifFile, '\0', sizeof(GifFileType));
+
+    if (!(Private = (GifFilePrivateType*) malloc(sizeof(GifFilePrivateType)))){
+	  _GifError = D_GIF_ERR_NOT_ENOUGH_MEM;
+	  free((char *) GifFile);
+	  return NULL;
+    }
+
+    GifFile->Private = (VoidPtr) Private;
+    Private->FileHandle = 0;
+    Private->File = 0;
+    Private->FileState = FILE_STATE_READ;
+
+	Private->Read     = readFunc;  /* TVT */
+	GifFile->UserData = userData;    /* TVT */
+
+    /* Lets see if this is a GIF file: */
+    if ( READ( GifFile, Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) {
+	  _GifError = D_GIF_ERR_READ_FAILED;
+	  free((char *) Private);
+	  free((char *) GifFile);
+	  return NULL;
+    }
+
+    /* The GIF Version number is ignored at this time. Maybe we should do    */
+    /* something more useful with it.					     */
+    Buf[GIF_STAMP_LEN] = 0;
+    if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) {
+	  _GifError = D_GIF_ERR_NOT_GIF_FILE;
+	  free((char *) Private);
+	  free((char *) GifFile);
+	  return NULL;
+    }
+
+    if (DGifGetScreenDesc(GifFile) == GIF_ERROR) {
+	  free((char *) Private);
+	  free((char *) GifFile);
+	  return NULL;
+    }
+
+    _GifError = 0;
+
+    return GifFile;
+}
+
+/******************************************************************************
+*   This routine should be called before any other DGif calls. Note that      *
+* this routine is called automatically from DGif file open routines.	      *
+******************************************************************************/
+int DGifGetScreenDesc(GifFileType *GifFile)
+{
+    int i, BitsPerPixel;
+    GifByteType Buf[3];
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (!IS_READABLE(Private)) {
+	/* This file was NOT open for reading: */
+	_GifError = D_GIF_ERR_NOT_READABLE;
+	return GIF_ERROR;
+    }
+
+    /* Put the screen descriptor into the file: */
+    if (DGifGetWord(GifFile, &GifFile->SWidth) == GIF_ERROR ||
+	DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR)
+	return GIF_ERROR;
+
+    if (READ( GifFile, Buf, 3) != 3) {
+	_GifError = D_GIF_ERR_READ_FAILED;
+	return GIF_ERROR;
+    }
+    GifFile->SColorResolution = (((Buf[0] & 0x70) + 1) >> 4) + 1;
+    BitsPerPixel = (Buf[0] & 0x07) + 1;
+    GifFile->SBackGroundColor = Buf[1];
+    if (Buf[0] & 0x80) {		     /* Do we have global color map? */
+
+	GifFile->SColorMap = MakeMapObject(1 << BitsPerPixel, NULL);
+
+	/* Get the global color map: */
+	for (i = 0; i < GifFile->SColorMap->ColorCount; i++) {
+	    if (READ(GifFile, Buf, 3) != 3) {
+		_GifError = D_GIF_ERR_READ_FAILED;
+		return GIF_ERROR;
+	    }
+	    GifFile->SColorMap->Colors[i].Red = Buf[0];
+	    GifFile->SColorMap->Colors[i].Green = Buf[1];
+	    GifFile->SColorMap->Colors[i].Blue = Buf[2];
+	}
+    }
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   This routine should be called before any attemp to read an image.         *
+******************************************************************************/
+int DGifGetRecordType(GifFileType *GifFile, GifRecordType *Type)
+{
+    GifByteType Buf;
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (!IS_READABLE(Private)) {
+	/* This file was NOT open for reading: */
+	_GifError = D_GIF_ERR_NOT_READABLE;
+	return GIF_ERROR;
+    }
+
+    if (READ( GifFile, &Buf, 1) != 1) {
+	_GifError = D_GIF_ERR_READ_FAILED;
+	return GIF_ERROR;
+    }
+
+    switch (Buf) {
+	case ',':
+	    *Type = IMAGE_DESC_RECORD_TYPE;
+	    break;
+	case '!':
+	    *Type = EXTENSION_RECORD_TYPE;
+	    break;
+	case ';':
+	    *Type = TERMINATE_RECORD_TYPE;
+	    break;
+	default:
+	    *Type = UNDEFINED_RECORD_TYPE;
+	    _GifError = D_GIF_ERR_WRONG_RECORD;
+	    return GIF_ERROR;
+    }
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   This routine should be called before any attemp to read an image.         *
+*   Note it is assumed the Image desc. header (',') has been read.	      *
+******************************************************************************/
+int DGifGetImageDesc(GifFileType *GifFile)
+{
+    int i, BitsPerPixel;
+    GifByteType Buf[3];
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+    SavedImage	*sp;
+
+    if (!IS_READABLE(Private)) {
+	/* This file was NOT open for reading: */
+	_GifError = D_GIF_ERR_NOT_READABLE;
+	return GIF_ERROR;
+    }
+
+    if (DGifGetWord(GifFile, &GifFile->Image.Left) == GIF_ERROR ||
+	DGifGetWord(GifFile, &GifFile->Image.Top) == GIF_ERROR ||
+	DGifGetWord(GifFile, &GifFile->Image.Width) == GIF_ERROR ||
+	DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR)
+	return GIF_ERROR;
+    if (READ(GifFile,Buf, 1) != 1) {
+	_GifError = D_GIF_ERR_READ_FAILED;
+	return GIF_ERROR;
+    }
+    BitsPerPixel = (Buf[0] & 0x07) + 1;
+    GifFile->Image.Interlace = (Buf[0] & 0x40);
+    if (Buf[0] & 0x80) {	    /* Does this image have local color map? */
+
+	if (GifFile->Image.ColorMap)
+        {
+	    FreeMapObject(GifFile->Image.ColorMap);
+        }
+
+	GifFile->Image.ColorMap = MakeMapObject(1 << BitsPerPixel, NULL);
+    
+	/* Get the image local color map: */
+	for (i = 0; i < GifFile->Image.ColorMap->ColorCount; i++) {
+	    if (READ(GifFile,Buf, 3) != 3) {
+		_GifError = D_GIF_ERR_READ_FAILED;
+		return GIF_ERROR;
+	    }
+	    GifFile->Image.ColorMap->Colors[i].Red = Buf[0];
+	    GifFile->Image.ColorMap->Colors[i].Green = Buf[1];
+	    GifFile->Image.ColorMap->Colors[i].Blue = Buf[2];
+	}
+    }
+
+    if (GifFile->SavedImages) {
+        if ((GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages,
+                                                          sizeof(SavedImage) * (GifFile->ImageCount + 1))) == NULL) {
+            _GifError = D_GIF_ERR_NOT_ENOUGH_MEM;
+            return GIF_ERROR;
+        }
+    } else {
+        if ((GifFile->SavedImages =
+             (SavedImage *)malloc(sizeof(SavedImage))) == NULL) {
+            _GifError = D_GIF_ERR_NOT_ENOUGH_MEM;
+            return GIF_ERROR;
+        }
+    }
+
+    sp = &GifFile->SavedImages[GifFile->ImageCount];
+    memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc));
+    if (GifFile->Image.ColorMap != NULL) {
+        sp->ImageDesc.ColorMap = MakeMapObject( 
+            GifFile->Image.ColorMap->ColorCount, 
+            GifFile->Image.ColorMap->Colors );
+    }
+    sp->RasterBits = (char *)NULL;
+    sp->ExtensionBlockCount = 0;
+    sp->ExtensionBlocks = (ExtensionBlock *)NULL;
+
+    GifFile->ImageCount++;
+
+    Private->PixelCount = (long) GifFile->Image.Width *
+        (long) GifFile->Image.Height;
+
+    DGifSetupDecompress(GifFile);  /* Reset decompress algorithm parameters. */
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+*  Get one full scanned line (Line) of length LineLen from GIF file.	      *
+******************************************************************************/
+int DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen)
+{
+    GifByteType *Dummy;
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (!IS_READABLE(Private)) {
+	/* This file was NOT open for reading: */
+	_GifError = D_GIF_ERR_NOT_READABLE;
+	return GIF_ERROR;
+    }
+
+    if (!LineLen) LineLen = GifFile->Image.Width;
+
+#if defined(__MSDOS__) || defined(__GNUC__)
+    if ((Private->PixelCount -= LineLen) > 0xffff0000UL) {
+#else
+    if ((Private->PixelCount -= LineLen) > 0xffff0000) {
+#endif /* __MSDOS__ */
+	_GifError = D_GIF_ERR_DATA_TOO_BIG;
+	return GIF_ERROR;
+    }
+
+    if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) {
+	if (Private->PixelCount == 0) {
+	    /* We probably would not be called any more, so lets clean 	     */
+	    /* everything before we return: need to flush out all rest of    */
+	    /* image until empty block (size 0) detected. We use GetCodeNext.*/
+	    do if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR)
+		return GIF_ERROR;
+	    while (Dummy != NULL);
+	}
+	return GIF_OK;
+    }
+    else
+	return GIF_ERROR;
+}
+
+/******************************************************************************
+* Put one pixel (Pixel) into GIF file.					      *
+******************************************************************************/
+int DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel)
+{
+    GifByteType *Dummy;
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (!IS_READABLE(Private)) {
+	/* This file was NOT open for reading: */
+	_GifError = D_GIF_ERR_NOT_READABLE;
+	return GIF_ERROR;
+    }
+
+#if defined(__MSDOS__) || defined(__GNUC__)
+    if (--Private->PixelCount > 0xffff0000UL)
+#else
+    if (--Private->PixelCount > 0xffff0000)
+#endif /* __MSDOS__ */
+    {
+	_GifError = D_GIF_ERR_DATA_TOO_BIG;
+	return GIF_ERROR;
+    }
+
+    if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) {
+	if (Private->PixelCount == 0) {
+	    /* We probably would not be called any more, so lets clean 	     */
+	    /* everything before we return: need to flush out all rest of    */
+	    /* image until empty block (size 0) detected. We use GetCodeNext.*/
+	    do if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR)
+		return GIF_ERROR;
+	    while (Dummy != NULL);
+	}
+	return GIF_OK;
+    }
+    else
+	return GIF_ERROR;
+}
+
+/******************************************************************************
+*   Get an extension block (see GIF manual) from gif file. This routine only  *
+* returns the first data block, and DGifGetExtensionNext shouldbe called      *
+* after this one until NULL extension is returned.			      *
+*   The Extension should NOT be freed by the user (not dynamically allocated).*
+*   Note it is assumed the Extension desc. header ('!') has been read.	      *
+******************************************************************************/
+int DGifGetExtension(GifFileType *GifFile, int *ExtCode,
+						    GifByteType **Extension)
+{
+    GifByteType Buf;
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (!IS_READABLE(Private)) {
+	/* This file was NOT open for reading: */
+	_GifError = D_GIF_ERR_NOT_READABLE;
+	return GIF_ERROR;
+    }
+
+    if (READ(GifFile,&Buf, 1) != 1) {
+	_GifError = D_GIF_ERR_READ_FAILED;
+	return GIF_ERROR;
+    }
+    *ExtCode = Buf;
+
+    return DGifGetExtensionNext(GifFile, Extension);
+}
+
+/******************************************************************************
+*   Get a following extension block (see GIF manual) from gif file. This      *
+* routine sould be called until NULL Extension is returned.		      *
+*   The Extension should NOT be freed by the user (not dynamically allocated).*
+******************************************************************************/
+int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **Extension)
+{
+    GifByteType Buf;
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (READ(GifFile,&Buf, 1) != 1) {
+	_GifError = D_GIF_ERR_READ_FAILED;
+	return GIF_ERROR;
+    }
+    if (Buf > 0) {
+	*Extension = Private->Buf;           /* Use private unused buffer. */
+	(*Extension)[0] = Buf;  /* Pascal strings notation (pos. 0 is len.). */
+	if (READ(GifFile,&((*Extension)[1]), Buf) != Buf) {
+	    _GifError = D_GIF_ERR_READ_FAILED;
+	    return GIF_ERROR;
+	}
+    }
+    else
+	*Extension = NULL;
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   This routine should be called last, to close the GIF file.		      *
+******************************************************************************/
+int DGifCloseFile(GifFileType *GifFile)
+{
+    GifFilePrivateType *Private;
+    FILE *File;
+
+    if (GifFile == NULL) return GIF_ERROR;
+
+    Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (!IS_READABLE(Private)) {
+	/* This file was NOT open for reading: */
+	_GifError = D_GIF_ERR_NOT_READABLE;
+	return GIF_ERROR;
+    }
+
+    File = Private->File;
+
+    if (GifFile->Image.ColorMap)
+	FreeMapObject(GifFile->Image.ColorMap);
+    if (GifFile->SColorMap)
+	FreeMapObject(GifFile->SColorMap);
+    if (Private)
+	free((char *) Private);
+    if (GifFile->SavedImages)
+	FreeSavedImages(GifFile);
+    free(GifFile);
+
+    if ( File && (fclose(File) != 0)) {
+	  _GifError = D_GIF_ERR_CLOSE_FAILED;
+	  return GIF_ERROR;
+    }
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   Get 2 bytes (word) from the given file:				      *
+******************************************************************************/
+static int DGifGetWord(GifFileType *GifFile, int *Word)
+{
+    unsigned char c[2];
+
+    if (READ(GifFile,c, 2) != 2) {
+	_GifError = D_GIF_ERR_READ_FAILED;
+	return GIF_ERROR;
+    }
+
+    *Word = (((unsigned int) c[1]) << 8) + c[0];
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   Get the image code in compressed form.  his routine can be called if the  *
+* information needed to be piped out as is. Obviously this is much faster     *
+* than decoding and encoding again. This routine should be followed by calls  *
+* to DGifGetCodeNext, until NULL block is returned.			      *
+*   The block should NOT be freed by the user (not dynamically allocated).    *
+******************************************************************************/
+int DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock)
+{
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (!IS_READABLE(Private)) {
+	/* This file was NOT open for reading: */
+	_GifError = D_GIF_ERR_NOT_READABLE;
+	return GIF_ERROR;
+    }
+
+    *CodeSize = Private->BitsPerPixel;
+
+    return DGifGetCodeNext(GifFile, CodeBlock);
+}
+
+/******************************************************************************
+*   Continue to get the image code in compressed form. This routine should be *
+* called until NULL block is returned.					      *
+*   The block should NOT be freed by the user (not dynamically allocated).    *
+******************************************************************************/
+int DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock)
+{
+    GifByteType Buf;
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (READ(GifFile,&Buf, 1) != 1) {
+	_GifError = D_GIF_ERR_READ_FAILED;
+	return GIF_ERROR;
+    }
+
+    if (Buf > 0) {
+	*CodeBlock = Private->Buf;	       /* Use private unused buffer. */
+	(*CodeBlock)[0] = Buf;  /* Pascal strings notation (pos. 0 is len.). */
+	if (READ(GifFile,&((*CodeBlock)[1]), Buf) != Buf) {
+	    _GifError = D_GIF_ERR_READ_FAILED;
+	    return GIF_ERROR;
+	}
+    }
+    else {
+	*CodeBlock = NULL;
+	Private->Buf[0] = 0;		   /* Make sure the buffer is empty! */
+	Private->PixelCount = 0;   /* And local info. indicate image read. */
+    }
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   Setup the LZ decompression for this image:				      *
+******************************************************************************/
+static int DGifSetupDecompress(GifFileType *GifFile)
+{
+    int i, BitsPerPixel;
+    GifByteType CodeSize;
+    unsigned int *Prefix;
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    READ(GifFile,&CodeSize, 1);    /* Read Code size from file. */
+    BitsPerPixel = CodeSize;
+
+    Private->Buf[0] = 0;			      /* Input Buffer empty. */
+    Private->BitsPerPixel = BitsPerPixel;
+    Private->ClearCode = (1 << BitsPerPixel);
+    Private->EOFCode = Private->ClearCode + 1;
+    Private->RunningCode = Private->EOFCode + 1;
+    Private->RunningBits = BitsPerPixel + 1;	 /* Number of bits per code. */
+    Private->MaxCode1 = 1 << Private->RunningBits;     /* Max. code + 1. */
+    Private->StackPtr = 0;		    /* No pixels on the pixel stack. */
+    Private->LastCode = NO_SUCH_CODE;
+    Private->CrntShiftState = 0;	/* No information in CrntShiftDWord. */
+    Private->CrntShiftDWord = 0;
+
+    Prefix = Private->Prefix;
+    for (i = 0; i <= LZ_MAX_CODE; i++) Prefix[i] = NO_SUCH_CODE;
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   The LZ decompression routine:					      *
+*   This version decompress the given gif file into Line of length LineLen.   *
+*   This routine can be called few times (one per scan line, for example), in *
+* order the complete the whole image.					      *
+******************************************************************************/
+static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line,
+								int LineLen)
+{
+    int i = 0, j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr;
+    GifByteType *Stack, *Suffix;
+    unsigned int *Prefix;
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    StackPtr = Private->StackPtr;
+    Prefix = Private->Prefix;
+    Suffix = Private->Suffix;
+    Stack = Private->Stack;
+    EOFCode = Private->EOFCode;
+    ClearCode = Private->ClearCode;
+    LastCode = Private->LastCode;
+
+    if (StackPtr != 0) {
+	/* Let pop the stack off before continueing to read the gif file: */
+	while (StackPtr != 0 && i < LineLen) Line[i++] = Stack[--StackPtr];
+    }
+
+    while (i < LineLen) {			    /* Decode LineLen items. */
+	if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR)
+    	    return GIF_ERROR;
+
+	if (CrntCode == EOFCode) {
+	    /* Note however that usually we will not be here as we will stop */
+	    /* decoding as soon as we got all the pixel, or EOF code will    */
+	    /* not be read at all, and DGifGetLine/Pixel clean everything.   */
+	    if (i != LineLen - 1 || Private->PixelCount != 0) {
+		_GifError = D_GIF_ERR_EOF_TOO_SOON;
+		return GIF_ERROR;
+	    }
+	    i++;
+	}
+	else if (CrntCode == ClearCode) {
+	    /* We need to start over again: */
+	    for (j = 0; j <= LZ_MAX_CODE; j++) Prefix[j] = NO_SUCH_CODE;
+	    Private->RunningCode = Private->EOFCode + 1;
+	    Private->RunningBits = Private->BitsPerPixel + 1;
+	    Private->MaxCode1 = 1 << Private->RunningBits;
+	    LastCode = Private->LastCode = NO_SUCH_CODE;
+	}
+	else {
+	    /* Its regular code - if in pixel range simply add it to output  */
+	    /* stream, otherwise trace to codes linked list until the prefix */
+	    /* is in pixel range:					     */
+	    if (CrntCode < ClearCode) {
+		/* This is simple - its pixel scalar, so add it to output:   */
+		Line[i++] = CrntCode;
+	    }
+	    else {
+		/* Its a code to needed to be traced: trace the linked list  */
+		/* until the prefix is a pixel, while pushing the suffix     */
+		/* pixels on our stack. If we done, pop the stack in reverse */
+		/* (thats what stack is good for!) order to output.	     */
+		if (Prefix[CrntCode] == NO_SUCH_CODE) {
+		    /* Only allowed if CrntCode is exactly the running code: */
+		    /* In that case CrntCode = XXXCode, CrntCode or the	     */
+		    /* prefix code is last code and the suffix char is	     */
+		    /* exactly the prefix of last code!			     */
+		    if (CrntCode == Private->RunningCode - 2) {
+			CrntPrefix = LastCode;
+			Suffix[Private->RunningCode - 2] =
+			Stack[StackPtr++] = DGifGetPrefixChar(Prefix,
+							LastCode, ClearCode);
+		    }
+		    else {
+			_GifError = D_GIF_ERR_IMAGE_DEFECT;
+			return GIF_ERROR;
+		    }
+		}
+		else
+		    CrntPrefix = CrntCode;
+
+		/* Now (if image is O.K.) we should not get an NO_SUCH_CODE  */
+		/* During the trace. As we might loop forever, in case of    */
+		/* defective image, we count the number of loops we trace    */
+		/* and stop if we got LZ_MAX_CODE. obviously we can not      */
+		/* loop more than that.					     */
+		j = 0;
+		while (j++ <= LZ_MAX_CODE &&
+		       CrntPrefix > ClearCode &&
+		       CrntPrefix <= LZ_MAX_CODE) {
+		    Stack[StackPtr++] = Suffix[CrntPrefix];
+		    CrntPrefix = Prefix[CrntPrefix];
+		}
+		if (j >= LZ_MAX_CODE || CrntPrefix > LZ_MAX_CODE) {
+		    _GifError = D_GIF_ERR_IMAGE_DEFECT;
+		    return GIF_ERROR;
+		}
+		/* Push the last character on stack: */
+		Stack[StackPtr++] = CrntPrefix;
+
+		/* Now lets pop all the stack into output: */
+		while (StackPtr != 0 && i < LineLen)
+		    Line[i++] = Stack[--StackPtr];
+	    }
+	    if (LastCode != NO_SUCH_CODE) {
+		Prefix[Private->RunningCode - 2] = LastCode;
+
+		if (CrntCode == Private->RunningCode - 2) {
+		    /* Only allowed if CrntCode is exactly the running code: */
+		    /* In that case CrntCode = XXXCode, CrntCode or the	     */
+		    /* prefix code is last code and the suffix char is	     */
+		    /* exactly the prefix of last code!			     */
+		    Suffix[Private->RunningCode - 2] =
+			DGifGetPrefixChar(Prefix, LastCode, ClearCode);
+		}
+		else {
+		    Suffix[Private->RunningCode - 2] =
+			DGifGetPrefixChar(Prefix, CrntCode, ClearCode);
+		}
+	    }
+	    LastCode = CrntCode;
+	}
+    }
+
+    Private->LastCode = LastCode;
+    Private->StackPtr = StackPtr;
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+* Routine to trace the Prefixes linked list until we get a prefix which is    *
+* not code, but a pixel value (less than ClearCode). Returns that pixel value.*
+* If image is defective, we might loop here forever, so we limit the loops to *
+* the maximum possible if image O.k. - LZ_MAX_CODE times.		      *
+******************************************************************************/
+static int DGifGetPrefixChar(unsigned int *Prefix, int Code, int ClearCode)
+{
+    int i = 0;
+
+    while (Code > ClearCode && i++ <= LZ_MAX_CODE) Code = Prefix[Code];
+    return Code;
+}
+
+/******************************************************************************
+*   Interface for accessing the LZ codes directly. Set Code to the real code  *
+* (12bits), or to -1 if EOF code is returned.				      *
+******************************************************************************/
+int DGifGetLZCodes(GifFileType *GifFile, int *Code)
+{
+    GifByteType *CodeBlock;
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (!IS_READABLE(Private)) {
+	/* This file was NOT open for reading: */
+	_GifError = D_GIF_ERR_NOT_READABLE;
+	return GIF_ERROR;
+    }
+
+    if (DGifDecompressInput(GifFile, Code) == GIF_ERROR)
+	return GIF_ERROR;
+
+    if (*Code == Private->EOFCode) {
+	/* Skip rest of codes (hopefully only NULL terminating block): */
+	do if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR)
+    	    return GIF_ERROR;
+	while (CodeBlock != NULL);
+
+	*Code = -1;
+    }
+    else if (*Code == Private->ClearCode) {
+	/* We need to start over again: */
+	Private->RunningCode = Private->EOFCode + 1;
+	Private->RunningBits = Private->BitsPerPixel + 1;
+	Private->MaxCode1 = 1 << Private->RunningBits;
+    }
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   The LZ decompression input routine:					      *
+*   This routine is responsable for the decompression of the bit stream from  *
+* 8 bits (bytes) packets, into the real codes.				      *
+*   Returns GIF_OK if read succesfully.					      *
+******************************************************************************/
+static int DGifDecompressInput(GifFileType *GifFile, int *Code)
+{
+    GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
+
+    GifByteType NextByte;
+    static unsigned int CodeMasks[] = {
+	0x0000, 0x0001, 0x0003, 0x0007,
+	0x000f, 0x001f, 0x003f, 0x007f,
+	0x00ff, 0x01ff, 0x03ff, 0x07ff,
+	0x0fff
+    };
+
+    while (Private->CrntShiftState < Private->RunningBits) {
+	/* Needs to get more bytes from input stream for next code: */
+	if (DGifBufferedInput(GifFile, Private->Buf, &NextByte)
+	    == GIF_ERROR) {
+	    return GIF_ERROR;
+	}
+	Private->CrntShiftDWord |=
+		((unsigned long) NextByte) << Private->CrntShiftState;
+	Private->CrntShiftState += 8;
+    }
+    *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits];
+
+    Private->CrntShiftDWord >>= Private->RunningBits;
+    Private->CrntShiftState -= Private->RunningBits;
+
+    /* If code cannt fit into RunningBits bits, must raise its size. Note */
+    /* however that codes above 4095 are used for special signaling.      */
+    if (++Private->RunningCode > Private->MaxCode1 &&
+	Private->RunningBits < LZ_BITS) {
+	Private->MaxCode1 <<= 1;
+	Private->RunningBits++;
+    }
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   This routines read one gif data block at a time and buffers it internally *
+* so that the decompression routine could access it.			      *
+*   The routine returns the next byte from its internal buffer (or read next  *
+* block in if buffer empty) and returns GIF_OK if succesful.		      *
+******************************************************************************/
+static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf,
+						      GifByteType *NextByte)
+{
+    if (Buf[0] == 0) {
+	/* Needs to read the next buffer - this one is empty: */
+	if (READ(GifFile, Buf, 1) != 1)
+	{
+	    _GifError = D_GIF_ERR_READ_FAILED;
+	    return GIF_ERROR;
+	}
+	if (READ(GifFile,&Buf[1], Buf[0]) != Buf[0])
+	{
+	    _GifError = D_GIF_ERR_READ_FAILED;
+	    return GIF_ERROR;
+	}
+	*NextByte = Buf[1];
+	Buf[1] = 2;	   /* We use now the second place as last char read! */
+	Buf[0]--;
+    }
+    else {
+	*NextByte = Buf[Buf[1]++];
+	Buf[0]--;
+    }
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+* This routine reads an entire GIF into core, hanging all its state info off  *
+* the GifFileType pointer.  Call DGifOpenFileName() or DGifOpenFileHandle()   *
+* first to initialize I/O.  Its inverse is EGifSpew().			      *
+* 
+ ******************************************************************************/
+int DGifSlurp(GifFileType *GifFile)
+{
+    int ImageSize;
+    GifRecordType RecordType;
+    SavedImage *sp;
+    GifByteType *ExtData;
+    SavedImage temp_save;
+
+    temp_save.ExtensionBlocks=NULL;
+    temp_save.ExtensionBlockCount=0;
+  
+    do {
+	if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR)
+	    return(GIF_ERROR);
+
+	switch (RecordType) {
+	    case IMAGE_DESC_RECORD_TYPE:
+		if (DGifGetImageDesc(GifFile) == GIF_ERROR)
+		    return(GIF_ERROR);
+
+		sp = &GifFile->SavedImages[GifFile->ImageCount-1];
+		ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height;
+
+		sp->RasterBits
+		    = (GifPixelType*) malloc(ImageSize * sizeof(GifPixelType));
+
+		if (DGifGetLine(GifFile, sp->RasterBits, ImageSize)
+		    == GIF_ERROR)
+		    return(GIF_ERROR);
+
+        if (temp_save.ExtensionBlocks) {
+            sp->ExtensionBlocks = temp_save.ExtensionBlocks;
+            sp->ExtensionBlockCount = temp_save.ExtensionBlockCount;
+
+            temp_save.ExtensionBlocks = NULL;
+            temp_save.ExtensionBlockCount=0;
+
+        /* FIXME: The following is wrong.  It is left in only for backwards
+         * compatibility.  Someday it should go away.  Use the
+         * sp->ExtensionBlocks->Function variable instead.
+         */
+            sp->Function = sp->ExtensionBlocks[0].Function;
+
+        }
+
+		break;
+
+	    case EXTENSION_RECORD_TYPE:
+		if (DGifGetExtension(GifFile,&temp_save.Function,&ExtData)==GIF_ERROR)
+		    return(GIF_ERROR);
+		while (ExtData != NULL) {
+            
+            /* Create an extension block with our data */
+            if (AddExtensionBlock(&temp_save, ExtData[0], &ExtData[1])
+               == GIF_ERROR)
+                return (GIF_ERROR); 
+            
+		    if (DGifGetExtensionNext(GifFile, &ExtData) == GIF_ERROR)
+			    return(GIF_ERROR);
+            temp_save.Function = 0;
+		}
+		break;
+
+	    case TERMINATE_RECORD_TYPE:
+		break;
+
+	    default:	/* Should be trapped by DGifGetRecordType */
+		break;
+	}
+    } while (RecordType != TERMINATE_RECORD_TYPE);
+
+    /* Just in case the Gif has an extension block without an associated
+     * image... (Should we save this into a savefile structure with no image
+     * instead?  Have to check if the present writing code can handle that as
+     * well....
+     */
+    if (temp_save.ExtensionBlocks)
+        FreeExtension(&temp_save);
+    
+    return(GIF_OK);
+}
+
diff --git a/Utilities/GDAL/frmts/gif/libungif/egif_lib.c b/Utilities/GDAL/frmts/gif/libungif/egif_lib.c
new file mode 100644
index 0000000000..283503ee55
--- /dev/null
+++ b/Utilities/GDAL/frmts/gif/libungif/egif_lib.c
@@ -0,0 +1,824 @@
+/******************************************************************************
+*   "Gif-Lib" - Yet another gif library.				      *
+*									      *
+* Written by:  Gershon Elber				Ver 1.1, Aug. 1990    *
+*******************************************************************************
+* The kernel of the GIF Encoding process can be found here.		      *
+*******************************************************************************
+* History:								      *
+* 14 Jun 89 - Version 1.0 by Gershon Elber.				      *
+*  3 Sep 90 - Version 1.1 by Gershon Elber (Support for Gif89, Unique names). *
+* 26 Jun 96 - Version 3.0 by Eric S. Raymond (Full GIF89 support)
+******************************************************************************/
+
+#ifdef __MSDOS__
+#include <io.h>
+#include <alloc.h>
+#include <sys\stat.h>
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef R6000
+#include <sys/mode.h>
+#endif
+#endif /* __MSDOS__ */
+
+#ifdef unix
+#include <unistd.h>
+#endif
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "gif_lib.h"
+#include "gif_lib_private.h"
+
+#define GIF87_STAMP	"GIF87a"         /* First chars in file - GIF stamp. */
+#define GIF89_STAMP	"GIF89a"         /* First chars in file - GIF stamp. */
+
+/* #define DEBUG_NO_PREFIX		          Dump only compressed data. */
+
+/* Masks given codes to BitsPerPixel, to make sure all codes are in range: */
+static GifPixelType CodeMask[] = {
+    0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
+};
+
+static char *GifVersionPrefix = GIF87_STAMP;
+
+#define WRITE(_gif,_buf,_len)   \
+  (((GifFilePrivateType*)_gif->Private)->Write ?    \
+   ((GifFilePrivateType*)_gif->Private)->Write(_gif,_buf,_len) :    \
+   fwrite(_buf, 1, _len, ((GifFilePrivateType*)_gif->Private)->File))
+
+static int EGifPutWord(int Word, GifFileType *GifFile);
+static int EGifSetupCompress(GifFileType *GifFile);
+static int EGifCompressLine(GifFileType *GifFile, GifPixelType *Line,
+								int LineLen);
+static int EGifCompressOutput(GifFileType *GifFile, int Code);
+static int EGifBufferedOutput(GifFileType *GifFile, GifByteType *Buf, int c);
+
+/******************************************************************************
+*   Open a new gif file for write, given by its name. If TestExistance then   *
+* if the file exists this routines fails (returns NULL).		      *
+*   Returns GifFileType pointer dynamically allocated which serves as the gif *
+* info record. _GifError is cleared if succesfull.			      *
+******************************************************************************/
+GifFileType *EGifOpenFileName(char *FileName, int TestExistance)
+{
+    int FileHandle;
+    GifFileType *GifFile;
+
+    if (TestExistance)
+	FileHandle = open(FileName,
+			  O_WRONLY | O_CREAT | O_EXCL
+#ifdef O_BINARY
+			                     | O_BINARY
+#endif /* __MSDOS__ */
+			                               ,
+			  S_IREAD | S_IWRITE);
+    else
+	FileHandle = open(FileName,
+			  O_WRONLY | O_CREAT | O_TRUNC
+#ifdef O_BINARY
+			                     | O_BINARY
+#endif /* __MSDOS__ */
+			                               ,
+			  S_IREAD | S_IWRITE);
+
+    if (FileHandle == -1) {
+	_GifError = E_GIF_ERR_OPEN_FAILED;
+	return NULL;
+    }
+    GifFile = EGifOpenFileHandle(FileHandle);
+    if (GifFile == (GifFileType*)NULL)
+        close(FileHandle);
+    return GifFile;
+}
+
+/******************************************************************************
+*   Update a new gif file, given its file handle, which must be opened for    *
+* write in binary mode.							      *
+*   Returns GifFileType pointer dynamically allocated which serves as the gif *
+* info record. _GifError is cleared if succesfull.			      *
+******************************************************************************/
+GifFileType *EGifOpenFileHandle(int FileHandle)
+{
+    GifFileType *GifFile;
+    GifFilePrivateType *Private;
+    FILE *f;
+
+    if ((GifFile = (GifFileType *) malloc(sizeof(GifFileType))) == NULL) {
+        _GifError = E_GIF_ERR_NOT_ENOUGH_MEM;
+        return NULL;
+    }
+
+    memset(GifFile, '\0', sizeof(GifFileType));
+
+    if ((Private = (GifFilePrivateType *)
+                   malloc(sizeof(GifFilePrivateType))) == NULL) {
+        free(GifFile);
+        _GifError = E_GIF_ERR_NOT_ENOUGH_MEM;
+        return NULL;
+    }
+
+#if defined(__MSDOS__) || defined(WIN32)
+    setmode(FileHandle, O_BINARY);      /* Make sure it is in binary mode. */
+    f = fdopen(FileHandle, "wb");           /* Make it into a stream: */
+    setvbuf(f, NULL, _IOFBF, GIF_FILE_BUFFER_SIZE);   /* And inc. stream buffer. */
+#else
+    f = fdopen(FileHandle, "w");           /* Make it into a stream: */
+#endif /* __MSDOS__ */
+
+    GifFile->Private = (VoidPtr) Private;
+    Private->FileHandle = FileHandle;
+    Private->File = f;
+    Private->FileState = FILE_STATE_WRITE;
+    
+    Private->Write = (OutputFunc)0; /* No user write routine (MRB) */
+    GifFile->UserData = (VoidPtr)0; /* No user write handle (MRB) */
+    
+    _GifError = 0;
+
+    return GifFile;
+}
+
+/******************************************************************************
+* Output constructor that takes user supplied output function.                *
+* Basically just a copy of EGifOpenFileHandle. (MRB)                          *
+******************************************************************************/
+GifFileType* EGifOpen(void* userData, OutputFunc writeFunc)
+{
+    GifFileType* GifFile;
+    GifFilePrivateType* Private;
+
+     if ((GifFile = (GifFileType *) malloc(sizeof(GifFileType))) == NULL) {
+        _GifError = E_GIF_ERR_NOT_ENOUGH_MEM;
+        return NULL;
+    }
+
+    memset(GifFile, '\0', sizeof(GifFileType));
+
+    if ((Private = (GifFilePrivateType *)
+                   malloc(sizeof(GifFilePrivateType))) == NULL) {
+        free(GifFile);
+        _GifError = E_GIF_ERR_NOT_ENOUGH_MEM;
+        return NULL;
+    }
+
+    GifFile->Private = (VoidPtr) Private;
+    Private->FileHandle = 0;
+    Private->File = (FILE *)0;
+    Private->FileState = FILE_STATE_WRITE;
+    
+    Private->Write = writeFunc; /* User write routine (MRB) */
+    GifFile->UserData = userData; /* User write handle (MRB) */
+    
+    _GifError = 0;
+
+    return GifFile;
+}
+
+/******************************************************************************
+*   Routine to set current GIF version. All files open for write will be      *
+* using this version until next call to this routine. Version consists of     *
+* 3 characters as "87a" or "89a". No test is made to validate the version.    *
+******************************************************************************/
+void EGifSetGifVersion(char *Version)
+{
+    strncpy(&GifVersionPrefix[3], Version, 3);
+}
+
+/******************************************************************************
+*   This routine should be called before any other EGif calls, immediately    *
+* follows the GIF file openning.					      *
+******************************************************************************/
+int EGifPutScreenDesc(GifFileType *GifFile,
+	int Width, int Height, int ColorRes, int BackGround,
+	ColorMapObject *ColorMap)
+{
+    int i;
+    GifByteType Buf[3];
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (Private->FileState & FILE_STATE_SCREEN) {
+	/* If already has screen descriptor - something is wrong! */
+	_GifError = E_GIF_ERR_HAS_SCRN_DSCR;
+	return GIF_ERROR;
+    }
+    if (!IS_WRITEABLE(Private)) {
+	/* This file was NOT open for writing: */
+	_GifError = E_GIF_ERR_NOT_WRITEABLE;
+	return GIF_ERROR;
+    }
+
+/* First write the version prefix into the file. */
+#ifndef DEBUG_NO_PREFIX
+    if (WRITE(GifFile, GifVersionPrefix, strlen(GifVersionPrefix)) !=
+						strlen(GifVersionPrefix)) {
+	_GifError = E_GIF_ERR_WRITE_FAILED;
+	return GIF_ERROR;
+    }
+#endif /* DEBUG_NO_PREFIX */
+
+    GifFile->SWidth = Width;
+    GifFile->SHeight = Height;
+    GifFile->SColorResolution = ColorRes;
+    GifFile->SBackGroundColor = BackGround;
+    if(ColorMap)
+      GifFile->SColorMap=MakeMapObject(ColorMap->ColorCount,ColorMap->Colors);
+    else
+      GifFile->SColorMap=NULL;
+
+    /* Put the screen descriptor into the file: */
+    EGifPutWord(Width, GifFile);
+    EGifPutWord(Height, GifFile);
+    Buf[0] = (ColorMap ? 0x80 : 0x00) |
+	     ((ColorRes - 1) << 4) |
+	     (ColorMap->BitsPerPixel - 1);
+    Buf[1] = BackGround;
+    Buf[2] = 0;
+#ifndef DEBUG_NO_PREFIX
+    WRITE(GifFile, Buf, 3);
+#endif /* DEBUG_NO_PREFIX */
+
+    /* If we have Global color map - dump it also: */
+#ifndef DEBUG_NO_PREFIX
+    if (ColorMap != NULL)
+	for (i = 0; i < ColorMap->ColorCount; i++) {
+	    /* Put the ColorMap out also: */
+	    Buf[0] = ColorMap->Colors[i].Red;
+	    Buf[1] = ColorMap->Colors[i].Green;
+	    Buf[2] = ColorMap->Colors[i].Blue;
+	    if (WRITE(GifFile, Buf, 3) != 3) {
+	        _GifError = E_GIF_ERR_WRITE_FAILED;
+		return GIF_ERROR;
+	    }
+	}
+#endif /* DEBUG_NO_PREFIX */
+
+    /* Mark this file as has screen descriptor, and no pixel written yet: */
+    Private->FileState |= FILE_STATE_SCREEN;
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   This routine should be called before any attemp to dump an image - any    *
+* call to any of the pixel dump routines.				      *
+******************************************************************************/
+int EGifPutImageDesc(GifFileType *GifFile,
+	int Left, int Top, int Width, int Height, int Interlace,
+	ColorMapObject *ColorMap)
+{
+    int i;
+    GifByteType Buf[3];
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (Private->FileState & FILE_STATE_IMAGE &&
+#if defined(__MSDOS__) || defined(__GNUC__)
+	Private->PixelCount > 0xffff0000UL) {
+#else
+	Private->PixelCount > 0xffff0000) {
+#endif /* __MSDOS__ */
+	/* If already has active image descriptor - something is wrong! */
+	_GifError = E_GIF_ERR_HAS_IMAG_DSCR;
+	return GIF_ERROR;
+    }
+    if (!IS_WRITEABLE(Private)) {
+	/* This file was NOT open for writing: */
+	_GifError = E_GIF_ERR_NOT_WRITEABLE;
+	return GIF_ERROR;
+    }
+    GifFile->Image.Left = Left;
+    GifFile->Image.Top = Top;
+    GifFile->Image.Width = Width;
+    GifFile->Image.Height = Height;
+    GifFile->Image.Interlace = Interlace;
+    if(ColorMap)
+      GifFile->Image.ColorMap =MakeMapObject(ColorMap->ColorCount,ColorMap->Colors);
+    else
+      GifFile->Image.ColorMap = NULL;
+
+    /* Put the image descriptor into the file: */
+    Buf[0] = ',';			       /* Image seperator character. */
+#ifndef DEBUG_NO_PREFIX
+    WRITE(GifFile, Buf, 1);
+#endif /* DEBUG_NO_PREFIX */
+    EGifPutWord(Left, GifFile);
+    EGifPutWord(Top, GifFile);
+    EGifPutWord(Width, GifFile);
+    EGifPutWord(Height, GifFile);
+    Buf[0] = (ColorMap ? 0x80 : 0x00) |
+	  (Interlace ? 0x40 : 0x00) |
+	  (ColorMap ? ColorMap->BitsPerPixel - 1 : 0);
+#ifndef DEBUG_NO_PREFIX
+    WRITE(GifFile, Buf, 1);
+#endif /* DEBUG_NO_PREFIX */
+
+    /* If we have Global color map - dump it also: */
+#ifndef DEBUG_NO_PREFIX
+    if (ColorMap != NULL)
+	for (i = 0; i < ColorMap->ColorCount; i++) {
+	    /* Put the ColorMap out also: */
+	    Buf[0] = ColorMap->Colors[i].Red;
+	    Buf[1] = ColorMap->Colors[i].Green;
+	    Buf[2] = ColorMap->Colors[i].Blue;
+	    if (WRITE(GifFile, Buf, 3) != 3) {
+	        _GifError = E_GIF_ERR_WRITE_FAILED;
+		return GIF_ERROR;
+	    }
+	}
+#endif /* DEBUG_NO_PREFIX */
+    if (GifFile->SColorMap == NULL && GifFile->Image.ColorMap == NULL)
+    {
+	_GifError = E_GIF_ERR_NO_COLOR_MAP;
+	return GIF_ERROR;
+    }
+
+    /* Mark this file as has screen descriptor: */
+    Private->FileState |= FILE_STATE_IMAGE;
+    Private->PixelCount = (long) Width * (long) Height;
+
+    EGifSetupCompress(GifFile);      /* Reset compress algorithm parameters. */
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+*  Put one full scanned line (Line) of length LineLen into GIF file.	      *
+******************************************************************************/
+int EGifPutLine(GifFileType *GifFile, GifPixelType *Line, int LineLen)
+{
+    int i;
+    GifPixelType Mask;
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (!IS_WRITEABLE(Private)) {
+	/* This file was NOT open for writing: */
+	_GifError = E_GIF_ERR_NOT_WRITEABLE;
+	return GIF_ERROR;
+    }
+
+    if (!LineLen)
+      LineLen = GifFile->Image.Width;
+    if (Private->PixelCount < (unsigned)LineLen) {
+	_GifError = E_GIF_ERR_DATA_TOO_BIG;
+	return GIF_ERROR;
+    }
+    Private->PixelCount -= LineLen;
+
+    /* Make sure the codes are not out of bit range, as we might generate    */
+    /* wrong code (because of overflow when we combine them) in this case:   */
+    Mask = CodeMask[Private->BitsPerPixel];
+    for (i = 0; i < LineLen; i++) Line[i] &= Mask;
+
+    return EGifCompressLine(GifFile, Line, LineLen);
+}
+
+/******************************************************************************
+* Put one pixel (Pixel) into GIF file.					      *
+******************************************************************************/
+int EGifPutPixel(GifFileType *GifFile, GifPixelType Pixel)
+{
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (!IS_WRITEABLE(Private)) {
+	/* This file was NOT open for writing: */
+	_GifError = E_GIF_ERR_NOT_WRITEABLE;
+	return GIF_ERROR;
+    }
+
+    if (Private->PixelCount == 0)
+    {
+	_GifError = E_GIF_ERR_DATA_TOO_BIG;
+	return GIF_ERROR;
+    }
+    --Private->PixelCount;
+
+    /* Make sure the code is not out of bit range, as we might generate	     */
+    /* wrong code (because of overflow when we combine them) in this case:   */
+    Pixel &= CodeMask[Private->BitsPerPixel];
+
+    return EGifCompressLine(GifFile, &Pixel, 1);
+}
+
+/******************************************************************************
+* Put a comment into GIF file using the GIF89 comment extension block.        *
+******************************************************************************/
+int EGifPutComment(GifFileType *GifFile, char *Comment)
+{
+    return EGifPutExtension(GifFile, COMMENT_EXT_FUNC_CODE, strlen(Comment),
+								Comment);
+}
+
+/******************************************************************************
+*   Put an extension block (see GIF manual) into gif file.		      *
+******************************************************************************/
+int EGifPutExtension(GifFileType *GifFile, int ExtCode, int ExtLen,
+							VoidPtr Extension)
+{
+    GifByteType Buf[3];
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (!IS_WRITEABLE(Private)) {
+	    /* This file was NOT open for writing: */
+	    _GifError = E_GIF_ERR_NOT_WRITEABLE;
+	    return GIF_ERROR;
+    }
+
+    if (ExtCode == 0)
+        WRITE(GifFile, (GifByteType*)&ExtLen, 1);
+    else {
+	    Buf[0] = '!';
+	    Buf[1] = ExtCode;
+	    Buf[2] = ExtLen;
+        WRITE(GifFile, Buf, 3);
+    }
+    WRITE(GifFile, Extension, ExtLen);
+    Buf[0] = 0;
+    WRITE(GifFile, Buf, 1);
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   Put the image code in compressed form. This routine can be called if the  *
+* information needed to be piped out as is. Obviously this is much faster     *
+* than decoding and encoding again. This routine should be followed by calls  *
+* to EGifPutCodeNext, until NULL block is given.			      *
+*   The block should NOT be freed by the user (not dynamically allocated).    *
+******************************************************************************/
+int EGifPutCode(GifFileType *GifFile, int CodeSize, GifByteType *CodeBlock)
+{
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (!IS_WRITEABLE(Private)) {
+        /* This file was NOT open for writing: */
+        _GifError = E_GIF_ERR_NOT_WRITEABLE;
+        return GIF_ERROR;
+    }
+
+    /* No need to dump code size as Compression set up does any for us: */
+    /*
+    Buf = CodeSize;
+    if (WRITE(GifFile, &Buf, 1) != 1) {
+        _GifError = E_GIF_ERR_WRITE_FAILED;
+        return GIF_ERROR;
+    }
+    */
+
+    return EGifPutCodeNext(GifFile, CodeBlock);
+}
+
+/******************************************************************************
+*   Continue to put the image code in compressed form. This routine should be *
+* called with blocks of code as read via DGifGetCode/DGifGetCodeNext. If      *
+* given buffer pointer is NULL, empty block is written to mark end of code.   *
+******************************************************************************/
+int EGifPutCodeNext(GifFileType *GifFile, GifByteType *CodeBlock)
+{
+    GifByteType Buf;
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (CodeBlock != NULL) {
+        if (WRITE(GifFile, CodeBlock, CodeBlock[0] + 1)
+                    != (unsigned)(CodeBlock[0] + 1)) {
+            _GifError = E_GIF_ERR_WRITE_FAILED;
+            return GIF_ERROR;
+        }
+    } else {
+        Buf = 0;
+        if (WRITE(GifFile, &Buf, 1) != 1) {
+            _GifError = E_GIF_ERR_WRITE_FAILED;
+            return GIF_ERROR;
+        }
+        Private->PixelCount = 0;   /* And local info. indicate image read. */
+    }
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   This routine should be called last, to close GIF file.		      *
+******************************************************************************/
+int EGifCloseFile(GifFileType *GifFile)
+{
+    GifByteType Buf;
+    GifFilePrivateType *Private;
+    FILE *File;
+
+    if (GifFile == NULL) return GIF_ERROR;
+
+    Private = (GifFilePrivateType *) GifFile->Private;
+    if (!IS_WRITEABLE(Private)) {
+	/* This file was NOT open for writing: */
+	_GifError = E_GIF_ERR_NOT_WRITEABLE;
+	return GIF_ERROR;
+    }
+
+    File = Private->File;
+
+    Buf = ';';
+    WRITE(GifFile, &Buf, 1);
+
+    if (GifFile->Image.ColorMap)
+	FreeMapObject(GifFile->Image.ColorMap);
+    if (GifFile->SColorMap)
+	FreeMapObject(GifFile->SColorMap);
+    if (Private) {
+	free((char *) Private);
+    }
+    free(GifFile);
+
+    if (File && fclose(File) != 0) {
+	_GifError = E_GIF_ERR_CLOSE_FAILED;
+	return GIF_ERROR;
+    }
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   Put 2 bytes (word) into the given file:				      *
+******************************************************************************/
+static int EGifPutWord(int Word, GifFileType *GifFile)
+{
+    char c[2];
+
+    c[0] = Word & 0xff;
+    c[1] = (Word >> 8) & 0xff;
+#ifndef DEBUG_NO_PREFIX
+    if (WRITE(GifFile, c, 2) == 2)
+	return GIF_OK;
+    else
+	return GIF_ERROR;
+#else
+    return GIF_OK;
+#endif /* DEBUG_NO_PREFIX */
+}
+
+/******************************************************************************
+*   Setup the LZ compression for this image:				      *
+******************************************************************************/
+static int EGifSetupCompress(GifFileType *GifFile)
+{
+    int BitsPerPixel;
+    GifByteType Buf;
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    /* Test and see what color map to use, and from it # bits per pixel: */
+    if (GifFile->Image.ColorMap)
+	BitsPerPixel = GifFile->Image.ColorMap->BitsPerPixel;
+    else if (GifFile->SColorMap)
+	BitsPerPixel = GifFile->SColorMap->BitsPerPixel;
+    else {
+	_GifError = E_GIF_ERR_NO_COLOR_MAP;
+	return GIF_ERROR;
+    }
+
+    Buf = BitsPerPixel = (BitsPerPixel < 2 ? 2 : BitsPerPixel);
+    WRITE(GifFile, &Buf, 1);     /* Write the Code size to file. */
+
+    Private->Buf[0] = 0;			  /* Nothing was output yet. */
+    Private->BitsPerPixel = BitsPerPixel;
+    Private->ClearCode = (1 << BitsPerPixel);
+    Private->EOFCode = Private->ClearCode + 1;
+    Private->RunningCode = 0;
+    Private->RunningBits = BitsPerPixel + 1;	 /* Number of bits per code. */
+    Private->MaxCode1 = 1 << Private->RunningBits;	   /* Max. code + 1. */
+    Private->CrntCode = FIRST_CODE;	   /* Signal that this is first one! */
+    Private->CrntShiftState = 0;      /* No information in CrntShiftDWord. */
+    Private->CrntShiftDWord = 0;
+
+    /* Send Clear to make sure the encoder is initialized. */
+    if (EGifCompressOutput(GifFile, Private->ClearCode) == GIF_ERROR) {
+	_GifError = E_GIF_ERR_DISK_IS_FULL;
+	return GIF_ERROR;
+    }
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   The LZ compression routine:						      *
+*   This version compress the given buffer Line of length LineLen.	      *
+*   This routine can be called few times (one per scan line, for example), in *
+* order the complete the whole image.					      *
+******************************************************************************/
+static int EGifCompressLine(GifFileType *GifFile, GifPixelType *Line,
+								int LineLen)
+{
+    int i = 0, CrntCode;
+    GifPixelType Pixel;
+    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
+
+    if (Private->CrntCode == FIRST_CODE)		  /* Its first time! */
+	CrntCode = Line[i++];
+    else
+        CrntCode = Private->CrntCode;     /* Get last code in compression. */
+
+    while (i < LineLen) {			    /* Decode LineLen items. */
+	Pixel = Line[i++];		      /* Get next pixel from stream. */
+    if (EGifCompressOutput(GifFile, CrntCode)
+        == GIF_ERROR) {
+        _GifError = E_GIF_ERR_DISK_IS_FULL;
+        return GIF_ERROR;
+    }
+    Private->RunningCode++;
+    CrntCode = Pixel;
+    if (Private->RunningCode >= (1 << (Private->BitsPerPixel)) - 2) {
+        if (EGifCompressOutput(GifFile, Private->ClearCode)
+            == GIF_ERROR) {
+            _GifError = E_GIF_ERR_DISK_IS_FULL;
+            return GIF_ERROR;
+        }
+        Private->RunningCode=0;
+    }
+    }
+
+    /* Preserve the current state of the compression algorithm: */
+    Private->CrntCode = CrntCode;
+
+    if (Private->PixelCount == 0)
+    {
+	/* We are done - output last Code and flush output buffers: */
+	if (EGifCompressOutput(GifFile, CrntCode)
+	    == GIF_ERROR) {
+	    _GifError = E_GIF_ERR_DISK_IS_FULL;
+	    return GIF_ERROR;
+	}
+	if (EGifCompressOutput(GifFile, Private->EOFCode)
+	    == GIF_ERROR) {
+	    _GifError = E_GIF_ERR_DISK_IS_FULL;
+	    return GIF_ERROR;
+	}
+	if (EGifCompressOutput(GifFile, FLUSH_OUTPUT) == GIF_ERROR) {
+	    _GifError = E_GIF_ERR_DISK_IS_FULL;
+	    return GIF_ERROR;
+	}
+    }
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+*   The LZ compression output routine:                                        *
+*   This routine is responsible for the compression of the bit stream into    *
+*   8 bits (bytes) packets.                                                   *
+*   Returns GIF_OK if written succesfully.                                    *
+******************************************************************************/
+static int EGifCompressOutput(GifFileType *GifFile, int Code)
+{
+    GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
+    int retval = GIF_OK;
+
+    if (Code == FLUSH_OUTPUT) {
+	while (Private->CrntShiftState > 0) {
+	    /* Get Rid of what is left in DWord, and flush it. */
+	    if (EGifBufferedOutput(GifFile, Private->Buf,
+		Private->CrntShiftDWord & 0xff) == GIF_ERROR)
+		    retval = GIF_ERROR;
+	    Private->CrntShiftDWord >>= 8;
+	    Private->CrntShiftState -= 8;
+	}
+	Private->CrntShiftState = 0;			   /* For next time. */
+	if (EGifBufferedOutput(GifFile, Private->Buf,
+	    FLUSH_OUTPUT) == GIF_ERROR)
+    	        retval = GIF_ERROR;
+    }
+    else {
+	Private->CrntShiftDWord |= ((long) Code) << Private->CrntShiftState;
+	Private->CrntShiftState += Private->RunningBits;
+	while (Private->CrntShiftState >= 8) {
+	    /* Dump out full bytes: */
+	    if (EGifBufferedOutput(GifFile, Private->Buf,
+		Private->CrntShiftDWord & 0xff) == GIF_ERROR)
+		    retval = GIF_ERROR;
+	    Private->CrntShiftDWord >>= 8;
+	    Private->CrntShiftState -= 8;
+	}
+    }
+
+    return retval;
+}
+
+/******************************************************************************
+*   This routines buffers the given characters until 255 characters are ready *
+* to be output. If Code is equal to -1 the buffer is flushed (EOF).	      *
+*   The buffer is Dumped with first byte as its size, as GIF format requires. *
+*   Returns GIF_OK if written succesfully.				      *
+******************************************************************************/
+static int EGifBufferedOutput(GifFileType *GifFile, GifByteType *Buf, int c)
+{
+    if (c == FLUSH_OUTPUT) {
+	/* Flush everything out. */
+	if (Buf[0] != 0 && WRITE(GifFile, Buf, Buf[0]+1) != (unsigned)(Buf[0] + 1))
+	{
+	    _GifError = E_GIF_ERR_WRITE_FAILED;
+	    return GIF_ERROR;
+	}
+	/* Mark end of compressed data, by an empty block (see GIF doc): */
+	Buf[0] = 0;
+	if (WRITE(GifFile, Buf, 1) != 1)
+	{
+	    _GifError = E_GIF_ERR_WRITE_FAILED;
+	    return GIF_ERROR;
+	}
+    }
+    else {
+	if (Buf[0] == 255) {
+	    /* Dump out this buffer - it is full: */
+	    if (WRITE(GifFile, Buf, Buf[0] + 1) != (unsigned)(Buf[0] + 1))
+	    {
+		_GifError = E_GIF_ERR_WRITE_FAILED;
+		return GIF_ERROR;
+	    }
+	    Buf[0] = 0;
+	}
+	Buf[++Buf[0]] = c;
+    }
+
+    return GIF_OK;
+}
+
+/******************************************************************************
+* This routine writes to disk an in-core representation of a GIF previously   *
+* created by DGifSlurp().						      *
+******************************************************************************/
+int EGifSpew(GifFileType *GifFileOut)
+{
+    int	i, j, gif89 = FALSE;
+    char *SavedStamp;
+
+    for (i = 0; i < GifFileOut->ImageCount; i++)
+    {
+        for (j = 0; j < GifFileOut->SavedImages[i].ExtensionBlockCount; j++) {
+            int function=GifFileOut->SavedImages[i].ExtensionBlocks[j].Function;
+
+	        if (function == COMMENT_EXT_FUNC_CODE
+                || function == GRAPHICS_EXT_FUNC_CODE
+                || function == PLAINTEXT_EXT_FUNC_CODE
+                || function == APPLICATION_EXT_FUNC_CODE)
+            gif89 = TRUE;
+        }
+    }
+
+    SavedStamp = GifVersionPrefix;
+    GifVersionPrefix = gif89 ? GIF89_STAMP : GIF87_STAMP;
+    if (EGifPutScreenDesc(GifFileOut,
+			  GifFileOut->SWidth,
+			  GifFileOut->SHeight,
+			  GifFileOut->SColorResolution,
+			  GifFileOut->SBackGroundColor,
+			  GifFileOut->SColorMap) == GIF_ERROR)
+    {
+	GifVersionPrefix = SavedStamp;
+	return(GIF_ERROR);
+    }
+    GifVersionPrefix = SavedStamp;
+
+    for (i = 0; i < GifFileOut->ImageCount; i++)
+    {
+	SavedImage	*sp = &GifFileOut->SavedImages[i];
+	int		SavedHeight = sp->ImageDesc.Height;
+	int		SavedWidth = sp->ImageDesc.Width;
+	ExtensionBlock	*ep;
+
+	/* this allows us to delete images by nuking their rasters */
+	if (sp->RasterBits == NULL)
+	    continue;
+	
+    if (sp->ExtensionBlocks)
+	{
+        for ( j = 0; j < sp->ExtensionBlockCount; j++) {
+            ep = &sp->ExtensionBlocks[j];
+            if (EGifPutExtension(GifFileOut,
+               (ep->Function != 0) ? ep->Function : '\0',
+               ep->ByteCount, ep->Bytes) == GIF_ERROR)
+                return (GIF_ERROR);
+        }
+    }
+
+	if (EGifPutImageDesc(GifFileOut,
+			     sp->ImageDesc.Left,
+			     sp->ImageDesc.Top,
+			     SavedWidth,
+			     SavedHeight,
+			     sp->ImageDesc.Interlace,
+			     sp->ImageDesc.ColorMap
+			     ) == GIF_ERROR)
+	    return(GIF_ERROR);
+
+	for (j = 0; j < SavedHeight; j++)
+	{
+	    if (EGifPutLine(GifFileOut,
+			    sp->RasterBits + j * SavedWidth,
+			    SavedWidth) == GIF_ERROR)
+		return(GIF_ERROR);
+	}
+    }
+
+    if (EGifCloseFile(GifFileOut) == GIF_ERROR)
+	return(GIF_ERROR);
+
+    return(GIF_OK);
+}
diff --git a/Utilities/GDAL/frmts/gif/libungif/gif_err.c b/Utilities/GDAL/frmts/gif/libungif/gif_err.c
new file mode 100644
index 0000000000..b7d7a632a5
--- /dev/null
+++ b/Utilities/GDAL/frmts/gif/libungif/gif_err.c
@@ -0,0 +1,133 @@
+/*****************************************************************************
+*   "Gif-Lib" - Yet another gif library.				     *
+*									     *
+* Written by:  Gershon Elber			IBM PC Ver 0.1,	Jun. 1989    *
+******************************************************************************
+* Handle error reporting for the GIF library.				     *
+******************************************************************************
+* History:								     *
+* 17 Jun 89 - Version 1.0 by Gershon Elber.				     *
+*****************************************************************************/
+
+#include <stdio.h>
+#include "gif_lib.h"
+
+#define PROGRAM_NAME	"GIF_LIBRARY"
+
+int _GifError = 0;
+
+#ifdef SYSV
+static char *VersionStr =
+        "Gif library module,\t\tEric S. Raymond\n\
+	(C) Copyright 1997 Eric S. Raymond\n";
+#else
+static char *VersionStr =
+	PROGRAM_NAME
+	"	IBMPC "
+	GIF_LIB_VERSION
+	"	Eric S. Raymond,	"
+	__DATE__ ",   " __TIME__ "\n"
+	"(C) Copyright 1997 Eric S. Raymond\n";
+#endif /* SYSV */
+
+/* this is just here to prevent unused variable warnings. */
+static char *cvsid_aw() { return( cvsid_aw() ? ((char *) NULL) : VersionStr ); }
+
+/*****************************************************************************
+* Return the last GIF error (0 if none) and reset the error.		     *
+*****************************************************************************/
+int GifLastError(void)
+{
+    int i = _GifError;
+
+    _GifError = 0;
+
+    return i;
+}
+
+/*****************************************************************************
+* Print the last GIF error to stderr.					     *
+*****************************************************************************/
+void PrintGifError(void)
+{
+    char *Err;
+
+    switch(_GifError) {
+	case E_GIF_ERR_OPEN_FAILED:
+	    Err = "Failed to open given file";
+	    break;
+	case E_GIF_ERR_WRITE_FAILED:
+	    Err = "Failed to Write to given file";
+	    break;
+	case E_GIF_ERR_HAS_SCRN_DSCR:
+	    Err = "Screen Descriptor already been set";
+	    break;
+	case E_GIF_ERR_HAS_IMAG_DSCR:
+	    Err = "Image Descriptor is still active";
+	    break;
+	case E_GIF_ERR_NO_COLOR_MAP:
+	    Err = "Neither Global Nor Local color map";
+	    break;
+	case E_GIF_ERR_DATA_TOO_BIG:
+	    Err = "#Pixels bigger than Width * Height";
+	    break;
+	case E_GIF_ERR_NOT_ENOUGH_MEM:
+	    Err = "Fail to allocate required memory";
+	    break;
+	case E_GIF_ERR_DISK_IS_FULL:
+	    Err = "Write failed (disk full?)";
+	    break;
+	case E_GIF_ERR_CLOSE_FAILED:
+	    Err = "Failed to close given file";
+	    break;
+	case E_GIF_ERR_NOT_WRITEABLE:
+	    Err = "Given file was not opened for write";
+	    break;
+	case D_GIF_ERR_OPEN_FAILED:
+	    Err = "Failed to open given file";
+	    break;
+	case D_GIF_ERR_READ_FAILED:
+	    Err = "Failed to Read from given file";
+	    break;
+	case D_GIF_ERR_NOT_GIF_FILE:
+	    Err = "Given file is NOT GIF file";
+	    break;
+	case D_GIF_ERR_NO_SCRN_DSCR:
+	    Err = "No Screen Descriptor detected";
+	    break;
+	case D_GIF_ERR_NO_IMAG_DSCR:
+	    Err = "No Image Descriptor detected";
+	    break;
+	case D_GIF_ERR_NO_COLOR_MAP:
+	    Err = "Neither Global Nor Local color map";
+	    break;
+	case D_GIF_ERR_WRONG_RECORD:
+	    Err = "Wrong record type detected";
+	    break;
+	case D_GIF_ERR_DATA_TOO_BIG:
+	    Err = "#Pixels bigger than Width * Height";
+	    break;
+	case D_GIF_ERR_NOT_ENOUGH_MEM:
+	    Err = "Fail to allocate required memory";
+	    break;
+	case D_GIF_ERR_CLOSE_FAILED:
+	    Err = "Failed to close given file";
+	    break;
+	case D_GIF_ERR_NOT_READABLE:
+	    Err = "Given file was not opened for read";
+	    break;
+	case D_GIF_ERR_IMAGE_DEFECT:
+	    Err = "Image is defective, decoding aborted";
+	    break;
+	case D_GIF_ERR_EOF_TOO_SOON:
+	    Err = "Image EOF detected, before image complete";
+	    break;
+	default:
+	    Err = NULL;
+	    break;
+    }
+    if (Err != NULL)
+	fprintf(stderr, "\nGIF-LIB error: %s.\n", Err);
+    else
+	fprintf(stderr, "\nGIF-LIB undefined error %d.\n", _GifError);
+}
diff --git a/Utilities/GDAL/frmts/gif/libungif/gif_lib.h b/Utilities/GDAL/frmts/gif/libungif/gif_lib.h
new file mode 100644
index 0000000000..35c3546183
--- /dev/null
+++ b/Utilities/GDAL/frmts/gif/libungif/gif_lib.h
@@ -0,0 +1,301 @@
+/******************************************************************************
+* In order to make life a little bit easier when using the GIF file format,   *
+* this library was written, and which does all the dirty work...              *
+*                                                                             *
+*                                        Written by Gershon Elber,  Jun. 1989 *
+*                                        Hacks by Eric S. Raymond,  Sep. 1992 *
+*******************************************************************************
+* History:                                                                    *
+* 14 Jun 89 - Version 1.0 by Gershon Elber.                                   *
+*  3 Sep 90 - Version 1.1 by Gershon Elber (Support for Gif89, Unique names)  *
+* 15 Sep 90 - Version 2.0 by Eric S. Raymond (Changes to suoport GIF slurp)   *
+* 26 Jun 96 - Version 3.0 by Eric S. Raymond (Full GIF89 support)             * 
+* 17 Dec 98 - Version 4.0 by Toshio Kuratomi (Fix extension writing code)     *
+******************************************************************************/
+
+#ifndef GIF_LIB_H
+#define GIF_LIB_H
+
+#define GIF_LIB_VERSION	" Version 4.0, "
+
+#define	GIF_ERROR	0
+#define GIF_OK		1
+
+#ifndef TRUE
+#define TRUE		1
+#define FALSE		0
+#endif
+
+#ifndef NULL
+#define NULL		0
+#endif /* NULL */
+
+#define GIF_FILE_BUFFER_SIZE 16384  /* Files uses bigger buffers than usual. */
+
+typedef	int		GifBooleanType;
+typedef	unsigned char	GifPixelType;
+typedef unsigned char *	GifRowType;
+typedef unsigned char	GifByteType;
+
+#define GIF_MESSAGE(Msg) fprintf(stderr, "\n%s: %s\n", PROGRAM_NAME, Msg)
+#define GIF_EXIT(Msg)	{ GIF_MESSAGE(Msg); exit(-3); }
+
+#ifdef SYSV
+#define VoidPtr char *
+#else
+#define VoidPtr void *
+#endif /* SYSV */
+
+typedef struct GifColorType {
+    GifByteType Red, Green, Blue;
+} GifColorType;
+
+typedef struct ColorMapObject
+{
+    int	ColorCount;
+    int BitsPerPixel;
+    GifColorType *Colors;		/* on malloc(3) heap */
+}
+ColorMapObject;
+
+typedef struct GifImageDesc {
+    int Left, Top, Width, Height,	/* Current image dimensions. */
+	Interlace;			/* Sequential/Interlaced lines. */
+    ColorMapObject *ColorMap;		/* The local color map */
+} GifImageDesc;
+
+typedef struct GifFileType {
+    int SWidth, SHeight,		/* Screen dimensions. */
+	SColorResolution, 		/* How many colors can we generate? */
+	SBackGroundColor;		/* I hope you understand this one... */
+    ColorMapObject *SColorMap;		/* NULL if not exists. */
+    int ImageCount;			/* Number of current image */
+    GifImageDesc Image;			/* Block describing current image */
+    struct SavedImage *SavedImages;	/* Use this to accumulate file state */
+    VoidPtr UserData;           /* hook to attach user data (TVT) */
+    VoidPtr Private;	  		/* Don't mess with this! */
+} GifFileType;
+
+typedef enum {
+    UNDEFINED_RECORD_TYPE,
+    SCREEN_DESC_RECORD_TYPE,
+    IMAGE_DESC_RECORD_TYPE,		/* Begin with ',' */
+    EXTENSION_RECORD_TYPE,		/* Begin with '!' */
+    TERMINATE_RECORD_TYPE		/* Begin with ';' */
+} GifRecordType;
+
+/* DumpScreen2Gif routine constants identify type of window/screen to dump.  */
+/* Note all values below 1000 are reserved for the IBMPC different display   */
+/* devices (it has many!) and are compatible with the numbering TC2.0        */
+/* (Turbo C 2.0 compiler for IBM PC) gives to these devices.		     */
+typedef enum {
+    GIF_DUMP_SGI_WINDOW = 1000,
+    GIF_DUMP_X_WINDOW = 1001
+} GifScreenDumpType;
+
+/* func type to read gif data from arbitrary sources (TVT) */
+typedef int (*InputFunc)(GifFileType*,GifByteType*,int);
+
+/* func type to write gif data ro arbitrary targets.
+ * Returns count of bytes written. (MRB)
+ */
+typedef int (*OutputFunc)(GifFileType *, const GifByteType *, int);
+/******************************************************************************
+*  GIF89 extension function codes                                             *
+******************************************************************************/
+
+#define COMMENT_EXT_FUNC_CODE		0xfe	/* comment */
+#define GRAPHICS_EXT_FUNC_CODE		0xf9	/* graphics control */
+#define PLAINTEXT_EXT_FUNC_CODE		0x01	/* plaintext */
+#define APPLICATION_EXT_FUNC_CODE	0xff	/* application block */
+
+/******************************************************************************
+* O.K., here are the routines one can access in order to encode GIF file:     *
+* (GIF_LIB file EGIF_LIB.C).						      *
+******************************************************************************/
+
+GifFileType *EGifOpenFileName(char *GifFileName, int GifTestExistance);
+GifFileType *EGifOpenFileHandle(int GifFileHandle);
+GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc);
+int EGifSpew(GifFileType *GifFile);
+void EGifSetGifVersion(char *Version);
+int EGifPutScreenDesc(GifFileType *GifFile,
+	int GifWidth, int GifHeight, int GifColorRes, int GifBackGround,
+	ColorMapObject *GifColorMap);
+int EGifPutImageDesc(GifFileType *GifFile,
+	int GifLeft, int GifTop, int Width, int GifHeight, int GifInterlace,
+	ColorMapObject *GifColorMap);
+int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen);
+int EGifPutPixel(GifFileType *GifFile, GifPixelType GifPixel);
+int EGifPutComment(GifFileType *GifFile, char *GifComment);
+int EGifPutExtension(GifFileType *GifFile, int GifExtCode, int GifExtLen,
+							VoidPtr GifExtension);
+int EGifPutCode(GifFileType *GifFile, int GifCodeSize,
+						   GifByteType *GifCodeBlock);
+int EGifPutCodeNext(GifFileType *GifFile, GifByteType *GifCodeBlock);
+int EGifCloseFile(GifFileType *GifFile);
+
+#define	E_GIF_ERR_OPEN_FAILED	1		/* And EGif possible errors. */
+#define	E_GIF_ERR_WRITE_FAILED	2
+#define E_GIF_ERR_HAS_SCRN_DSCR	3
+#define E_GIF_ERR_HAS_IMAG_DSCR	4
+#define E_GIF_ERR_NO_COLOR_MAP	5
+#define E_GIF_ERR_DATA_TOO_BIG	6
+#define E_GIF_ERR_NOT_ENOUGH_MEM 7
+#define E_GIF_ERR_DISK_IS_FULL	8
+#define E_GIF_ERR_CLOSE_FAILED	9
+#define E_GIF_ERR_NOT_WRITEABLE	10
+
+/******************************************************************************
+* O.K., here are the routines one can access in order to decode GIF file:     *
+* (GIF_LIB file DGIF_LIB.C).						      *
+******************************************************************************/
+
+GifFileType *DGifOpenFileName(const char *GifFileName);
+GifFileType *DGifOpenFileHandle(int GifFileHandle);
+GifFileType *DGifOpen( void* userPtr, InputFunc readFunc );  /* new one (TVT) */
+int DGifSlurp(GifFileType *GifFile);
+int DGifGetScreenDesc(GifFileType *GifFile);
+int DGifGetRecordType(GifFileType *GifFile, GifRecordType *GifType);
+int DGifGetImageDesc(GifFileType *GifFile);
+int DGifGetLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen);
+int DGifGetPixel(GifFileType *GifFile, GifPixelType GifPixel);
+int DGifGetComment(GifFileType *GifFile, char *GifComment);
+int DGifGetExtension(GifFileType *GifFile, int *GifExtCode,
+						GifByteType **GifExtension);
+int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **GifExtension);
+int DGifGetCode(GifFileType *GifFile, int *GifCodeSize,
+						GifByteType **GifCodeBlock);
+int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock);
+int DGifGetLZCodes(GifFileType *GifFile, int *GifCode);
+int DGifCloseFile(GifFileType *GifFile);
+
+#define	D_GIF_ERR_OPEN_FAILED	101		/* And DGif possible errors. */
+#define	D_GIF_ERR_READ_FAILED	102
+#define	D_GIF_ERR_NOT_GIF_FILE	103
+#define D_GIF_ERR_NO_SCRN_DSCR	104
+#define D_GIF_ERR_NO_IMAG_DSCR	105
+#define D_GIF_ERR_NO_COLOR_MAP	106
+#define D_GIF_ERR_WRONG_RECORD	107
+#define D_GIF_ERR_DATA_TOO_BIG	108
+#define D_GIF_ERR_NOT_ENOUGH_MEM 109
+#define D_GIF_ERR_CLOSE_FAILED	110
+#define D_GIF_ERR_NOT_READABLE	111
+#define D_GIF_ERR_IMAGE_DEFECT	112
+#define D_GIF_ERR_EOF_TOO_SOON	113
+
+/******************************************************************************
+* O.K., here are the routines from GIF_LIB file QUANTIZE.C.		      *
+******************************************************************************/
+int QuantizeBuffer(unsigned int Width, unsigned int Height, int *ColorMapSize,
+	GifByteType *RedInput, GifByteType *GreenInput, GifByteType *BlueInput,
+	GifByteType *OutputBuffer, GifColorType *OutputColorMap);
+
+
+/******************************************************************************
+* O.K., here are the routines from GIF_LIB file QPRINTF.C.		      *
+******************************************************************************/
+extern int GifQuietPrint;
+
+#ifdef USE_VARARGS
+extern void GifQprintf();
+#else
+extern void GifQprintf(char *Format, ...);
+#endif /* USE_VARARGS */
+
+/******************************************************************************
+* O.K., here are the routines from GIF_LIB file GIF_ERR.C.		      *
+******************************************************************************/
+extern void PrintGifError(void);
+extern int GifLastError(void);
+
+/******************************************************************************
+* O.K., here are the routines from GIF_LIB file DEV2GIF.C.		      *
+******************************************************************************/
+extern int DumpScreen2Gif(char *FileName,
+			  int ReqGraphDriver,
+			  int ReqGraphMode1,
+			  int ReqGraphMode2,
+			  int ReqGraphMode3);
+
+/*****************************************************************************
+ *
+ * Everything below this point is new after version 1.2, supporting `slurp
+ * mode' for doing I/O in two big belts with all the image-bashing in core.
+ *
+ *****************************************************************************/
+
+/******************************************************************************
+* Color Map handling from ALLOCGIF.C					      *
+******************************************************************************/
+
+extern ColorMapObject *MakeMapObject(int ColorCount, GifColorType *ColorMap);
+extern void FreeMapObject(ColorMapObject *Object);
+extern ColorMapObject *UnionColorMap(ColorMapObject *ColorIn1,
+			      ColorMapObject *ColorIn2,
+			      GifPixelType ColorTransIn2[]);
+extern int BitSize(int n);
+
+/******************************************************************************
+* Support for the in-core structures allocation (slurp mode).		      *
+******************************************************************************/
+
+/* This is the in-core version of an extension record */
+typedef struct {
+    int		ByteCount;
+    char	*Bytes;		/* on malloc(3) heap */
+    int Function;       /* Holds the type of the Extension block. */
+} ExtensionBlock;
+
+/* This holds an image header, its unpacked raster bits, and extensions */
+typedef struct SavedImage {
+    GifImageDesc	ImageDesc;
+
+    char		*RasterBits;		/* on malloc(3) heap */
+
+    int			Function; /* DEPRECATED: Use ExtensionBlocks[x].Function
+                           * instead */
+    int			ExtensionBlockCount;
+    ExtensionBlock	*ExtensionBlocks;	/* on malloc(3) heap */
+} SavedImage;
+
+extern void ApplyTranslation(SavedImage *Image, GifPixelType Translation[]);
+
+extern void MakeExtension(SavedImage *New, int Function);
+extern int AddExtensionBlock(SavedImage *New, int Len, char ExtData[]);
+extern void FreeExtension(SavedImage *Image);
+
+extern SavedImage *MakeSavedImage(GifFileType *GifFile, SavedImage *CopyFrom);
+extern void FreeSavedImages(GifFileType *GifFile);
+
+/******************************************************************************
+* The library's internal utility font					      *
+******************************************************************************/
+
+#define GIF_FONT_WIDTH	8
+#define GIF_FONT_HEIGHT	8
+extern unsigned char AsciiTable[][GIF_FONT_WIDTH];
+
+extern void DrawText(SavedImage *Image,
+		     const int x, const int y,
+		     const char *legend,
+		     const int color);
+
+extern void DrawBox(SavedImage *Image,
+		     const int x, const int y,
+		     const int w, const int d,
+		     const int color);
+
+void DrawRectangle(SavedImage *Image,
+		     const int x, const int y,
+		     const int w, const int d,
+		     const int color);
+
+extern void DrawBoxedText(SavedImage *Image,
+		     const int x, const int y,
+		     const char *legend,
+		     const int border,
+		     const int bg,
+		     const int fg);
+
+#endif /* GIF_LIB_H */
diff --git a/Utilities/GDAL/frmts/gif/libungif/gif_lib_private.h b/Utilities/GDAL/frmts/gif/libungif/gif_lib_private.h
new file mode 100644
index 0000000000..305477b922
--- /dev/null
+++ b/Utilities/GDAL/frmts/gif/libungif/gif_lib_private.h
@@ -0,0 +1,50 @@
+#ifndef GIF_LIB_PRIVATE_H
+#define GIF_LIB_PRIVATE_H
+
+#include "gif_lib.h"
+
+#define PROGRAM_NAME	"GIFLIB"
+
+#define LZ_MAX_CODE	4095		/* Biggest code possible in 12 bits. */
+#define LZ_BITS		12
+
+#define FLUSH_OUTPUT		4096    /* Impossible code, to signal flush. */
+#define FIRST_CODE		4097    /* Impossible code, to signal first. */
+#define NO_SUCH_CODE		4098    /* Impossible code, to signal empty. */
+
+#define FILE_STATE_WRITE    0x01
+#define FILE_STATE_SCREEN   0x02
+#define FILE_STATE_IMAGE    0x04
+#define FILE_STATE_READ     0x08
+
+#define IS_READABLE(Private)    (Private->FileState & FILE_STATE_READ)
+#define IS_WRITEABLE(Private)   (Private->FileState & FILE_STATE_WRITE)
+
+
+typedef struct GifFilePrivateType {
+    int FileState,
+	FileHandle,			     /* Where all this data goes to! */
+	BitsPerPixel,	    /* Bits per pixel (Codes uses at least this + 1). */
+	ClearCode,				       /* The CLEAR LZ code. */
+	EOFCode,				         /* The EOF LZ code. */
+	RunningCode,		    /* The next code algorithm can generate. */
+	RunningBits,/* The number of bits required to represent RunningCode. */
+	MaxCode1,  /* 1 bigger than max. possible code, in RunningBits bits. */
+	LastCode,		        /* The code before the current code. */
+	CrntCode,				  /* Current algorithm code. */
+	StackPtr,		         /* For character stack (see below). */
+	CrntShiftState;		        /* Number of bits in CrntShiftDWord. */
+    unsigned long CrntShiftDWord;     /* For bytes decomposition into codes. */
+    unsigned long PixelCount;		       /* Number of pixels in image. */
+    FILE *File;						  /* File as stream. */
+    InputFunc Read;                 /* function to read gif input (TVT) */
+    OutputFunc Write;               /* function to write gif output (MRB) */
+    GifByteType Buf[256];	       /* Compressed input is buffered here. */
+    GifByteType Stack[LZ_MAX_CODE];	 /* Decoded pixels are stacked here. */
+    GifByteType Suffix[LZ_MAX_CODE+1];	       /* So we can trace the codes. */
+    unsigned int Prefix[LZ_MAX_CODE+1];
+} GifFilePrivateType;
+
+extern int _GifError;
+
+#endif /* GIF_LIB_PRIVATE_H */
diff --git a/Utilities/GDAL/frmts/gif/libungif/gifalloc.c b/Utilities/GDAL/frmts/gif/libungif/gifalloc.c
new file mode 100644
index 0000000000..3739835388
--- /dev/null
+++ b/Utilities/GDAL/frmts/gif/libungif/gifalloc.c
@@ -0,0 +1,345 @@
+/*****************************************************************************
+*   "Gif-Lib" - Yet another gif library.				     *
+*									     *
+* Written by:  Gershon Elber				Ver 0.1, Jun. 1989   *
+* Extensively hacked by: Eric S. Raymond		Ver 1.?, Sep 1992    *
+******************************************************************************
+* GIF construction tools						      *
+******************************************************************************
+* History:								     *
+* 15 Sep 92 - Version 1.0 by Eric Raymond.				     *
+*****************************************************************************/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "gif_lib.h"
+
+#define MAX(x, y)	(((x) > (y)) ? (x) : (y))
+
+/******************************************************************************
+* Miscellaneous utility functions					      *
+******************************************************************************/
+
+int BitSize(int n)
+/* return smallest bitfield size n will fit in */
+{
+    register int i;
+
+    for (i = 1; i <= 8; i++)
+	if ((1 << i) >= n)
+	    break;
+    return(i);
+}
+
+
+/******************************************************************************
+* Color map object functions						      *
+******************************************************************************/
+
+ColorMapObject *MakeMapObject(int ColorCount, GifColorType *ColorMap)
+/*
+ * Allocate a color map of given size; initialize with contents of
+ * ColorMap if that pointer is non-NULL.
+ */
+{
+    ColorMapObject *Object;
+
+    if (ColorCount != (1 << BitSize(ColorCount)))
+	return((ColorMapObject *)NULL);
+
+    Object = (ColorMapObject *)malloc(sizeof(ColorMapObject));
+    if (Object == (ColorMapObject *)NULL)
+	return((ColorMapObject *)NULL);
+
+    Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType));
+    if (Object->Colors == (GifColorType *)NULL)
+	return((ColorMapObject *)NULL);
+
+    Object->ColorCount = ColorCount;
+    Object->BitsPerPixel = BitSize(ColorCount);
+
+    if (ColorMap)
+	memcpy((char *)Object->Colors,
+	       (char *)ColorMap, ColorCount * sizeof(GifColorType));
+
+    return(Object);
+}
+
+void FreeMapObject(ColorMapObject *Object)
+/*
+ * Free a color map object
+ */
+{
+    free(Object->Colors);
+    free(Object);
+}
+
+#ifdef DEBUG
+void DumpColorMap(ColorMapObject *Object, FILE *fp)
+{
+    if (Object)
+    {
+	int i, j, Len = Object->ColorCount;
+
+	for (i = 0; i < Len; i+=4) {
+	    for (j = 0; j < 4 && j < Len; j++) {
+		fprintf(fp,
+			"%3d: %02x %02x %02x   ", i + j,
+		       Object->Colors[i + j].Red,
+		       Object->Colors[i + j].Green,
+		       Object->Colors[i + j].Blue);
+	    }
+	    fprintf(fp, "\n");
+	}
+    }
+}
+#endif /* DEBUG */
+
+ColorMapObject *UnionColorMap(
+			 ColorMapObject *ColorIn1,
+			 ColorMapObject *ColorIn2,
+			 GifPixelType ColorTransIn2[])
+/*
+ * Compute the union of two given color maps and return it.  If result can't 
+ * fit into 256 colors, NULL is returned, the allocated union otherwise.
+ * ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are
+ * copied iff they didn't exist before.  ColorTransIn2 maps the old
+ * ColorIn2 into ColorUnion color map table.
+ */
+{
+    int i, j, CrntSlot, RoundUpTo, NewBitSize;
+    ColorMapObject *ColorUnion;
+
+    /*
+     * Allocate table which will hold the result for sure.
+     */
+    ColorUnion
+	= MakeMapObject(MAX(ColorIn1->ColorCount,ColorIn2->ColorCount)*2,NULL);
+
+    if (ColorUnion == NULL)
+	return(NULL);
+
+    /* Copy ColorIn1 to ColorUnionSize; */
+    for (i = 0; i < ColorIn1->ColorCount; i++)
+	ColorUnion->Colors[i] = ColorIn1->Colors[i];
+    CrntSlot = ColorIn1->ColorCount;
+
+    /*
+     * Potentially obnoxious hack:
+     *
+     * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end
+     * of table 1.  This is very useful if your display is limited to
+     * 16 colors.
+     */
+    while (ColorIn1->Colors[CrntSlot-1].Red == 0
+	   && ColorIn1->Colors[CrntSlot-1].Green == 0
+	   && ColorIn1->Colors[CrntSlot-1].Red == 0)
+	CrntSlot--;
+
+    /* Copy ColorIn2 to ColorUnionSize (use old colors if they exist): */
+    for (i = 0; i < ColorIn2->ColorCount && CrntSlot<=256; i++)
+    {
+	/* Let's see if this color already exists: */
+	for (j = 0; j < ColorIn1->ColorCount; j++)
+	    if (memcmp(&ColorIn1->Colors[j], &ColorIn2->Colors[i], sizeof(GifColorType)) == 0)
+		break;
+
+	if (j < ColorIn1->ColorCount)
+	    ColorTransIn2[i] = j;	/* color exists in Color1 */
+	else
+	{
+	    /* Color is new - copy it to a new slot: */
+	    ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i];
+	    ColorTransIn2[i] = CrntSlot++;
+	}
+    }
+
+    if (CrntSlot > 256)
+    {
+	FreeMapObject(ColorUnion);
+	return((ColorMapObject *)NULL);
+    }
+
+    NewBitSize = BitSize(CrntSlot);
+    RoundUpTo = (1 << NewBitSize);
+
+    if (RoundUpTo != ColorUnion->ColorCount)
+    {
+	register GifColorType	*Map = ColorUnion->Colors;
+
+	/*
+	 * Zero out slots up to next power of 2.
+	 * We know these slots exist because of the way ColorUnion's
+	 * start dimension was computed.
+	 */
+	for (j = CrntSlot; j < RoundUpTo; j++)
+	    Map[j].Red = Map[j].Green = Map[j].Blue = 0;
+
+	/* perhaps we can shrink the map? */
+	if (RoundUpTo < ColorUnion->ColorCount)
+	    ColorUnion->Colors 
+		= (GifColorType *)realloc(Map, sizeof(GifColorType)*RoundUpTo);
+    }
+
+    ColorUnion->ColorCount = RoundUpTo;
+    ColorUnion->BitsPerPixel = NewBitSize;
+
+    return(ColorUnion);
+}
+
+void ApplyTranslation(SavedImage *Image, GifPixelType Translation[])
+/*
+ * Apply a given color translation to the raster bits of an image
+ */
+{
+    register int i;
+    register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width;
+
+    for (i = 0; i < RasterSize; i++)
+	Image->RasterBits[i] = Translation[(int)Image->RasterBits[i]];
+}
+
+/******************************************************************************
+* Extension record functions						      *
+******************************************************************************/
+
+void MakeExtension(SavedImage *New, int Function)
+{
+    New->Function = Function;
+    /*
+     * Someday we might have to deal with multiple extensions.
+     */
+}
+
+int AddExtensionBlock(SavedImage *New, int Len, char ExtData[])
+{
+    ExtensionBlock	*ep;
+
+    if (New->ExtensionBlocks == NULL)
+	New->ExtensionBlocks = (ExtensionBlock *)malloc(sizeof(ExtensionBlock));
+    else
+	New->ExtensionBlocks =
+	    (ExtensionBlock *)realloc(New->ExtensionBlocks,
+		      sizeof(ExtensionBlock) * (New->ExtensionBlockCount + 1));
+
+    if (New->ExtensionBlocks == NULL)
+	return(GIF_ERROR);
+
+    ep = &New->ExtensionBlocks[New->ExtensionBlockCount++];
+
+    if ((ep->Bytes = (char *)malloc(ep->ByteCount = Len)) == NULL)
+	return(GIF_ERROR);
+
+    if (ExtData) {
+	    memcpy(ep->Bytes, ExtData, Len);
+        ep->Function = New->Function;
+    }
+
+    return(GIF_OK);
+}
+
+void FreeExtension(SavedImage *Image)
+{
+    ExtensionBlock	*ep;
+
+    for (ep = Image->ExtensionBlocks;
+	 ep < Image->ExtensionBlocks + Image->ExtensionBlockCount;
+	 ep++)
+	(void) free((char *)ep->Bytes);
+    free((char *)Image->ExtensionBlocks);
+    Image->ExtensionBlocks = NULL;
+}
+
+/******************************************************************************
+* Image block allocation functions					      *
+******************************************************************************/
+SavedImage *MakeSavedImage(GifFileType *GifFile, SavedImage *CopyFrom)
+/*
+ * Append an image block to the SavedImages array  
+ */
+{
+    SavedImage	*sp;
+
+    if (GifFile->SavedImages == NULL)
+	GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
+    else
+	GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages,
+				sizeof(SavedImage) * (GifFile->ImageCount+1));
+
+    if (GifFile->SavedImages == NULL)
+	return((SavedImage *)NULL);
+    else
+    {
+	sp = &GifFile->SavedImages[GifFile->ImageCount++];
+	memset((char *)sp, '\0', sizeof(SavedImage));
+
+	if (CopyFrom)
+	{
+	    memcpy((char *)sp, CopyFrom, sizeof(SavedImage));
+
+	    /*
+	     * Make our own allocated copies of the heap fields in the
+	     * copied record.  This guards against potential aliasing
+	     * problems.
+	     */
+
+	    /* first, the local color map */
+	    if (sp->ImageDesc.ColorMap)
+		sp->ImageDesc.ColorMap =
+		    MakeMapObject(CopyFrom->ImageDesc.ColorMap->ColorCount,
+				  CopyFrom->ImageDesc.ColorMap->Colors);
+
+	    /* next, the raster */
+	    sp->RasterBits = (char *)malloc(sizeof(GifPixelType)
+				* CopyFrom->ImageDesc.Height
+				* CopyFrom->ImageDesc.Width);
+	    memcpy(sp->RasterBits,
+		   CopyFrom->RasterBits,
+		   sizeof(GifPixelType)
+			* CopyFrom->ImageDesc.Height
+			* CopyFrom->ImageDesc.Width);
+
+	    /* finally, the extension blocks */
+	    if (sp->ExtensionBlocks)
+	    {
+		sp->ExtensionBlocks
+		    = (ExtensionBlock*)malloc(sizeof(ExtensionBlock)
+					      * CopyFrom->ExtensionBlockCount);
+		memcpy(sp->ExtensionBlocks,
+		   CopyFrom->ExtensionBlocks,
+		   sizeof(ExtensionBlock)
+		   	* CopyFrom->ExtensionBlockCount);
+
+		/*
+		 * For the moment, the actual blocks can take their
+		 * chances with free().  We'll fix this later. 
+		 */
+	    }
+	}
+
+	return(sp);
+    }
+}
+
+void FreeSavedImages(GifFileType *GifFile)
+{
+    SavedImage	*sp;
+
+    for (sp = GifFile->SavedImages;
+	 sp < GifFile->SavedImages + GifFile->ImageCount;
+	 sp++)
+    {
+	if (sp->ImageDesc.ColorMap)
+	    FreeMapObject(sp->ImageDesc.ColorMap);
+
+	if (sp->RasterBits)
+	    free((char *)sp->RasterBits);
+
+	if (sp->ExtensionBlocks)
+	    FreeExtension(sp);
+    }
+    free((char *) GifFile->SavedImages);
+}
+
+
+
diff --git a/Utilities/GDAL/frmts/gif/libungif/makefile.vc b/Utilities/GDAL/frmts/gif/libungif/makefile.vc
new file mode 100644
index 0000000000..e78152cbcf
--- /dev/null
+++ b/Utilities/GDAL/frmts/gif/libungif/makefile.vc
@@ -0,0 +1,14 @@
+
+OBJ	=	\
+	dgif_lib.obj egif_lib.obj gif_err.obj gifalloc.obj
+
+GDAL_ROOT	=	..\..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/gif/makefile.vc b/Utilities/GDAL/frmts/gif/makefile.vc
new file mode 100644
index 0000000000..1a0d1fe6ec
--- /dev/null
+++ b/Utilities/GDAL/frmts/gif/makefile.vc
@@ -0,0 +1,23 @@
+
+OBJ	=	\
+	gifdataset.obj
+
+GDAL_ROOT	=	..\..
+
+EXTRAFLAGS = 	-Ilibungif
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+	cd libungif
+	$(MAKE) /f makefile.vc
+	cd ..
+
+clean:
+	-del *.obj
+	cd libungif
+	$(MAKE) /f makefile.vc clean
+	cd ..
+
+
diff --git a/Utilities/GDAL/frmts/grass/GNUmakefile b/Utilities/GDAL/frmts/grass/GNUmakefile
new file mode 100644
index 0000000000..e735b1b1d6
--- /dev/null
+++ b/Utilities/GDAL/frmts/grass/GNUmakefile
@@ -0,0 +1,28 @@
+
+
+include ../../GDALmake.opt
+
+#OBJ	=	grassdataset.o
+
+ifeq ($(GRASS_SETTING),libgrass)
+OBJ	=	grassdataset.o
+else
+OBJ	=	grass57dataset.o
+endif
+
+CPPFLAGS	:=	$(GRASS_INCLUDE) $(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ) $(OBJ)
+
+install-obj:	$(O_OBJ)
+
+dist:
+	cp -r pkg gdal-grass-$(GDAL_VER)
+	rm -rf gdal-grass-$(GDAL_VER)/CVS
+	cp grass57dataset.cpp gdal-grass-$(GDAL_VER)
+	cp ../../ogr/ogrsf_frmts/grass/*.{cpp,h} gdal-grass-$(GDAL_VER)
+	tar czvf gdal-grass-$(GDAL_VER).tar.gz ./gdal-grass-$(GDAL_VER)
+	rm -rf gdal-grass-$(GDAL_VER)
diff --git a/Utilities/GDAL/frmts/grass/frmt_grass.html b/Utilities/GDAL/frmts/grass/frmt_grass.html
new file mode 100644
index 0000000000..ff95a3138c
--- /dev/null
+++ b/Utilities/GDAL/frmts/grass/frmt_grass.html
@@ -0,0 +1,76 @@
+<html>
+<head>
+<title>GRASS -- GRASS Rasters</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>GRASS -- GRASS Rasters</h1>
+
+GDAL optionally supports reading of existing GRASS raster layers (cells, and
+image groups), but not writing or export.  The support for GRASS raster 
+layers is determined when the library is configured, and requires libgrass
+to be pre-installed (see Notes below). <p>
+
+GRASS rasters can be selected in several ways.<p>
+
+<ol>
+<li> The full path to the cellhd file for the cell can be specified.  This
+is not a relative path, or at least it must contain all the path components
+within the database including the database root itself.  The following
+example opens the cell "proj_tm" within the mapset "PERMANENT" of the
+location "proj_tm" in the grass database located at /u/data/grassdb.<p>
+eg.<br>
+<pre>
+  % gdalinfo /u/data/grassdb/proj_tm/PERMANENT/cellhd/proj_tm
+</pre>
+<li> The full path to the directory containing information about an imagery
+group (or the REF file within it) can be specified to refer to the whole
+group as a single dataset.  The following examples do the same thing.<p>
+eg.<br>
+<pre>
+  % gdalinfo /usr2/data/grassdb/imagery/raw/group/testmff/REF
+  % gdalinfo /usr2/data/grassdb/imagery/raw/group/testmff
+</pre>
+<li>  If there is a correct .grassrc5
+setup in the users home directory then cells or imagery groups may be opened 
+just by the cell name.  This only works for cells or images in the current 
+location and mapset as defined in the .grassrc5 file. <p>
+</ol>
+
+
+
+
+The following features are supported by the GDAL/GRASS link.<p>
+
+<ul>
+<li> Up to 256 entries from cell colormaps are read (0-255).  
+<li> Compressed and uncompressed integer, floating point and double precision
+cells are all supported.  Integer cells are classified with a band type of
+"Byte" if the 1-byte per pixel format is used, or UInt16 if the two byte
+per pixel format is used.  Otherwise integer cells are treated as UInt32.
+<li> Georeferencing information is properly read from GRASS.
+<li> An attempt is made to translate coordinate systems, but some conversions
+may be flawed, in particular in handling of datums and units.
+</ul>
+
+See Also:<p>
+
+<ul>
+<li> <a href="http://grass.itc.it/">GRASS GIS Home Page</a>
+<li> <a href="http://gdal.velocet.ca/projects/grass/">libgrass page</a>
+</ul>
+
+<hr>
+<h2>NOTES on driver variations</h2>
+
+For GRASS 5.7 Radim Blazek has moved the driver to using the GRASS
+shared libraries directly instead of using libgrass.  Currently (GDAL 1.2.2 and later)
+both version of the driver are available and can be configured using
+"--with-libgrass" for the libgrass variant or "--with-grass=&lt;dir&gt;" for the 
+new GRASS 5.7 library based version.  The GRASS 5.7 driver version is currently
+not supporting coordinate system access, though it is hoped that will be
+corrected at some point.<p>
+
+</body>
+</html>
diff --git a/Utilities/GDAL/frmts/grass/grass57dataset.cpp b/Utilities/GDAL/frmts/grass/grass57dataset.cpp
new file mode 100644
index 0000000000..3ad35bc97d
--- /dev/null
+++ b/Utilities/GDAL/frmts/grass/grass57dataset.cpp
@@ -0,0 +1,994 @@
+/******************************************************************************
+ * $Id: grass57dataset.cpp,v 1.6 2006/02/13 17:39:31 rblazek Exp $
+ *
+ * Project:  GRASS Driver
+ * Purpose:  Implement GRASS raster read/write support
+ *           This version is for GRASS 5.7+ and uses GRASS libraries
+ *           directly instead of using libgrass. 
+ * Author:   Frank Warmerdam <warmerdam@pobox.com>
+ *           Radim Blazek <blazek@itc.it>
+ *
+ ******************************************************************************
+ * Copyright (c) 2000 Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: grass57dataset.cpp,v $
+ * Revision 1.6  2006/02/13 17:39:31  rblazek
+ * header files moved to grass
+ *
+ * Revision 1.5  2005/09/30 21:07:57  fwarmerdam
+ * applied patches from bug 822, add georef support
+ *
+ * Revision 1.4  2005/05/06 18:21:14  fwarmerdam
+ * Applied patch from Radim.  Better logic to reset the region.
+ *
+ * Revision 1.3  2004/09/24 14:28:49  fwarmerdam
+ * fixed some typos with floating point (from Radim)
+ *
+ * Revision 1.2  2004/09/22 20:39:15  fwarmerdam
+ * some updates to reduce spurious error reports
+ *
+ * Revision 1.1  2004/09/13 18:18:26  fwarmerdam
+ * Variant code from Radim that works with GRASS 5.7 libraries directly.
+ * Currently coordinate system support is broken with this version.
+ *
+ */
+
+#include <stdlib.h>
+
+extern "C" {
+#ifdef __cplusplus
+#define class _class
+#endif
+#include <grass/imagery.h>
+#ifdef __cplusplus
+#undef class
+#endif
+    
+#include <grass/gprojects.h>
+#include <grass/gis.h>
+}
+
+#include "gdal_priv.h"
+#include "cpl_string.h"
+#include "ogr_spatialref.h"
+
+#define GRASS_MAX_COLORS 100000  // what is the right value
+
+CPL_CVSID("$Id: grass57dataset.cpp,v 1.6 2006/02/13 17:39:31 rblazek Exp $");
+
+CPL_C_START
+void	GDALRegister_GRASS(void);
+CPL_C_END
+
+/************************************************************************/
+/*                         Grass2CPLErrorHook()                         */
+/************************************************************************/
+
+int Grass2CPLErrorHook( char * pszMessage, int bFatal )
+
+{
+    if( !bFatal )
+        //CPLDebug( "GRASS", "%s", pszMessage );
+        CPLError( CE_Warning, CPLE_AppDefined, "GRASS warning: %s", pszMessage );
+    else
+        CPLError( CE_Warning, CPLE_AppDefined, "GRASS fatal error: %s", pszMessage );
+
+    return 0;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*				GRASSDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class GRASSRasterBand;
+
+class GRASSDataset : public GDALDataset
+{
+    friend class GRASSRasterBand;
+
+    char	*pszGisdbase;  
+    char	*pszLocation;  /* LOCATION_NAME */
+    char	*pszElement;   /* cellhd or group */
+
+    struct Cell_head sCellInfo; /* raster region */ 
+    
+    char	*pszProjection;
+
+    double	adfGeoTransform[6];
+
+  public:
+                 GRASSDataset();
+                 ~GRASSDataset();
+
+    virtual const char *GetProjectionRef(void);
+    virtual CPLErr GetGeoTransform( double * );
+
+    static GDALDataset *Open( GDALOpenInfo * );
+
+  private:
+    static bool SplitPath ( char *, char **, char **, char **, char **, char ** );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            GRASSRasterBand                           */
+/* ==================================================================== */
+/************************************************************************/
+
+class GRASSRasterBand : public GDALRasterBand
+{
+    friend class GRASSDataset;
+
+    char        *pszCellName;
+    char        *pszMapset;
+    int		hCell;
+    int         nGRSType; // GRASS raster type: CELL_TYPE, FCELL_TYPE, DCELL_TYPE
+    bool        nativeNulls; // use GRASS native NULL values
+
+    struct Colors sGrassColors;
+    GDALColorTable *poCT;
+
+    struct Cell_head sOpenWindow; /* the region when the raster was opened */ 
+
+    int		bHaveMinMax;
+    double	dfCellMin;
+    double	dfCellMax;
+
+    double	dfNoData;
+
+    bool        valid;
+
+  public:
+
+                   GRASSRasterBand( GRASSDataset *, int, 
+                                    const char *, const char * );
+    virtual        ~GRASSRasterBand();
+
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual CPLErr IRasterIO ( GDALRWFlag, int, int, int, int, void *, int, int, GDALDataType, int, int );
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual GDALColorTable *GetColorTable();
+    virtual double GetMinimum( int *pbSuccess = NULL );
+    virtual double GetMaximum( int *pbSuccess = NULL );
+    virtual double GetNoDataValue( int *pbSuccess = NULL );
+
+  private:
+    CPLErr ResetReading( struct Cell_head * );
+    
+};
+
+
+/************************************************************************/
+/*                          GRASSRasterBand()                           */
+/************************************************************************/
+
+GRASSRasterBand::GRASSRasterBand( GRASSDataset *poDS, int nBand,
+                                  const char * pszMapset,
+                                  const char * pszCellName )
+
+{
+    struct Cell_head	sCellInfo;
+
+    // Note: GISDBASE, LOCATION_NAME ans MAPSET was set in GRASSDataset::Open
+
+    this->poDS = poDS;
+    this->nBand = nBand;
+    this->valid = false;
+
+    this->pszCellName = G_store ( (char *) pszCellName );
+    this->pszMapset = G_store ( (char *) pszMapset );
+
+    G_get_cellhd( (char *) pszCellName, (char *) pszMapset, &sCellInfo );
+    nGRSType = G_raster_map_type( (char *) pszCellName, (char *) pszMapset );
+
+/* -------------------------------------------------------------------- */
+/*      Get min/max values.                                             */
+/* -------------------------------------------------------------------- */
+    struct FPRange sRange;
+
+    if( G_read_fp_range( (char *) pszCellName, (char *) pszMapset, 
+                         &sRange ) == -1 )
+    {
+        bHaveMinMax = FALSE;
+    }
+    else
+    {
+        bHaveMinMax = TRUE;
+        G_get_fp_range_min_max( &sRange, &dfCellMin, &dfCellMax );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Setup band type, and preferred nodata value.                    */
+/* -------------------------------------------------------------------- */
+    // Negative values are also (?) stored as 4 bytes (format = 3) 
+    //       => raster with format < 3 has only positive values
+
+    // GRASS modules usually do not waste space and only the format necessary to keep 
+    // full raster values range is used -> no checks if shorter type could be used
+    
+    if( nGRSType == CELL_TYPE ) {
+	if ( sCellInfo.format == 0 ) {  // 1 byte / cell -> possible range 0,255
+	    if ( bHaveMinMax && dfCellMin > 0 ) {
+                this->eDataType = GDT_Byte;
+		dfNoData = 0.0;
+	    } else if ( bHaveMinMax && dfCellMax < 255 ) {
+                this->eDataType = GDT_Byte;
+		dfNoData = 255.0;
+	    } else { // maximum is not known or full range is used
+		this->eDataType = GDT_UInt16;
+		dfNoData = 256.0;
+	    }
+	    nativeNulls = false;
+	} else if ( sCellInfo.format == 1 ) {  // 2 bytes / cell -> possible range 0,65535
+	    if ( bHaveMinMax && dfCellMin > 0 ) {
+		this->eDataType = GDT_UInt16;
+		dfNoData = 0.0;
+	    } else if ( bHaveMinMax && dfCellMax < 65535 ) {
+                this->eDataType = GDT_UInt16;
+		dfNoData = 65535;
+	    } else { // maximum is not known or full range is used
+		CELL cval;
+		this->eDataType = GDT_Int32; 
+		G_set_c_null_value ( &cval, 1);
+		dfNoData = (double) cval;
+		nativeNulls = true;
+	    }
+	    nativeNulls = false;
+	} else {  // 3-4 bytes 
+	    CELL cval;
+	    this->eDataType = GDT_Int32;
+	    G_set_c_null_value ( &cval, 1);
+	    dfNoData = (double) cval;
+	    nativeNulls = true;
+	}
+    } 
+    else if( nGRSType == FCELL_TYPE ) {
+	FCELL fval;
+        this->eDataType = GDT_Float32;
+	G_set_f_null_value ( &fval, 1);
+	dfNoData = (double) fval;
+	nativeNulls = true;
+    }
+    else if( nGRSType == DCELL_TYPE )
+    {
+	DCELL dval;
+        this->eDataType = GDT_Float64;
+	G_set_d_null_value ( &dval, 1);
+	dfNoData = (double) dval;
+	nativeNulls = true;
+    }
+
+    nBlockXSize = poDS->nRasterXSize;;
+    nBlockYSize = 1;
+
+    G_set_window( &(((GRASSDataset *)poDS)->sCellInfo) );
+    if ( (hCell = G_open_cell_old((char *) pszCellName, (char *) pszMapset)) < 0 ) {
+	CPLError( CE_Warning, CPLE_AppDefined, "GRASS: Cannot open raster '%s'", pszCellName );
+	return;
+    }
+    G_copy((void *) &sOpenWindow, (void *) &(((GRASSDataset *)poDS)->sCellInfo), sizeof(struct Cell_head));
+
+/* -------------------------------------------------------------------- */
+/*      Do we have a color table?                                       */
+/* -------------------------------------------------------------------- */
+    poCT = NULL;
+    if( G_read_colors( (char *) pszCellName, (char *) pszMapset, &sGrassColors ) == 1 )
+    {
+	int maxcolor; 
+	CELL min, max;
+
+	G_get_color_range ( &min, &max, &sGrassColors);
+
+        if ( bHaveMinMax ) {
+	    if ( max < dfCellMax ) {
+	       maxcolor = max;
+            } else {
+	       maxcolor = (int) ceil ( dfCellMax );
+	    }
+	    if ( maxcolor > GRASS_MAX_COLORS ) { 
+		maxcolor = GRASS_MAX_COLORS;
+                CPLDebug( "GRASS", "Too many values, color table cut to %d entries.", maxcolor );
+	    }
+	} else {
+	    if ( max < GRASS_MAX_COLORS ) {
+	       maxcolor = max;
+            } else {
+	       maxcolor = GRASS_MAX_COLORS;
+               CPLDebug( "GRASS", "Too many values, color table set to %d entries.", maxcolor );
+	    }
+        }
+	    
+        poCT = new GDALColorTable();
+        for( int iColor = 0; iColor <= maxcolor; iColor++ )
+        {
+            int	nRed, nGreen, nBlue;
+            GDALColorEntry    sColor;
+
+            if( G_get_color( iColor, &nRed, &nGreen, &nBlue, &sGrassColors ) )
+            {
+                sColor.c1 = nRed;
+                sColor.c2 = nGreen;
+                sColor.c3 = nBlue;
+                sColor.c4 = 255;
+
+                poCT->SetColorEntry( iColor, &sColor );
+            }
+            else
+            {
+                sColor.c1 = 0;
+                sColor.c2 = 0;
+                sColor.c3 = 0;
+                sColor.c4 = 0;
+
+                poCT->SetColorEntry( iColor, &sColor );
+            }
+        }
+	    
+	/* Create metadata enries for color table rules */
+	char key[200], value[200];
+	int rcount = G_colors_count ( &sGrassColors );
+
+	sprintf ( value, "%d", rcount );
+	this->SetMetadataItem( "COLOR_TABLE_RULES_COUNT", value );
+
+	/* Add the rules in reverse order */
+	for ( int i = rcount-1; i >= 0; i-- ) {
+	    DCELL val1, val2;
+	    unsigned char r1, g1, b1, r2, g2, b2;
+
+	     G_get_f_color_rule ( &val1, &r1, &g1, &b1, &val2, &r2, &g2, &b2, &sGrassColors, i );
+		
+
+	     sprintf ( key, "COLOR_TABLE_RULE_RGB_%d", rcount-i-1 );
+	     sprintf ( value, "%e %e %d %d %d %d %d %d", val1, val2, r1, g1, b1, r2, g2, b2 );
+	     this->SetMetadataItem( key, value );
+	}
+    } else {
+	this->SetMetadataItem( "COLOR_TABLE_RULES_COUNT", "0" );
+    }
+    
+    this->valid = true;
+}
+
+/************************************************************************/
+/*                          ~GRASSRasterBand()                          */
+/************************************************************************/
+
+GRASSRasterBand::~GRASSRasterBand()
+{
+    if( poCT != NULL ) {
+        G_free_colors( &sGrassColors );
+        delete poCT;
+    }
+
+    if( hCell >= 0 )
+        G_close_cell( hCell );
+    
+    if ( pszCellName )
+        free ( pszCellName );
+
+    if ( pszMapset )
+        free ( pszMapset );
+}
+
+/************************************************************************/
+/*                             ResetReading                             */
+/*                                                                      */
+/* Reset current window and reopen cell if the window has changed,      */
+/* reset GRASS variables                                                */
+/*                                                                      */
+/* Returns CE_Failure if fails, otherwise CE_None                       */
+/************************************************************************/
+CPLErr GRASSRasterBand::ResetReading ( struct Cell_head *sNewWindow )
+{
+
+    /* Check if the window has changed */
+    if ( sNewWindow->north  != sOpenWindow.north  || sNewWindow->south  != sOpenWindow.south ||
+	 sNewWindow->east   != sOpenWindow.east   || sNewWindow->west   != sOpenWindow.west ||
+	 sNewWindow->ew_res != sOpenWindow.ew_res || sNewWindow->ns_res != sOpenWindow.ns_res ||
+	 sNewWindow->rows   != sOpenWindow.rows   || sNewWindow->cols   != sOpenWindow.cols )
+    {
+        G_close_cell( hCell );
+
+	/* Set window */
+	G_set_window( sNewWindow );
+
+	/* Open raster */
+	G__setenv( "GISDBASE", ((GRASSDataset *)poDS)->pszGisdbase );
+	G__setenv( "LOCATION_NAME", ((GRASSDataset *)poDS)->pszLocation );
+	G__setenv( "MAPSET", pszMapset); 
+	G_reset_mapsets();
+	G_add_mapset_to_search_path ( pszMapset );
+	
+	if ( (hCell = G_open_cell_old( pszCellName, pszMapset)) < 0 ) {
+	    CPLError( CE_Warning, CPLE_AppDefined, "GRASS: Cannot open raster '%s'", pszCellName );
+	    return CE_Failure;
+	}
+
+	G_copy((void *) &sOpenWindow, (void *) sNewWindow, sizeof(struct Cell_head));
+	
+    }
+    else
+    {
+        /* The windows are identical, check current window */
+        struct Cell_head sCurrentWindow;
+
+        G_get_window ( &sCurrentWindow );
+
+        if ( sNewWindow->north  != sCurrentWindow.north  || sNewWindow->south  != sCurrentWindow.south ||
+             sNewWindow->east   != sCurrentWindow.east   || sNewWindow->west   != sCurrentWindow.west ||
+             sNewWindow->ew_res != sCurrentWindow.ew_res || sNewWindow->ns_res != sCurrentWindow.ns_res ||
+             sNewWindow->rows   != sCurrentWindow.rows   || sNewWindow->cols   != sCurrentWindow.cols
+             )
+        {
+            /* Reset window */
+            G_set_window( sNewWindow );
+        }
+    }
+
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/*                                                                      */
+/************************************************************************/
+
+CPLErr GRASSRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, void * pImage )
+
+{
+    // Reset window because IRasterIO could be previosly called
+    if ( ResetReading ( &(((GRASSDataset *)poDS)->sCellInfo) ) != CE_None ) {
+       return CE_Failure;
+    }       
+    
+    if ( eDataType == GDT_Byte || eDataType == GDT_UInt16 ) {
+        CELL  *cbuf;
+
+	cbuf = G_allocate_c_raster_buf();
+	G_get_c_raster_row ( hCell, cbuf, nBlockYOff );	
+
+	/* Reset NULLs */
+	for ( int col = 0; col < nBlockXSize; col++ ) {
+	    if ( G_is_c_null_value(&(cbuf[col])) )
+		cbuf[col] = (CELL) dfNoData;
+	}
+
+	GDALCopyWords ( (void *) cbuf, GDT_Int32, sizeof(CELL), 
+	                pImage, eDataType, GDALGetDataTypeSize(eDataType)/8,
+			nBlockXSize );    
+
+	free ( cbuf );
+
+    } else if ( eDataType == GDT_Int32 ) {
+	G_get_c_raster_row ( hCell, (CELL *) pImage, nBlockYOff );
+    } else if ( eDataType == GDT_Float32 ) {
+	G_get_f_raster_row ( hCell, (FCELL *) pImage, nBlockYOff );
+    } else if ( eDataType == GDT_Float64 ) {
+	G_get_d_raster_row ( hCell, (DCELL *) pImage, nBlockYOff );
+    }
+	
+    return CE_None;
+}
+
+/************************************************************************/
+/*                             IRasterIO()                              */
+/*                                                                      */
+/************************************************************************/
+
+CPLErr GRASSRasterBand::IRasterIO ( GDALRWFlag eRWFlag,
+	                           int nXOff, int nYOff, int nXSize, int nYSize,
+				   void * pData, int nBufXSize, int nBufYSize,
+				   GDALDataType eBufType,
+				   int nPixelSpace, int nLineSpace )
+{
+    /* GRASS library does that, we have only calculate and reset the region in map units
+     * and if the region has changed, reopen the raster */
+    
+    /* Calculate the region */
+    struct Cell_head sWindow;
+    struct Cell_head *psDsWindow;
+
+    psDsWindow = &(((GRASSDataset *)poDS)->sCellInfo);
+    
+    sWindow.north = psDsWindow->north - nYOff * psDsWindow->ns_res; 
+    sWindow.south = sWindow.north - nYSize * psDsWindow->ns_res; 
+    sWindow.west = psDsWindow->west + nXOff * psDsWindow->ew_res; 
+    sWindow.east = sWindow.west + nXSize * psDsWindow->ew_res; 
+    sWindow.proj = psDsWindow->proj;
+    sWindow.zone = psDsWindow->zone;
+
+    sWindow.cols = nBufXSize;
+    sWindow.rows = nBufYSize;
+     
+    /* Reset resolution */
+    G_adjust_Cell_head ( &sWindow, 1, 1);
+
+    ResetReading ( &sWindow );
+    
+    /* Read Data */
+    CELL  *cbuf = NULL;
+    FCELL *fbuf = NULL;
+    DCELL *dbuf = NULL;
+    bool  direct = false;
+
+    /* Reset space if default (0) */
+    if ( nPixelSpace == 0 )
+	nPixelSpace = GDALGetDataTypeSize ( eBufType ) / 8;
+
+    if ( nLineSpace == 0 )
+	nLineSpace = nBufXSize * nPixelSpace;
+
+    if ( nGRSType == CELL_TYPE && ( !nativeNulls || eBufType != GDT_Int32 || sizeof(CELL) != 4 ||
+		                    nPixelSpace != sizeof(CELL) )  ) 
+    {
+	cbuf = G_allocate_c_raster_buf();
+    } else if( nGRSType == FCELL_TYPE && ( eBufType != GDT_Float32 || nPixelSpace != sizeof(FCELL) ) ) {
+	fbuf = G_allocate_f_raster_buf();
+    } else if( nGRSType == DCELL_TYPE && ( eBufType != GDT_Float64 || nPixelSpace != sizeof(DCELL) ) ) {
+	dbuf = G_allocate_d_raster_buf();
+    } else {
+	direct = true;
+    }
+
+    for ( int row = 0; row < nBufYSize; row++ ) {
+        char *pnt = (char *)pData + row * nLineSpace;
+	
+	if ( nGRSType == CELL_TYPE ) {
+	    if ( direct ) {
+		G_get_c_raster_row ( hCell, (CELL *) pnt, row );
+	    } else {
+		G_get_c_raster_row ( hCell, cbuf, row );
+		
+		/* Reset NULLs */
+		for ( int col = 0; col < nBufXSize; col++ ) {
+		    if ( G_is_c_null_value(&(cbuf[col])) ) 
+			cbuf[col] = (CELL) dfNoData;
+		}
+
+		GDALCopyWords ( (void *) cbuf, GDT_Int32, sizeof(CELL), 
+			        (void *)  pnt,  eBufType, nPixelSpace,
+				nBufXSize ); 
+	    }
+	} else if( nGRSType == FCELL_TYPE ) {
+	    if ( direct ) {
+		G_get_f_raster_row ( hCell, (FCELL *) pnt, row );
+	    } else {
+		G_get_f_raster_row ( hCell, fbuf, row );
+		
+		GDALCopyWords ( (void *) fbuf, GDT_Float32, sizeof(FCELL), 
+			        (void *)  pnt,  eBufType, nPixelSpace,
+				nBufXSize ); 
+	    }
+	} else if( nGRSType == DCELL_TYPE ) {
+	    if ( direct ) {
+		G_get_d_raster_row ( hCell, (DCELL *) pnt, row );
+	    } else {
+		G_get_d_raster_row ( hCell, dbuf, row );
+		
+		GDALCopyWords ( (void *) dbuf, GDT_Float64, sizeof(DCELL), 
+			        (void *)  pnt,  eBufType, nPixelSpace,
+				nBufXSize ); 
+	    }
+	}
+    }
+
+    if ( cbuf ) free ( cbuf );
+    if ( fbuf ) free ( fbuf );
+    if ( dbuf ) free ( dbuf );
+    
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp GRASSRasterBand::GetColorInterpretation()
+
+{
+    if( poCT != NULL )
+        return GCI_PaletteIndex;
+    else
+        return GCI_GrayIndex;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *GRASSRasterBand::GetColorTable()
+
+{
+    return poCT;
+}
+
+/************************************************************************/
+/*                             GetMinimum()                             */
+/************************************************************************/
+
+double GRASSRasterBand::GetMinimum( int *pbSuccess )
+
+{
+    if( pbSuccess )
+        *pbSuccess = bHaveMinMax;
+
+    if( bHaveMinMax )
+        return dfCellMin;
+
+    else if( eDataType == GDT_Float32 || eDataType == GDT_Float64 )
+        return -4294967295.0;
+    else
+        return 0;
+}
+
+/************************************************************************/
+/*                             GetMaximum()                             */
+/************************************************************************/
+
+double GRASSRasterBand::GetMaximum( int *pbSuccess )
+
+{
+    if( pbSuccess )
+        *pbSuccess = bHaveMinMax;
+
+    if( bHaveMinMax )
+        return dfCellMax;
+
+    else if( eDataType == GDT_Float32 || eDataType == GDT_Float64 )
+        return 4294967295.0;
+    else if( eDataType == GDT_UInt32 )
+        return 4294967295.0;
+    else if( eDataType == GDT_UInt16 )
+        return 65535;
+    else 
+        return 255;
+}
+
+/************************************************************************/
+/*                           GetNoDataValue()                           */
+/************************************************************************/
+
+double GRASSRasterBand::GetNoDataValue( int *pbSuccess )
+
+{
+    if( pbSuccess )
+        *pbSuccess = TRUE;
+
+    return dfNoData;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             GRASSDataset                             */
+/* ==================================================================== */
+/************************************************************************/
+
+
+/************************************************************************/
+/*                            GRASSDataset()                            */
+/************************************************************************/
+
+GRASSDataset::GRASSDataset()
+{
+    pszProjection = NULL;
+
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+}
+
+/************************************************************************/
+/*                           ~GRASSDataset()                            */
+/************************************************************************/
+
+GRASSDataset::~GRASSDataset()
+{
+    
+    if ( pszGisdbase )
+	free ( pszGisdbase );
+    
+    if ( pszLocation )
+        free ( pszLocation );
+    
+    if ( pszElement )
+	free ( pszElement );
+
+    CPLFree( pszProjection );
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *GRASSDataset::GetProjectionRef() 
+{
+    if( pszProjection == NULL )
+        return "";
+    else
+        return pszProjection;
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr GRASSDataset::GetGeoTransform( double * padfGeoTransform ) 
+{
+    memcpy( padfGeoTransform, adfGeoTransform, sizeof(double) * 6 );
+    
+    return CE_None;
+}
+
+/************************************************************************/
+/*                            SplitPath()                               */
+/* Split full path to cell or group to:                                 */
+/*     gisdbase, location, mapset, element, name                        */
+/* New string are allocated and should be freed when no longer needed.  */
+/*                                                                      */
+/* Returns: true - OK                                                   */
+/*          false - failed                                              */
+/************************************************************************/
+bool GRASSDataset::SplitPath( char *path, char **gisdbase, char **location, 
+	                      char **mapset, char **element, char **name )
+{
+    char *p, *ptr[5], *tmp;
+    int  i = 0;
+    
+    *gisdbase = *location = *mapset = *element = *name = NULL;
+    
+    if ( !path || strlen(path) == 0 ) 
+	return false;
+
+    tmp = G_store ( path );
+
+    while ( (p = strrchr(tmp,'/')) != NULL  && i < 4 ) {
+	*p = '\0';
+	
+	if ( strlen(p+1) == 0 ) /* repeated '/' */
+	    continue;
+
+	ptr[i++] = p+1;
+    }
+
+    /* Note: empty GISDBASE == 0 is not accepted (relative path) */
+    if ( i != 4 ) {
+        free ( tmp );
+	return false;
+    }
+
+    *gisdbase = G_store ( tmp );
+    *location = G_store ( ptr[3] );
+    *mapset   = G_store ( ptr[2] );
+    *element  = G_store ( ptr[1] );
+    *name     = G_store ( ptr[0] );
+
+    free ( tmp );
+    return true;
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+typedef int (*GrassErrorHandler)(char *, int);
+
+GDALDataset *GRASSDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    char	*pszGisdb = NULL, *pszLoc = NULL;
+    char	*pszMapset = NULL, *pszElem = NULL, *pszName = NULL;
+    char        **papszCells = NULL;
+    char        **papszMapsets = NULL;
+    static char fake_gisbase[50];
+    static bool hasGisbase;
+
+/* -------------------------------------------------------------------- */
+/*      Does this even look like a grass file path?                     */
+/* -------------------------------------------------------------------- */
+    if( strstr(poOpenInfo->pszFilename,"/cellhd/") == NULL
+        && strstr(poOpenInfo->pszFilename,"/group/") == NULL )
+        return NULL;
+
+    /* Always init, if no rasters are opened G_no_gisinit resets the projection and 
+     * rasters in different projection may be then opened */
+
+    // Don't use GISRC file and read/write GRASS variables (from location G_VAR_GISRC) to memory only.
+    G_set_gisrc_mode ( G_GISRC_MODE_MEMORY );
+
+    // Init GRASS libraries (required)
+    G_no_gisinit();  // Doesn't check write permissions for mapset compare to G_gisinit
+
+    // Set error function
+    G_set_error_routine ( (GrassErrorHandler) Grass2CPLErrorHook );
+    
+    
+    if ( !getenv( "GISBASE" ) ) {
+	// we are outside a GRASS session
+	// TODO: use function instead of hardcoded path
+	sprintf(fake_gisbase, "GISBASE=/usr/local/share/gdal/grass/" );	
+        putenv( fake_gisbase );
+	hasGisbase = false;
+    } else {
+	hasGisbase = true;
+    }
+
+    if ( !SplitPath( poOpenInfo->pszFilename, &pszGisdb, &pszLoc, &pszMapset,
+                     &pszElem, &pszName) ) {
+	return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check element name                                              */
+/* -------------------------------------------------------------------- */
+    if ( strcmp(pszElem,"cellhd") != 0 && strcmp(pszElem,"group") != 0 ) { 
+	free(pszGisdb); 
+        free(pszLoc); 
+        free(pszMapset); 
+        free(pszElem); 
+        free(pszName);
+	return NULL;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Set GRASS variables                                             */
+/* -------------------------------------------------------------------- */
+
+    G__setenv( "GISDBASE", pszGisdb );
+    G__setenv( "LOCATION_NAME", pszLoc );
+    G__setenv( "MAPSET", pszMapset); // group is searched only in current mapset 
+    G_reset_mapsets();
+    G_add_mapset_to_search_path ( pszMapset );
+
+/* -------------------------------------------------------------------- */
+/*      Check if this is a valid grass cell.                            */
+/* -------------------------------------------------------------------- */
+    if ( strcmp(pszElem,"cellhd") == 0 ) {
+	
+        if ( G_find_file2("cell", pszName, pszMapset) == NULL ) {
+	    free(pszGisdb); free(pszLoc); free(pszMapset); free(pszElem); free(pszName);
+	    return NULL;
+	}
+
+	papszMapsets = CSLAddString( papszMapsets, pszMapset );
+	papszCells = CSLAddString( papszCells, pszName );
+    }
+/* -------------------------------------------------------------------- */
+/*      Check if this is a valid GRASS imagery group.                   */
+/* -------------------------------------------------------------------- */
+    else {
+        struct Ref ref;
+
+        I_init_group_ref( &ref );
+        if ( I_get_group_ref( pszName, &ref ) == 0 ) {
+	    free(pszGisdb); free(pszLoc); free(pszMapset); free(pszElem); free(pszName);
+	    return NULL;
+	}
+        
+        for( int iRef = 0; iRef < ref.nfiles; iRef++ ) 
+	{
+            papszCells = CSLAddString( papszCells, ref.file[iRef].name );
+            papszMapsets = CSLAddString( papszMapsets, ref.file[iRef].mapset );
+            G_add_mapset_to_search_path ( ref.file[iRef].mapset );
+        }
+
+        I_free_group_ref( &ref );
+    }
+    
+    free( pszMapset );
+    free( pszName );
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    GRASSDataset 	*poDS;
+
+    poDS = new GRASSDataset();
+
+    /* notdef: should only allow read access to an existing cell, right? */
+    poDS->eAccess = poOpenInfo->eAccess;
+
+    poDS->pszGisdbase = pszGisdb;
+    poDS->pszLocation = pszLoc;
+    poDS->pszElement = pszElem;
+    
+/* -------------------------------------------------------------------- */
+/*      Capture some information from the file that is of interest.     */
+/* -------------------------------------------------------------------- */
+    
+    if( G_get_cellhd( papszCells[0], papszMapsets[0], &(poDS->sCellInfo) ) != 0 ) {
+	CPLError( CE_Warning, CPLE_AppDefined, "GRASS: Cannot open raster header");
+	/* TODO: delete poDS ? */
+	return NULL;
+    }
+
+    poDS->nRasterXSize = poDS->sCellInfo.cols;
+    poDS->nRasterYSize = poDS->sCellInfo.rows;
+
+    poDS->adfGeoTransform[0] = poDS->sCellInfo.west;
+    poDS->adfGeoTransform[1] = poDS->sCellInfo.ew_res;
+    poDS->adfGeoTransform[2] = 0.0;
+    poDS->adfGeoTransform[3] = poDS->sCellInfo.north;
+    poDS->adfGeoTransform[4] = 0.0;
+    poDS->adfGeoTransform[5] = -1 * poDS->sCellInfo.ns_res;
+    
+/* -------------------------------------------------------------------- */
+/*      Try to get a projection definition.                             */
+/* -------------------------------------------------------------------- */
+    struct Key_Value *projinfo, *projunits;
+
+    if ( hasGisbase ) {
+	projinfo = G_get_projinfo();
+	projunits = G_get_projunits();
+        poDS->pszProjection = GPJ_grass_to_wkt ( projinfo, projunits, 0, 0);
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    for( int iBand = 0; papszCells[iBand] != NULL; iBand++ )
+    {
+	GRASSRasterBand *rb = new GRASSRasterBand( poDS, iBand+1, papszMapsets[iBand], 
+                                                                  papszCells[iBand] );
+
+	if ( !rb->valid ) {
+	    CPLError( CE_Warning, CPLE_AppDefined, "GRASS: Cannot open raster band %d", iBand);
+	    // TODO: delete poDS ?
+	    return NULL;
+	}
+
+        poDS->SetBand( iBand+1, rb );
+    }
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                          GDALRegister_GRASS()                        */
+/************************************************************************/
+
+void GDALRegister_GRASS()
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "GRASS" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "GRASS" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "GRASS Database Rasters (5.7+)" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_grass.html" );
+        
+        poDriver->pfnOpen = GRASSDataset::Open;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/grass/grassdataset.cpp b/Utilities/GDAL/frmts/grass/grassdataset.cpp
new file mode 100644
index 0000000000..667ee14d42
--- /dev/null
+++ b/Utilities/GDAL/frmts/grass/grassdataset.cpp
@@ -0,0 +1,647 @@
+/******************************************************************************
+ * $Id: grassdataset.cpp,v 1.16 2004/02/20 22:58:00 warmerda Exp $
+ *
+ * Project:  GRASS Driver
+ * Purpose:  Implement GRASS raster read/write support
+ * Author:   Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: grassdataset.cpp,v $
+ * Revision 1.16  2004/02/20 22:58:00  warmerda
+ * Revert last change ... doesn't work with libgrass (CVS).  See further info
+ * in bug report (bug 408).
+ *
+ * Revision 1.15  2003/10/06 13:35:27  warmerda
+ * Fixed GrassErrorHandler typedef as per:
+ * http://bugzilla.remotesensing.org/show_bug.cgi?id=408
+ *
+ * Revision 1.14  2002/12/03 14:05:36  warmerda
+ * Modified to try and map byte nodata values outside the min/max range.
+ *
+ * Revision 1.13  2002/12/03 05:24:31  warmerda
+ * implement nodata support for all pixel types
+ *
+ * Revision 1.12  2002/09/04 06:50:37  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.11  2002/06/12 21:12:25  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.10  2002/01/24 15:50:23  warmerda
+ * Improved nodata handling.  Still may be problems with integer cells
+ * where all zeros will be considered to be nodata values.
+ *
+ * Revision 1.9  2002/01/23 14:56:27  warmerda
+ * Added link to help.
+ *
+ * Revision 1.8  2001/11/11 23:50:59  warmerda
+ * added required class keyword to friend declarations
+ *
+ * Revision 1.7  2001/07/18 04:51:56  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.6  2000/09/26 20:30:04  warmerda
+ * added support for imagery groups
+ *
+ * Revision 1.5  2000/09/23 02:22:29  warmerda
+ * added geotransform support
+ *
+ * Revision 1.4  2000/09/22 03:20:06  warmerda
+ * added projection support, and error handling
+ *
+ * Revision 1.3  2000/09/20 17:03:21  warmerda
+ * Added colortable support.
+ *
+ * Revision 1.2  2000/09/14 21:07:33  warmerda
+ * modified to use new G_check_cell() function
+ *
+ * Revision 1.1  2000/09/11 13:31:52  warmerda
+ * New
+ *
+ */
+
+#include <libgrass.h>
+
+#include "gdal_priv.h"
+#include "cpl_string.h"
+#include "ogr_spatialref.h"
+
+CPL_CVSID("$Id: grassdataset.cpp,v 1.16 2004/02/20 22:58:00 warmerda Exp $");
+
+CPL_C_START
+void	GDALRegister_GRASS(void);
+CPL_C_END
+
+/************************************************************************/
+/*                         Grass2CPLErrorHook()                         */
+/************************************************************************/
+
+int Grass2CPLErrorHook( char * pszMessage, int bFatal )
+
+{
+    if( !bFatal )
+        CPLDebug( "libgrass", "%s", pszMessage );
+    else
+        CPLError( CE_Fatal, CPLE_AppDefined, "libgrass: %s", pszMessage );
+
+    return 0;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*				GRASSDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class GRASSRasterBand;
+
+class GRASSDataset : public GDALDataset
+{
+    friend class GRASSRasterBand;
+
+    char	*pszProjection;
+
+    double	adfGeoTransform[6];
+
+  public:
+                 GRASSDataset();
+                 ~GRASSDataset();
+
+    virtual const char *GetProjectionRef(void);
+    virtual CPLErr GetGeoTransform( double * );
+
+    static GDALDataset *Open( GDALOpenInfo * );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            GRASSRasterBand                           */
+/* ==================================================================== */
+/************************************************************************/
+
+class GRASSRasterBand : public GDALRasterBand
+{
+    friend class GRASSDataset;
+
+    int		hCell;
+    int         nGRSType;
+
+    GDALColorTable *poCT;
+
+    int		bHaveMinMax;
+    double	dfCellMin;
+    double	dfCellMax;
+
+    double	dfNoData;
+
+  public:
+
+                   GRASSRasterBand( GRASSDataset *, int, 
+                                    const char *, const char * );
+    virtual        ~GRASSRasterBand();
+
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual GDALColorTable *GetColorTable();
+    virtual double GetMinimum( int *pbSuccess = NULL );
+    virtual double GetMaximum( int *pbSuccess = NULL );
+    virtual double GetNoDataValue( int *pbSuccess = NULL );
+};
+
+
+/************************************************************************/
+/*                          GRASSRasterBand()                           */
+/************************************************************************/
+
+GRASSRasterBand::GRASSRasterBand( GRASSDataset *poDS, int nBand,
+                                  const char * pszMapset,
+                                  const char * pszCellName )
+
+{
+    struct Cell_head	sCellInfo;
+
+    this->poDS = poDS;
+    this->nBand = nBand;
+
+    G_get_cellhd( (char *) pszCellName, (char *) pszMapset, &sCellInfo );
+    nGRSType = G_raster_map_type( (char *) pszCellName, (char *) pszMapset );
+
+/* -------------------------------------------------------------------- */
+/*      Get min/max values.                                             */
+/* -------------------------------------------------------------------- */
+    struct FPRange sRange;
+
+    if( G_read_fp_range( (char *) pszCellName, (char *) pszMapset, 
+                         &sRange ) == -1 )
+    {
+        bHaveMinMax = FALSE;
+    }
+    else
+    {
+        bHaveMinMax = TRUE;
+        G_get_fp_range_min_max( &sRange, &dfCellMin, &dfCellMax );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Setup band type, and preferred nodata value.                    */
+/* -------------------------------------------------------------------- */
+    dfNoData = 0.0;
+    if( nGRSType == CELL_TYPE && sCellInfo.format == 0 )
+    {
+        if( bHaveMinMax && dfCellMin < 1.0 && dfCellMax > 254.0 )
+        {
+            this->eDataType = GDT_UInt16;
+            dfNoData = 256.0;
+        }
+        else
+        {
+            this->eDataType = GDT_Byte;
+            if( dfCellMax < 255.0 )
+                dfNoData = 255.0;
+            else
+                dfNoData = 0.0;
+        }
+    }
+    else if( nGRSType == CELL_TYPE && sCellInfo.format == 1 )
+    {
+        this->eDataType = GDT_UInt16;
+        dfNoData = 65535.0;
+    }
+    else if( nGRSType == CELL_TYPE )
+    {
+        this->eDataType = GDT_UInt32;
+        dfNoData = 65535.0;
+    }
+    else if( nGRSType == FCELL_TYPE )
+    {
+        this->eDataType = GDT_Float32;
+        dfNoData = -12345.0;
+    }
+    else if( nGRSType == DCELL_TYPE )
+    {
+        this->eDataType = GDT_Float64;
+        dfNoData = -12345.0;
+    }
+    
+    nBlockXSize = poDS->nRasterXSize;;
+    nBlockYSize = 1;
+
+    hCell = G_open_cell_old((char *) pszCellName, (char *) pszMapset);
+
+/* -------------------------------------------------------------------- */
+/*      Do we have a color table?                                       */
+/* -------------------------------------------------------------------- */
+    struct Colors sGrassColors;
+
+    poCT = NULL;
+    if( G_read_colors( (char *) pszCellName, (char *) pszMapset, 
+                       &sGrassColors ) == 1 )
+    {
+        poCT = new GDALColorTable();
+        for( int iColor = 0; iColor < 256; iColor++ )
+        {
+            int	nRed, nGreen, nBlue;
+            GDALColorEntry    sColor;
+
+            if( G_get_color( iColor, &nRed, &nGreen, &nBlue, &sGrassColors ) )
+            {
+                sColor.c1 = nRed;
+                sColor.c2 = nGreen;
+                sColor.c3 = nBlue;
+                sColor.c4 = 255;
+
+                poCT->SetColorEntry( iColor, &sColor );
+            }
+            else
+            {
+                sColor.c1 = 0;
+                sColor.c2 = 0;
+                sColor.c3 = 0;
+                sColor.c4 = 0;
+
+                poCT->SetColorEntry( iColor, &sColor );
+            }
+        }
+
+        G_free_colors( &sGrassColors );
+    }
+}
+
+/************************************************************************/
+/*                          ~GRASSRasterBand()                          */
+/************************************************************************/
+
+GRASSRasterBand::~GRASSRasterBand()
+
+{
+    if( poCT != NULL )
+        delete poCT;
+
+    if( hCell >= 0 )
+        G_close_cell( hCell );
+}
+
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/*                                                                      */
+/*      We only do "null" testing for floating point values.  We        */
+/*      assume integer values are having the null raster entries set    */
+/*      to zero which is the "nodata" value for integer layers.         */
+/************************************************************************/
+
+CPLErr GRASSRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+
+{
+    char *pachNullBuf;
+    
+    pachNullBuf = (char *) CPLMalloc(nBlockXSize);
+    G_get_null_value_row( hCell, pachNullBuf, nBlockYOff );
+            
+    if( eDataType == GDT_Float32 || eDataType == GDT_Float64 
+        || eDataType == GDT_UInt32 )
+    {
+        G_get_raster_row( hCell, pImage, nBlockYOff, nGRSType  );
+
+        for( int i = 0; i < nBlockXSize; i++ )
+        {
+            if( pachNullBuf[i] != 0 )
+            {
+                if( eDataType == GDT_UInt32 )
+                    ((GUInt32 *) pImage)[i] = (GUInt32) dfNoData;
+                else if( eDataType == GDT_Float32 )
+                    ((float *) pImage)[i] = dfNoData;
+                else 
+                    ((double *) pImage)[i] = dfNoData;
+            }
+        }
+        
+    }
+    else
+    {
+        GUInt32 *panRow = (GUInt32 *) CPLMalloc(4 * nBlockXSize);
+        
+        G_get_raster_row( hCell, panRow, nBlockYOff, nGRSType  );
+
+        for( int i = 0; i < nBlockXSize; i++ )
+        {
+            if( pachNullBuf[i] != 0 )
+                panRow[i] = (GUInt32) dfNoData;
+        }
+
+        GDALCopyWords( panRow, GDT_UInt32, 4, 
+                       pImage, eDataType, GDALGetDataTypeSize(eDataType)/8,
+                       nBlockXSize );
+
+        CPLFree( panRow );
+    }
+
+    CPLFree( pachNullBuf );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp GRASSRasterBand::GetColorInterpretation()
+
+{
+    if( poCT != NULL )
+        return GCI_PaletteIndex;
+    else
+        return GCI_GrayIndex;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *GRASSRasterBand::GetColorTable()
+
+{
+    return poCT;
+}
+
+/************************************************************************/
+/*                             GetMinimum()                             */
+/************************************************************************/
+
+double GRASSRasterBand::GetMinimum( int *pbSuccess )
+
+{
+    if( pbSuccess )
+        *pbSuccess = bHaveMinMax;
+
+    if( bHaveMinMax )
+        return dfCellMin;
+
+    else if( eDataType == GDT_Float32 || eDataType == GDT_Float64 )
+        return -4294967295.0;
+    else
+        return 0;
+}
+
+/************************************************************************/
+/*                             GetMaximum()                             */
+/************************************************************************/
+
+double GRASSRasterBand::GetMaximum( int *pbSuccess )
+
+{
+    if( pbSuccess )
+        *pbSuccess = bHaveMinMax;
+
+    if( bHaveMinMax )
+        return dfCellMax;
+
+    else if( eDataType == GDT_Float32 || eDataType == GDT_Float64 )
+        return 4294967295.0;
+    else if( eDataType == GDT_UInt32 )
+        return 4294967295.0;
+    else if( eDataType == GDT_UInt16 )
+        return 65535;
+    else 
+        return 255;
+}
+
+/************************************************************************/
+/*                           GetNoDataValue()                           */
+/************************************************************************/
+
+double GRASSRasterBand::GetNoDataValue( int *pbSuccess )
+
+{
+    if( pbSuccess )
+        *pbSuccess = TRUE;
+
+    return dfNoData;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             GRASSDataset                             */
+/* ==================================================================== */
+/************************************************************************/
+
+
+/************************************************************************/
+/*                            GRASSDataset()                            */
+/************************************************************************/
+
+GRASSDataset::GRASSDataset()
+
+{
+    pszProjection = NULL;
+
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+}
+
+/************************************************************************/
+/*                           ~GRASSDataset()                            */
+/************************************************************************/
+
+GRASSDataset::~GRASSDataset()
+
+{
+    CPLFree( pszProjection );
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *GRASSDataset::GetProjectionRef() 
+{
+    if( pszProjection == NULL )
+        return "";
+    else
+        return pszProjection;
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr GRASSDataset::GetGeoTransform( double * padfGeoTransform ) 
+{
+    memcpy( padfGeoTransform, adfGeoTransform, sizeof(double) * 6 );
+    
+    return CE_None;
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+typedef int (*GrassErrorHandler)();
+
+GDALDataset *GRASSDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    static int	bDoneGISInit = FALSE;
+    char	*pszMapset = NULL, *pszCell = NULL;
+    char        **papszCells = NULL;
+    char        **papszMapsets = NULL;
+
+    if( !bDoneGISInit )
+    {
+        G_set_error_routine( (GrassErrorHandler) Grass2CPLErrorHook );
+        G_gisinit_2( "GDAL", NULL, NULL, NULL );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check if this is a valid grass cell.                            */
+/* -------------------------------------------------------------------- */
+    if( G_check_cell( poOpenInfo->pszFilename, &pszMapset, &pszCell ) )
+    {
+        papszCells = CSLAddString( papszCells, pszCell );
+        papszMapsets = CSLAddString( papszMapsets, pszMapset );
+
+        G_free( pszMapset );
+        G_free( pszCell );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Check if this is a valid GRASS imagery group.                   */
+/* -------------------------------------------------------------------- */
+    else if( I_check_group( poOpenInfo->pszFilename, &pszMapset, &pszCell ) )
+    {
+        struct Ref ref;
+
+        I_init_group_ref( &ref );
+        I_get_group_ref( pszCell, &ref );
+        
+        for( int iRef = 0; iRef < ref.nfiles; iRef++ )
+        {
+            papszCells = CSLAddString( papszCells, ref.file[iRef].name );
+            papszMapsets = CSLAddString( papszMapsets, ref.file[iRef].mapset );
+        }
+
+        I_free_group_ref( &ref );
+
+        G_free( pszMapset );
+        G_free( pszCell );
+    }
+
+    else
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    GRASSDataset 	*poDS;
+
+    poDS = new GRASSDataset();
+
+    /* notdef: should only allow read access to an existing cell, right? */
+    poDS->eAccess = poOpenInfo->eAccess;
+    
+/* -------------------------------------------------------------------- */
+/*      Capture some information from the file that is of interest.     */
+/* -------------------------------------------------------------------- */
+    struct Cell_head	sCellInfo;
+    
+    if( G_get_cellhd( papszCells[0], papszMapsets[0], &sCellInfo ) != 0 )
+    {
+        /* notdef: report failure. */
+        return NULL;
+    }
+
+    poDS->nRasterXSize = sCellInfo.cols;
+    poDS->nRasterYSize = sCellInfo.rows;
+
+    G_set_window( &sCellInfo );
+
+    poDS->adfGeoTransform[0] = sCellInfo.west;
+    poDS->adfGeoTransform[1] = sCellInfo.ew_res;
+    poDS->adfGeoTransform[2] = 0.0;
+    poDS->adfGeoTransform[3] = sCellInfo.north;
+    poDS->adfGeoTransform[4] = 0.0;
+    poDS->adfGeoTransform[5] = -1 * sCellInfo.ns_res;
+
+/* -------------------------------------------------------------------- */
+/*      Try to get a projection definition.                             */
+/* -------------------------------------------------------------------- */
+    char	*pszProj4;
+
+    pszProj4 = G_get_cell_as_proj4( papszCells[0], papszMapsets[0] );
+    if( pszProj4 != NULL )
+    {
+        OGRSpatialReference   oSRS;
+
+        if( oSRS.importFromProj4( pszProj4 ) == OGRERR_NONE )
+        {
+            oSRS.exportToWkt( &(poDS->pszProjection) );
+        }
+
+        G_free( pszProj4 );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    for( int iBand = 0; papszCells[iBand] != NULL; iBand++ )
+    {
+        poDS->SetBand( iBand+1, 
+                       new GRASSRasterBand( poDS, iBand+1, 
+                                            papszMapsets[iBand], 
+                                            papszCells[iBand] ) );
+    }
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                          GDALRegister_GRASS()                        */
+/************************************************************************/
+
+void GDALRegister_GRASS()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "GRASS" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "GRASS" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "GRASS Database Rasters" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_grass.html" );
+        
+        poDriver->pfnOpen = GRASSDataset::Open;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/grass/pkg/Makefile.in b/Utilities/GDAL/frmts/grass/pkg/Makefile.in
new file mode 100644
index 0000000000..252d42b9d7
--- /dev/null
+++ b/Utilities/GDAL/frmts/grass/pkg/Makefile.in
@@ -0,0 +1,49 @@
+                                                                                                                                                                                                                                                               
+CC	=	@CC@
+CXX	=	@CXX@
+LD	=	@CXX@
+
+CPPFLAGS = -DUSE_CPL -DGRASS_GISBASE=\"@GRASS_GISBASE@\" \
+	  @GDAL_INC@ @GRASS_INCLUDE@ @CPPFLAGS@ 
+CXXFLAGS = @CXX_WFLAGS@ @CXX_PIC@ 
+
+RANLIB		=	@RANLIB@
+SO_EXT		=	@SO_EXT@
+LD_SHARED	=	@LD_SHARED@
+
+LIBS	=	@LIBS@
+
+GRASSTABLES_DIR = @prefix@/share/gdal/grass
+
+AUTOLOAD_DIR	=	@AUTOLOAD_DIR@
+
+GLIBNAME =	gdal_GRASS.so
+OLIBNAME =	ogr_GRASS.so
+
+default:	$(GLIBNAME) $(OLIBNAME)
+
+install:	default
+	install -d $(AUTOLOAD_DIR)
+	cp $(GLIBNAME) $(AUTOLOAD_DIR)
+	cp $(OLIBNAME) $(AUTOLOAD_DIR)
+	test -d ${GRASSTABLES_DIR} || mkdir ${GRASSTABLES_DIR}
+	test -d ${GRASSTABLES_DIR}/etc || mkdir ${GRASSTABLES_DIR}/etc
+	cp @GRASS_GISBASE@/etc/ellipse.table ${GRASSTABLES_DIR}/etc
+	cp @GRASS_GISBASE@/etc/datum.table ${GRASSTABLES_DIR}/etc	
+
+clean:
+	rm -f $(OLIBNAME) $(GLIBNAME) *.o 
+
+distclean: clean
+	rm -fr Makefile config.status config.log autom*.cache
+
+
+$(GLIBNAME):	grass57dataset.o
+	$(LD_SHARED) grass57dataset.o $(LIBS) -o $(GLIBNAME)
+
+$(OLIBNAME):	ogrgrassdriver.o ogrgrassdatasource.o ogrgrasslayer.o 
+	$(LD_SHARED) ogrgrassdriver.o ogrgrassdatasource.o ogrgrasslayer.o $(LIBS) -o $(OLIBNAME)
+
+%.o:	%.cpp
+	$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
+
diff --git a/Utilities/GDAL/frmts/grass/pkg/README b/Utilities/GDAL/frmts/grass/pkg/README
new file mode 100644
index 0000000000..c3a9c4a193
--- /dev/null
+++ b/Utilities/GDAL/frmts/grass/pkg/README
@@ -0,0 +1,63 @@
+	Standalone GRASS 6 Drivers for GDAL and OGR
+	===========================================
+
+This package contains standalone drivers for GRASS raster and vector
+files that can be built after GDAL has been built and installed as an 
+"autoload" driver.  
+
+This is particularly useful in resolving problems with GRASS depending
+on GDAL, but GDAL with GRASS support depending on GRASS.  With this 
+package you can configure and install GDAL normally (--without-grass), then
+build and install GRASS normally and finally build and install this driver.
+
+To build this driver it is necessary for it to find GDAL and GRASS support
+files.  Typically the configure and build process would look something like:
+
+./configure --with-gdal=/usr/local/bin/gdal-config --with-grass=/usr/local/grass-6.0.0
+make
+sudo make install
+
+See also:
+
+  http://www.gdal.org/
+  http://grass.itc.it/
+
+
+---
+
+FAQs
+----
+
+
+Question:
+
+I am trying to install gdal-grass 1.3.1 on Red hat enterprise linux
+advanced server 3.0. I have previously installed gdal 1.3.1 without-
+grass, and Grass 6.0.1 with-gdal. I have tried to configure gdal-grass
+with:
+
+./configure --with-gdal=/usr/local/gdal/bin/gdal-config --with-
+grass=/usr/local/grass-6.0.1
+
+It seems to find gdal alright, but then balks at the Grass location. The
+Grass location specified above is indeed the correct location. I have
+also tried adding --with-grass=/usr/local/grass-6.0.1/lib, but with no
+success. My error is:
+
+...
+checking for G_asprintf in -lgrass_gis ... no
+configure: error: --with-grass=/usr/local/grass-6.0.1 requested, but
+libraries not found?
+
+
+Answer:
+
+Your problem is likely to be solved by editing /etc/ld.so.conf to 
+include the locations of proj, gdal, grass, and geos. Specifically, 
+the full path to both gdal-config and geos-config, and the full paths 
+to the library locations of proj (often /usr/local/lib) and grass (/
+usr/local/grass-6.0.1/lib). After editing ld.so.conf, run ldconfig, 
+and you should be good to go.
+
+I ran into this problem this weekend (and posted for help to this 
+list), so it seems to be a pretty common issue. 
diff --git a/Utilities/GDAL/frmts/grass/pkg/aclocal.m4 b/Utilities/GDAL/frmts/grass/pkg/aclocal.m4
new file mode 100644
index 0000000000..7bffcf46da
--- /dev/null
+++ b/Utilities/GDAL/frmts/grass/pkg/aclocal.m4
@@ -0,0 +1,202 @@
+AC_DEFUN(AC_COMPILER_LOCALHACK,
+[
+  AC_MSG_CHECKING([if local/include already standard])
+
+  rm -f comp.out
+  echo 'int main() { int i = 1; if( *((unsigned char *) &i) == 0 ) printf( "BIGENDIAN"); return 0; }' >> conftest.c
+  ${CC} $CPPFLAGS $EXTRA_INCLUDES -o conftest conftest.c 2> comp.out
+  COMP_CHECK=`grep "system directory" comp.out | grep /usr/local/include`
+  if test -z "$COMP_CHECK" ; then 
+     AC_MSG_RESULT([no, everything is ok])
+  else
+     AC_MSG_RESULT([yes, stripping extras])
+     CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-I\/usr\/local\/include //"`
+     CFLAGS=`echo "$CFLAGS " | sed "s/-I\/usr\/local\/include //"`
+     EXTRA_INCLUDES=`echo "$EXTRA_INCLUDES " | sed "s/-I\/usr\/local\/include //"`
+  fi 
+  rm -f comp.out
+])
+
+AC_DEFUN(AC_COMPILER_WFLAGS,
+[
+	# Remove -g from compile flags, we will add via CFG variable if
+	# we need it.
+	CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-g //"`
+	CFLAGS=`echo "$CFLAGS " | sed "s/-g //"`
+
+	# check for GNU compiler, and use -Wall
+	if test "$GCC" = "yes"; then
+		C_WFLAGS="-Wall"
+		AC_DEFINE(USE_GNUCC, 1, [Define to 1, if you have GNU C
+		compiler])
+	fi
+	if test "$GXX" = "yes"; then
+		CXX_WFLAGS="-Wall"
+		AC_DEFINE(USE_GNUCC, 1, [Define to 1, if you have GNU C
+		compiler])
+	fi
+	AC_SUBST(CXX_WFLAGS,$CXX_WFLAGS)
+	AC_SUBST(C_WFLAGS,$C_WFLAGS)
+])
+
+AC_DEFUN(AC_COMPILER_PIC,
+[
+	echo 'void f(){}' > conftest.c
+	if test -z "`${CC-cc} -fPIC -c conftest.c 2>&1`"; then
+	  C_PIC=-fPIC
+	else
+	  C_PIC=
+	fi
+	if test -z "`${CXX-g++} -fPIC -c conftest.c 2>&1`"; then
+	  CXX_PIC=-fPIC
+	else
+	  CXX_PIC=
+	fi
+	rm -f conftest*
+
+	AC_SUBST(CXX_PIC,$CXX_PIC)
+	AC_SUBST(C_PIC,$C_PIC)
+])
+
+dnl
+dnl Try to find something to link shared libraries with.  Use "c++ -shared"
+dnl in preference to "ld -shared" because it will link in required c++
+dnl run time support for us. 
+dnl
+AC_DEFUN(AC_LD_SHARED,
+[
+  echo 'void g(); int main(){ g(); return 0; }' > conftest1.c
+
+  echo '#include <stdio.h>' > conftest2.c
+  echo 'void g(); void g(){printf("");}' >> conftest2.c
+  ${CC} ${C_PIC} -c conftest2.c
+
+  SO_EXT="so"
+  export SO_EXT
+  LD_SHARED="/bin/true"
+  if test ! -z "`uname -a | grep IRIX`" ; then
+    IRIX_ALL=-all
+  else
+    IRIX_ALL=
+  fi
+
+  AC_ARG_WITH(ld-shared,[  --with-ld-shared=cmd    provide shared library link],,)
+
+  if test "$with_ld_shared" != "" ; then
+    if test "$with_ld_shared" = "no" ; then
+      echo "user disabled shared library support."	
+    else
+      echo "using user supplied .so link command ... $with_ld_shared"	
+    fi
+    LD_SHARED="$with_ld_shared"
+  fi
+
+  dnl Check For Cygwin case.  Actually verify that the produced DLL works.
+
+  if test ! -z "`uname -a | grep CYGWIN`" \
+        -a "$LD_SHARED" = "/bin/true" \
+	-a -z "`gcc -shared conftest2.o -o libconftest.dll`" ; then
+    if test -z "`${CC} conftest1.c -L./ -lconftest -o conftest1 2>&1`"; then
+      LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH"
+      if test -z "$LD_LIBRARY_PATH" ; then
+        LD_LIBRARY_PATH="`pwd`"
+      else
+        LD_LIBRARY_PATH="`pwd`:$LD_LIBRARY_PATH"
+      fi
+      export LD_LIBRARY_PATH
+      if test -z "`./conftest1 2>&1`" ; then
+        echo "checking for Cygwin gcc -shared ... yes"
+        LD_SHARED="c++ -shared"
+        SO_EXT="dll"
+      fi
+      LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD"
+    fi
+  fi
+
+  dnl Test special MacOS (Darwin) case. 
+
+  if test ! -z "`uname | grep Darwin`" \
+          -a "$LD_SHARED" = "/bin/true" \
+          -a -z "`${CXX} -dynamiclib conftest2.o -o libconftest.so 2>&1`" ; then
+    ${CC} -c conftest1.c
+    if test -z "`${CXX} conftest1.o libconftest.so -o conftest1 2>&1`"; then
+      DYLD_LIBRARY_PATH_OLD="$DYLD_LIBRARY_PATH"
+      if test -z "$DYLD_LIBRARY_PATH" ; then
+        DYLD_LIBRARY_PATH="`pwd`"
+      else
+        DYLD_LIBRARY_PATH="`pwd`:$DYLD_LIBRARY_PATH"
+      fi
+      export DYLD_LIBRARY_PATH
+      if test -z "`./conftest1 2>&1`" ; then
+        echo "checking for ${CXX} -dynamiclib ... yes"
+        LD_SHARED="${CXX} -dynamiclib"
+	SO_EXT=dylib
+      fi
+      DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH_OLD"
+    fi
+    rm -f conftest1.o
+  fi
+
+  if test "$LD_SHARED" = "/bin/true" \
+	-a -z "`${CXX} -shared $IRIX_ALL conftest2.o -o libconftest.so 2>&1|grep -v WARNING`" ; then
+    if test -z "`${CC} conftest1.c libconftest.so -o conftest1 2>&1`"; then
+      LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH"
+      if test -z "$LD_LIBRARY_PATH" ; then
+        LD_LIBRARY_PATH="`pwd`"
+      else
+        LD_LIBRARY_PATH="`pwd`:$LD_LIBRARY_PATH"
+      fi
+      export LD_LIBRARY_PATH
+      if test -z "`./conftest1 2>&1`" ; then
+        echo "checking for ${CXX} -shared ... yes"
+        LD_SHARED="${CXX} -shared $IRIX_ALL"
+      else
+        echo "checking for ${CXX} -shared ... no(3)"
+      fi
+      LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD"
+    else
+      echo "checking for ${CXX} -shared ... no(2)"
+    fi
+  else 
+    if test "$LD_SHARED" = "/bin/true" ; then
+      echo "checking for ${CXX} -shared ... no(1)"
+    fi
+  fi
+
+  if test "$LD_SHARED" = "/bin/true" \
+          -a -z "`ld -shared conftest2.o -o libconftest.so 2>&1`" ; then
+    if test -z "`${CC} conftest1.c libconftest.so -o conftest1 2>&1`"; then
+      LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH"
+      if test -z "$LD_LIBRARY_PATH" ; then
+        LD_LIBRARY_PATH="`pwd`"
+      else
+        LD_LIBRARY_PATH="`pwd`:$LD_LIBRARY_PATH"
+      fi
+      export LD_LIBRARY_PATH
+      if test -z "`./conftest1 2>&1`" ; then
+        echo "checking for ld -shared ... yes"
+        LD_SHARED="ld -shared"
+      fi
+      LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD"
+    fi
+  fi
+
+  if test "$LD_SHARED" = "/bin/true" ; then
+    echo "checking for ld -shared ... no"
+    if test ! -x /bin/true ; then
+      LD_SHARED=/usr/bin/true
+    fi
+  fi
+  if test "$LD_SHARED" = "no" ; then
+    if test -x /bin/true ; then
+      LD_SHARED=/bin/true
+    else
+      LD_SHARED=/usr/bin/true
+    fi
+  fi
+
+  rm -f conftest* libconftest* 
+
+  AC_SUBST(LD_SHARED,$LD_SHARED)
+  AC_SUBST(SO_EXT,$SO_EXT)
+])
diff --git a/Utilities/GDAL/frmts/grass/pkg/configure b/Utilities/GDAL/frmts/grass/pkg/configure
new file mode 100755
index 0000000000..23b007f82f
--- /dev/null
+++ b/Utilities/GDAL/frmts/grass/pkg/configure
@@ -0,0 +1,3901 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete.  It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="Makefile.in"
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CXX CXXFLAGS ac_ct_CXX RANLIB ac_ct_RANLIB CXX_PIC C_PIC LD_SHARED SO_EXT CXX_WFLAGS C_WFLAGS GDAL_CONFIG GDAL_INC AUTOLOAD_DIR GRASS_INCLUDE GRASS_GISBASE LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_option in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    eval "enable_$ac_feature=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_$ac_feature='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_$ac_package='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/-/_/g'`
+    eval "with_$ac_package=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+    eval "$ac_envvar='$ac_optarg'"
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+	      localstatedir libdir includedir oldincludedir infodir mandir
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$0" : 'X\(//\)[^/]' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+   { (exit 1); exit 1; }; }
+  else
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+  fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+  { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+   { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+ac_env_CXX_set=${CXX+set}
+ac_env_CXX_value=$CXX
+ac_cv_env_CXX_set=${CXX+set}
+ac_cv_env_CXX_value=$CXX
+ac_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_env_CXXFLAGS_value=$CXXFLAGS
+ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_cv_env_CXXFLAGS_value=$CXXFLAGS
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+  cat <<_ACEOF
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+			  [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+			  [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [EPREFIX/bin]
+  --sbindir=DIR          system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR       program executables [EPREFIX/libexec]
+  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
+  --libdir=DIR           object code libraries [EPREFIX/lib]
+  --includedir=DIR       C header files [PREFIX/include]
+  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
+  --infodir=DIR          info documentation [PREFIX/info]
+  --mandir=DIR           man documentation [PREFIX/man]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+  cat <<\_ACEOF
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-ld-shared=cmd    provide shared library link
+  --with-gdal=PATH        GDAL (PATH is path to gdal-config)
+  --with-autoload=DIR      Directory for autoload drivers
+  --with-grass=ARG        Include GRASS support (ARG=GRASS install tree dir)
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have
+              headers in a nonstandard directory <include dir>
+  CXX         C++ compiler command
+  CXXFLAGS    C++ compiler flags
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  ac_popdir=`pwd`
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d $ac_dir || continue
+    ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+    cd $ac_dir
+    # Check for guested configure; otherwise get Cygnus style configure.
+    if test -f $ac_srcdir/configure.gnu; then
+      echo
+      $SHELL $ac_srcdir/configure.gnu  --help=recursive
+    elif test -f $ac_srcdir/configure; then
+      echo
+      $SHELL $ac_srcdir/configure  --help=recursive
+    elif test -f $ac_srcdir/configure.ac ||
+	   test -f $ac_srcdir/configure.in; then
+      echo
+      $ac_configure --help
+    else
+      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi
+    cd $ac_popdir
+  done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+  cat <<\_ACEOF
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo               = `(hostinfo) 2>/dev/null               || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+      # Get rid of the leading space.
+      ac_sep=" "
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+{
+  (set) 2>&1 |
+    case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      sed -n \
+	"s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+      ;;
+    *)
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+}
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=$`echo $ac_var`
+      echo "$ac_var='"'"'$ac_val'"'"'"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=$`echo $ac_var`
+	echo "$ac_var='"'"'$ac_val'"'"'"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      sed "/^$/d" confdefs.h | sort
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      echo "$as_me: caught signal $ac_signal"
+    echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core &&
+  rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+     ' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+	       sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+  eval ac_new_val="\$ac_env_${ac_var}_value"
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	{ echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	{ echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
+echo "$as_me:   former value:  $ac_old_val" >&2;}
+	{ echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
+echo "$as_me:   current value: $ac_new_val" >&2;}
+	ac_cache_corrupted=:
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CC" && break
+done
+
+  CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+  (eval $ac_link_default) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Find the output, starting from the most likely.  This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+	;;
+    conftest.$ac_ext )
+	# This is the source file.
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	# FIXME: I believe we export ac_cv_exeext for Libtool,
+	# but it would be cool to find out if it's true.  Does anybody
+	# maintain Libtool? --akim.
+	export ac_cv_exeext
+	break;;
+    * )
+	break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  export ac_cv_exeext
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std1 is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std1.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX			-qlanglvl=ansi
+# Ultrix and OSF/1	-std1
+# HP-UX 10.20 and later	-Ae
+# HP-UX older versions	-Aa -D_HPUX_SOURCE
+# SVR4			-Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+  x|xno)
+    echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+  *)
+    echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+    CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C.  Since we use `exit',
+# in C++ we need to declare it.  In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+  choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cc
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CXX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  echo "$as_me:$LINENO: result: $CXX" >&5
+echo "${ECHO_T}$CXX" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5
+echo "${ECHO_T}$ac_ct_CXX" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CXX" && break
+done
+test -n "$ac_ct_CXX" || ac_ct_CXX="g++"
+
+  CXX=$ac_ct_CXX
+fi
+
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C++ compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6
+if test "${ac_cv_cxx_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6
+GXX=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+CXXFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5
+echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cxx_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cxx_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cxx_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":"
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  RANLIB=$ac_ct_RANLIB
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+
+	echo 'void f(){}' > conftest.c
+	if test -z "`${CC-cc} -fPIC -c conftest.c 2>&1`"; then
+	  C_PIC=-fPIC
+	else
+	  C_PIC=
+	fi
+	if test -z "`${CXX-g++} -fPIC -c conftest.c 2>&1`"; then
+	  CXX_PIC=-fPIC
+	else
+	  CXX_PIC=
+	fi
+	rm -f conftest*
+
+	CXX_PIC=$CXX_PIC
+
+	C_PIC=$C_PIC
+
+
+
+  echo 'void g(); int main(){ g(); return 0; }' > conftest1.c
+
+  echo '#include <stdio.h>' > conftest2.c
+  echo 'void g(); void g(){printf("");}' >> conftest2.c
+  ${CC} ${C_PIC} -c conftest2.c
+
+  SO_EXT="so"
+  export SO_EXT
+  LD_SHARED="/bin/true"
+  if test ! -z "`uname -a | grep IRIX`" ; then
+    IRIX_ALL=-all
+  else
+    IRIX_ALL=
+  fi
+
+
+# Check whether --with-ld-shared or --without-ld-shared was given.
+if test "${with_ld_shared+set}" = set; then
+  withval="$with_ld_shared"
+
+fi;
+
+  if test "$with_ld_shared" != "" ; then
+    if test "$with_ld_shared" = "no" ; then
+      echo "user disabled shared library support."
+    else
+      echo "using user supplied .so link command ... $with_ld_shared"
+    fi
+    LD_SHARED="$with_ld_shared"
+  fi
+
+
+  if test ! -z "`uname -a | grep CYGWIN`" \
+        -a "$LD_SHARED" = "/bin/true" \
+	-a -z "`gcc -shared conftest2.o -o libconftest.dll`" ; then
+    if test -z "`${CC} conftest1.c -L./ -lconftest -o conftest1 2>&1`"; then
+      LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH"
+      if test -z "$LD_LIBRARY_PATH" ; then
+        LD_LIBRARY_PATH="`pwd`"
+      else
+        LD_LIBRARY_PATH="`pwd`:$LD_LIBRARY_PATH"
+      fi
+      export LD_LIBRARY_PATH
+      if test -z "`./conftest1 2>&1`" ; then
+        echo "checking for Cygwin gcc -shared ... yes"
+        LD_SHARED="c++ -shared"
+        SO_EXT="dll"
+      fi
+      LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD"
+    fi
+  fi
+
+
+  if test ! -z "`uname | grep Darwin`" \
+          -a "$LD_SHARED" = "/bin/true" \
+          -a -z "`${CXX} -dynamiclib conftest2.o -o libconftest.so 2>&1`" ; then
+    ${CC} -c conftest1.c
+    if test -z "`${CXX} conftest1.o libconftest.so -o conftest1 2>&1`"; then
+      DYLD_LIBRARY_PATH_OLD="$DYLD_LIBRARY_PATH"
+      if test -z "$DYLD_LIBRARY_PATH" ; then
+        DYLD_LIBRARY_PATH="`pwd`"
+      else
+        DYLD_LIBRARY_PATH="`pwd`:$DYLD_LIBRARY_PATH"
+      fi
+      export DYLD_LIBRARY_PATH
+      if test -z "`./conftest1 2>&1`" ; then
+        echo "checking for ${CXX} -dynamiclib ... yes"
+        LD_SHARED="${CXX} -dynamiclib"
+	SO_EXT=dylib
+      fi
+      DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH_OLD"
+    fi
+    rm -f conftest1.o
+  fi
+
+  if test "$LD_SHARED" = "/bin/true" \
+	-a -z "`${CXX} -shared $IRIX_ALL conftest2.o -o libconftest.so 2>&1|grep -v WARNING`" ; then
+    if test -z "`${CC} conftest1.c libconftest.so -o conftest1 2>&1`"; then
+      LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH"
+      if test -z "$LD_LIBRARY_PATH" ; then
+        LD_LIBRARY_PATH="`pwd`"
+      else
+        LD_LIBRARY_PATH="`pwd`:$LD_LIBRARY_PATH"
+      fi
+      export LD_LIBRARY_PATH
+      if test -z "`./conftest1 2>&1`" ; then
+        echo "checking for ${CXX} -shared ... yes"
+        LD_SHARED="${CXX} -shared $IRIX_ALL"
+      else
+        echo "checking for ${CXX} -shared ... no(3)"
+      fi
+      LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD"
+    else
+      echo "checking for ${CXX} -shared ... no(2)"
+    fi
+  else
+    if test "$LD_SHARED" = "/bin/true" ; then
+      echo "checking for ${CXX} -shared ... no(1)"
+    fi
+  fi
+
+  if test "$LD_SHARED" = "/bin/true" \
+          -a -z "`ld -shared conftest2.o -o libconftest.so 2>&1`" ; then
+    if test -z "`${CC} conftest1.c libconftest.so -o conftest1 2>&1`"; then
+      LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH"
+      if test -z "$LD_LIBRARY_PATH" ; then
+        LD_LIBRARY_PATH="`pwd`"
+      else
+        LD_LIBRARY_PATH="`pwd`:$LD_LIBRARY_PATH"
+      fi
+      export LD_LIBRARY_PATH
+      if test -z "`./conftest1 2>&1`" ; then
+        echo "checking for ld -shared ... yes"
+        LD_SHARED="ld -shared"
+      fi
+      LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD"
+    fi
+  fi
+
+  if test "$LD_SHARED" = "/bin/true" ; then
+    echo "checking for ld -shared ... no"
+    if test ! -x /bin/true ; then
+      LD_SHARED=/usr/bin/true
+    fi
+  fi
+  if test "$LD_SHARED" = "no" ; then
+    if test -x /bin/true ; then
+      LD_SHARED=/bin/true
+    else
+      LD_SHARED=/usr/bin/true
+    fi
+  fi
+
+  rm -f conftest* libconftest*
+
+  LD_SHARED=$LD_SHARED
+
+  SO_EXT=$SO_EXT
+
+
+
+	# Remove -g from compile flags, we will add via CFG variable if
+	# we need it.
+	CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-g //"`
+	CFLAGS=`echo "$CFLAGS " | sed "s/-g //"`
+
+	# check for GNU compiler, and use -Wall
+	if test "$GCC" = "yes"; then
+		C_WFLAGS="-Wall"
+
+cat >>confdefs.h <<\_ACEOF
+#define USE_GNUCC 1
+_ACEOF
+
+	fi
+	if test "$GXX" = "yes"; then
+		CXX_WFLAGS="-Wall"
+
+cat >>confdefs.h <<\_ACEOF
+#define USE_GNUCC 1
+_ACEOF
+
+	fi
+	CXX_WFLAGS=$CXX_WFLAGS
+
+	C_WFLAGS=$C_WFLAGS
+
+
+
+
+
+# Check whether --with-gdal or --without-gdal was given.
+if test "${with_gdal+set}" = set; then
+  withval="$with_gdal"
+
+fi;
+
+if test "$with_gdal" = "yes" -o "$with_gdal" = "" ; then
+
+  if test "`basename xx/$with_gdal`" = "gdal-config" ; then
+    GDAL_CONFIG="$with_gdal"
+  fi
+
+  if test -z "$GDAL_CONFIG" ; then
+    # Extract the first word of "gdal-config", so it can be a program name with args.
+set dummy gdal-config; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_path_GDAL_CONFIG+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $GDAL_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_GDAL_CONFIG="$GDAL_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_GDAL_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_path_GDAL_CONFIG" && ac_cv_path_GDAL_CONFIG="no"
+  ;;
+esac
+fi
+GDAL_CONFIG=$ac_cv_path_GDAL_CONFIG
+
+if test -n "$GDAL_CONFIG"; then
+  echo "$as_me:$LINENO: result: $GDAL_CONFIG" >&5
+echo "${ECHO_T}$GDAL_CONFIG" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  fi
+
+  if test "$GDAL_CONFIG" = "no" ; then
+    { { echo "$as_me:$LINENO: error: couldn't find gdal-config" >&5
+echo "$as_me: error: couldn't find gdal-config" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+
+elif test -n "$with_gdal" -a "$with_gdal" != "no" ; then
+
+  GDAL_CONFIG=$with_gdal
+
+  if test -f "$GDAL_CONFIG" -a -x "$GDAL_CONFIG" ; then
+    echo "$as_me:$LINENO: result: user supplied gdal-config ($GDAL_CONFIG)" >&5
+echo "${ECHO_T}user supplied gdal-config ($GDAL_CONFIG)" >&6
+  else
+    { { echo "$as_me:$LINENO: error: '$GDAL_CONFIG' is not an executable.  Make sure you use --with-gdal=/path/to/gdal-config" >&5
+echo "$as_me: error: '$GDAL_CONFIG' is not an executable.  Make sure you use --with-gdal=/path/to/gdal-config" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+
+else
+
+  { { echo "$as_me:$LINENO: error: gdal required to build GDAL GRASS 5.7 driver" >&5
+echo "$as_me: error: gdal required to build GDAL GRASS 5.7 driver" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+
+LIBS="`$GDAL_CONFIG --libs` $LIBS"
+GDAL_INC=`$GDAL_CONFIG --cflags`
+
+GDAL_INC=$GDAL_INC
+
+
+
+# Check whether --with-autoload or --without-autoload was given.
+if test "${with_autoload+set}" = set; then
+  withval="$with_autoload"
+
+fi;
+
+if test "$with_autoload" != "" ; then
+  AUTOLOAD_DIR=$with_autoload
+else
+  if $GDAL_CONFIG --autoload > /dev/null 2>&1 ; then
+    AUTOLOAD_DIR=`$GDAL_CONFIG --autoload`
+  else
+    AUTOLOAD_DIR=`$GDAL_CONFIG --prefix`/lib/gdalplugins
+  fi
+fi
+
+echo "$as_me:$LINENO: result: using $AUTOLOAD_DIR as GDAL shared library autoload directory" >&5
+echo "${ECHO_T}using $AUTOLOAD_DIR as GDAL shared library autoload directory" >&6
+AUTOLOAD_DIR=$AUTOLOAD_DIR
+
+
+
+GRASS_SETTING=no
+GRASS_INCLUDE=
+GRASS_GISBASE=
+export GRASS_INCLUDE GRASS_SETTING GRASS_GISBASE
+
+
+# Check whether --with-grass or --without-grass was given.
+if test "${with_grass+set}" = set; then
+  withval="$with_grass"
+
+fi;
+
+if test "$with_grass" = "no" ; then
+  { { echo "$as_me:$LINENO: error: grass required for this driver, please install GRASS 5.7 and rebuild" >&5
+echo "$as_me: error: grass required for this driver, please install GRASS 5.7 and rebuild" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+if test "$with_grass" != "yes" ; then
+
+
+echo "$as_me:$LINENO: checking for G_asprintf in -lgrass_gis" >&5
+echo $ECHO_N "checking for G_asprintf in -lgrass_gis... $ECHO_C" >&6
+if test "${ac_cv_lib_grass_gis_G_asprintf+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lgrass_gis -L$with_grass/lib -lgrass_datetime -lgrass_gproj -lgrass_vect $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char G_asprintf ();
+int
+main ()
+{
+G_asprintf ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_grass_gis_G_asprintf=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_grass_gis_G_asprintf=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_grass_gis_G_asprintf" >&5
+echo "${ECHO_T}$ac_cv_lib_grass_gis_G_asprintf" >&6
+if test $ac_cv_lib_grass_gis_G_asprintf = yes; then
+  GRASS_SETTING=grass57+
+else
+  GRASS_SETTING=no
+fi
+
+
+  if test "$GRASS_SETTING" = "grass57+" ; then
+    LIBS="-L$with_grass/lib -lgrass_I -lgrass_vask -lgrass_gmath -lgrass_gis -lgrass_datetime -lgrass_gproj -lgrass_vect -lgrass_dbmibase -lgrass_dbmiclient $LIBS"
+    GRASS_INCLUDE="-I$with_grass/include"
+    GRASS_GISBASE="$with_grass"
+  else
+    { { echo "$as_me:$LINENO: error: --with-grass=$with_grass requested, but libraries not found!" >&5
+echo "$as_me: error: --with-grass=$with_grass requested, but libraries not found!" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+fi
+
+GRASS_INCLUDE=$GRASS_INCLUDE
+
+GRASS_GISBASE=$GRASS_GISBASE
+
+
+
+rm -f conftest*
+
+          ac_config_files="$ac_config_files Makefile"
+
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[	 ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[	 ]*$//;
+}'
+fi
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then we branch to the quote section.  Otherwise,
+# look for a macro that doesn't take arguments.
+cat >confdef2opt.sed <<\_ACEOF
+t clear
+: clear
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 (][^	 (]*([^)]*)\)[	 ]*\(.*\),-D\1=\2,g
+t quote
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 ][^	 ]*\)[	 ]*\(.*\),-D\1=\2,g
+t quote
+d
+: quote
+s,[	 `~#$^&*(){}\\|;'"<>?],\\&,g
+s,\[,\\&,g
+s,\],\\&,g
+s,\$,$$,g
+p
+_ACEOF
+# We use echo to avoid assuming a particular line-breaking character.
+# The extra dot is to prevent the shell from consuming trailing
+# line-breaks from the sub-command output.  A line-break within
+# single-quotes doesn't work because, if this script is created in a
+# platform that uses two characters for line-breaks (e.g., DOS), tr
+# would break.
+ac_LF_and_DOT=`echo; echo .`
+DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'`
+rm -f confdef2opt.sed
+
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_i=`echo "$ac_i" |
+	 sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+  # 2. Add them.
+  ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.  Logging --version etc. is OK.
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+  echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+  echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+  echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+  echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number, then exit
+  -q, --quiet      do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+  --file=FILE[:TEMPLATE]
+		   instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.59,
+  with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value.  By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "x$1" : 'x\([^=]*\)='`
+    ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  -*)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  *) # This is not an option, so the user has probably given explicit
+     # arguments.
+     ac_option=$1
+     ac_need_defaults=false;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --vers* | -V )
+    echo "$ac_cs_version"; exit 0 ;;
+  --he | --h)
+    # Conflict between --help and --header
+    { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    echo "$ac_cs_usage"; exit 0 ;;
+  --debug | --d* | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+    ac_need_defaults=false;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1" ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+  echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+  exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+  case "$ac_config_target" in
+  # Handling of arguments.
+  "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+  *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./confstat$$-$RANDOM
+  (umask 077 && mkdir $tmp)
+} ||
+{
+   echo "$me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+  # Protect against being on the right side of a sed subst in config.status.
+  sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+   s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@CXX@,$CXX,;t t
+s,@CXXFLAGS@,$CXXFLAGS,;t t
+s,@ac_ct_CXX@,$ac_ct_CXX,;t t
+s,@RANLIB@,$RANLIB,;t t
+s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
+s,@CXX_PIC@,$CXX_PIC,;t t
+s,@C_PIC@,$C_PIC,;t t
+s,@LD_SHARED@,$LD_SHARED,;t t
+s,@SO_EXT@,$SO_EXT,;t t
+s,@CXX_WFLAGS@,$CXX_WFLAGS,;t t
+s,@C_WFLAGS@,$C_WFLAGS,;t t
+s,@GDAL_CONFIG@,$GDAL_CONFIG,;t t
+s,@GDAL_INC@,$GDAL_INC,;t t
+s,@AUTOLOAD_DIR@,$AUTOLOAD_DIR,;t t
+s,@GRASS_INCLUDE@,$GRASS_INCLUDE,;t t
+s,@GRASS_GISBASE@,$GRASS_GISBASE,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+  cat >>$CONFIG_STATUS <<\_ACEOF
+  # Split the substitutions into bite-sized pieces for seds with
+  # small command number limits, like on Digital OSF/1 and HP-UX.
+  ac_max_sed_lines=48
+  ac_sed_frag=1 # Number of current file.
+  ac_beg=1 # First line for current file.
+  ac_end=$ac_max_sed_lines # Line after last line for current file.
+  ac_more_lines=:
+  ac_sed_cmds=
+  while $ac_more_lines; do
+    if test $ac_beg -gt 1; then
+      sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    else
+      sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    fi
+    if test ! -s $tmp/subs.frag; then
+      ac_more_lines=false
+    else
+      # The purpose of the label and of the branching condition is to
+      # speed up the sed processing (if there are no `@' at all, there
+      # is no need to browse any of the substitutions).
+      # These are the two extra sed commands mentioned above.
+      (echo ':t
+  /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+      if test -z "$ac_sed_cmds"; then
+	ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+      else
+	ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+      fi
+      ac_sed_frag=`expr $ac_sed_frag + 1`
+      ac_beg=$ac_end
+      ac_end=`expr $ac_end + $ac_max_sed_lines`
+    fi
+  done
+  if test -z "$ac_sed_cmds"; then
+    ac_sed_cmds=cat
+  fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+  ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+
+  if test x"$ac_file" != x-; then
+    { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+    rm -f "$ac_file"
+  fi
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    configure_input=
+  else
+    configure_input="$ac_file.  "
+  fi
+  configure_input=$configure_input"Generated from `echo $ac_file_in |
+				     sed 's,.*/,,'` by configure."
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+  sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+  rm -f $tmp/stdin
+  if test x"$ac_file" != x-; then
+    mv $tmp/out $ac_file
+  else
+    cat $tmp/out
+    rm -f $tmp/out
+  fi
+
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+
+
+
+
diff --git a/Utilities/GDAL/frmts/grass/pkg/configure.in b/Utilities/GDAL/frmts/grass/pkg/configure.in
new file mode 100644
index 0000000000..70702d4c84
--- /dev/null
+++ b/Utilities/GDAL/frmts/grass/pkg/configure.in
@@ -0,0 +1,147 @@
+dnl ***************************************************************************
+dnl $Id: configure.in,v 1.8 2006/04/04 02:54:27 fwarmerdam Exp $
+dnl
+dnl Project:  GDAL GRASS Plugin
+dnl Purpose:  Configure source file.
+dnl Author:   Frank Warmerdam, warmerdam@pobox.com
+dnl
+dnl ***************************************************************************
+dnl Copyright (c) 2005, Frank Warmerdam
+dnl
+dnl Permission is hereby granted, free of charge, to any person obtaining a
+dnl copy of this software and associated documentation files (the "Software"),
+dnl to deal in the Software without restriction, including without limitation
+dnl the rights to use, copy, modify, merge, publish, distribute, sublicense,
+dnl and/or sell copies of the Software, and to permit persons to whom the
+dnl Software is furnished to do so, subject to the following conditions:
+dnl
+dnl The above copyright notice and this permission notice shall be included
+dnl in all copies or substantial portions of the Software.
+dnl
+dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+dnl OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+dnl FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+dnl THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+dnl LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+dnl FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+dnl DEALINGS IN THE SOFTWARE.
+dnl ***************************************************************************
+
+dnl Disable configure caching ... it causes lots of hassles.
+define([AC_CACHE_LOAD], )
+define([AC_CACHE_SAVE], )
+
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(Makefile.in)
+
+dnl We require autoconf 2.52+ for libtool support on cygwin/mingw hosts
+AC_PREREQ(2.52)
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_CXX
+
+AC_PROG_RANLIB
+AC_COMPILER_PIC
+AC_LD_SHARED
+AC_COMPILER_WFLAGS
+
+dnl ---------------------------------------------------------------------------
+dnl Find GDAL
+dnl ---------------------------------------------------------------------------
+
+AC_ARG_WITH(gdal,
+[  --with-gdal[=PATH]        GDAL (PATH is path to gdal-config)],,)
+
+if test "$with_gdal" = "yes" -o "$with_gdal" = "" ; then
+
+  if test "`basename xx/$with_gdal`" = "gdal-config" ; then
+    GDAL_CONFIG="$with_gdal"
+  fi
+
+  if test -z "$GDAL_CONFIG" ; then
+    AC_PATH_PROG(GDAL_CONFIG, gdal-config, no)
+  fi
+
+  if test "$GDAL_CONFIG" = "no" ; then
+    AC_MSG_ERROR([couldn't find gdal-config])
+  fi
+
+elif test -n "$with_gdal" -a "$with_gdal" != "no" ; then
+
+  GDAL_CONFIG=$with_gdal
+
+  if test -f "$GDAL_CONFIG" -a -x "$GDAL_CONFIG" ; then
+    AC_MSG_RESULT([user supplied gdal-config ($GDAL_CONFIG)])
+  else
+    AC_MSG_ERROR(['$GDAL_CONFIG' is not an executable.  Make sure you use --with-gdal=/path/to/gdal-config])
+  fi
+
+else
+
+  AC_MSG_ERROR([gdal required to build GDAL GRASS 5.7 driver])
+
+fi
+
+LIBS="`$GDAL_CONFIG --libs` $LIBS"
+GDAL_INC=`$GDAL_CONFIG --cflags`
+
+AC_SUBST(GDAL_INC,    $GDAL_INC)
+
+dnl ---------------------------------------------------------------------------
+dnl Where to put driver?
+dnl ---------------------------------------------------------------------------
+AC_ARG_WITH(autoload,[  --with-autoload[=DIR]      Directory for autoload drivers],,)
+
+if test "$with_autoload" != "" ; then
+  AUTOLOAD_DIR=$with_autoload
+else
+  if $GDAL_CONFIG --autoload > /dev/null 2>&1 ; then
+    AUTOLOAD_DIR=`$GDAL_CONFIG --autoload`
+  else
+    AUTOLOAD_DIR=`$GDAL_CONFIG --prefix`/lib/gdalplugins
+  fi
+fi
+
+AC_MSG_RESULT(using $AUTOLOAD_DIR as GDAL shared library autoload directory)
+AC_SUBST(AUTOLOAD_DIR,$AUTOLOAD_DIR)
+
+dnl ---------------------------------------------------------------------------
+dnl Find GRASS 5.7
+dnl ---------------------------------------------------------------------------
+
+GRASS_SETTING=no
+GRASS_INCLUDE=
+GRASS_GISBASE=
+export GRASS_INCLUDE GRASS_SETTING GRASS_GISBASE
+
+AC_ARG_WITH(grass,[  --with-grass[=ARG]        Include GRASS support (ARG=GRASS install tree dir)],,)
+
+if test "$with_grass" = "no" ; then
+  AC_MSG_ERROR([grass required for this driver, please install GRASS 5.7 and rebuild])
+fi
+
+if test "$with_grass" != "yes" ; then
+
+  AC_CHECK_LIB(grass_gis,G_asprintf,GRASS_SETTING=grass57+,GRASS_SETTING=no,-L$with_grass/lib -lgrass_datetime -lgrass_gproj -lgrass_vect)
+   
+  if test "$GRASS_SETTING" = "grass57+" ; then   
+    LIBS="-L$with_grass/lib -lgrass_I -lgrass_vask -lgrass_gmath -lgrass_gis -lgrass_datetime -lgrass_gproj -lgrass_vect -lgrass_dbmibase -lgrass_dbmiclient $LIBS"
+    GRASS_INCLUDE="-I$with_grass/include"
+    GRASS_GISBASE="$with_grass"
+  else
+    AC_MSG_ERROR([--with-grass=$with_grass requested, but libraries not found!])
+  fi
+fi
+
+AC_SUBST(GRASS_INCLUDE,$GRASS_INCLUDE)
+AC_SUBST(GRASS_GISBASE,$GRASS_GISBASE)
+
+dnl ---------------------------------------------------------------------------
+
+rm -f conftest*
+
+AC_OUTPUT(Makefile)
+
+
+
diff --git a/Utilities/GDAL/frmts/gtiff/GNUmakefile b/Utilities/GDAL/frmts/gtiff/GNUmakefile
new file mode 100644
index 0000000000..d8a0a6a282
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/GNUmakefile
@@ -0,0 +1,48 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	geotiff.o gt_wkt_srs.o gt_overview.o \
+		tif_ovrcache.o tif_overview.o tif_float.o tifvsi.o
+
+TIFF_OPTS	=	
+SUBLIBS 	=
+
+ifeq ($(TIFF_SETTING),internal)
+SUBLIBS	:= lib-tiff $(SUBLIBS) 
+TIFF_OPTS	:=	-Ilibtiff $(TIFF_OPTS)
+endif
+
+ifeq ($(GEOTIFF_SETTING),internal)
+SUBLIBS	:= lib-geotiff $(SUBLIBS) 
+TIFF_OPTS	:=	-Ilibgeotiff $(TIFF_OPTS)
+endif
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(TIFF_OPTS) $(CPPFLAGS)
+
+default:	$(OBJ) $(SUBLIBS)
+
+clean:
+	rm -f *.o $(O_OBJ)
+	(cd libtiff; $(MAKE) clean)
+	(cd libgeotiff; $(MAKE) clean)
+
+gt_test:	gt_test.o gt_gs.o cpl_csv.o
+	$(CC) gt_test.o gt_gs.o cpl_csv.o ../../port/*.o \
+		libgeotiff/libgeotiff.a libtiff/libtiff.a $(LIBS) -o gt_test
+
+gt_write:	gt_write.o gt_gs.o cpl_csv.o
+	$(CC) gt_write.o gt_gs.o cpl_csv.o ../../port/*.o \
+		libgeotiff/libgeotiff.a libtiff/libtiff.a $(LIBS) -o gt_write
+
+epsg_to_wkt:	epsg_to_wkt.o gt_wkt_srs.o 
+	$(CXX) epsg_to_wkt.o gt_wkt_srs.o ../../port/*.o \
+	    libgeotiff/libgeotiff.a libtiff/libtiff.a \
+	    $(GDAL_LIB) $(LIBS) -o epsg_to_wkt
+
+lib-tiff:
+	(cd libtiff; $(MAKE) install-obj)
+
+lib-geotiff:
+	(cd libgeotiff; $(MAKE) install-obj)
+
+install-obj:	$(SUBLIBS) $(O_OBJ)
diff --git a/Utilities/GDAL/frmts/gtiff/epsg_to_wkt.cpp b/Utilities/GDAL/frmts/gtiff/epsg_to_wkt.cpp
new file mode 100644
index 0000000000..d0edec2357
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/epsg_to_wkt.cpp
@@ -0,0 +1,354 @@
+/******************************************************************************
+ * $Id: epsg_to_wkt.cpp,v 1.4 2006/04/05 00:06:14 fwarmerdam Exp $
+ *
+ * Project:  EPSG To WKT Translator
+ * Purpose:  Mainline for EPSG to WKT Translator
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: epsg_to_wkt.cpp,v $
+ * Revision 1.4  2006/04/05 00:06:14  fwarmerdam
+ * fix contact info
+ *
+ * Revision 1.3  2001/07/18 04:51:56  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.2  1999/09/10 13:42:30  warmerda
+ * converted to adams new format, added comparitors
+ *
+ * Revision 1.1  1999/09/09 13:55:43  warmerda
+ * New
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "tiffio.h"
+#include "geotiffio.h"
+#include "xtiffio.h"
+#include "geo_normalize.h"
+#include "cpl_csv.h"
+#include "ogr_spatialref.h"
+
+CPL_CVSID("$Id: epsg_to_wkt.cpp,v 1.4 2006/04/05 00:06:14 fwarmerdam Exp $");
+
+static char *PCSToOGISDefn( int, int );
+static void ProcessAllPCSCodes();
+static void EmitWKTString( const char *, FILE * );
+static void WKTComparitor( const char *, const char * );
+
+CPL_C_START
+char *  GTIFGetOGISDefn( GTIFDefn * );
+CPL_C_END
+
+/************************************************************************/
+/*                                main()                                */
+/************************************************************************/
+
+int main( int argc, char ** argv )
+
+{
+    if( argc == 3 )
+    {
+        WKTComparitor( argv[1], argv[2] );
+    }
+    else
+        ProcessAllPCSCodes();
+    
+    exit( 0 );
+}
+
+/************************************************************************/
+/*                         ProcessAllPCSCodes()                         */
+/************************************************************************/
+
+static void ProcessAllPCSCodes()
+
+{
+    FILE	*fp;
+    const char  *pszFilename = CSVFilename( "horiz_cs.csv" );
+
+/* -------------------------------------------------------------------- */
+/*      Get access to the table.                                        */
+/* -------------------------------------------------------------------- */
+    fp = VSIFOpen( pszFilename, "rt" );
+    if( fp == NULL )
+        return;
+
+/* -------------------------------------------------------------------- */
+/*      Scan the file from the beginning, replacing the ``current       */
+/*      record'' in our structure with the one that is found.           */
+/* -------------------------------------------------------------------- */
+    VSIRewind( fp );
+    CPLReadLine( fp );		/* throw away the header line */
+    
+    while( TRUE )
+    {
+        int		nPCS;
+        char		**papszFields;
+        
+        papszFields = CSVReadParseLine( fp );
+        if( papszFields == NULL || papszFields[0] == NULL )
+            break;
+
+        nPCS = atoi(papszFields[0]);
+        CSLDestroy( papszFields );
+
+        char	*pszWKT;
+
+        pszWKT = PCSToOGISDefn( nPCS, nPCS > 3999 && nPCS < 5000 );
+
+        if( pszWKT != NULL )
+        {
+            printf( "EPSG=%d\n", nPCS );
+            EmitWKTString( pszWKT, stdout );
+            CPLFree( pszWKT );
+        }
+    }
+
+    VSIFClose( fp );
+}
+
+/************************************************************************/
+/*                           EmitWKTString()                            */
+/************************************************************************/
+
+static void EmitWKTString( const char * pszWKT, FILE * fpOut )
+
+{
+    int		i;
+
+    for( i = 0; pszWKT[i] != '\0'; i++ )
+    {
+        fputc( pszWKT[i], fpOut );
+//        if( pszWKT[i] == ',' && pszWKT[i+1] >= 'A' && pszWKT[i+1] <= 'Z' )
+//            fputc('\n', fpOut );
+    }
+    fputc('\n', fpOut );
+}
+
+/************************************************************************/
+/*                           PCSToOGISDefn()                            */
+/*                                                                      */
+/*      In order to reuse all the logic in GTIFGetDefn(), we            */
+/*      actually write out a tiny TIFF file with the PCS set.           */
+/************************************************************************/
+
+static char *PCSToOGISDefn( int nCode, int bIsGCS )
+
+{
+    TIFF	*hTIFF;
+    GTIF	*hGTIF;
+
+/* -------------------------------------------------------------------- */
+/*      Write a tiny GeoTIFF file with the PCS code.                    */
+/* -------------------------------------------------------------------- */
+    hTIFF = XTIFFOpen( "temp.tif", "w+" );
+
+    
+    TIFFSetField( hTIFF, TIFFTAG_IMAGEWIDTH, 2 );
+    TIFFSetField( hTIFF, TIFFTAG_IMAGELENGTH, 2 );
+    TIFFSetField( hTIFF, TIFFTAG_BITSPERSAMPLE, 8 );
+    TIFFSetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, 1 );
+    TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG );
+    TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK );
+    TIFFSetField( hTIFF, TIFFTAG_ROWSPERSTRIP, 2 );
+
+    hGTIF = GTIFNew( hTIFF );
+
+    if( bIsGCS )
+    {
+        GTIFKeySet( hGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                    ModelTypeGeographic );
+        GTIFKeySet( hGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1, nCode );
+    }
+    else
+    {
+        GTIFKeySet( hGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                    ModelTypeProjected );
+        GTIFKeySet( hGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nCode );
+    }
+    
+    GTIFWriteKeys( hGTIF );
+    GTIFFree( hGTIF );
+
+    TIFFWriteEncodedStrip( hTIFF, 0, "    ", 4 );
+    
+    XTIFFClose( hTIFF );
+
+/* -------------------------------------------------------------------- */
+/*      Read a GeoTIFF definition from this file.                       */
+/* -------------------------------------------------------------------- */
+    GTIFDefn	sGeoTIFFProj;
+    int		bSuccess;
+    
+    hTIFF = XTIFFOpen( "temp.tif", "r" );
+
+    hGTIF = GTIFNew( hTIFF );
+    bSuccess = GTIFGetDefn( hGTIF, &sGeoTIFFProj );
+    GTIFFree( hGTIF );
+
+    XTIFFClose( hTIFF );
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup the file.                                               */
+/* -------------------------------------------------------------------- */
+    unlink( "temp.tif" );
+
+    if( bSuccess && sGeoTIFFProj.GCS != KvUserDefined )
+        return( GTIFGetOGISDefn( &sGeoTIFFProj ) );
+    else
+        return NULL;
+}
+
+/************************************************************************/
+/*                              ReadWKT()                               */
+/*                                                                      */
+/*      Read EPSG code, and WKT from input file.                        */
+/************************************************************************/
+
+static OGRSpatialReference *ReadWKT( FILE * fp, int *pnEPSG )
+
+{
+    const char	*pszLine;
+
+    pszLine = CPLReadLine( fp );
+    if( pszLine == NULL || !EQUALN(pszLine,"EPSG=",5) )
+        return NULL;
+
+    *pnEPSG = atoi(pszLine+5);
+
+    pszLine = CPLReadLine( fp );
+    if( pszLine == NULL )
+        return NULL;
+    else
+        return new OGRSpatialReference( pszLine );
+}
+
+/************************************************************************/
+/*                           WKTCompareItem()                           */
+/************************************************************************/
+
+static int WKTCompareItem( const char * pszFile1,
+                           OGRSpatialReference * poSRS1,
+                           const char * pszFile2,
+                           OGRSpatialReference * poSRS2,
+                           const char * pszTargetItem, int nEPSG )
+
+{
+    if( poSRS1->GetAttrValue(pszTargetItem) == NULL
+        && poSRS1->GetAttrValue(pszTargetItem) == NULL )
+        return TRUE;
+    
+    if( poSRS1->GetAttrValue(pszTargetItem) == NULL )
+    {
+        printf( "File %s missing %s for EPSG %d\n",
+                pszFile1, pszTargetItem, nEPSG );
+        return FALSE;
+    }
+
+    if( poSRS2->GetAttrValue(pszTargetItem) == NULL )
+    {
+        printf( "File %s missing %s for EPSG %d\n",
+                pszFile1, pszTargetItem, nEPSG );
+        return FALSE;
+    }
+
+    if( !EQUAL(poSRS1->GetAttrValue(pszTargetItem),
+               poSRS2->GetAttrValue(pszTargetItem)) )
+    {
+        printf( "Datum mismatch for EPSG %d:\n"
+                "%s: %s\n"
+                "%s: %s\n",
+                nEPSG,
+                pszFile1, poSRS1->GetAttrValue(pszTargetItem),
+                pszFile2, poSRS2->GetAttrValue(pszTargetItem) );
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                           WKTComparitor()                            */
+/************************************************************************/
+
+static void WKTComparitor( const char * pszFile1, const char * pszFile2 )
+
+{
+    FILE	*fp1, *fp2;
+
+/* -------------------------------------------------------------------- */
+/*      Open files                                                      */
+/* -------------------------------------------------------------------- */
+    fp1 = VSIFOpen( pszFile1, "rt" );
+    if( fp1 == NULL )
+    {
+        fprintf( stderr, "Could not open %s.\n", pszFile1 );
+        return;
+    }
+
+    fp2 = VSIFOpen( pszFile2, "rt" );
+    if( fp2 == NULL )
+    {
+        fprintf( stderr, "Could not open %s.\n", pszFile2 );
+        return;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read a code from each file.                                     */
+/* -------------------------------------------------------------------- */
+    OGRSpatialReference *poSRS1, *poSRS2;
+    int			nEPSG1, nEPSG2;
+    
+    while( (poSRS1 = ReadWKT(fp1,&nEPSG1)) != NULL )
+    {
+        poSRS2 = ReadWKT(fp2,&nEPSG2);
+
+        if( nEPSG1 != nEPSG2 )
+        {
+            printf( "EPSG codes (%d, %d) don't match, bailing out.\n",
+                    nEPSG1, nEPSG2 );
+            break;
+        }
+
+        WKTCompareItem( pszFile1, poSRS1, pszFile2, poSRS2,
+                        "DATUM", nEPSG1 );
+        
+        WKTCompareItem( pszFile1, poSRS1, pszFile2, poSRS2,
+                        "PROJECTION", nEPSG1 );
+        
+        delete poSRS1;
+        delete poSRS2;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    VSIFClose( fp2 );
+    VSIFClose( fp1 );
+}
+
+
diff --git a/Utilities/GDAL/frmts/gtiff/frmt_gtiff.html b/Utilities/GDAL/frmts/gtiff/frmt_gtiff.html
new file mode 100644
index 0000000000..b3cb3505e4
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/frmt_gtiff.html
@@ -0,0 +1,113 @@
+<html>
+<head>
+<title>GTiff -- GeoTIFF File Format</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>GTiff -- GeoTIFF File Format</h1>
+
+Most forms of TIFF and GeoTIFF files are supported by GDAL for reading, and
+somewhat less varieties can be written.<p>
+
+Currently band types of Byte, UInt16, Int16, UInt32, Int32, Float32, Float64, 
+CInt16, CInt32, CFloat32 and CFloat64 are supported for
+reading and writing.  
+Paletted images will return palette information associated with
+the band.  The compression formats listed below should be supported for
+reading as well. <p>
+
+As well, one bit files, and some other unusual formulations of GeoTIFF file,
+such as YCbCr color model files, are automatically translated into RGBA
+(red, green, blue, alpha) form, and treated as four eight bit bands. <p>
+
+<h2>Georeferencing</h2>
+
+Most GeoTIFF projections should be supported, with the caveat that in order
+to translate uncommon Projected, and Geographic coordinate systems into 
+OGC WKT it is necessary to have the EPSG .csv files avalable.  They must
+be found at the location pointed to by the GEOTIFF_CSV environment variable.
+<p>
+
+Georeferencing from GeoTIFF is supported in the form of one tiepoint and
+pixel size, a transformation matrix, or a list of GCPs.<p>
+
+If no georeferencing
+information is available in the TIFF file itself, GDAL will also check for,
+and use an ESRI <a href="frmt_various.html#WLD">world file</a> with the
+extention .tfw, .tiffw or .wld, as well as a MapInfo .tab file (only control
+points used, Coordsys ignored).<P>
+
+<h2>Creation Issues</h2>
+
+GeoTIFF files can be created with any GDAL defined band type, including
+the complex types.  Created files may have any number of bands.  Files 
+with exactly 3 bands will be
+given a photometric interpretation of RGB, files with exactly four bands
+will have a photometric interpretation of RGBA, while all other combinations 
+will have a photometric interpretation of MIN_IS_WHITE.  Files with
+pseudo-color tables, or GCPs can currently only be created when creating from 
+an existing GDAL dataset with those objects (GDALDriver::CreateCopy()).<p>
+
+Creation Options:<p>
+
+<ul>
+
+<li> <b>TFW=YES</b>: Force the generation of an associated ESRI world
+file (.tfw).See a <a href="frmt_various.html#WLD">World Files</a> section
+for details.<p>
+
+<li> <b>INTERLEAVE=[BAND,PIXEL]</b>: By default TIFF files with band
+interleaving (PLANARCONFIG_SEPARATE in TIFF terminology) are created.  These
+are slightly more efficient for some purposes, but some applications only 
+support pixel interleaved TIFF files.  In these cases pass INTERLEAVE=PIXEL
+to produce pixel interleaved TIFF files (PLANARCONFIG_CONTIG in TIFF
+terminology).<p>
+
+<li> <b>TILED=YES</b>: By default stripped TIFF files are created.  This
+option can be used to force creation of tiled TIFF files.<p>
+
+<li> <b>BLOCKXSIZE=n</b>: Sets tile width, defaults to 256.<p>
+
+<li> <b>BLOCKYSIZE=n</b>: Set tile or strip height.  Tile height defaults to
+256, strip height defaults to a value such that one strip is 8K or less. <p>
+
+<li> <b>COMPRESS=[JPEG/LZW/PACKBITS/DEFLATE/NONE]</b>: Set the compression
+to use.  JPEG should only be used with Byte data.  LZW is normally 
+unavailable.  None is the default.<p>
+
+<li> <b>PREDICTOR=[0/1/2]</b>: Set the predictory for LZW or DEFLATE compression. The default is 0 (no predictory), 1 is horizontal differencing and 2 is floating point predictory.<p>
+
+<li> <b>JPEG_QUALITY=[1-100]</b>:  Set the JPEG quality when using JPEG compression.  A value of 100 is best quality (least compression), and 1 is worst quality (best compression).  The default is 75.<p>
+
+<li> <b>PROFILE=[GDALGeoTIFF/GeoTIFF/BASELINE]</b>: Control what non-baseline
+tags are emitted by GDAL.  With <tt>GDALGeoTIFF</tt> (the default) various 
+GDAL custom tags may be written.  With <tt>GeoTIFF</tt> only GeoTIFF tags
+will be added to the baseline.  With <tt>BASELINE</tt> no GDAL or GeoTIFF
+tags will be written.  BASELINE is occationally useful when writing files to
+be read by applications intolerant of unrecognised tags.<p>
+
+<li>
+<b>PHOTOMETRIC=[MINISBLACK/MINISWHITE/RGB/CMYK/YCBCR/CIELAB/ICCLAB/ITULAB]</b>: 
+Set the photometric interpretation tag. Default is MINISBLACK, but if the
+input image has 3 or 4 bands of Byte type, then RGB will be selected. You can
+override default photometric using this option.<p>
+
+<li> <b>ALPHA=YES</b>: The first "extrasample" is marked as being alpha if
+there are any extra samples.  This is necessary if you want to produce
+a greyscale TIFF file with an alpha band (for instance). <p>
+
+</ul>
+
+GeoTIFF supports internal creation of overviews (with gdaladdo for instance).
+
+See Also:<p>
+
+<ul>
+<li> <a href="http://www.remotesensing.org/geotiff/geotiff.html">GeoTIFF Information Page</a>
+<li> <a href="http://www.remotesensing.org/libtiff/">libtiff Page</a>
+
+</ul>
+
+</body>
+</html>
diff --git a/Utilities/GDAL/frmts/gtiff/geotiff.cpp b/Utilities/GDAL/frmts/gtiff/geotiff.cpp
new file mode 100644
index 0000000000..cad35fabb2
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/geotiff.cpp
@@ -0,0 +1,4752 @@
+/******************************************************************************
+ * $Id: geotiff.cpp,v 1.163 2006/04/13 16:38:16 fwarmerdam Exp $
+ *
+ * Project:  GeoTIFF Driver
+ * Purpose:  GDAL GeoTIFF support.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1998, 2002, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: geotiff.cpp,v $
+ * Revision 1.163  2006/04/13 16:38:16  fwarmerdam
+ * Added USE_RRD support.
+ *
+ * Revision 1.162  2006/04/10 15:52:23  fwarmerdam
+ * Fixed min-is-white color table for less than 8bit data.
+ *
+ * Revision 1.161  2006/04/05 21:08:16  fwarmerdam
+ * Added NBITS metadata on bands.
+ * Apply PAM projection and geotransform information (if available) in
+ * preference to what is available in the GeoTIFF file.
+ *
+ * Revision 1.160  2006/04/05 04:16:44  fwarmerdam
+ * Report error on an attempt to create an uncompressed TIFF file larger
+ * than 4GB.
+ *
+ * Revision 1.159  2006/04/03 02:05:12  fwarmerdam
+ * Pass GeoTransform and projection requests on to GDALPamDataset if
+ * we don't have information from the TIFF file.
+ *
+ * Revision 1.158  2006/03/30 16:49:09  fwarmerdam
+ * Reorder setfield calls to put jpeg stuff pretty late.  This helps resolve
+ * problems producing ycbcr jpeg files.
+ * Also added support for unpacking YCbCr subsampled data in IReadBlock(),
+ * and broader support for the CONVERT_YCBCR_TO_RGB config option.
+ *
+ * Revision 1.157  2006/02/17 15:46:06  fwarmerdam
+ * Ensure that an image is considered paletted if it has a palette,
+ * even if the photometric interpretation is wrong.
+ *
+ * Revision 1.156  2006/02/12 23:52:53  fwarmerdam
+ * Allow one or two GCPs if that is all the tiepoints there are.
+ *
+ * Revision 1.155  2005/12/21 00:36:59  fwarmerdam
+ * Added compression image_structure metadata.
+ *
+ * Revision 1.154  2005/10/28 03:40:19  fwarmerdam
+ * Fixed problems with odd-bits support of contiguous images.
+ *
+ * Revision 1.153  2005/10/25 18:48:22  fwarmerdam
+ * make sure we handle missing blocks on read: bug 975
+ *
+ * Revision 1.152  2005/10/24 18:13:34  fwarmerdam
+ * Fixed memory leak of pszProjection string.
+ *
+ * Revision 1.151  2005/10/18 13:43:53  fwarmerdam
+ * Default to PLANAR_CONTIG for PROFILE=GeoTIFF too.
+ *
+ * Revision 1.150  2005/10/18 13:42:49  fwarmerdam
+ * Force INTERLEAVE=PIXEL if PROFILE=BASELINE.
+ *
+ * Revision 1.149  2005/10/15 22:14:36  fwarmerdam
+ * GTiffDataset::FlushBlockBuf() fixed to call SetDirectory().  Otherwise
+ * sometimes the block would not be flushed out "against" the the right
+ * overview.
+ *
+ * Revision 1.148  2005/10/13 01:20:48  fwarmerdam
+ * restructured to use gdalmajorobject multidomain metadata
+ *
+ * Revision 1.147  2005/09/25 18:04:40  fwarmerdam
+ * added JPEG_QUALITY option
+ *
+ * Revision 1.146  2005/09/20 18:16:53  fwarmerdam
+ * enable support for NONE compress option
+ *
+ * Revision 1.145  2005/09/12 00:29:00  fwarmerdam
+ * use VSI_TIFFOpen() instead of XTIFFOpen to get arbitrary redirection
+ *
+ * Revision 1.144  2005/08/11 17:27:05  fwarmerdam
+ * fixed so that PAM copyinfo does not override profile option
+ *
+ * Revision 1.143  2005/07/26 17:53:33  fwarmerdam
+ * Added error check.
+ *
+ * Revision 1.142  2005/07/26 15:14:00  fwarmerdam
+ * Added check on geotiff size.
+ *
+ * Revision 1.141  2005/07/07 13:26:48  fwarmerdam
+ * Fixed colormap conversion to 16bit in CreateCopy() per report from
+ * Uwe Schmitz on gdal-dev.
+ *
+ * Revision 1.140  2005/05/22 21:00:44  fwarmerdam
+ * metadata support rewritten to support multiple domains
+ *
+ * Revision 1.139  2005/05/22 16:58:12  dron
+ * Added PlanarConfiguration parameter to the TIFF_WriteOverview().
+ *
+ * Revision 1.138  2005/04/23 16:38:43  fwarmerdam
+ * Handle more integer datatypes (ie 2-7 bits, 17+ bits) via
+ * OddBits class.
+ *
+ * Revision 1.137  2005/04/15 18:45:29  fwarmerdam
+ * added area or point metadata
+ *
+ * Revision 1.136  2005/04/15 18:24:24  dron
+ * Added "PREDICTOR" option (works with LZW and ZIP compression types).
+ *
+ * Revision 1.135  2005/04/13 15:32:20  dron
+ * Added support for 16- and 24-bit floating point TIFFs (as per TechNote 3).
+ *
+ * Revision 1.134  2005/03/22 19:40:59  fwarmerdam
+ * Added support for forcing RGB color mode with YCBCR files and JPEG
+ * compression.  Added special (fast) case for 12bit to 16bit conversion
+ * in the OddBits raster band class.  Fixed colorinterp reporting for
+ * YCbCr files forced to RGB.
+ *
+ * Revision 1.133  2005/03/22 05:12:15  fwarmerdam
+ * first crack at support 9-15 bit depths
+ *
+ * Revision 1.132  2005/03/07 14:23:49  fwarmerdam
+ * Added PROFILE support in CreateCopy().
+ *
+ * Revision 1.131  2005/02/15 16:08:34  fwarmerdam
+ * dont put out scale and offset if defaulted
+ *
+ * Revision 1.130  2005/02/10 04:31:11  fwarmerdam
+ * added option to keep YCbCr in raw form
+ *
+ * Revision 1.129  2005/01/15 16:15:09  fwarmerdam
+ * trimmed revision history.
+ *
+ * Revision 1.128  2005/01/15 16:13:17  fwarmerdam
+ * Use GTiffRasterBand as base for 1bit and RGB band specialized types.
+ * Generalized metadata support, include band metadata.
+ * Added support for offset/scale as special metadata items.
+ */
+
+#include "gdal_pam.h"
+#define CPL_SERV_H_INCLUDED
+
+#include "tiffio.h"
+#include "xtiffio.h"
+#include "geotiff.h"
+#include "geo_normalize.h"
+#include "geovalues.h"
+#include "tif_ovrcache.h"
+#include "cpl_string.h"
+#include "cpl_csv.h"
+#include "cpl_minixml.h"
+
+CPL_CVSID("$Id: geotiff.cpp,v 1.163 2006/04/13 16:38:16 fwarmerdam Exp $");
+
+CPL_C_START
+char CPL_DLL *  GTIFGetOGISDefn( GTIF *, GTIFDefn * );
+int  CPL_DLL   GTIFSetFromOGISDefn( GTIF *, const char * );
+const char * GDALDefaultCSVFilename( const char *pszBasename );
+GUInt32 HalfToFloat( GUInt16 );
+GUInt32 TripleToFloat( GUInt32 );
+CPL_C_END
+
+static void GTiffOneTimeInit();
+#define TIFFTAG_GDAL_METADATA  42112
+#define TIFFTAG_GDAL_NODATA    42113
+
+TIFF* VSI_TIFFOpen(const char* name, const char* mode);
+
+/************************************************************************/
+/* ==================================================================== */
+/*				GTiffDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class GTiffRasterBand;
+class GTiffRGBABand;
+class GTiffBitmapBand;
+
+class GTiffDataset : public GDALPamDataset
+{
+    friend class GTiffRasterBand;
+    friend class GTiffRGBABand;
+    friend class GTiffBitmapBand;
+    friend class GTiffOddBitsBand;
+    
+    TIFF	*hTIFF;
+
+    uint32      nDirOffset;
+    int		bBase;
+
+    uint16	nPlanarConfig;
+    uint16	nSamplesPerPixel;
+    uint16	nBitsPerSample;
+    uint32	nRowsPerStrip;
+    uint16	nPhotometric;
+    uint16      nSampleFormat;
+    uint16      nCompression;
+    
+    int		nBlocksPerBand;
+
+    uint32      nBlockXSize;
+    uint32	nBlockYSize;
+
+    int		nLoadedBlock;		/* or tile */
+    int         bLoadedBlockDirty;  
+    GByte	*pabyBlockBuf;
+
+    CPLErr      LoadBlockBuf( int );
+    CPLErr      FlushBlockBuf();
+
+    char	*pszProjection;
+    double	adfGeoTransform[6];
+    int		bGeoTransformValid;
+
+    char	*pszTFWFilename;
+
+    int		bNewDataset;            /* product of Create() */
+    int         bTreatAsRGBA;
+    int         bCrystalized;
+
+    void	Crystalize();
+
+    GDALColorTable *poColorTable;
+
+    void	WriteGeoTIFFInfo();
+    int		SetDirectory( uint32 nDirOffset = 0 );
+    void        SetupTFW(const char *pszBasename);
+
+    int		nOverviewCount;
+    GTiffDataset **papoOverviewDS;
+
+    int		nGCPCount;
+    GDAL_GCP	*pasGCPList;
+
+    int         IsBlockAvailable( int nBlockId );
+
+    int         bGeoTIFFInfoChanged;
+    int         bNoDataSet;
+    int         bNoDataChanged;
+    double      dfNoDataValue;
+
+    int 	bMetadataChanged;
+
+    void        ApplyPamInfo();
+
+  public:
+                 GTiffDataset();
+                 ~GTiffDataset();
+
+    virtual const char *GetProjectionRef(void);
+    virtual CPLErr SetProjection( const char * );
+    virtual CPLErr GetGeoTransform( double * );
+    virtual CPLErr SetGeoTransform( double * );
+
+    virtual int    GetGCPCount();
+    virtual const char *GetGCPProjection();
+    virtual const GDAL_GCP *GetGCPs();
+    CPLErr         SetGCPs( int, const GDAL_GCP *, const char * );
+
+    virtual CPLErr IBuildOverviews( const char *, int, int *, int, int *, 
+                                    GDALProgressFunc, void * );
+
+    CPLErr	   OpenOffset( TIFF *, uint32 nDirOffset, int, GDALAccess );
+
+    static GDALDataset *OpenDir( const char *pszFilename );
+    static GDALDataset *Open( GDALOpenInfo * );
+    static GDALDataset *Create( const char * pszFilename,
+                                int nXSize, int nYSize, int nBands,
+                                GDALDataType eType, char ** papszParmList );
+    virtual void    FlushCache( void );
+
+    virtual CPLErr  SetMetadata( char **, const char * = "" );
+    virtual CPLErr  SetMetadataItem( const char*, const char*, 
+                                     const char* = "" );
+    virtual void   *GetInternalHandle( const char * );
+
+    // only needed by createcopy and close code.
+    static void	    WriteMetadata( GDALDataset *, TIFF *, int );
+    static void	    WriteNoDataValue( TIFF *, double );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            GTiffRasterBand                           */
+/* ==================================================================== */
+/************************************************************************/
+
+class GTiffRasterBand : public GDALPamRasterBand
+{
+    friend class GTiffDataset;
+
+    GDALColorInterp         eBandInterp;
+
+    int    bHaveOffsetScale;
+    double dfOffset;
+    double dfScale;
+
+  public:
+                   GTiffRasterBand( GTiffDataset *, int );
+
+    // should override RasterIO eventually.
+    
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual CPLErr IWriteBlock( int, int, void * ); 
+
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual GDALColorTable *GetColorTable();
+    virtual CPLErr          SetColorTable( GDALColorTable * );
+    virtual double	    GetNoDataValue( int * );
+    virtual CPLErr	    SetNoDataValue( double );
+
+    virtual double GetOffset( int *pbSuccess = NULL );
+    virtual CPLErr SetOffset( double dfNewValue );
+    virtual double GetScale( int *pbSuccess = NULL );
+    virtual CPLErr SetScale( double dfNewValue );
+
+    virtual CPLErr  SetMetadata( char **, const char * = "" );
+    virtual CPLErr  SetMetadataItem( const char*, const char*, 
+                                     const char* = "" );
+    virtual int    GetOverviewCount();
+    virtual GDALRasterBand *GetOverview( int );
+};
+
+/************************************************************************/
+/*                           GTiffRasterBand()                          */
+/************************************************************************/
+
+GTiffRasterBand::GTiffRasterBand( GTiffDataset *poDS, int nBand )
+
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+
+    bHaveOffsetScale = FALSE;
+    dfOffset = 0.0;
+    dfScale = 1.0;
+
+/* -------------------------------------------------------------------- */
+/*      Get the GDAL data type.                                         */
+/* -------------------------------------------------------------------- */
+    uint16		nSampleFormat = poDS->nSampleFormat;
+
+    eDataType = GDT_Unknown;
+
+    if( poDS->nBitsPerSample <= 8 )
+        eDataType = GDT_Byte;
+    else if( poDS->nBitsPerSample <= 16 )
+    {
+        if( nSampleFormat == SAMPLEFORMAT_INT )
+            eDataType = GDT_Int16;
+        else
+            eDataType = GDT_UInt16;
+    }
+    else if( poDS->nBitsPerSample == 32 )
+    {
+        if( nSampleFormat == SAMPLEFORMAT_COMPLEXINT )
+            eDataType = GDT_CInt16;
+        else if( nSampleFormat == SAMPLEFORMAT_IEEEFP )
+            eDataType = GDT_Float32;
+        else if( nSampleFormat == SAMPLEFORMAT_INT )
+            eDataType = GDT_Int32;
+        else
+            eDataType = GDT_UInt32;
+    }
+    else if( poDS->nBitsPerSample == 64 )
+    {
+        if( nSampleFormat == SAMPLEFORMAT_IEEEFP )
+            eDataType = GDT_Float64;
+        else if( nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP )
+            eDataType = GDT_CFloat32;
+        else if( nSampleFormat == SAMPLEFORMAT_COMPLEXINT )
+            eDataType = GDT_CInt32;
+    }
+    else if( poDS->nBitsPerSample == 128 )
+    {
+        if( nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP )
+            eDataType = GDT_CFloat64;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try to work out band color interpretation.                      */
+/* -------------------------------------------------------------------- */
+    if( poDS->poColorTable != NULL && nBand == 1 ) 
+        eBandInterp = GCI_PaletteIndex;
+    else if( poDS->nPhotometric == PHOTOMETRIC_RGB 
+             || (poDS->nPhotometric == PHOTOMETRIC_YCBCR 
+                 && poDS->nCompression == COMPRESSION_JPEG 
+                 && CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB",
+                                                       "YES") )) )
+    {
+        if( nBand == 1 )
+            eBandInterp = GCI_RedBand;
+        else if( nBand == 2 )
+            eBandInterp = GCI_GreenBand;
+        else if( nBand == 3 )
+            eBandInterp = GCI_BlueBand;
+        else
+        {
+            uint16 *v;
+            uint16 count = 0;
+
+            if( TIFFGetField( poDS->hTIFF, TIFFTAG_EXTRASAMPLES, &count, &v) )
+            {
+                if( nBand - 3 <= count && v[nBand-4] == EXTRASAMPLE_ASSOCALPHA )
+                    eBandInterp = GCI_AlphaBand;
+                else
+                    eBandInterp = GCI_Undefined;
+            }
+            else if( nBand == 4 )
+                eBandInterp = GCI_AlphaBand;
+            else
+                eBandInterp = GCI_Undefined;
+        }
+    }
+    else if( poDS->nPhotometric == PHOTOMETRIC_YCBCR )
+    {
+        if( nBand == 1 )
+            eBandInterp = GCI_YCbCr_YBand;
+        else if( nBand == 2 )
+            eBandInterp = GCI_YCbCr_CbBand;
+        else if( nBand == 3 )
+            eBandInterp = GCI_YCbCr_CrBand;
+        else
+        {
+            uint16 *v;
+            uint16 count = 0;
+
+            if( TIFFGetField( poDS->hTIFF, TIFFTAG_EXTRASAMPLES, &count, &v) )
+            {
+                if( nBand - 3 <= count && v[nBand-4] == EXTRASAMPLE_ASSOCALPHA )
+                    eBandInterp = GCI_AlphaBand;
+                else
+                    eBandInterp = GCI_Undefined;
+            }
+            else if( nBand == 4 )
+                eBandInterp = GCI_AlphaBand;
+            else
+                eBandInterp = GCI_Undefined;
+        }
+    }
+    else if( poDS->nPhotometric == PHOTOMETRIC_MINISBLACK && nBand == 1 )
+        eBandInterp = GCI_GrayIndex;
+    else
+    {
+        uint16 *v;
+        uint16 count = 0;
+
+        if( TIFFGetField( poDS->hTIFF, TIFFTAG_EXTRASAMPLES, &count, &v ) )
+        {
+            int nBaseSamples;
+            nBaseSamples = poDS->nSamplesPerPixel - count;
+
+            if( nBand > nBaseSamples 
+                && v[nBand-nBaseSamples-1] == EXTRASAMPLE_ASSOCALPHA )
+                eBandInterp = GCI_AlphaBand;
+            else
+                eBandInterp = GCI_Undefined;
+        }
+        else
+            eBandInterp = GCI_Undefined;
+    }
+
+/* -------------------------------------------------------------------- */
+/*	Establish block size for strip or tiles.			*/
+/* -------------------------------------------------------------------- */
+    nBlockXSize = poDS->nBlockXSize;
+    nBlockYSize = poDS->nBlockYSize;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr GTiffRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                    void * pImage )
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+    int			nBlockBufSize, nBlockId, nBlockIdBand0;
+    CPLErr		eErr = CE_None;
+
+    poGDS->SetDirectory();
+
+    if( TIFFIsTiled(poGDS->hTIFF) )
+        nBlockBufSize = TIFFTileSize( poGDS->hTIFF );
+    else
+    {
+        CPLAssert( nBlockXOff == 0 );
+        nBlockBufSize = TIFFStripSize( poGDS->hTIFF );
+    }
+
+    nBlockIdBand0 = nBlockXOff + nBlockYOff * nBlocksPerRow;
+    if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE )
+        nBlockId = nBlockIdBand0 + (nBand-1) * poGDS->nBlocksPerBand;
+    else
+        nBlockId = nBlockIdBand0;
+        
+/* -------------------------------------------------------------------- */
+/*      Handle the case of a strip or tile that doesn't exist yet.      */
+/*      Just set to zeros and return.                                   */
+/* -------------------------------------------------------------------- */
+    if( !poGDS->IsBlockAvailable(nBlockId) )
+    {
+        memset( pImage, 0,
+                nBlockXSize * nBlockYSize
+                * GDALGetDataTypeSize(eDataType) / 8 );
+        return CE_None;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Handle simple case (separate, onesampleperpixel)		*/
+/* -------------------------------------------------------------------- */
+    if( poGDS->nBands == 1
+        || poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE )
+    {
+        if( TIFFIsTiled( poGDS->hTIFF ) )
+        {
+            if( TIFFReadEncodedTile( poGDS->hTIFF, nBlockId, pImage,
+                                     nBlockBufSize ) == -1 )
+            {
+                memset( pImage, 0, nBlockBufSize );
+                CPLError( CE_Failure, CPLE_AppDefined,
+                          "TIFFReadEncodedTile() failed.\n" );
+                
+                eErr = CE_Failure;
+            }
+        }
+        else
+        {
+            if( TIFFReadEncodedStrip( poGDS->hTIFF, nBlockId, pImage,
+                                      nBlockBufSize ) == -1 )
+            {
+                memset( pImage, 0, nBlockBufSize );
+                CPLError( CE_Failure, CPLE_AppDefined,
+                          "TIFFReadEncodedStrip() failed.\n" );
+                
+                eErr = CE_Failure;
+            }
+        }
+
+        return eErr;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Load desired block                                              */
+/* -------------------------------------------------------------------- */
+    eErr = poGDS->LoadBlockBuf( nBlockId );
+    if( eErr != CE_None )
+        return eErr;
+
+/* -------------------------------------------------------------------- */
+/*      Special case for YCbCr subsampled data.                         */
+/* -------------------------------------------------------------------- */
+#ifdef notdef
+    if( (eBandInterp == GCI_YCbCr_YBand 
+         || eBandInterp == GCI_YCbCr_CbBand
+         ||  eBandInterp == GCI_YCbCr_CrBand)
+        && poGDS->nBitsPerSample == 8 )
+    {
+	uint16 hs, vs;
+        int iX, iY;
+
+	TIFFGetFieldDefaulted( poGDS->hTIFF, TIFFTAG_YCBCRSUBSAMPLING, 
+                               &hs, &vs);
+        
+        for( iY = 0; iY < nBlockYSize; iY++ )
+        {
+            for( iX = 0; iX < nBlockXSize; iX++ )
+            {
+                int iBlock = (iY / vs) * (nBlockXSize/hs) + (iX / hs);
+                GByte *pabySrcBlock = poGDS->pabyBlockBuf + 
+                    (vs * hs + 2) * iBlock;
+                
+                if( eBandInterp == GCI_YCbCr_YBand )
+                    ((GByte *)pImage)[iY*nBlockXSize + iX] = 
+                        pabySrcBlock[(iX % hs) + (iY % vs) * hs];
+                else if( eBandInterp == GCI_YCbCr_CbBand )
+                    ((GByte *)pImage)[iY*nBlockXSize + iX] = 
+                        pabySrcBlock[vs * hs + 0];
+                else if( eBandInterp == GCI_YCbCr_CrBand )
+                    ((GByte *)pImage)[iY*nBlockXSize + iX] = 
+                        pabySrcBlock[vs * hs + 1];
+            }
+        }
+
+        return CE_None;
+    }
+#endif
+        
+/* -------------------------------------------------------------------- */
+/*      Handle simple case of eight bit data, and pixel interleaving.   */
+/* -------------------------------------------------------------------- */
+    if( poGDS->nBitsPerSample == 8 )
+    {
+        int	i, nBlockPixels;
+        GByte	*pabyImage;
+        
+        pabyImage = poGDS->pabyBlockBuf + nBand - 1;
+
+        nBlockPixels = nBlockXSize * nBlockYSize;
+        for( i = 0; i < nBlockPixels; i++ )
+        {
+            ((GByte *) pImage)[i] = *pabyImage;
+            pabyImage += poGDS->nBands;
+        }
+    }
+
+    else
+    {
+        int	i, nBlockPixels, nWordBytes;
+        GByte	*pabyImage;
+
+        nWordBytes = poGDS->nBitsPerSample / 8;
+        pabyImage = poGDS->pabyBlockBuf + (nBand - 1) * nWordBytes;
+
+        nBlockPixels = nBlockXSize * nBlockYSize;
+        for( i = 0; i < nBlockPixels; i++ )
+        {
+            for( int j = 0; j < nWordBytes; j++ )
+            {
+                ((GByte *) pImage)[i*nWordBytes + j] = pabyImage[j];
+            }
+            pabyImage += poGDS->nBands * nWordBytes;
+        }
+    }
+
+    return eErr;
+}
+
+/************************************************************************/
+/*                            IWriteBlock()                             */
+/*                                                                      */
+/*      This is still limited to writing stripped datasets.             */
+/************************************************************************/
+
+CPLErr GTiffRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
+                                     void * pImage )
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+    int		nBlockId, nBlockBufSize;
+    CPLErr      eErr = CE_None;
+
+    poGDS->Crystalize();
+    poGDS->SetDirectory();
+
+    CPLAssert( poGDS != NULL
+               && nBlockXOff >= 0
+               && nBlockYOff >= 0
+               && pImage != NULL );
+
+/* -------------------------------------------------------------------- */
+/*      Handle case of "separate" images                                */
+/* -------------------------------------------------------------------- */
+    if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE
+        || poGDS->nBands == 1 )
+    {
+        nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow
+            + (nBand-1) * poGDS->nBlocksPerBand;
+        
+        if( TIFFIsTiled(poGDS->hTIFF) )
+        {
+            nBlockBufSize = TIFFTileSize( poGDS->hTIFF );
+            if( TIFFWriteEncodedTile( poGDS->hTIFF, nBlockId, pImage, 
+                                      nBlockBufSize ) == -1 )
+                eErr = CE_Failure;
+        }
+        else
+        {
+            nBlockBufSize = TIFFStripSize( poGDS->hTIFF );
+            if( TIFFWriteEncodedStrip( poGDS->hTIFF, nBlockId, pImage, 
+                                       nBlockBufSize ) == -1 )
+                eErr = CE_Failure;
+        }
+
+        return eErr;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Handle case of pixel interleaved (PLANARCONFIG_CONTIG) images.  */
+/* -------------------------------------------------------------------- */
+
+    nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow;
+        
+    eErr = poGDS->LoadBlockBuf( nBlockId );
+    if( eErr != CE_None )
+        return eErr;
+
+/* -------------------------------------------------------------------- */
+/*      Handle simple case of eight bit data, and pixel interleaving.   */
+/* -------------------------------------------------------------------- */
+    int	i, nBlockPixels, nWordBytes;
+    GByte	*pabyImage;
+
+    nWordBytes = poGDS->nBitsPerSample / 8;
+    pabyImage = poGDS->pabyBlockBuf + (nBand - 1) * nWordBytes;
+    
+    nBlockPixels = nBlockXSize * nBlockYSize;
+    for( i = 0; i < nBlockPixels; i++ )
+    {
+        for( int j = 0; j < nWordBytes; j++ )
+        {
+            pabyImage[j] = ((GByte *) pImage)[i*nWordBytes + j];
+        }
+        pabyImage += poGDS->nBands * nWordBytes;
+    }
+
+    poGDS->bLoadedBlockDirty = TRUE;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                             GetOffset()                              */
+/************************************************************************/
+
+double GTiffRasterBand::GetOffset( int *pbSuccess )
+
+{
+    if( pbSuccess )
+        *pbSuccess = bHaveOffsetScale;
+    return dfOffset;
+}
+
+/************************************************************************/
+/*                             SetOffset()                              */
+/************************************************************************/
+
+CPLErr GTiffRasterBand::SetOffset( double dfNewValue )
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+
+    poGDS->bMetadataChanged = TRUE;
+
+    bHaveOffsetScale = TRUE;
+    dfOffset = dfNewValue;
+    return CE_None;
+}
+
+/************************************************************************/
+/*                              GetScale()                              */
+/************************************************************************/
+
+double GTiffRasterBand::GetScale( int *pbSuccess )
+
+{
+    if( pbSuccess )
+        *pbSuccess = bHaveOffsetScale;
+    return dfScale;
+}
+
+/************************************************************************/
+/*                              SetScale()                              */
+/************************************************************************/
+
+CPLErr GTiffRasterBand::SetScale( double dfNewValue )
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+
+    poGDS->bMetadataChanged = TRUE;
+
+    bHaveOffsetScale = TRUE;
+    dfScale = dfNewValue;
+    return CE_None;
+}
+
+/************************************************************************/
+/*                            SetMetadata()                             */
+/************************************************************************/
+
+CPLErr GTiffRasterBand::SetMetadata( char ** papszMD, const char *pszDomain )
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+
+    poGDS->bMetadataChanged = TRUE;
+    return GDALPamRasterBand::SetMetadata( papszMD, pszDomain );
+}
+
+/************************************************************************/
+/*                          SetMetadataItem()                           */
+/************************************************************************/
+
+CPLErr GTiffRasterBand::SetMetadataItem( const char *pszName, 
+                                         const char *pszValue,
+                                         const char *pszDomain )
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+
+    poGDS->bMetadataChanged = TRUE;
+    return GDALPamRasterBand::SetMetadataItem( pszName, pszValue, pszDomain );
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp GTiffRasterBand::GetColorInterpretation()
+
+{
+    return eBandInterp;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *GTiffRasterBand::GetColorTable()
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+
+    if( nBand == 1 )
+        return poGDS->poColorTable;
+    else
+        return NULL;
+}
+
+/************************************************************************/
+/*                           SetColorTable()                            */
+/************************************************************************/
+
+CPLErr GTiffRasterBand::SetColorTable( GDALColorTable * poCT )
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+
+/* -------------------------------------------------------------------- */
+/*      Check if this is even a candidate for applying a PCT.           */
+/* -------------------------------------------------------------------- */
+    if( poGDS->bCrystalized )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "SetColorTable() not supported for existing TIFF files." );
+        return CE_Failure;
+    }
+
+    if( poGDS->nSamplesPerPixel != 1 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "SetColorTable() not supported for multi-sample TIFF files." );
+        return CE_Failure;
+    }
+        
+    if( eDataType != GDT_Byte && eDataType != GDT_UInt16 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "SetColorTable() only supported for Byte or UInt16 bands in TIFF format." );
+        return CE_Failure;
+    }
+        
+/* -------------------------------------------------------------------- */
+/*      Write out the colortable, and update the configuration.         */
+/* -------------------------------------------------------------------- */
+    int nColors;
+
+    if( eDataType == GDT_Byte )
+        nColors = 256;
+    else
+        nColors = 65536;
+
+    unsigned short *panTRed, *panTGreen, *panTBlue;
+
+    panTRed = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors);
+    panTGreen = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors);
+    panTBlue = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors);
+
+    for( int iColor = 0; iColor < nColors; iColor++ )
+    {
+        if( iColor < poCT->GetColorEntryCount() )
+        {
+            GDALColorEntry  sRGB;
+            
+            poCT->GetColorEntryAsRGB( iColor, &sRGB );
+            
+            panTRed[iColor] = (unsigned short) (257 * sRGB.c1);
+            panTGreen[iColor] = (unsigned short) (257 * sRGB.c2);
+            panTBlue[iColor] = (unsigned short) (257 * sRGB.c3);
+        }
+        else
+        {
+            panTRed[iColor] = panTGreen[iColor] = panTBlue[iColor] = 0;
+        }
+    }
+
+    TIFFSetField( poGDS->hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE );
+    TIFFSetField( poGDS->hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue );
+
+    CPLFree( panTRed );
+    CPLFree( panTGreen );
+    CPLFree( panTBlue );
+
+    if( poGDS->poColorTable )
+        delete poGDS->poColorTable;
+
+    poGDS->poColorTable = poCT->Clone();
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           GetNoDataValue()                           */
+/************************************************************************/
+
+double GTiffRasterBand::GetNoDataValue( int * pbSuccess )
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+
+    if( pbSuccess )
+        *pbSuccess = poGDS->bNoDataSet;
+
+    return poGDS->dfNoDataValue;
+}
+
+/************************************************************************/
+/*                           SetNoDataValue()                           */
+/************************************************************************/
+
+CPLErr GTiffRasterBand::SetNoDataValue( double dfNoData )
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+
+    poGDS->bNoDataSet = TRUE;
+    poGDS->bNoDataChanged = TRUE;
+    poGDS->dfNoDataValue = dfNoData;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetOverviewCount()                          */
+/************************************************************************/
+
+int GTiffRasterBand::GetOverviewCount()
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+
+    if( poGDS->nOverviewCount > 0 )
+        return poGDS->nOverviewCount;
+    else
+        return GDALRasterBand::GetOverviewCount();
+}
+
+/************************************************************************/
+/*                            GetOverview()                             */
+/************************************************************************/
+
+GDALRasterBand *GTiffRasterBand::GetOverview( int i )
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+
+    if( poGDS->nOverviewCount > 0 )
+    {
+        if( i < 0 || i >= poGDS->nOverviewCount )
+            return NULL;
+        else
+            return poGDS->papoOverviewDS[i]->GetRasterBand(nBand);
+    }
+    else
+        return GDALRasterBand::GetOverview( i );
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             GTiffRGBABand                            */
+/* ==================================================================== */
+/************************************************************************/
+
+class GTiffRGBABand : public GTiffRasterBand
+{
+    friend class GTiffDataset;
+
+  public:
+
+                   GTiffRGBABand( GTiffDataset *, int );
+
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual CPLErr IWriteBlock( int, int, void * );
+
+    virtual GDALColorInterp GetColorInterpretation();
+};
+
+
+/************************************************************************/
+/*                           GTiffRGBABand()                            */
+/************************************************************************/
+
+GTiffRGBABand::GTiffRGBABand( GTiffDataset *poDS, int nBand )
+        : GTiffRasterBand( poDS, nBand )
+
+{
+    eDataType = GDT_Byte;
+}
+
+/************************************************************************/
+/*                            IWriteBlock()                             */
+/************************************************************************/
+
+CPLErr GTiffRGBABand::IWriteBlock( int, int, void * )
+
+{
+    CPLError( CE_Failure, CPLE_AppDefined, 
+              "RGBA interpreted raster bands are read-only." );
+    return CE_Failure;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr GTiffRGBABand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                    void * pImage )
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+    int			nBlockBufSize, nBlockId;
+    CPLErr		eErr = CE_None;
+
+    poGDS->SetDirectory();
+
+    nBlockBufSize = 4 * nBlockXSize * nBlockYSize;
+    nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow;
+
+/* -------------------------------------------------------------------- */
+/*      Allocate a temporary buffer for this strip.                     */
+/* -------------------------------------------------------------------- */
+    if( poGDS->pabyBlockBuf == NULL )
+    {
+        poGDS->pabyBlockBuf = (GByte *) VSICalloc( 1, nBlockBufSize );
+        if( poGDS->pabyBlockBuf == NULL )
+        {
+            CPLError( CE_Failure, CPLE_OutOfMemory,
+                   "Unable to allocate %d bytes for a temporary strip buffer\n"
+                      "in GeoTIFF driver.",
+                      nBlockBufSize );
+            
+            return( CE_Failure );
+        }
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Read the strip                                                  */
+/* -------------------------------------------------------------------- */
+    if( poGDS->nLoadedBlock != nBlockId )
+    {
+        if( TIFFIsTiled( poGDS->hTIFF ) )
+        {
+            if( TIFFReadRGBATile(poGDS->hTIFF, 
+                                 nBlockXOff * nBlockXSize, 
+                                 nBlockYOff * nBlockYSize,
+                                 (uint32 *) poGDS->pabyBlockBuf) == -1 )
+            {
+                /* Once TIFFError() is properly hooked, this can go away */
+                CPLError( CE_Failure, CPLE_AppDefined,
+                          "TIFFReadRGBATile() failed." );
+                
+                memset( poGDS->pabyBlockBuf, 0, nBlockBufSize );
+                
+                eErr = CE_Failure;
+            }
+        }
+        else
+        {
+            if( TIFFReadRGBAStrip(poGDS->hTIFF, 
+                                  nBlockId * nBlockYSize,
+                                  (uint32 *) poGDS->pabyBlockBuf) == -1 )
+            {
+                /* Once TIFFError() is properly hooked, this can go away */
+                CPLError( CE_Failure, CPLE_AppDefined,
+                          "TIFFReadRGBAStrip() failed." );
+                
+                memset( poGDS->pabyBlockBuf, 0, nBlockBufSize );
+                
+                eErr = CE_Failure;
+            }
+        }
+    }
+
+    poGDS->nLoadedBlock = nBlockId;
+                              
+/* -------------------------------------------------------------------- */
+/*      Handle simple case of eight bit data, and pixel interleaving.   */
+/* -------------------------------------------------------------------- */
+    int   iDestLine, nBO;
+    int   nThisBlockYSize;
+
+    if( (nBlockYOff+1) * nBlockYSize > GetYSize()
+        && !TIFFIsTiled( poGDS->hTIFF ) )
+        nThisBlockYSize = GetYSize() - nBlockYOff * nBlockYSize;
+    else
+        nThisBlockYSize = nBlockYSize;
+
+#ifdef CPL_LSB
+    nBO = nBand - 1;
+#else
+    nBO = 4 - nBand;
+#endif
+
+    for( iDestLine = 0; iDestLine < nThisBlockYSize; iDestLine++ )
+    {
+        int	nSrcOffset;
+
+        nSrcOffset = (nThisBlockYSize - iDestLine - 1) * nBlockXSize * 4;
+
+        GDALCopyWords( poGDS->pabyBlockBuf + nBO + nSrcOffset, GDT_Byte, 4,
+                       ((GByte *) pImage)+iDestLine*nBlockXSize, GDT_Byte, 1, 
+                       nBlockXSize );
+    }
+
+    return eErr;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp GTiffRGBABand::GetColorInterpretation()
+
+{
+    if( nBand == 1 )
+        return GCI_RedBand;
+    else if( nBand == 2 )
+        return GCI_GreenBand;
+    else if( nBand == 3 )
+        return GCI_BlueBand;
+    else
+        return GCI_AlphaBand;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             GTiffBitmapBand                          */
+/* ==================================================================== */
+/************************************************************************/
+
+class GTiffBitmapBand : public GTiffRasterBand
+{
+    friend class GTiffDataset;
+
+    GDALColorTable *poColorTable;
+
+  public:
+
+                   GTiffBitmapBand( GTiffDataset *, int );
+    virtual       ~GTiffBitmapBand();
+
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual CPLErr IWriteBlock( int, int, void * );
+
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual GDALColorTable *GetColorTable();
+};
+
+
+/************************************************************************/
+/*                           GTiffBitmapBand()                          */
+/************************************************************************/
+
+GTiffBitmapBand::GTiffBitmapBand( GTiffDataset *poDS, int nBand )
+        : GTiffRasterBand( poDS, nBand )
+
+{
+
+    if( nBand != 1 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "One bit deep TIFF files only supported with one sample per pixel (band)." );
+    }
+
+    eDataType = GDT_Byte;
+
+    if( poDS->poColorTable != NULL )
+        poColorTable = poDS->poColorTable->Clone();
+    else
+    {
+        GDALColorEntry	oWhite, oBlack;
+
+        oWhite.c1 = 255;
+        oWhite.c2 = 255;
+        oWhite.c3 = 255;
+        oWhite.c4 = 255;
+
+        oBlack.c1 = 0;
+        oBlack.c2 = 0;
+        oBlack.c3 = 0;
+        oBlack.c4 = 255;
+
+        poColorTable = new GDALColorTable();
+        
+        if( poDS->nPhotometric == PHOTOMETRIC_MINISWHITE )
+        {
+            poColorTable->SetColorEntry( 0, &oWhite );
+            poColorTable->SetColorEntry( 1, &oBlack );
+        }
+        else
+        {
+            poColorTable->SetColorEntry( 0, &oBlack );
+            poColorTable->SetColorEntry( 1, &oWhite );
+        }
+    }
+}
+
+/************************************************************************/
+/*                          ~GTiffBitmapBand()                          */
+/************************************************************************/
+
+GTiffBitmapBand::~GTiffBitmapBand()
+
+{
+    delete poColorTable;
+}
+
+/************************************************************************/
+/*                            IWriteBlock()                             */
+/************************************************************************/
+
+CPLErr GTiffBitmapBand::IWriteBlock( int, int, void * )
+
+{
+    CPLError( CE_Failure, CPLE_AppDefined, 
+              "One bit raster bands are read-only." );
+    return CE_Failure;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr GTiffBitmapBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                    void * pImage )
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+    int			nBlockBufSize, nBlockId;
+    CPLErr		eErr = CE_None;
+
+    poGDS->SetDirectory();
+
+    if( TIFFIsTiled(poGDS->hTIFF) )
+        nBlockBufSize = TIFFTileSize( poGDS->hTIFF );
+    else
+    {
+        CPLAssert( nBlockXOff == 0 );
+        nBlockBufSize = TIFFStripSize( poGDS->hTIFF );
+    }
+
+    nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow;
+
+/* -------------------------------------------------------------------- */
+/*      Load the block buffer.                                          */
+/* -------------------------------------------------------------------- */
+    eErr = poGDS->LoadBlockBuf( nBlockId );
+    if( eErr != CE_None )
+        return eErr;
+
+/* -------------------------------------------------------------------- */
+/*      Translate 1bit data to eight bit.                               */
+/* -------------------------------------------------------------------- */
+    int	  iDstOffset=0, iLine;
+    register GByte *pabyBlockBuf = poGDS->pabyBlockBuf;
+
+    for( iLine = 0; iLine < nBlockYSize; iLine++ )
+    {
+        int iSrcOffset, iPixel;
+
+        iSrcOffset = ((nBlockXSize+7) >> 3) * 8 * iLine;
+
+        for( iPixel = 0; iPixel < nBlockXSize; iPixel++, iSrcOffset++ )
+        {
+            if( pabyBlockBuf[iSrcOffset >>3] & (0x80 >> (iSrcOffset & 0x7)) )
+                ((GByte *) pImage)[iDstOffset++] = 1;
+            else
+                ((GByte *) pImage)[iDstOffset++] = 0;
+        }
+    }
+    
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp GTiffBitmapBand::GetColorInterpretation()
+
+{
+    return GCI_PaletteIndex;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *GTiffBitmapBand::GetColorTable()
+
+{
+    return poColorTable;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             GTiffOddBitsBand                         */
+/* ==================================================================== */
+/************************************************************************/
+
+class GTiffOddBitsBand : public GTiffRasterBand
+{
+    friend class GTiffDataset;
+  public:
+
+                   GTiffOddBitsBand( GTiffDataset *, int );
+    virtual       ~GTiffOddBitsBand();
+
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual CPLErr IWriteBlock( int, int, void * );
+};
+
+
+/************************************************************************/
+/*                           GTiffOddBitsBand()                          */
+/************************************************************************/
+
+GTiffOddBitsBand::GTiffOddBitsBand( GTiffDataset *poGDS, int nBand )
+        : GTiffRasterBand( poGDS, nBand )
+
+{
+    eDataType = GDT_Byte;
+    if( poGDS->nSampleFormat == SAMPLEFORMAT_IEEEFP )
+        eDataType = GDT_Float32;
+    else if( poGDS->nBitsPerSample > 8 && poGDS->nBitsPerSample < 16 )
+        eDataType = GDT_UInt16;
+    else if( poGDS->nBitsPerSample > 16 )
+        eDataType = GDT_UInt32;
+}
+
+/************************************************************************/
+/*                          ~GTiffOddBitsBand()                          */
+/************************************************************************/
+
+GTiffOddBitsBand::~GTiffOddBitsBand()
+
+{
+}
+
+/************************************************************************/
+/*                            IWriteBlock()                             */
+/************************************************************************/
+
+CPLErr GTiffOddBitsBand::IWriteBlock( int, int, void * )
+
+{
+    CPLError( CE_Failure, CPLE_AppDefined, 
+              "Odd bits raster bands are read-only." );
+    return CE_Failure;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr GTiffOddBitsBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                    void * pImage )
+
+{
+    GTiffDataset	*poGDS = (GTiffDataset *) poDS;
+    int			nBlockBufSize, nBlockId;
+    CPLErr		eErr = CE_None;
+
+    poGDS->SetDirectory();
+
+    if( TIFFIsTiled(poGDS->hTIFF) )
+        nBlockBufSize = TIFFTileSize( poGDS->hTIFF );
+    else
+    {
+        CPLAssert( nBlockXOff == 0 );
+        nBlockBufSize = TIFFStripSize( poGDS->hTIFF );
+    }
+
+    nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow;
+
+    if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE )
+        nBlockId += (nBand-1) * poGDS->nBlocksPerBand;
+
+/* -------------------------------------------------------------------- */
+/*	Handle the case of a strip in a writable file that doesn't	*/
+/*	exist yet, but that we want to read.  Just set to zeros and	*/
+/*	return.								*/
+/* -------------------------------------------------------------------- */
+    if( poGDS->eAccess == GA_Update && !poGDS->IsBlockAvailable(nBlockId) )
+    {
+        memset( pImage, 0,
+                nBlockXSize * nBlockYSize
+                * GDALGetDataTypeSize(eDataType) / 8 );
+        return CE_None;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Load the block buffer.                                          */
+/* -------------------------------------------------------------------- */
+    eErr = poGDS->LoadBlockBuf( nBlockId );
+    if( eErr != CE_None )
+        return eErr;
+
+/* -------------------------------------------------------------------- */
+/*      Handle the case of 16- and 24-bit floating point data as per    */
+/*      TIFF Technical Note 3.                                          */
+/* -------------------------------------------------------------------- */
+    if( eDataType == GDT_Float32 && poGDS->nBitsPerSample < 32 )
+    {
+        int	i, nBlockPixels, nWordBytes, iSkipBytes;
+        GByte	*pabyImage;
+
+        nWordBytes = poGDS->nBitsPerSample / 8;
+        pabyImage = poGDS->pabyBlockBuf + (nBand - 1) * nWordBytes;
+        iSkipBytes = ( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) ?
+            nWordBytes : poGDS->nBands * nWordBytes;
+
+        nBlockPixels = nBlockXSize * nBlockYSize;
+        if ( poGDS->nBitsPerSample == 16 )
+        {
+            for( i = 0; i < nBlockPixels; i++ )
+            {
+                ((GUInt32 *) pImage)[i] =
+                    HalfToFloat( *((GUInt16 *)pabyImage) );
+                pabyImage += iSkipBytes;
+            }
+        }
+        else if ( poGDS->nBitsPerSample == 24 )
+        {
+            for( i = 0; i < nBlockPixels; i++ )
+            {
+                ((GUInt32 *) pImage)[i] =
+                    TripleToFloat( ((GUInt32)*(pabyImage + 2) << 16)
+                                   | ((GUInt32)*(pabyImage + 1) << 8)
+                                   | (GUInt32)*pabyImage );
+                pabyImage += iSkipBytes;
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Special case for moving 12bit data somewhat more efficiently.   */
+/* -------------------------------------------------------------------- */
+    else if( poGDS->nBitsPerSample == 12 )
+    {
+        int	iPixel, iBitOffset = 0;
+        int     iPixelBitSkip, iBandBitOffset, iX, iY, nBitsPerLine;
+
+        if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG )
+        {
+            iPixelBitSkip = poGDS->nBands * poGDS->nBitsPerSample;
+            iBandBitOffset = (nBand-1) * poGDS->nBitsPerSample;
+        }
+        else
+        {
+            iPixelBitSkip = poGDS->nBitsPerSample;
+            iBandBitOffset = 0;
+        }
+
+        // bits per line rounds up to next byte boundary.
+        nBitsPerLine = nBlockXSize * iPixelBitSkip;
+        if( (nBitsPerLine & 7) != 0 )
+            nBitsPerLine = (nBitsPerLine + 7) & (~7);
+
+        iPixel = 0;
+        for( iY = 0; iY < nBlockYSize; iY++ )
+        {
+            iBitOffset = iBandBitOffset + iY * nBitsPerLine;
+
+            for( iX = 0; iX < nBlockXSize; iX++ )
+            {
+                int iByte = iBitOffset>>3;
+
+                if( (iBitOffset & 0x7) == 0 )
+                {
+                    /* starting on byte boundary */
+                    
+                    ((GUInt16 *) pImage)[iPixel++] = 
+                        (poGDS->pabyBlockBuf[iByte] << 4)
+                        | (poGDS->pabyBlockBuf[iByte+1] >> 4);
+                }
+                else
+                {
+                    /* starting off byte boundary */
+                    
+                    ((GUInt16 *) pImage)[iPixel++] = 
+                        ((poGDS->pabyBlockBuf[iByte] & 0xf) << 8)
+                        | (poGDS->pabyBlockBuf[iByte+1]);
+                }
+                iBitOffset += iPixelBitSkip;
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Handle 1-32 bit integer data.                                   */
+/* -------------------------------------------------------------------- */
+    else
+    {
+        int	iBit, iPixel, iBitOffset = 0;
+        int     iPixelBitSkip, iBandBitOffset, iX, iY, nBitsPerLine;
+
+        if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG )
+        {
+            iPixelBitSkip = poGDS->nBands * poGDS->nBitsPerSample;
+            iBandBitOffset = (nBand-1) * poGDS->nBitsPerSample;
+        }
+        else
+        {
+            iPixelBitSkip = poGDS->nBitsPerSample;
+            iBandBitOffset = 0;
+        }
+
+        // bits per line rounds up to next byte boundary.
+        nBitsPerLine = nBlockXSize * iPixelBitSkip;
+        if( (nBitsPerLine & 7) != 0 )
+            nBitsPerLine = (nBitsPerLine + 7) & (~7);
+
+        iPixel = 0;
+        for( iY = 0; iY < nBlockYSize; iY++ )
+        {
+            iBitOffset = iBandBitOffset + iY * nBitsPerLine;
+
+            for( iX = 0; iX < nBlockXSize; iX++ )
+            {
+                int  nOutWord = 0;
+
+                for( iBit = 0; iBit < poGDS->nBitsPerSample; iBit++ )
+                {
+                    if( poGDS->pabyBlockBuf[iBitOffset>>3] 
+                        & (0x80 >>(iBitOffset & 7)) )
+                        nOutWord |= (1 << (poGDS->nBitsPerSample - 1 - iBit));
+                    iBitOffset++;
+                } 
+
+                iBitOffset= iBitOffset + iPixelBitSkip - poGDS->nBitsPerSample;
+                
+                if( eDataType == GDT_Byte )
+                    ((GByte *) pImage)[iPixel++] = nOutWord;
+                else if( eDataType == GDT_UInt16 )
+                    ((GUInt16 *) pImage)[iPixel++] = nOutWord;
+                else if( eDataType == GDT_UInt32 )
+                    ((GUInt32 *) pImage)[iPixel++] = nOutWord;
+            }
+        }
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            GTiffDataset                              */
+/* ==================================================================== */
+/************************************************************************/
+
+
+/************************************************************************/
+/*                            GTiffDataset()                            */
+/************************************************************************/
+
+GTiffDataset::GTiffDataset()
+
+{
+    nLoadedBlock = -1;
+    bLoadedBlockDirty = FALSE;
+    pabyBlockBuf = NULL;
+    hTIFF = NULL;
+    bNewDataset = FALSE;
+    bCrystalized = TRUE;
+    poColorTable = NULL;
+    bNoDataSet = FALSE;
+    bNoDataChanged = FALSE;
+    dfNoDataValue = -9999.0;
+    pszProjection = CPLStrdup("");
+    bBase = TRUE;
+    bTreatAsRGBA = FALSE;
+    nOverviewCount = 0;
+    papoOverviewDS = NULL;
+    nDirOffset = 0;
+
+    pszTFWFilename = NULL;
+
+    bGeoTransformValid = FALSE;
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+
+    nGCPCount = 0;
+    pasGCPList = NULL;
+}
+
+/************************************************************************/
+/*                           ~GTiffDataset()                            */
+/************************************************************************/
+
+GTiffDataset::~GTiffDataset()
+
+{
+    Crystalize();
+
+    FlushCache();
+
+    if( bBase )
+    {
+        for( int i = 0; i < nOverviewCount; i++ )
+        {
+            delete papoOverviewDS[i];
+        }
+        CPLFree( papoOverviewDS );
+    }
+
+    SetDirectory();
+
+    if( poColorTable != NULL )
+        delete poColorTable;
+
+    if( GetAccess() == GA_Update && bBase )
+    {
+        if( bNewDataset || bMetadataChanged )
+            WriteMetadata( this, hTIFF, TRUE );
+        
+        if( bNewDataset || bGeoTIFFInfoChanged )
+            WriteGeoTIFFInfo();
+
+	if( bNoDataChanged )
+            WriteNoDataValue( hTIFF, dfNoDataValue );
+        
+        if( bNewDataset || bMetadataChanged || bGeoTIFFInfoChanged || bNoDataChanged )
+        {
+#if defined(TIFFLIB_VERSION)
+#if  TIFFLIB_VERSION > 20010925 && TIFFLIB_VERSION != 20011807
+            TIFFRewriteDirectory( hTIFF );
+#endif
+#endif
+        }
+    }
+
+    if( bBase )
+    {
+        XTIFFClose( hTIFF );
+    }
+
+    if( nGCPCount > 0 )
+    {
+        GDALDeinitGCPs( nGCPCount, pasGCPList );
+        CPLFree( pasGCPList );
+    }
+
+    if( pszTFWFilename != NULL )
+        CPLFree( pszTFWFilename );
+    CPLFree( pszProjection );
+}
+
+/************************************************************************/
+/*                           FlushBlockBuf()                            */
+/************************************************************************/
+
+CPLErr GTiffDataset::FlushBlockBuf()
+
+{
+    int		nBlockBufSize;
+    CPLErr      eErr = CE_None;
+
+    if( nLoadedBlock < 0 || !bLoadedBlockDirty )
+        return CE_None;
+
+    SetDirectory();
+
+    if( TIFFIsTiled(hTIFF) )
+        nBlockBufSize = TIFFTileSize( hTIFF );
+    else
+        nBlockBufSize = TIFFStripSize( hTIFF );
+
+    bLoadedBlockDirty = FALSE;
+
+    if( TIFFIsTiled( hTIFF ) )
+    {
+        if( TIFFWriteEncodedTile(hTIFF, nLoadedBlock, pabyBlockBuf,
+                                 nBlockBufSize) == -1 )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "TIFFWriteEncodedTile() failed." );
+            eErr = CE_Failure;
+        }
+    }
+    else
+    {
+        if( TIFFWriteEncodedStrip(hTIFF, nLoadedBlock, pabyBlockBuf,
+                                 nBlockBufSize) == -1 )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "TIFFWriteEncodedStrip() failed." );
+            eErr = CE_Failure;
+        }
+    }
+
+    return eErr;;
+}
+
+/************************************************************************/
+/*                            LoadBlockBuf()                            */
+/*                                                                      */
+/*      Load working block buffer with request block (tile/strip).      */
+/************************************************************************/
+
+CPLErr GTiffDataset::LoadBlockBuf( int nBlockId )
+
+{
+    int	nBlockBufSize;
+    CPLErr	eErr = CE_None;
+
+    if( nLoadedBlock == nBlockId )
+        return CE_None;
+
+/* -------------------------------------------------------------------- */
+/*      This is rather overkill, but relatively harmless so we do it    */
+/*      here to be sure.                                                */
+/* -------------------------------------------------------------------- */
+    if( nCompression == COMPRESSION_JPEG 
+        && nPhotometric == PHOTOMETRIC_YCBCR 
+        && CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB",
+                                              "YES") ) )
+    {
+        TIFFSetField(hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If we have a dirty loaded block, flush it out first.            */
+/* -------------------------------------------------------------------- */
+    if( nLoadedBlock != -1 && bLoadedBlockDirty )
+    {
+        eErr = FlushBlockBuf();
+        if( eErr != CE_None )
+            return eErr;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get block size.                                                 */
+/* -------------------------------------------------------------------- */
+    if( TIFFIsTiled(hTIFF) )
+        nBlockBufSize = TIFFTileSize( hTIFF );
+    else
+        nBlockBufSize = TIFFStripSize( hTIFF );
+
+/* -------------------------------------------------------------------- */
+/*      Allocate a temporary buffer for this strip.                     */
+/* -------------------------------------------------------------------- */
+    if( pabyBlockBuf == NULL )
+    {
+        pabyBlockBuf = (GByte *) VSICalloc( 1, nBlockBufSize );
+        if( pabyBlockBuf == NULL )
+        {
+            CPLError( CE_Failure, CPLE_OutOfMemory,
+                      "Unable to allocate %d bytes for a temporary strip buffer\n"
+                      "in GeoTIFF driver.",
+                      nBlockBufSize );
+            
+            return( CE_Failure );
+        }
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      If we don't have this block already loaded, and we know it      */
+/*      doesn't yet exist on disk, just zero the memory buffer and      */
+/*      pretend we loaded it.                                           */
+/* -------------------------------------------------------------------- */
+    if( eAccess == GA_Update && !IsBlockAvailable( nBlockId ) )
+    {
+        memset( pabyBlockBuf, 0, nBlockBufSize );
+        nLoadedBlock = nBlockId;
+        return CE_None;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Load the block, if it isn't our current block.                  */
+/* -------------------------------------------------------------------- */
+    if( TIFFIsTiled( hTIFF ) )
+    {
+        if( TIFFReadEncodedTile(hTIFF, nBlockId, pabyBlockBuf,
+                                nBlockBufSize) == -1 )
+        {
+            /* Once TIFFError() is properly hooked, this can go away */
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "TIFFReadEncodedTile() failed." );
+                
+            memset( pabyBlockBuf, 0, nBlockBufSize );
+                
+            eErr = CE_Failure;
+        }
+    }
+    else
+    {
+        if( TIFFReadEncodedStrip(hTIFF, nBlockId, pabyBlockBuf,
+                                 nBlockBufSize) == -1 )
+        {
+            /* Once TIFFError() is properly hooked, this can go away */
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "TIFFReadEncodedStrip() failed." );
+                
+            memset( pabyBlockBuf, 0, nBlockBufSize );
+                
+            eErr = CE_Failure;
+        }
+    }
+
+    nLoadedBlock = nBlockId;
+    bLoadedBlockDirty = FALSE;
+
+    return eErr;
+}
+
+
+/************************************************************************/
+/*                             Crystalize()                             */
+/*                                                                      */
+/*      Make sure that the directory information is written out for     */
+/*      a new file, require before writing any imagery data.            */
+/************************************************************************/
+
+void GTiffDataset::Crystalize()
+
+{
+    if( !bCrystalized )
+    {
+        bCrystalized = TRUE;
+
+        TIFFWriteCheck( hTIFF, TIFFIsTiled(hTIFF), "GTiffDataset::Crystalize");
+        TIFFWriteDirectory( hTIFF );
+
+        TIFFSetDirectory( hTIFF, 0 );
+        nDirOffset = TIFFCurrentDirOffset( hTIFF );
+    }
+}
+
+
+/************************************************************************/
+/*                          IsBlockAvailable()                          */
+/*                                                                      */
+/*      Return TRUE if the indicated strip/tile is available.  We       */
+/*      establish this by testing if the stripbytecount is zero.  If    */
+/*      zero then the block has never been committed to disk.           */
+/************************************************************************/
+
+int GTiffDataset::IsBlockAvailable( int nBlockId )
+
+{
+    uint32 *panByteCounts = NULL;
+
+    if( ( TIFFIsTiled( hTIFF ) 
+          && TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts ) )
+        || ( !TIFFIsTiled( hTIFF ) 
+          && TIFFGetField( hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts ) ) )
+    {
+        if( panByteCounts == NULL )
+            return FALSE;
+        else
+            return panByteCounts[nBlockId] != 0;
+    }
+    else
+        return FALSE;
+}
+
+/************************************************************************/
+/*                             FlushCache()                             */
+/*                                                                      */
+/*      We override this so we can also flush out local tiff strip      */
+/*      cache if need be.                                               */
+/************************************************************************/
+
+void GTiffDataset::FlushCache()
+
+{
+    GDALPamDataset::FlushCache();
+
+    if( bLoadedBlockDirty && nLoadedBlock != -1 )
+        FlushBlockBuf();
+
+    CPLFree( pabyBlockBuf );
+    pabyBlockBuf = NULL;
+    nLoadedBlock = -1;
+    bLoadedBlockDirty = FALSE;
+}
+
+/************************************************************************/
+/*                         TIFF_OvLevelAdjust()                         */
+/*                                                                      */
+/*      Some overview levels cannot be achieved closely enough to be    */
+/*      recognised as the desired overview level.  This function        */
+/*      will adjust an overview level to one that is achievable on      */
+/*      the given raster size.                                          */
+/*                                                                      */
+/*      For instance a 1200x1200 image on which a 256 level overview    */
+/*      is request will end up generating a 5x5 overview.  However,     */
+/*      this will appear to the system be a level 240 overview.         */
+/*      This function will adjust 256 to 240 based on knowledge of      */
+/*      the image size.                                                 */
+/*                                                                      */
+/*      This is a copy of the GDALOvLevelAdjust() function in           */
+/*      gdaldefaultoverviews.cpp.                                       */
+/************************************************************************/
+
+static int TIFF_OvLevelAdjust( int nOvLevel, int nXSize )
+
+{
+    int	nOXSize = (nXSize + nOvLevel - 1) / nOvLevel;
+    
+    return (int) (0.5 + nXSize / (double) nOXSize);
+}
+
+/************************************************************************/
+/*                          IBuildOverviews()                           */
+/************************************************************************/
+
+CPLErr GTiffDataset::IBuildOverviews( 
+    const char * pszResampling, 
+    int nOverviews, int * panOverviewList,
+    int nBands, int * panBandList,
+    GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    CPLErr       eErr = CE_None;
+    int          i;
+    GTiffDataset *poODS;
+
+    if( !pfnProgress( 0.0, NULL, pProgressData ) )
+    {
+        CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
+        return CE_Failure;
+    }
+
+    Crystalize();
+
+    TIFFFlush( hTIFF );
+
+/* -------------------------------------------------------------------- */
+/*      If we don't have read access, then create the overviews externally.*/
+/* -------------------------------------------------------------------- */
+    if( GetAccess() != GA_Update )
+    {
+        CPLError( CE_Warning, CPLE_AppDefined,
+                  "File open for read-only accessing, "
+                  "creating overviews externally." );
+
+        return GDALDataset::IBuildOverviews( 
+            pszResampling, nOverviews, panOverviewList, 
+            nBands, panBandList, pfnProgress, pProgressData );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If RRD overviews requested, then invoke generic handling.       */
+/* -------------------------------------------------------------------- */
+    if( CSLTestBoolean(CPLGetConfigOption( "USE_RRD", "NO" )) )
+    {
+        return GDALDataset::IBuildOverviews( 
+            pszResampling, nOverviews, panOverviewList, 
+            nBands, panBandList, pfnProgress, pProgressData );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Our TIFF overview support currently only works safely if all    */
+/*      bands are handled at the same time.                             */
+/* -------------------------------------------------------------------- */
+    if( nBands != GetRasterCount() )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported,
+                  "Generation of overviews in TIFF currently only"
+                  " supported when operating on all bands.\n" 
+                  "Operation failed.\n" );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Do we have a palette?  If so, create a TIFF compatible version. */
+/* -------------------------------------------------------------------- */
+    unsigned short	anTRed[65536], anTGreen[65536], anTBlue[65536];
+    unsigned short      *panRed=NULL, *panGreen=NULL, *panBlue=NULL;
+
+    if( nPhotometric == PHOTOMETRIC_PALETTE && poColorTable != NULL )
+    {
+        int nColors;
+
+        if( nBitsPerSample == 8 )
+            nColors = 256;
+        else
+            nColors = 65536;
+        
+        for( int iColor = 0; iColor < nColors; iColor++ )
+        {
+            if( iColor < poColorTable->GetColorEntryCount() )
+            {
+                GDALColorEntry  sRGB;
+
+                poColorTable->GetColorEntryAsRGB( iColor, &sRGB );
+
+                anTRed[iColor] = (unsigned short) (256 * sRGB.c1);
+                anTGreen[iColor] = (unsigned short) (256 * sRGB.c2);
+                anTBlue[iColor] = (unsigned short) (256 * sRGB.c3);
+            }
+            else
+            {
+                anTRed[iColor] = anTGreen[iColor] = anTBlue[iColor] = 0;
+            }
+        }
+
+        panRed = anTRed;
+        panGreen = anTGreen;
+        panBlue = anTBlue;
+    }
+        
+/* -------------------------------------------------------------------- */
+/*      Establish which of the overview levels we already have, and     */
+/*      which are new.  We assume that band 1 of the file is            */
+/*      representative.                                                 */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < nOverviews && eErr == CE_None; i++ )
+    {
+        int   j;
+
+        for( j = 0; j < nOverviewCount; j++ )
+        {
+            int    nOvFactor;
+
+            poODS = papoOverviewDS[j];
+
+            nOvFactor = (int) 
+                (0.5 + GetRasterXSize() / (double) poODS->GetRasterXSize());
+
+            if( nOvFactor == panOverviewList[i] )
+                panOverviewList[i] *= -1;
+        }
+
+        if( panOverviewList[i] > 0 )
+        {
+            uint32	nOverviewOffset;
+            int         nOXSize, nOYSize;
+
+            nOXSize = (GetRasterXSize() + panOverviewList[i] - 1) 
+                / panOverviewList[i];
+            nOYSize = (GetRasterYSize() + panOverviewList[i] - 1)
+                / panOverviewList[i];
+
+            nOverviewOffset = 
+                TIFF_WriteOverview( hTIFF, nOXSize, nOYSize, 
+                                    nBitsPerSample, nPlanarConfig,
+                                    nSamplesPerPixel, 128, 128, TRUE,
+                                    nCompression, nPhotometric, nSampleFormat, 
+                                    panRed, panGreen, panBlue, FALSE );
+
+            if( nOverviewOffset == 0 )
+            {
+                eErr = CE_Failure;
+                continue;
+            }
+
+            poODS = new GTiffDataset();
+            if( poODS->OpenOffset( hTIFF, nOverviewOffset, FALSE, 
+                                   GA_Update ) != CE_None )
+            {
+                delete poODS;
+                eErr = CE_Failure;
+            }
+            else
+            {
+                nOverviewCount++;
+                papoOverviewDS = (GTiffDataset **)
+                    CPLRealloc(papoOverviewDS, 
+                               nOverviewCount * (sizeof(void*)));
+                papoOverviewDS[nOverviewCount-1] = poODS;
+            }
+        }
+        else
+            panOverviewList[i] *= -1;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Refresh old overviews that were listed.                         */
+/* -------------------------------------------------------------------- */
+    GDALRasterBand **papoOverviewBands;
+
+    papoOverviewBands = (GDALRasterBand **) 
+        CPLCalloc(sizeof(void*),nOverviews);
+
+    for( int iBand = 0; iBand < nBands && eErr == CE_None; iBand++ )
+    {
+        GDALRasterBand *poBand;
+        int            nNewOverviews;
+
+        poBand = GetRasterBand( panBandList[iBand] );
+
+        nNewOverviews = 0;
+        for( i = 0; i < nOverviews && poBand != NULL; i++ )
+        {
+            int   j;
+            
+            for( j = 0; j < poBand->GetOverviewCount(); j++ )
+            {
+                int    nOvFactor;
+                GDALRasterBand * poOverview = poBand->GetOverview( j );
+
+                nOvFactor = (int) 
+                  (0.5 + poBand->GetXSize() / (double) poOverview->GetXSize());
+
+                if( nOvFactor == panOverviewList[i] 
+                    || nOvFactor == TIFF_OvLevelAdjust( panOverviewList[i],
+                                                        poBand->GetXSize() ) )
+                {
+                    papoOverviewBands[nNewOverviews++] = poOverview;
+                }
+            }
+        }
+
+        void         *pScaledProgressData;
+
+        pScaledProgressData = 
+            GDALCreateScaledProgress( iBand / (double) nBands, 
+                                      (iBand+1) / (double) nBands,
+                                      pfnProgress, pProgressData );
+
+        eErr = GDALRegenerateOverviews( poBand,
+                                        nNewOverviews, papoOverviewBands,
+                                        pszResampling, 
+                                        GDALScaledProgress, 
+                                        pScaledProgressData);
+
+        GDALDestroyScaledProgress( pScaledProgressData );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    CPLFree( papoOverviewBands );
+
+    pfnProgress( 1.0, NULL, pProgressData );
+
+    return eErr;
+}
+
+
+/************************************************************************/
+/*                          WriteGeoTIFFInfo()                          */
+/************************************************************************/
+
+void GTiffDataset::WriteGeoTIFFInfo()
+
+{
+/* -------------------------------------------------------------------- */
+/*      If the geotransform is the default, don't bother writing it.    */
+/* -------------------------------------------------------------------- */
+    if( adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0
+        || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0
+        || adfGeoTransform[4] != 0.0 || ABS(adfGeoTransform[5]) != 1.0 )
+    {
+
+/* -------------------------------------------------------------------- */
+/*      Write the transform.  If we have a normal north-up image we     */
+/*      use the tiepoint plus pixelscale otherwise we use a matrix.     */
+/* -------------------------------------------------------------------- */
+	if( adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0 
+            && adfGeoTransform[5] < 0.0 )
+	{
+	    double	adfPixelScale[3], adfTiePoints[6];
+
+	    adfPixelScale[0] = adfGeoTransform[1];
+	    adfPixelScale[1] = fabs(adfGeoTransform[5]);
+	    adfPixelScale[2] = 0.0;
+	    
+	    TIFFSetField( hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale );
+	    
+	    adfTiePoints[0] = 0.0;
+	    adfTiePoints[1] = 0.0;
+	    adfTiePoints[2] = 0.0;
+	    adfTiePoints[3] = adfGeoTransform[0];
+	    adfTiePoints[4] = adfGeoTransform[3];
+	    adfTiePoints[5] = 0.0;
+	    
+	    TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints );
+	}
+	else
+	{
+	    double	adfMatrix[16];
+	    
+	    memset(adfMatrix,0,sizeof(double) * 16);
+	    
+	    adfMatrix[0] = adfGeoTransform[1];
+	    adfMatrix[1] = adfGeoTransform[2];
+	    adfMatrix[3] = adfGeoTransform[0];
+	    adfMatrix[4] = adfGeoTransform[4];
+	    adfMatrix[5] = adfGeoTransform[5];
+	    adfMatrix[7] = adfGeoTransform[3];
+	    adfMatrix[15] = 1.0;
+	    
+	    TIFFSetField( hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix );
+	}
+/* -------------------------------------------------------------------- */
+/*      Are we maintaining a .tfw file?                                 */
+/* -------------------------------------------------------------------- */
+	if( pszTFWFilename != NULL )
+	{
+	    FILE	*fp;
+
+	    fp = VSIFOpen( pszTFWFilename, "wt" );
+	    
+	    fprintf( fp, "%.10f\n", adfGeoTransform[1] );
+	    fprintf( fp, "%.10f\n", adfGeoTransform[4] );
+	    fprintf( fp, "%.10f\n", adfGeoTransform[2] );
+	    fprintf( fp, "%.10f\n", adfGeoTransform[5] );
+	    fprintf( fp, "%.10f\n", adfGeoTransform[0] 
+		     + 0.5 * adfGeoTransform[1]
+		     + 0.5 * adfGeoTransform[2] );
+	    fprintf( fp, "%.10f\n", adfGeoTransform[3]
+		     + 0.5 * adfGeoTransform[4]
+		     + 0.5 * adfGeoTransform[5] );
+	    VSIFClose( fp );
+	}
+    }
+    else if( GetGCPCount() > 0 )
+    {
+	double	*padfTiePoints;
+	int		iGCP;
+	
+	padfTiePoints = (double *) 
+	    CPLMalloc( 6 * sizeof(double) * GetGCPCount() );
+
+	for( iGCP = 0; iGCP < GetGCPCount(); iGCP++ )
+	{
+
+	    padfTiePoints[iGCP*6+0] = pasGCPList[iGCP].dfGCPPixel;
+	    padfTiePoints[iGCP*6+1] = pasGCPList[iGCP].dfGCPLine;
+	    padfTiePoints[iGCP*6+2] = 0;
+	    padfTiePoints[iGCP*6+3] = pasGCPList[iGCP].dfGCPX;
+	    padfTiePoints[iGCP*6+4] = pasGCPList[iGCP].dfGCPY;
+	    padfTiePoints[iGCP*6+5] = pasGCPList[iGCP].dfGCPZ;
+	}
+
+	TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 
+		      6 * GetGCPCount(), padfTiePoints );
+	CPLFree( padfTiePoints );
+	
+        CPLFree( pszProjection );
+	pszProjection = CPLStrdup( GetGCPProjection() );
+    }
+
+/* -------------------------------------------------------------------- */
+/*	Write out projection definition.				*/
+/* -------------------------------------------------------------------- */
+    if( pszProjection != NULL && !EQUAL( pszProjection, "" ) )
+    {
+        GTIF	*psGTIF;
+
+        psGTIF = GTIFNew( hTIFF );
+        GTIFSetFromOGISDefn( psGTIF, pszProjection );
+
+        if( GetMetadataItem( GDALMD_AREA_OR_POINT ) 
+            && EQUAL(GetMetadataItem(GDALMD_AREA_OR_POINT),
+                     GDALMD_AOP_POINT) )
+        {
+            GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
+                       RasterPixelIsPoint);
+        }
+
+        GTIFWriteKeys( psGTIF );
+        GTIFFree( psGTIF );
+    }
+}
+
+/************************************************************************/
+/*                         AppendMetadataItem()                         */
+/************************************************************************/
+
+static void AppendMetadataItem( CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
+                                const char *pszKey, const char *pszValue,
+                                int nBand, const char *pszRole, 
+                                const char *pszDomain )
+
+{
+    char szBandId[32];
+    CPLXMLNode *psItem;
+
+/* -------------------------------------------------------------------- */
+/*      Create the Item element, and subcomponents.                     */
+/* -------------------------------------------------------------------- */
+    psItem = CPLCreateXMLNode( NULL, CXT_Element, "Item" );
+    CPLCreateXMLNode( CPLCreateXMLNode( psItem, CXT_Attribute, "name"),
+                      CXT_Text, pszKey );
+
+    if( nBand > 0 )
+    {
+        sprintf( szBandId, "%d", nBand - 1 );
+        CPLCreateXMLNode( CPLCreateXMLNode( psItem,CXT_Attribute,"sample"),
+                          CXT_Text, szBandId );
+    }
+
+    if( pszRole != NULL )
+        CPLCreateXMLNode( CPLCreateXMLNode( psItem,CXT_Attribute,"role"),
+                          CXT_Text, pszRole );
+
+    if( pszDomain != NULL && strlen(pszDomain) > 0 )
+        CPLCreateXMLNode( CPLCreateXMLNode( psItem,CXT_Attribute,"domain"),
+                          CXT_Text, pszDomain );
+
+    char *pszEscapedItemValue = CPLEscapeString(pszValue,-1,CPLES_XML);
+    CPLCreateXMLNode( psItem, CXT_Text, pszEscapedItemValue );
+    CPLFree( pszEscapedItemValue );
+
+/* -------------------------------------------------------------------- */
+/*      Create root, if missing.                                        */
+/* -------------------------------------------------------------------- */
+    if( *ppsRoot == NULL )
+        *ppsRoot = CPLCreateXMLNode( NULL, CXT_Element, "GDALMetadata" );
+
+/* -------------------------------------------------------------------- */
+/*      Append item to tail.  We keep track of the tail to avoid        */
+/*      O(nsquared) time as the list gets longer.                       */
+/* -------------------------------------------------------------------- */
+    if( *ppsTail == NULL )
+        CPLAddXMLChild( *ppsRoot, psItem );
+    else
+        CPLAddXMLSibling( *ppsTail, psItem );
+    
+    *ppsTail = psItem;
+}
+
+/************************************************************************/
+/*                         WriteMDMDMetadata()                          */
+/************************************************************************/
+
+static void WriteMDMetadata( GDALMultiDomainMetadata *poMDMD, TIFF *hTIFF,
+                             CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail, 
+                             int nBand )
+
+{
+    int iDomain;
+    char **papszDomainList;
+
+/* ==================================================================== */
+/*      Process each domain.                                            */
+/* ==================================================================== */
+    papszDomainList = poMDMD->GetDomainList();
+    for( iDomain = 0; papszDomainList && papszDomainList[iDomain]; iDomain++ )
+    {
+        char **papszMD = poMDMD->GetMetadata( papszDomainList[iDomain] );
+        int iItem;
+
+/* -------------------------------------------------------------------- */
+/*      Process each item in this domain.                               */
+/* -------------------------------------------------------------------- */
+        for( iItem = 0; papszMD && papszMD[iItem]; iItem++ )
+        {
+            const char *pszItemValue;
+            char *pszItemName = NULL;
+            
+            pszItemValue = CPLParseNameValue( papszMD[iItem], &pszItemName );
+            
+/* -------------------------------------------------------------------- */
+/*      Convert into XML item or handle as a special TIFF tag.          */
+/* -------------------------------------------------------------------- */
+            if( strlen(papszDomainList[iDomain]) == 0 
+                && nBand == 0 && EQUALN(pszItemName,"TIFFTAG_",8) )
+            {
+                if( EQUAL(pszItemName,"TIFFTAG_DOCUMENTNAME") )
+                    TIFFSetField( hTIFF, TIFFTAG_DOCUMENTNAME, pszItemValue );
+                else if( EQUAL(pszItemName,"TIFFTAG_IMAGEDESCRIPTION") )
+                    TIFFSetField( hTIFF, TIFFTAG_IMAGEDESCRIPTION, pszItemValue );
+                else if( EQUAL(pszItemName,"TIFFTAG_SOFTWARE") )
+                    TIFFSetField( hTIFF, TIFFTAG_SOFTWARE, pszItemValue );
+                else if( EQUAL(pszItemName,"TIFFTAG_DATETIME") )
+                    TIFFSetField( hTIFF, TIFFTAG_DATETIME, pszItemValue );
+                else if( EQUAL(pszItemName,"TIFFTAG_XRESOLUTION") )
+                    TIFFSetField( hTIFF, TIFFTAG_XRESOLUTION, atof(pszItemValue) );
+                else if( EQUAL(pszItemName,"TIFFTAG_YRESOLUTION") )
+                    TIFFSetField( hTIFF, TIFFTAG_YRESOLUTION, atof(pszItemValue) );
+                else if( EQUAL(pszItemName,"TIFFTAG_RESOLUTIONUNIT") )
+                    TIFFSetField( hTIFF, TIFFTAG_RESOLUTIONUNIT, atoi(pszItemValue) );
+            }
+            else if( nBand == 0 && EQUAL(pszItemName,GDALMD_AREA_OR_POINT) )
+                /* do nothing, handled elsewhere */;
+            else
+                AppendMetadataItem( ppsRoot, ppsTail, 
+                                    pszItemName, pszItemValue, 
+                                    nBand, NULL, papszDomainList[iDomain] );
+
+            CPLFree( pszItemName );
+        }
+    }
+}
+
+/************************************************************************/
+/*                           WriteMetadata()                            */
+/************************************************************************/
+
+void GTiffDataset::WriteMetadata( GDALDataset *poSrcDS, TIFF *hTIFF,
+                                  int bSrcIsGeoTIFF )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Convert all the remaining metadata into a simple XML            */
+/*      format.                                                         */
+/* -------------------------------------------------------------------- */
+    CPLXMLNode *psRoot = NULL, *psTail = NULL;
+
+    if( bSrcIsGeoTIFF )
+    {
+        WriteMDMetadata( &(((GTiffDataset *)poSrcDS)->oMDMD), 
+                         hTIFF, &psRoot, &psTail, 0 );
+    }
+    else
+    {
+        char **papszMD = poSrcDS->GetMetadata();
+
+        if( CSLCount(papszMD) > 0 )
+        {
+            GDALMultiDomainMetadata oMDMD;
+            oMDMD.SetMetadata( papszMD );
+
+            WriteMDMetadata( &oMDMD, hTIFF, &psRoot, &psTail, 0 );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      We also need to address band specific metadata, and special     */
+/*      "role" metadata.                                                */
+/* -------------------------------------------------------------------- */
+    int nBand;
+    for( nBand = 1; nBand <= poSrcDS->GetRasterCount(); nBand++ )
+    {
+        GDALRasterBand *poBand = poSrcDS->GetRasterBand( nBand );
+
+        if( bSrcIsGeoTIFF )
+        {
+            WriteMDMetadata( &(((GTiffRasterBand *)poBand)->oMDMD), 
+                             hTIFF, &psRoot, &psTail, nBand );
+        }
+        else
+        {
+            char **papszMD = poBand->GetMetadata();
+            
+            if( CSLCount(papszMD) > 0 )
+            {
+                GDALMultiDomainMetadata oMDMD;
+                oMDMD.SetMetadata( papszMD );
+                
+                WriteMDMetadata( &oMDMD, hTIFF, &psRoot, &psTail, nBand );
+            }
+        }
+
+        int bSuccess;
+        double dfOffset = poBand->GetOffset( &bSuccess );
+        double dfScale = poBand->GetScale();
+
+        if( bSuccess && (dfOffset != 0.0 || dfScale != 1.0) )
+        {
+            char szValue[128];
+
+            sprintf( szValue, "%.16g", dfOffset );
+            AppendMetadataItem( &psRoot, &psTail, "OFFSET", szValue, nBand, 
+                                "offset", "" );
+            sprintf( szValue, "%.16g", dfScale );
+            AppendMetadataItem( &psRoot, &psTail, "SCALE", szValue, nBand, 
+                                "scale", "" );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Write out the generic XML metadata if there is any.             */
+/* -------------------------------------------------------------------- */
+    if( psRoot != NULL )
+    {
+        char *pszXML_MD = CPLSerializeXMLTree( psRoot );
+        if( strlen(pszXML_MD) > 32000 )
+        {
+            CPLError( CE_Warning, CPLE_AppDefined, 
+                      "Lost metadata writing to GeoTIFF ... too large to fit in tag." );
+        }
+        else
+        {
+            TIFFSetField( hTIFF, TIFFTAG_GDAL_METADATA, pszXML_MD );
+        }
+        CPLFree( pszXML_MD );
+        CPLDestroyXMLNode( psRoot );
+    }
+}
+
+/************************************************************************/
+/*                         WriteNoDataValue()                           */
+/************************************************************************/
+
+void GTiffDataset::WriteNoDataValue( TIFF *hTIFF, double dfNoData )
+
+{
+    const char *pszText;
+    
+    pszText = CPLSPrintf( "%.16g", dfNoData );
+    TIFFSetField( hTIFF, TIFFTAG_GDAL_NODATA, pszText );
+}
+
+/************************************************************************/
+/*                            SetDirectory()                            */
+/************************************************************************/
+
+int GTiffDataset::SetDirectory( uint32 nNewOffset )
+
+{
+    Crystalize();
+
+    if( nNewOffset == 0 )
+        nNewOffset = nDirOffset;
+
+    if( nNewOffset == 0)
+        return TRUE;
+
+    if( TIFFCurrentDirOffset(hTIFF) == nNewOffset )
+        return TRUE;
+
+    if( GetAccess() == GA_Update )
+        TIFFFlush( hTIFF );
+    
+    return TIFFSetSubDirectory( hTIFF, nNewOffset );
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *GTiffDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    TIFF	*hTIFF;
+
+/* -------------------------------------------------------------------- */
+/*      We have a special hook for handling opening a specific          */
+/*      directory of a TIFF file.                                       */
+/* -------------------------------------------------------------------- */
+    if( EQUALN(poOpenInfo->pszFilename,"GTIFF_DIR:",10) )
+        return OpenDir( poOpenInfo->pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*	First we check to see if the file has the expected header	*/
+/*	bytes.								*/    
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->nHeaderBytes < 2 )
+        return NULL;
+
+    if( (poOpenInfo->pabyHeader[0] != 'I' || poOpenInfo->pabyHeader[1] != 'I')
+        && (poOpenInfo->pabyHeader[0] != 'M' || poOpenInfo->pabyHeader[1] != 'M'))
+        return NULL;
+
+    if( poOpenInfo->pabyHeader[2] == 43 && poOpenInfo->pabyHeader[3] == 0 )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "This is a BigTIFF file.  BigTIFF is not supported by this\n"
+                  "version of GDAL and libtiff." );
+        return NULL;
+    }
+
+    if( (poOpenInfo->pabyHeader[2] != 0x2A || poOpenInfo->pabyHeader[3] != 0)
+        && (poOpenInfo->pabyHeader[3] != 0x2A || poOpenInfo->pabyHeader[2] != 0) )
+        return NULL;
+
+    GTiffOneTimeInit();
+
+/* -------------------------------------------------------------------- */
+/*      Try opening the dataset.                                        */
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->eAccess == GA_ReadOnly )
+	hTIFF = VSI_TIFFOpen( poOpenInfo->pszFilename, "r" );
+    else
+        hTIFF = VSI_TIFFOpen( poOpenInfo->pszFilename, "r+" );
+    
+    if( hTIFF == NULL )
+        return( NULL );
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    GTiffDataset 	*poDS;
+
+    poDS = new GTiffDataset();
+    poDS->SetDescription( poOpenInfo->pszFilename );
+
+    if( poDS->OpenOffset( hTIFF,TIFFCurrentDirOffset(hTIFF), TRUE,
+                          poOpenInfo->eAccess ) != CE_None )
+    {
+        delete poDS;
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for external overviews.                                   */
+/* -------------------------------------------------------------------- */
+    poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
+    
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+    poDS->ApplyPamInfo();
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                            ApplyPamInfo()                            */
+/*                                                                      */
+/*      PAM Information, if available, overrides the GeoTIFF            */
+/*      geotransform and projection definition.  Check for them         */
+/*      now.                                                            */
+/************************************************************************/
+
+void GTiffDataset::ApplyPamInfo()
+
+{
+    double adfPamGeoTransform[6];
+
+    if( GDALPamDataset::GetGeoTransform( adfPamGeoTransform ) == CE_None )
+    {
+        memcpy( adfGeoTransform, adfPamGeoTransform, sizeof(double)*6 );
+        bGeoTransformValid = TRUE;
+    }
+
+    const char *pszPamSRS = GDALPamDataset::GetProjectionRef();
+
+    if( pszPamSRS != NULL && strlen(pszPamSRS) > 0 )
+    {
+        CPLFree( pszProjection );
+        pszProjection = CPLStrdup( pszPamSRS );
+    }
+}
+
+/************************************************************************/
+/*                              OpenDir()                               */
+/*                                                                      */
+/*      Open a specific directory as encoded into a filename.           */
+/************************************************************************/
+
+GDALDataset *GTiffDataset::OpenDir( const char *pszCompositeName )
+
+{
+    if( !EQUALN(pszCompositeName,"GTIFF_DIR:",10) )
+        return NULL;
+    
+/* -------------------------------------------------------------------- */
+/*      Split out filename, and dir#/offset.                            */
+/* -------------------------------------------------------------------- */
+    const char *pszFilename = pszCompositeName + 10;
+    int        bAbsolute = FALSE;
+    uint32     nOffset;
+    
+    if( EQUALN(pszFilename,"off:",4) )
+    {
+        bAbsolute = TRUE;
+        pszFilename += 4;
+    }
+
+    nOffset = atol(pszFilename);
+    pszFilename += 1;
+
+    while( *pszFilename != '\0' && pszFilename[-1] != ':' )
+        pszFilename++;
+
+    if( *pszFilename == '\0' || nOffset == 0 )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Unable to extract offset or filename, should take the form\n"
+                  "GTIFF_DIR:<dir>:filename or GTIFF_DIR:off:<dir_offset>:filename" );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try opening the dataset.                                        */
+/* -------------------------------------------------------------------- */
+    TIFF	*hTIFF;
+
+    GTiffOneTimeInit();
+
+    hTIFF = VSI_TIFFOpen( pszFilename, "r" );
+    if( hTIFF == NULL )
+        return( NULL );
+
+/* -------------------------------------------------------------------- */
+/*      If a directory was requested by index, advance to it now.       */
+/* -------------------------------------------------------------------- */
+    if( !bAbsolute )
+    {
+        while( nOffset > 1 )
+        {
+            if( TIFFReadDirectory( hTIFF ) == 0 )
+            {
+                XTIFFClose( hTIFF );
+                CPLError( CE_Failure, CPLE_OpenFailed, 
+                          "Requested directory %d not found." );
+                return NULL;
+            }
+            nOffset--;
+        }
+
+        nOffset = TIFFCurrentDirOffset( hTIFF );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    GTiffDataset 	*poDS;
+
+    poDS = new GTiffDataset();
+    poDS->SetDescription( pszFilename );
+
+    if( poDS->OpenOffset( hTIFF, nOffset, FALSE, GA_ReadOnly ) != CE_None )
+    {
+        delete poDS;
+        return NULL;
+    }
+    else
+    {
+        return poDS;
+    }
+}
+
+/************************************************************************/
+/*                             OpenOffset()                             */
+/*                                                                      */
+/*      Initialize the GTiffDataset based on a passed in file           */
+/*      handle, and directory offset to utilize.  This is called for    */
+/*      full res, and overview pages.                                   */
+/************************************************************************/
+
+CPLErr GTiffDataset::OpenOffset( TIFF *hTIFFIn, uint32 nDirOffsetIn, 
+				 int bBaseIn, GDALAccess eAccess )
+
+{
+    uint32	nXSize, nYSize;
+    int		bTreatAsBitmap = FALSE;
+    int         bTreatAsOdd = FALSE;
+
+    hTIFF = hTIFFIn;
+
+    nDirOffset = nDirOffsetIn;
+
+    SetDirectory( nDirOffsetIn );
+
+    bBase = bBaseIn;
+
+    this->eAccess = eAccess;
+
+/* -------------------------------------------------------------------- */
+/*      Capture some information from the file that is of interest.     */
+/* -------------------------------------------------------------------- */
+    TIFFGetField( hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize );
+    TIFFGetField( hTIFF, TIFFTAG_IMAGELENGTH, &nYSize );
+    nRasterXSize = nXSize;
+    nRasterYSize = nYSize;
+
+    if( !TIFFGetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSamplesPerPixel ) )
+        nBands = 1;
+    else
+        nBands = nSamplesPerPixel;
+    
+    if( !TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &(nBitsPerSample)) )
+        nBitsPerSample = 1;
+    
+    if( !TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &(nPlanarConfig) ) )
+        nPlanarConfig = PLANARCONFIG_CONTIG;
+    
+    if( !TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric) ) )
+        nPhotometric = PHOTOMETRIC_MINISBLACK;
+    
+    if( !TIFFGetField( hTIFF, TIFFTAG_SAMPLEFORMAT, &(nSampleFormat) ) )
+        nSampleFormat = SAMPLEFORMAT_UINT;
+    
+    if( !TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &(nCompression) ) )
+        nCompression = COMPRESSION_NONE;
+
+/* -------------------------------------------------------------------- */
+/*      Get strip/tile layout.                                          */
+/* -------------------------------------------------------------------- */
+    if( TIFFIsTiled(hTIFF) )
+    {
+        TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &(nBlockXSize) );
+        TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &(nBlockYSize) );
+    }
+    else
+    {
+        if( !TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP,
+                           &(nRowsPerStrip) ) )
+        {
+            CPLError( CE_Warning, CPLE_AppDefined, 
+                      "RowsPerStrip not defined ... assuming all one strip." );
+            nRowsPerStrip = nYSize; /* dummy value */
+        }
+
+        nBlockXSize = nRasterXSize;
+        nBlockYSize = MIN(nRowsPerStrip,nYSize);
+    }
+        
+    nBlocksPerBand =
+        ((nYSize + nBlockYSize - 1) / nBlockYSize)
+        * ((nXSize + nBlockXSize  - 1) / nBlockXSize);
+
+/* -------------------------------------------------------------------- */
+/*      Should we handle this using the GTiffBitmapBand?                */
+/* -------------------------------------------------------------------- */
+    if( nBitsPerSample == 1 && nBands == 1 )
+        bTreatAsBitmap = TRUE;
+
+/* -------------------------------------------------------------------- */
+/*      Should we treat this via the RGBA interface?                    */
+/* -------------------------------------------------------------------- */
+    if( !bTreatAsBitmap && !(nBitsPerSample > 8) 
+        && nPhotometric == PHOTOMETRIC_CIELAB ||
+        nPhotometric == PHOTOMETRIC_LOGL ||
+        nPhotometric == PHOTOMETRIC_LOGLUV ||
+        ( nPhotometric == PHOTOMETRIC_YCBCR 
+          && CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB",
+                                                "YES") ))  )
+    {
+        char	szMessage[1024];
+
+        if( TIFFRGBAImageOK( hTIFF, szMessage ) == 1 )
+        {
+            bTreatAsRGBA = TRUE;
+            nBands = 4;
+        }
+        else
+        {
+            CPLDebug( "GTiff", "TIFFRGBAImageOK says:\n%s", szMessage );
+        }
+    }
+        
+/* -------------------------------------------------------------------- */
+/*      Should we treat this via the odd bits interface?                */
+/* -------------------------------------------------------------------- */
+    if ( nSampleFormat == SAMPLEFORMAT_IEEEFP )
+    {
+        if ( nBitsPerSample == 16 || nBitsPerSample == 24 )
+            bTreatAsOdd = TRUE;
+    }
+    else if ( !bTreatAsRGBA && !bTreatAsBitmap
+              && nBitsPerSample != 8
+              && nBitsPerSample != 16
+              && nBitsPerSample != 32
+              && nBitsPerSample != 64 
+              && nBitsPerSample != 128 )
+        bTreatAsOdd = TRUE;
+
+/* -------------------------------------------------------------------- */
+/*      Capture the color table if there is one.                        */
+/* -------------------------------------------------------------------- */
+    unsigned short	*panRed, *panGreen, *panBlue;
+
+    if( bTreatAsRGBA 
+        || TIFFGetField( hTIFF, TIFFTAG_COLORMAP, 
+                         &panRed, &panGreen, &panBlue) == 0 )
+    {
+	// Build inverted palette if we have inverted photometric.
+	// Pixel values remains unchanged.
+	if( nPhotometric == PHOTOMETRIC_MINISWHITE )
+	{
+	    GDALColorEntry  oEntry;
+	    int		    iColor, nColorCount;
+	    
+	    poColorTable = new GDALColorTable();
+	    nColorCount = 1 << nBitsPerSample;
+
+	    for ( iColor = 0; iColor < nColorCount; iColor++ )
+	    {
+		oEntry.c1 = oEntry.c2 = oEntry.c3 = 
+                    (255 * (nColorCount - 1 - iColor)) / (nColorCount-1);
+		oEntry.c4 = 255;
+		poColorTable->SetColorEntry( iColor, &oEntry );
+	    }
+
+	    nPhotometric = PHOTOMETRIC_PALETTE;
+	}
+	else
+	    poColorTable = NULL;
+    }
+    else
+    {
+        int	nColorCount;
+        GDALColorEntry oEntry;
+
+        poColorTable = new GDALColorTable();
+
+        nColorCount = 1 << nBitsPerSample;
+
+        for( int iColor = nColorCount - 1; iColor >= 0; iColor-- )
+        {
+            oEntry.c1 = panRed[iColor] / 256;
+            oEntry.c2 = panGreen[iColor] / 256;
+            oEntry.c3 = panBlue[iColor] / 256;
+            oEntry.c4 = 255;
+
+            poColorTable->SetColorEntry( iColor, &oEntry );
+        }
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    for( int iBand = 0; iBand < nBands; iBand++ )
+    {
+        if( bTreatAsRGBA )
+            SetBand( iBand+1, new GTiffRGBABand( this, iBand+1 ) );
+        else if( bTreatAsBitmap )
+            SetBand( iBand+1, new GTiffBitmapBand( this, iBand+1 ) );
+        else if( bTreatAsOdd )
+            SetBand( iBand+1, new GTiffOddBitsBand( this, iBand+1 ) );
+        else
+            SetBand( iBand+1, new GTiffRasterBand( this, iBand+1 ) );
+    }
+
+    if( GetRasterBand(1)->GetRasterDataType() == GDT_Unknown )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported,
+                  "Unsupported TIFF configuration." );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get the transform or gcps from the GeoTIFF file.                */
+/* -------------------------------------------------------------------- */
+    if( bBaseIn )
+    {
+        char    *pszTabWKT = NULL;
+        double	*padfTiePoints, *padfScale, *padfMatrix;
+        uint16	nCount;
+
+        adfGeoTransform[0] = 0.0;
+        adfGeoTransform[1] = 1.0;
+        adfGeoTransform[2] = 0.0;
+        adfGeoTransform[3] = 0.0;
+        adfGeoTransform[4] = 0.0;
+        adfGeoTransform[5] = 1.0;
+    
+        if( TIFFGetField(hTIFF,TIFFTAG_GEOPIXELSCALE,&nCount,&padfScale )
+            && nCount >= 2 
+            && padfScale[0] != 0.0 && padfScale[1] != 0.0 )
+        {
+            adfGeoTransform[1] = padfScale[0];
+            adfGeoTransform[5] = - ABS(padfScale[1]);
+
+            if( TIFFGetField(hTIFF,TIFFTAG_GEOTIEPOINTS,&nCount,&padfTiePoints )
+                && nCount >= 6 )
+            {
+                adfGeoTransform[0] =
+                    padfTiePoints[3] - padfTiePoints[0] * adfGeoTransform[1];
+                adfGeoTransform[3] =
+                    padfTiePoints[4] - padfTiePoints[1] * adfGeoTransform[5];
+
+                bGeoTransformValid = TRUE;
+            }
+        }
+
+        else if( TIFFGetField(hTIFF,TIFFTAG_GEOTRANSMATRIX,&nCount,&padfMatrix ) 
+                 && nCount == 16 )
+        {
+            adfGeoTransform[0] = padfMatrix[3];
+            adfGeoTransform[1] = padfMatrix[0];
+            adfGeoTransform[2] = padfMatrix[1];
+            adfGeoTransform[3] = padfMatrix[7];
+            adfGeoTransform[4] = padfMatrix[4];
+            adfGeoTransform[5] = padfMatrix[5];
+            bGeoTransformValid = TRUE;
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Otherwise try looking for a .tfw, .tifw or .wld file.           */
+/* -------------------------------------------------------------------- */
+        else
+        {
+            bGeoTransformValid = 
+                GDALReadWorldFile( GetDescription(), "tfw", adfGeoTransform );
+
+            if( !bGeoTransformValid )
+            {
+                bGeoTransformValid = 
+                    GDALReadWorldFile( GetDescription(), "tifw", adfGeoTransform );
+            }
+            if( !bGeoTransformValid )
+            {
+                bGeoTransformValid = 
+                    GDALReadWorldFile( GetDescription(), "wld", adfGeoTransform );
+            }
+            if( !bGeoTransformValid )
+            {
+                int bTabFileOK = 
+                    GDALReadTabFile( GetDescription(), adfGeoTransform, 
+                                     &pszTabWKT, &nGCPCount, &pasGCPList );
+
+                if( bTabFileOK && nGCPCount == 0 )
+                    bGeoTransformValid = TRUE;
+            }
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Check for GCPs.  Note, we will allow there to be GCPs and a     */
+/*      transform in some circumstances.                                */
+/* -------------------------------------------------------------------- */
+        if( TIFFGetField(hTIFF,TIFFTAG_GEOTIEPOINTS,&nCount,&padfTiePoints )
+            && !bGeoTransformValid )
+        {
+            nGCPCount = nCount / 6;
+            pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPCount);
+        
+            for( int iGCP = 0; iGCP < nGCPCount; iGCP++ )
+            {
+                char	szID[32];
+
+                sprintf( szID, "%d", iGCP+1 );
+                pasGCPList[iGCP].pszId = CPLStrdup( szID );
+                pasGCPList[iGCP].pszInfo = CPLStrdup("");
+                pasGCPList[iGCP].dfGCPPixel = padfTiePoints[iGCP*6+0];
+                pasGCPList[iGCP].dfGCPLine = padfTiePoints[iGCP*6+1];
+                pasGCPList[iGCP].dfGCPX = padfTiePoints[iGCP*6+3];
+                pasGCPList[iGCP].dfGCPY = padfTiePoints[iGCP*6+4];
+                pasGCPList[iGCP].dfGCPZ = padfTiePoints[iGCP*6+5];
+            }
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Capture the GeoTIFF projection, if available.                   */
+/* -------------------------------------------------------------------- */
+        GTIF 	*hGTIF;
+        GTIFDefn	sGTIFDefn;
+
+        CPLFree( pszProjection );
+        pszProjection = NULL;
+    
+        hGTIF = GTIFNew(hTIFF);
+
+        if ( !hGTIF )
+        {
+            CPLError( CE_Warning, CPLE_AppDefined,
+                      "GeoTIFF tags apparently corrupt, they are being ignored." );
+        }
+        else
+        {
+            if( GTIFGetDefn( hGTIF, &sGTIFDefn ) )
+            {
+                pszProjection = GTIFGetOGISDefn( hGTIF, &sGTIFDefn );
+            }
+
+            // Is this a pixel-is-point dataset?
+            short nRasterType;
+            
+
+            if( GTIFKeyGet(hGTIF, GTRasterTypeGeoKey, &nRasterType, 
+                           0, 1 ) == 1 )
+            {
+                if( nRasterType == (short) RasterPixelIsPoint )
+                    SetMetadataItem( GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT );
+                else
+                    SetMetadataItem( GDALMD_AREA_OR_POINT, GDALMD_AOP_AREA );
+            }
+
+            GTIFFree( hGTIF );
+        }
+        
+/* -------------------------------------------------------------------- */
+/*      If we didn't get a geotiff projection definition, but we did    */
+/*      get one from the .tab file, use that instead.                   */
+/* -------------------------------------------------------------------- */
+        if( pszTabWKT != NULL 
+            && (pszProjection == NULL || pszProjection[0] == '\0') )
+        {
+            CPLFree( pszProjection );
+            pszProjection = pszTabWKT;
+            pszTabWKT = NULL;
+        }
+        
+        if( pszProjection == NULL )
+        {
+            pszProjection = CPLStrdup( "" );
+        }
+        
+        if( pszTabWKT != NULL )
+            CPLFree( pszTabWKT );
+
+        bGeoTIFFInfoChanged = FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Capture some other potentially interesting information.         */
+/* -------------------------------------------------------------------- */
+        char	*pszText, szWorkMDI[200];
+        float   fResolution;
+        uint16  nResUnits;
+
+        if( TIFFGetField( hTIFF, TIFFTAG_DOCUMENTNAME, &pszText ) )
+            SetMetadataItem( "TIFFTAG_DOCUMENTNAME",  pszText );
+
+        if( TIFFGetField( hTIFF, TIFFTAG_IMAGEDESCRIPTION, &pszText ) )
+            SetMetadataItem( "TIFFTAG_IMAGEDESCRIPTION", pszText );
+
+        if( TIFFGetField( hTIFF, TIFFTAG_SOFTWARE, &pszText ) )
+            SetMetadataItem( "TIFFTAG_SOFTWARE", pszText );
+
+        if( TIFFGetField( hTIFF, TIFFTAG_DATETIME, &pszText ) )
+            SetMetadataItem( "TIFFTAG_DATETIME", pszText );
+
+        if( TIFFGetField( hTIFF, TIFFTAG_XRESOLUTION, &fResolution ) )
+        {
+            sprintf( szWorkMDI, "%.8g", fResolution );
+            SetMetadataItem( "TIFFTAG_XRESOLUTION", szWorkMDI );
+        }
+
+        if( TIFFGetField( hTIFF, TIFFTAG_YRESOLUTION, &fResolution ) )
+        {
+            sprintf( szWorkMDI, "%.8g", fResolution );
+            SetMetadataItem( "TIFFTAG_YRESOLUTION", szWorkMDI );
+        }
+
+        if( TIFFGetField( hTIFF, TIFFTAG_RESOLUTIONUNIT, &nResUnits ) )
+        {
+            if( nResUnits == RESUNIT_NONE )
+                sprintf( szWorkMDI, "%d (unitless)", nResUnits );
+            else if( nResUnits == RESUNIT_INCH )
+                sprintf( szWorkMDI, "%d (pixels/inch)", nResUnits );
+            else if( nResUnits == RESUNIT_CENTIMETER )
+                sprintf( szWorkMDI, "%d (pixels/cm)", nResUnits );
+            else
+                sprintf( szWorkMDI, "%d", nResUnits );
+            SetMetadataItem( "TIFFTAG_RESOLUTIONUNIT", szWorkMDI );
+        }
+
+        if( nCompression == COMPRESSION_NONE )
+            /* no compression tag */;
+        else if( nCompression == COMPRESSION_CCITTRLE )
+            SetMetadataItem( "COMPRESSION", "CCITTRLE", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_CCITTFAX3 )
+            SetMetadataItem( "COMPRESSION", "CCITTFAX3", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_CCITTFAX4 )
+            SetMetadataItem( "COMPRESSION", "CCITTFAX4", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_LZW )
+            SetMetadataItem( "COMPRESSION", "LZW", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_OJPEG )
+            SetMetadataItem( "COMPRESSION", "OJPEG", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_JPEG )
+            SetMetadataItem( "COMPRESSION", "JPEG", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_NEXT )
+            SetMetadataItem( "COMPRESSION", "NEXT", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_CCITTRLEW )
+            SetMetadataItem( "COMPRESSION", "CCITTRLEW", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_PACKBITS )
+            SetMetadataItem( "COMPRESSION", "PACKBITS", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_THUNDERSCAN )
+            SetMetadataItem( "COMPRESSION", "THUNDERSCAN", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_PIXARFILM )
+            SetMetadataItem( "COMPRESSION", "PIXARFILM", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_PIXARLOG )
+            SetMetadataItem( "COMPRESSION", "PIXARLOG", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_DEFLATE )
+            SetMetadataItem( "COMPRESSION", "DEFLATE", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_ADOBE_DEFLATE )
+            SetMetadataItem( "COMPRESSION", "DEFLATE", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_DCS )
+            SetMetadataItem( "COMPRESSION", "DCS", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_JBIG )
+            SetMetadataItem( "COMPRESSION", "JBIG", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_SGILOG )
+            SetMetadataItem( "COMPRESSION", "SGILOG", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_SGILOG24 )
+            SetMetadataItem( "COMPRESSION", "SGILOG24", "IMAGE_STRUCTURE" );
+        else if( nCompression == COMPRESSION_JP2000 )
+            SetMetadataItem( "COMPRESSION", "JP2000", "IMAGE_STRUCTURE" );
+        else
+        {
+            CPLString oComp;
+            SetMetadataItem( "COMPRESSION", 
+                             (const char *) oComp.Printf( "%d", nCompression));
+        }
+
+        if( nBitsPerSample < 8 )
+        {
+            for (int i = 0; i < nBands; ++i)
+                GetRasterBand(i+1)->SetMetadataItem( "NBITS", 
+                    CPLString().Printf( "%ld", nBitsPerSample ) );
+        }
+        
+        if( TIFFGetField( hTIFF, TIFFTAG_GDAL_METADATA, &pszText ) )
+        {
+            CPLXMLNode *psRoot = CPLParseXMLString( pszText );
+            CPLXMLNode *psItem = NULL;
+
+            if( psRoot != NULL && psRoot->eType == CXT_Element
+                && EQUAL(psRoot->pszValue,"GDALMetadata") )
+                psItem = psRoot->psChild;
+
+            for( ; psItem != NULL; psItem = psItem->psNext )
+            {
+                const char *pszKey, *pszValue, *pszRole, *pszDomain; 
+                char *pszUnescapedValue;
+                int nBand;
+
+                if( psItem->eType != CXT_Element
+                    || !EQUAL(psItem->pszValue,"Item") )
+                    continue;
+
+                pszKey = CPLGetXMLValue( psItem, "name", NULL );
+                pszValue = CPLGetXMLValue( psItem, NULL, NULL );
+                nBand = atoi(CPLGetXMLValue( psItem, "sample", "-1" )) + 1;
+                pszRole = CPLGetXMLValue( psItem, "role", "" );
+                pszDomain = CPLGetXMLValue( psItem, "domain", "" );
+                
+                if( pszKey == NULL || pszValue == NULL )
+                    continue;
+
+                pszUnescapedValue = CPLUnescapeString( pszValue, NULL, 
+                                                       CPLES_XML );
+                if( nBand == 0 )
+                    SetMetadataItem( pszKey, pszUnescapedValue, pszDomain );
+                else
+                {
+                    GDALRasterBand *poBand = GetRasterBand(nBand);
+                    if( poBand != NULL )
+                    {
+                        if( EQUAL(pszRole,"scale") )
+                            poBand->SetScale( atof(pszUnescapedValue) );
+                        else if( EQUAL(pszRole,"offset") )
+                            poBand->SetOffset( atof(pszUnescapedValue) );
+                        else
+                            poBand->SetMetadataItem(pszKey,pszUnescapedValue,
+                                                    pszDomain );
+                    }
+                }
+                CPLFree( pszUnescapedValue );
+            }
+
+            CPLDestroyXMLNode( psRoot );
+        }
+
+        bMetadataChanged = FALSE;
+	
+        if( TIFFGetField( hTIFF, TIFFTAG_GDAL_NODATA, &pszText ) )
+        {
+	    bNoDataSet = TRUE;
+	    dfNoDataValue = atof( pszText );
+	}
+
+	bNoDataChanged = FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If this is a "base" raster, we should scan for any              */
+/*      associated overviews.                                           */
+/* -------------------------------------------------------------------- */
+    if( bBase )
+    {
+        while( !TIFFLastDirectory( hTIFF ) 
+               && TIFFReadDirectory( hTIFF ) != 0 )
+        {
+            uint32	nThisDir = TIFFCurrentDirOffset(hTIFF);
+            uint32	nSubType;
+
+            if( TIFFGetField(hTIFF, TIFFTAG_SUBFILETYPE, &nSubType)
+                && (nSubType & FILETYPE_REDUCEDIMAGE) )
+            {
+                GTiffDataset	*poODS;
+
+                poODS = new GTiffDataset();
+                if( poODS->OpenOffset( hTIFF, nThisDir, FALSE, 
+                                       eAccess ) != CE_None 
+                    || poODS->GetRasterCount() != GetRasterCount() )
+                {
+                    delete poODS;
+                }
+                else
+                {
+                    CPLDebug( "GTiff", "Opened %dx%d overview.\n", 
+                              poODS->GetRasterXSize(), poODS->GetRasterYSize());
+                    nOverviewCount++;
+                    papoOverviewDS = (GTiffDataset **)
+                        CPLRealloc(papoOverviewDS, 
+                                   nOverviewCount * (sizeof(void*)));
+                    papoOverviewDS[nOverviewCount-1] = poODS;
+                }
+            }
+
+            SetDirectory( nThisDir );
+        }
+    }
+
+    return( CE_None );
+}
+
+/************************************************************************/
+/*                              SetupTFW()                              */
+/************************************************************************/
+
+void GTiffDataset::SetupTFW( const char *pszTIFFilename )
+
+{
+    char	*pszPath;
+    char	*pszBasename;
+    
+    pszPath = CPLStrdup( CPLGetPath(pszTIFFilename) );
+    pszBasename = CPLStrdup( CPLGetBasename(pszTIFFilename) );
+
+    pszTFWFilename = CPLStrdup( CPLFormFilename(pszPath,pszBasename,"tfw") );
+    
+    CPLFree( pszPath );
+    CPLFree( pszBasename );
+}
+
+/************************************************************************/
+/*                            GTiffCreate()                             */
+/*                                                                      */
+/*      Shared functionality between GTiffDataset::Create() and         */
+/*      GTiffCreateCopy() for creating TIFF file based on a set of      */
+/*      options and a configuration.                                    */
+/************************************************************************/
+
+TIFF *GTiffCreate( const char * pszFilename,
+                   int nXSize, int nYSize, int nBands,
+                   GDALDataType eType,
+                   char **papszParmList )
+
+{
+    TIFF		*hTIFF;
+    int                 nBlockXSize = 0, nBlockYSize = 0;
+    int                 bTiled = FALSE;
+    int                 nCompression = COMPRESSION_NONE;
+    int                 nPredictor = 1, nJpegQuality = -1;
+    uint16              nSampleFormat;
+    int			nPlanar;
+    const char          *pszValue;
+    const char          *pszProfile;
+
+    GTiffOneTimeInit();
+
+/* -------------------------------------------------------------------- */
+/*      Blow on a few errors.                                           */
+/* -------------------------------------------------------------------- */
+    if( nXSize < 1 || nYSize < 1 || nBands < 1 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Attempt to create %dx%dx%d TIFF file, but width, height and bands\n"
+                  "must be positive.", 
+                  nXSize, nYSize, nBands );
+
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*	Setup values based on options.					*/
+/* -------------------------------------------------------------------- */
+    pszProfile = CSLFetchNameValue(papszParmList,"PROFILE");
+    if( pszProfile == NULL )
+        pszProfile = "GDALGeoTIFF";
+
+    if( CSLFetchNameValue(papszParmList,"TILED") != NULL )
+        bTiled = TRUE;
+
+    pszValue = CSLFetchNameValue(papszParmList,"BLOCKXSIZE");
+    if( pszValue != NULL )
+        nBlockXSize = atoi( pszValue );
+
+    pszValue = CSLFetchNameValue(papszParmList,"BLOCKYSIZE");
+    if( pszValue != NULL )
+        nBlockYSize = atoi( pszValue );
+
+    pszValue = CSLFetchNameValue(papszParmList,"INTERLEAVE");
+    if( pszValue != NULL )
+    {
+        pszValue = CSLFetchNameValue(papszParmList,"INTERLEAVE");
+        if( EQUAL( pszValue, "PIXEL" ) )
+            nPlanar = PLANARCONFIG_CONTIG;
+        else if( EQUAL( pszValue, "BAND" ) )
+            nPlanar = PLANARCONFIG_SEPARATE;
+        else
+        {
+            CPLError( CE_Failure, CPLE_AppDefined, 
+                      "INTERLEAVE=%s unsupported, value must be PIXEL or BAND.",
+                      pszValue );
+            return NULL;
+        }
+    }
+    else 
+    {
+        if( nBands == 1 
+            || EQUAL(pszProfile,"BASELINE") 
+            || EQUAL(pszProfile,"GeoTIFF") )
+            nPlanar = PLANARCONFIG_CONTIG;
+        else
+            nPlanar = PLANARCONFIG_SEPARATE;
+    }
+
+    pszValue = CSLFetchNameValue( papszParmList, "COMPRESS" );
+    if( pszValue  != NULL )
+    {
+        if( EQUAL( pszValue, "NONE" ) )
+            nCompression = COMPRESSION_NONE;
+        else if( EQUAL( pszValue, "JPEG" ) )
+            nCompression = COMPRESSION_JPEG;
+        else if( EQUAL( pszValue, "LZW" ) )
+            nCompression = COMPRESSION_LZW;
+        else if( EQUAL( pszValue, "PACKBITS" ))
+            nCompression = COMPRESSION_PACKBITS;
+        else if( EQUAL( pszValue, "DEFLATE" ) || EQUAL( pszValue, "ZIP" ))
+            nCompression = COMPRESSION_ADOBE_DEFLATE;
+        else
+            CPLError( CE_Warning, CPLE_IllegalArg, 
+                      "COMPRESS=%s value not recognised, ignoring.",
+                      pszValue );
+    }
+
+    pszValue = CSLFetchNameValue( papszParmList, "PREDICTOR" );
+    if( pszValue  != NULL )
+        nPredictor =  atoi( pszValue );
+
+    pszValue = CSLFetchNameValue( papszParmList, "JPEG_QUALITY" );
+    if( pszValue  != NULL )
+        nJpegQuality = atoi( pszValue );
+
+/* -------------------------------------------------------------------- */
+/*      Check if we are producing an uncompressed file and it is        */
+/*      certain to be larger than 4GB.                                  */
+/* -------------------------------------------------------------------- */
+    if( nCompression == COMPRESSION_NONE )
+    {
+        if( nXSize * ((double)nYSize) * nBands * (GDALGetDataTypeSize(eType)/8)
+            > 4000000000.0 )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined, 
+                      "A %d pixels x %d lines x %d bands %s image would be larger than 4GB\n"
+                      "but this is the largest size a TIFF can be.  Creation failed.",
+                      nXSize, nYSize, nBands, GDALGetDataTypeName(eType) );
+            return NULL;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try opening the dataset.                                        */
+/* -------------------------------------------------------------------- */
+    hTIFF = VSI_TIFFOpen( pszFilename, "w+" );
+    if( hTIFF == NULL )
+    {
+        if( CPLGetLastErrorNo() == 0 )
+            CPLError( CE_Failure, CPLE_OpenFailed,
+                      "Attempt to create new tiff file `%s'\n"
+                      "failed in XTIFFOpen().\n",
+                      pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Setup some standard flags.                                      */
+/* -------------------------------------------------------------------- */
+    TIFFSetField( hTIFF, TIFFTAG_IMAGEWIDTH, nXSize );
+    TIFFSetField( hTIFF, TIFFTAG_IMAGELENGTH, nYSize );
+    TIFFSetField( hTIFF, TIFFTAG_BITSPERSAMPLE, GDALGetDataTypeSize(eType) );
+
+    if( eType == GDT_Int16 || eType == GDT_Int32 )
+        nSampleFormat = SAMPLEFORMAT_INT;
+    else if( eType == GDT_CInt16 || eType == GDT_CInt32 )
+        nSampleFormat = SAMPLEFORMAT_COMPLEXINT;
+    else if( eType == GDT_Float32 || eType == GDT_Float64 )
+        nSampleFormat = SAMPLEFORMAT_IEEEFP;
+    else if( eType == GDT_CFloat32 || eType == GDT_CFloat64 )
+        nSampleFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
+    else
+        nSampleFormat = SAMPLEFORMAT_UINT;
+
+    TIFFSetField( hTIFF, TIFFTAG_SAMPLEFORMAT, nSampleFormat );
+    TIFFSetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, nBands );
+    TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, nPlanar );
+
+/* -------------------------------------------------------------------- */
+/*      Setup Photometric Interpretation. Take this value from the user */
+/*      passed option or guess correct value otherwise.                 */
+/* -------------------------------------------------------------------- */
+    int nSamplesAccountedFor = 1;
+
+    pszValue = CSLFetchNameValue(papszParmList,"PHOTOMETRIC");
+    if( pszValue != NULL )
+    {
+        if( EQUAL( pszValue, "MINISBLACK" ) )
+            TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK );
+        else if( EQUAL( pszValue, "MINISWHITE" ) )
+            TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE );
+        else if( EQUAL( pszValue, "RGB" ))
+        {
+            TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB );
+            nSamplesAccountedFor = 3;
+        }
+        else if( EQUAL( pszValue, "CMYK" ))
+        {
+            TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED );
+            nSamplesAccountedFor = 4;
+        }
+        else if( EQUAL( pszValue, "YCBCR" ))
+        {
+            TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR );
+            nSamplesAccountedFor = 3;
+        }
+        else if( EQUAL( pszValue, "CIELAB" ))
+        {
+            TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB );
+            nSamplesAccountedFor = 3;
+        }
+        else if( EQUAL( pszValue, "ICCLAB" ))
+        {
+            TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ICCLAB );
+            nSamplesAccountedFor = 3;
+        }
+        else if( EQUAL( pszValue, "ITULAB" ))
+        {
+            TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ITULAB );
+            nSamplesAccountedFor = 3;
+        }
+        else
+        {
+            CPLError( CE_Warning, CPLE_IllegalArg, 
+                      "PHOTOMETRIC=%s value not recognised, ignoring.\n"
+                      "Set the Photometric Interpretation as MINISBLACK.", 
+                      pszValue );
+            TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK );
+        }
+    }
+    else
+    {
+        /* 
+         * If image contains 3 or 4 bands and datatype is Byte then we will
+         * assume it is RGB. In all other cases assume it is MINISBLACK.
+         */
+        if( nBands == 3 && eType == GDT_Byte )
+        {
+            TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB );
+            nSamplesAccountedFor = 3;
+        }
+        else if( nBands == 4 && eType == GDT_Byte )
+        {
+            uint16 v[1];
+
+            v[0] = EXTRASAMPLE_ASSOCALPHA;
+            TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
+            TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB );
+            nSamplesAccountedFor = 4;
+        }
+        else
+        {
+            TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK );
+            nSamplesAccountedFor = 1;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If there are extra samples, we need to mark them with an        */
+/*      appropriate extrasamples definition here.                       */
+/* -------------------------------------------------------------------- */
+    if( nBands > nSamplesAccountedFor )
+    {
+        uint16 *v;
+        int i;
+        int nExtraSamples = nBands - nSamplesAccountedFor;
+
+        v = (uint16 *) CPLMalloc( sizeof(uint16) * nExtraSamples );
+
+        if( CSLFetchBoolean(papszParmList,"ALPHA",FALSE) )
+            v[0] = EXTRASAMPLE_ASSOCALPHA;
+        else
+            v[0] = EXTRASAMPLE_UNSPECIFIED;
+            
+        for( i = 1; i < nExtraSamples; i++ )
+            v[i] = EXTRASAMPLE_UNSPECIFIED;
+
+        TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples, v );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Setup tiling/stripping flags.                                   */
+/* -------------------------------------------------------------------- */
+    if( bTiled )
+    {
+        if( nBlockXSize == 0 )
+            nBlockXSize = 256;
+        
+        if( nBlockYSize == 0 )
+            nBlockYSize = 256;
+
+        TIFFSetField( hTIFF, TIFFTAG_TILEWIDTH, nBlockXSize );
+        TIFFSetField( hTIFF, TIFFTAG_TILELENGTH, nBlockYSize );
+    }
+    else
+    {
+        uint32 nRowsPerStrip;
+
+        if( nBlockYSize == 0 )
+            nRowsPerStrip = MIN(nYSize, (int)TIFFDefaultStripSize(hTIFF,0));
+        else
+            nRowsPerStrip = nBlockYSize;
+        
+        TIFFSetField( hTIFF, TIFFTAG_ROWSPERSTRIP, nRowsPerStrip );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Set compression related tags.                                   */
+/* -------------------------------------------------------------------- */
+    TIFFSetField( hTIFF, TIFFTAG_COMPRESSION, nCompression );
+    if ( nCompression == COMPRESSION_LZW ||
+         nCompression == COMPRESSION_ADOBE_DEFLATE )
+        TIFFSetField( hTIFF, TIFFTAG_PREDICTOR, nPredictor );
+    if( nCompression == COMPRESSION_JPEG 
+        && nJpegQuality != -1 )
+        TIFFSetField( hTIFF, TIFFTAG_JPEGQUALITY, nJpegQuality );
+        
+    return( hTIFF );
+}
+
+/************************************************************************/
+/*                               Create()                               */
+/*                                                                      */
+/*      Create a new GeoTIFF or TIFF file.                              */
+/************************************************************************/
+
+GDALDataset *GTiffDataset::Create( const char * pszFilename,
+                                   int nXSize, int nYSize, int nBands,
+                                   GDALDataType eType,
+                                   char **papszParmList )
+
+{
+    GTiffDataset *	poDS;
+    TIFF		*hTIFF;
+
+/* -------------------------------------------------------------------- */
+/*      Create the underlying TIFF file.                                */
+/* -------------------------------------------------------------------- */
+    hTIFF = GTiffCreate( pszFilename, nXSize, nYSize, nBands, 
+                         eType, papszParmList );
+
+    if( hTIFF == NULL )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Create the new GTiffDataset object.                             */
+/* -------------------------------------------------------------------- */
+    poDS = new GTiffDataset();
+    poDS->hTIFF = hTIFF;
+
+    poDS->nRasterXSize = nXSize;
+    poDS->nRasterYSize = nYSize;
+    poDS->eAccess = GA_Update;
+    poDS->bNewDataset = TRUE;
+    poDS->bCrystalized = FALSE;
+    poDS->pszProjection = CPLStrdup("");
+    poDS->nSamplesPerPixel = (uint16) nBands;
+
+    TIFFGetField( hTIFF, TIFFTAG_SAMPLEFORMAT, &(poDS->nSampleFormat) );
+    TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &(poDS->nPlanarConfig) );
+    TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &(poDS->nPhotometric) );
+    TIFFGetField( hTIFF, TIFFTAG_BITSPERSAMPLE, &(poDS->nBitsPerSample) );
+    TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &(poDS->nCompression) );
+
+    if( TIFFIsTiled(hTIFF) )
+    {
+        TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &(poDS->nBlockXSize) );
+        TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &(poDS->nBlockYSize) );
+    }
+    else
+    {
+        if( !TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP,
+                           &(poDS->nRowsPerStrip) ) )
+            poDS->nRowsPerStrip = 1; /* dummy value */
+
+        poDS->nBlockXSize = nXSize;
+        poDS->nBlockYSize = MIN((int)poDS->nRowsPerStrip,nYSize);
+    }
+
+    poDS->nBlocksPerBand =
+        ((nYSize + poDS->nBlockYSize - 1) / poDS->nBlockYSize)
+        * ((nXSize + poDS->nBlockXSize - 1) / poDS->nBlockXSize);
+
+/* -------------------------------------------------------------------- */
+/*      Do we need a TFW file?                                          */
+/* -------------------------------------------------------------------- */
+    if( CSLFetchBoolean( papszParmList, "TFW", FALSE ) 
+        || CSLFetchBoolean( papszParmList, "WORLDFILE", FALSE ) )
+        poDS->SetupTFW( pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    int		iBand;
+
+    for( iBand = 0; iBand < nBands; iBand++ )
+    {
+        poDS->SetBand( iBand+1, new GTiffRasterBand( poDS, iBand+1 ) );
+    }
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                             CreateCopy()                             */
+/************************************************************************/
+
+static GDALDataset *
+GTiffCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, 
+                 int bStrict, char ** papszOptions, 
+                 GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    TIFF	*hTIFF;
+    int		nXSize = poSrcDS->GetRasterXSize();
+    int		nYSize = poSrcDS->GetRasterYSize();
+    int		nBands = poSrcDS->GetRasterCount();
+    int         iBand;
+    GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
+    CPLErr      eErr = CE_None;
+    uint16	nPlanarConfig;
+        
+/* -------------------------------------------------------------------- */
+/*      Check, whether all bands in input dataset has the same type.    */
+/* -------------------------------------------------------------------- */
+    for ( iBand = 2; iBand <= nBands; iBand++ )
+    {
+        if ( eType != poSrcDS->GetRasterBand(iBand)->GetRasterDataType() )
+        {
+            if ( bStrict )
+            {
+                CPLError( CE_Failure, CPLE_AppDefined,
+                          "Unable to export GeoTIFF file with different datatypes per\n"
+                          "different bands. All bands should have the same types in TIFF." );
+                return NULL;
+            }
+            else
+            {
+                CPLError( CE_Warning, CPLE_AppDefined,
+                          "Unable to export GeoTIFF file with different datatypes per\n"
+                          "different bands. All bands should have the same types in TIFF." );
+            }
+        }
+    }
+
+    if( !pfnProgress( 0.0, NULL, pProgressData ) )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Capture the profile.                                            */
+/* -------------------------------------------------------------------- */
+    const char          *pszProfile;
+    int                 bGeoTIFF;
+
+    pszProfile = CSLFetchNameValue(papszOptions,"PROFILE");
+    if( pszProfile == NULL )
+        pszProfile = "GDALGeoTIFF";
+
+    if( !EQUAL(pszProfile,"BASELINE") 
+        && !EQUAL(pszProfile,"GeoTIFF") 
+        && !EQUAL(pszProfile,"GDALGeoTIFF") )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "PROFILE=%s not supported in GTIFF driver.", 
+                  pszProfile );
+        return NULL;
+    }
+    
+    if( EQUAL(pszProfile,"BASELINE") )
+        bGeoTIFF = FALSE;
+    else
+        bGeoTIFF = TRUE;
+
+/* -------------------------------------------------------------------- */
+/*      Create the file.                                                */
+/* -------------------------------------------------------------------- */
+    hTIFF = GTiffCreate( pszFilename, nXSize, nYSize, nBands, 
+                         eType, papszOptions );
+
+    if( hTIFF == NULL )
+        return NULL;
+
+    TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &nPlanarConfig );
+
+/* -------------------------------------------------------------------- */
+/*      Are we really producing an RGBA image?  If so, set the          */
+/*      associated alpha information.                                   */
+/* -------------------------------------------------------------------- */
+    if( nBands == 4 
+        && poSrcDS->GetRasterBand(4)->GetColorInterpretation()==GCI_AlphaBand)
+    {
+        uint16 v[1];
+
+        v[0] = EXTRASAMPLE_ASSOCALPHA;
+	TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
+        TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If the output is jpeg compressed, and the input is RGB make     */
+/*      sure we note that.                                              */
+/* -------------------------------------------------------------------- */
+    uint16      nCompression;
+
+    if( !TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &(nCompression) ) )
+        nCompression = COMPRESSION_NONE;
+
+    if( nCompression == COMPRESSION_JPEG )
+    {
+        if( nBands >= 3 
+            && (poSrcDS->GetRasterBand(1)->GetColorInterpretation() 
+                == GCI_YCbCr_YBand)
+            && (poSrcDS->GetRasterBand(2)->GetColorInterpretation() 
+                == GCI_YCbCr_CbBand)
+            && (poSrcDS->GetRasterBand(3)->GetColorInterpretation() 
+                == GCI_YCbCr_CrBand) )
+        {
+            /* do nothing ... */
+        }
+        else
+        {
+            /* we assume RGB if it isn't explicitly YCbCr */
+            CPLDebug( "GTiff", "Setting JPEGCOLORMODE_RGB" );
+            TIFFSetField( hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB );
+        }
+    }
+        
+/* -------------------------------------------------------------------- */
+/*      Does the source image consist of one band, with a palette?      */
+/*      If so, copy over.                                               */
+/* -------------------------------------------------------------------- */
+    if( nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() != NULL 
+        && eType == GDT_Byte )
+    {
+        unsigned short	anTRed[256], anTGreen[256], anTBlue[256];
+        GDALColorTable *poCT;
+
+        poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
+        
+        for( int iColor = 0; iColor < 256; iColor++ )
+        {
+            if( iColor < poCT->GetColorEntryCount() )
+            {
+                GDALColorEntry  sRGB;
+
+                poCT->GetColorEntryAsRGB( iColor, &sRGB );
+
+                anTRed[iColor] = (unsigned short) (257 * sRGB.c1);
+                anTGreen[iColor] = (unsigned short) (257 * sRGB.c2);
+                anTBlue[iColor] = (unsigned short) (257 * sRGB.c3);
+            }
+            else
+            {
+                anTRed[iColor] = anTGreen[iColor] = anTBlue[iColor] = 0;
+            }
+        }
+
+        TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE );
+        TIFFSetField( hTIFF, TIFFTAG_COLORMAP, anTRed, anTGreen, anTBlue );
+    }
+    else if( nBands == 1 
+             && poSrcDS->GetRasterBand(1)->GetColorTable() != NULL 
+             && eType == GDT_UInt16 )
+    {
+        unsigned short	anTRed[65536], anTGreen[65536], anTBlue[65536];
+        GDALColorTable *poCT;
+
+        poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
+        
+        for( int iColor = 0; iColor < 65536; iColor++ )
+        {
+            if( iColor < poCT->GetColorEntryCount() )
+            {
+                GDALColorEntry  sRGB;
+
+                poCT->GetColorEntryAsRGB( iColor, &sRGB );
+
+                anTRed[iColor] = (unsigned short) (256 * sRGB.c1);
+                anTGreen[iColor] = (unsigned short) (256 * sRGB.c2);
+                anTBlue[iColor] = (unsigned short) (256 * sRGB.c3);
+            }
+            else
+            {
+                anTRed[iColor] = anTGreen[iColor] = anTBlue[iColor] = 0;
+            }
+        }
+
+        TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE );
+        TIFFSetField( hTIFF, TIFFTAG_COLORMAP, anTRed, anTGreen, anTBlue );
+    }
+    else if( poSrcDS->GetRasterBand(1)->GetColorTable() != NULL )
+        CPLError( CE_Warning, CPLE_AppDefined,
+                  "Unable to export color table to GeoTIFF file.  Color tables\n"
+                  "can only be written to 1 band Byte or UInt16 GeoTIFF files." );
+
+/* -------------------------------------------------------------------- */
+/*      Transfer some TIFF specific metadata, if available.  Should     */
+/*      we push this into .pam if we are avoiding GDAL tags?            */
+/* -------------------------------------------------------------------- */
+    if( EQUAL(pszProfile,"GDALGeoTIFF") )
+        GTiffDataset::WriteMetadata( poSrcDS, hTIFF, FALSE );
+
+/* -------------------------------------------------------------------- */
+/* 	Write NoData value, if exist.                                   */
+/* -------------------------------------------------------------------- */
+    if( EQUAL(pszProfile,"GDALGeoTIFF") )
+    {
+        int		bSuccess;
+        double	dfNoData;
+    
+        dfNoData = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bSuccess );
+        if ( bSuccess )
+            GTiffDataset::WriteNoDataValue( hTIFF, dfNoData );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Write affine transform if it is meaningful.                     */
+/* -------------------------------------------------------------------- */
+    const char *pszProjection = NULL;
+    double      adfGeoTransform[6];
+    
+    if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None
+        && (adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0
+            || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0
+            || adfGeoTransform[4] != 0.0 || ABS(adfGeoTransform[5]) != 1.0 ))
+    {
+        if( bGeoTIFF )
+        {
+            if( adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0 
+                && adfGeoTransform[5] < 0.0 )
+            {
+
+                double	adfPixelScale[3], adfTiePoints[6];
+
+                adfPixelScale[0] = adfGeoTransform[1];
+                adfPixelScale[1] = fabs(adfGeoTransform[5]);
+                adfPixelScale[2] = 0.0;
+
+                TIFFSetField( hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale );
+            
+                adfTiePoints[0] = 0.0;
+                adfTiePoints[1] = 0.0;
+                adfTiePoints[2] = 0.0;
+                adfTiePoints[3] = adfGeoTransform[0];
+                adfTiePoints[4] = adfGeoTransform[3];
+                adfTiePoints[5] = 0.0;
+        
+                TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints );
+            }
+            else
+            {
+                double	adfMatrix[16];
+
+                memset(adfMatrix,0,sizeof(double) * 16);
+
+                adfMatrix[0] = adfGeoTransform[1];
+                adfMatrix[1] = adfGeoTransform[2];
+                adfMatrix[3] = adfGeoTransform[0];
+                adfMatrix[4] = adfGeoTransform[4];
+                adfMatrix[5] = adfGeoTransform[5];
+                adfMatrix[7] = adfGeoTransform[3];
+                adfMatrix[15] = 1.0;
+
+                TIFFSetField( hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix );
+            }
+            
+            pszProjection = poSrcDS->GetProjectionRef();
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Do we need a TFW file?                                          */
+/* -------------------------------------------------------------------- */
+        if( CSLFetchBoolean( papszOptions, "TFW", FALSE ) )
+            GDALWriteWorldFile( pszFilename, "tfw", adfGeoTransform );
+        else if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) )
+            GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Otherwise write tiepoints if they are available.                */
+/* -------------------------------------------------------------------- */
+    else if( poSrcDS->GetGCPCount() > 0 && bGeoTIFF )
+    {
+        const GDAL_GCP *pasGCPs = poSrcDS->GetGCPs();
+        double	*padfTiePoints;
+
+        padfTiePoints = (double *) 
+            CPLMalloc(6*sizeof(double)*poSrcDS->GetGCPCount());
+
+        for( int iGCP = 0; iGCP < poSrcDS->GetGCPCount(); iGCP++ )
+        {
+
+            padfTiePoints[iGCP*6+0] = pasGCPs[iGCP].dfGCPPixel;
+            padfTiePoints[iGCP*6+1] = pasGCPs[iGCP].dfGCPLine;
+            padfTiePoints[iGCP*6+2] = 0;
+            padfTiePoints[iGCP*6+3] = pasGCPs[iGCP].dfGCPX;
+            padfTiePoints[iGCP*6+4] = pasGCPs[iGCP].dfGCPY;
+            padfTiePoints[iGCP*6+5] = pasGCPs[iGCP].dfGCPZ;
+        }
+
+        TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 
+                      6*poSrcDS->GetGCPCount(), padfTiePoints );
+        CPLFree( padfTiePoints );
+        
+        pszProjection = poSrcDS->GetGCPProjection();
+    }
+
+    else
+        pszProjection = poSrcDS->GetProjectionRef();
+
+/* -------------------------------------------------------------------- */
+/*      Write the projection information, if possible.                  */
+/* -------------------------------------------------------------------- */
+    if( pszProjection != NULL && strlen(pszProjection) > 0 && bGeoTIFF )
+    {
+        GTIF	*psGTIF;
+
+        psGTIF = GTIFNew( hTIFF );
+        GTIFSetFromOGISDefn( psGTIF, pszProjection );
+
+        if( poSrcDS->GetMetadataItem( GDALMD_AREA_OR_POINT ) 
+            && EQUAL(poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT),
+                     GDALMD_AOP_POINT) )
+        {
+            GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
+                       RasterPixelIsPoint);
+        }
+
+        GTIFWriteKeys( psGTIF );
+        GTIFFree( psGTIF );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Copy image data ... tiled.                                      */
+/* -------------------------------------------------------------------- */
+    if( TIFFIsTiled( hTIFF ) && nPlanarConfig == PLANARCONFIG_SEPARATE )
+    {
+        uint32  nBlockXSize;
+        uint32	nBlockYSize;
+        int     nTileSize, nTilesAcross, nTilesDown, iTileX, iTileY;
+        GByte   *pabyTile;
+        int     nTilesDone = 0, nPixelSize;
+
+        TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &nBlockXSize );
+        TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &nBlockYSize );
+        
+        nTilesAcross = (nXSize+nBlockXSize-1) / nBlockXSize;
+        nTilesDown = (nYSize+nBlockYSize-1) / nBlockYSize;
+
+        nPixelSize = GDALGetDataTypeSize(eType) / 8;
+        nTileSize =  nPixelSize * nBlockXSize * nBlockYSize;
+        pabyTile = (GByte *) CPLMalloc(nTileSize);
+
+        for(iBand = 0; eErr == CE_None && iBand < nBands; iBand++ )
+        {
+            GDALRasterBand *poBand = poSrcDS->GetRasterBand(iBand+1);
+
+            for( iTileY = 0; 
+                 eErr == CE_None && iTileY < nTilesDown; 
+                 iTileY++ )
+            {
+                for( iTileX = 0; 
+                     eErr == CE_None && iTileX < nTilesAcross; 
+                     iTileX++ )
+                {
+                    int   nThisBlockXSize = nBlockXSize;
+                    int   nThisBlockYSize = nBlockYSize;
+
+                    if( (int) ((iTileX+1) * nBlockXSize) > nXSize )
+                    {
+                        nThisBlockXSize = nXSize - iTileX*nBlockXSize;
+                        memset( pabyTile, 0, nTileSize );
+                    }
+
+                    if( (int) ((iTileY+1) * nBlockYSize) > nYSize )
+                    {
+                        nThisBlockYSize = nYSize - iTileY*nBlockYSize;
+                        memset( pabyTile, 0, nTileSize );
+                    }
+
+                    eErr = poBand->RasterIO( GF_Read, 
+                                             iTileX * nBlockXSize, 
+                                             iTileY * nBlockYSize, 
+                                             nThisBlockXSize, 
+                                             nThisBlockYSize,
+                                             pabyTile,
+                                             nThisBlockXSize, 
+                                             nThisBlockYSize, eType,
+                                             nPixelSize, 
+                                             nBlockXSize * nPixelSize );
+
+                    TIFFWriteEncodedTile( hTIFF, nTilesDone, pabyTile, 
+                                          nTileSize );
+
+                    nTilesDone++;
+
+                    if( eErr == CE_None 
+                        && !pfnProgress( nTilesDone / 
+                                         ((double) nTilesAcross * nTilesDown * nBands),
+                                         NULL, pProgressData ) )
+                    {
+                        eErr = CE_Failure;
+                        CPLError( CE_Failure, CPLE_UserInterrupt, 
+                                  "User terminated CreateCopy()" );
+                    }
+                }
+                
+            }
+        }
+
+        CPLFree( pabyTile );
+    }
+/* -------------------------------------------------------------------- */
+/*      Copy image data, one scanline at a time.                        */
+/* -------------------------------------------------------------------- */
+    else if( !TIFFIsTiled(hTIFF) && nPlanarConfig == PLANARCONFIG_SEPARATE )
+    {
+        int     nLinesDone = 0, nPixelSize, nLineSize;
+        GByte   *pabyLine;
+
+        nPixelSize = GDALGetDataTypeSize(eType) / 8;
+        nLineSize =  nPixelSize * nXSize;
+        pabyLine = (GByte *) CPLMalloc(nLineSize);
+
+        for( int iBand = 0; 
+             eErr == CE_None && iBand < nBands; 
+             iBand++ )
+        {
+            GDALRasterBand *poBand = poSrcDS->GetRasterBand(iBand+1);
+
+            for( int iLine = 0; 
+                 eErr == CE_None && iLine < nYSize; 
+                 iLine++ )
+            {
+                eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, 
+                                         pabyLine, nXSize, 1, eType, 
+                                         0, 0 );
+                if( eErr == CE_None 
+                    && TIFFWriteScanline( hTIFF, pabyLine, iLine, 
+                                          (tsample_t) iBand ) == -1 )
+                {
+                    CPLError( CE_Failure, CPLE_AppDefined,
+                              "TIFFWriteScanline failed." );
+                    eErr = CE_Failure;
+                }
+
+                nLinesDone++;
+                if( eErr == CE_None 
+                    && !pfnProgress( nLinesDone / 
+                                     ((double) nYSize * nBands), 
+                                     NULL, pProgressData ) )
+                {
+                    eErr = CE_Failure;
+                    CPLError( CE_Failure, CPLE_UserInterrupt, 
+                              "User terminated CreateCopy()" );
+                }
+            }
+        }
+
+        CPLFree( pabyLine );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Copy image data ... tiled.                                      */
+/* -------------------------------------------------------------------- */
+    else if( TIFFIsTiled( hTIFF ) && nPlanarConfig == PLANARCONFIG_CONTIG )
+    {
+        uint32  nBlockXSize;
+        uint32	nBlockYSize;
+        int     nTileSize, nTilesAcross, nTilesDown, iTileX, iTileY, nElemSize;
+        GByte   *pabyTile;
+        int     nTilesDone = 0, nPixelSize;
+
+        TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &nBlockXSize );
+        TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &nBlockYSize );
+        
+        nTilesAcross = (nXSize+nBlockXSize-1) / nBlockXSize;
+        nTilesDown = (nYSize+nBlockYSize-1) / nBlockYSize;
+
+        nElemSize = GDALGetDataTypeSize(eType) / 8;
+        nPixelSize = nElemSize * nBands;
+        nTileSize =  nPixelSize * nBlockXSize * nBlockYSize;
+        pabyTile = (GByte *) CPLMalloc(nTileSize);
+
+        for( iTileY = 0; 
+             eErr == CE_None && iTileY < nTilesDown; 
+             iTileY++ )
+        {
+            for( iTileX = 0; 
+                 eErr == CE_None && iTileX < nTilesAcross; 
+                 iTileX++ )
+            {
+                int   nThisBlockXSize = nBlockXSize;
+                int   nThisBlockYSize = nBlockYSize;
+                
+                if( (int) ((iTileX+1) * nBlockXSize) > nXSize )
+                {
+                    nThisBlockXSize = nXSize - iTileX*nBlockXSize;
+                    memset( pabyTile, 0, nTileSize );
+                }
+
+                if( (int) ((iTileY+1) * nBlockYSize) > nYSize )
+                {
+                    nThisBlockYSize = nYSize - iTileY*nBlockYSize;
+                    memset( pabyTile, 0, nTileSize );
+                }
+
+                for( int iBand = 0; 
+                     eErr == CE_None && iBand < nBands; 
+                     iBand++ )
+                {
+                    GDALRasterBand *poBand = poSrcDS->GetRasterBand(iBand+1);
+
+                    eErr = poBand->RasterIO( GF_Read, 
+                                             iTileX * nBlockXSize, 
+                                             iTileY * nBlockYSize, 
+                                             nThisBlockXSize, 
+                                             nThisBlockYSize,
+                                             pabyTile + iBand * nElemSize,
+                                             nThisBlockXSize, 
+                                             nThisBlockYSize, eType,
+                                             nPixelSize, 
+                                             nBlockXSize * nPixelSize );
+                }
+
+                TIFFWriteEncodedTile( hTIFF, nTilesDone, pabyTile, 
+                                      nTileSize );
+
+                nTilesDone++;
+
+                if( eErr == CE_None 
+                    && !pfnProgress( nTilesDone / 
+                                     ((double) nTilesAcross * nTilesDown),
+                                     NULL, pProgressData ) )
+                {
+                    eErr = CE_Failure;
+                    CPLError( CE_Failure, CPLE_UserInterrupt, 
+                              "User terminated CreateCopy()" );
+                }
+            }
+        }
+
+        CPLFree( pabyTile );
+    }
+/* -------------------------------------------------------------------- */
+/*      Copy image data, one scanline at a time.                        */
+/* -------------------------------------------------------------------- */
+    else if( !TIFFIsTiled(hTIFF) && nPlanarConfig == PLANARCONFIG_CONTIG )
+    {
+        int     nLinesDone = 0, nPixelSize, nLineSize, nElemSize;
+        GByte   *pabyLine;
+
+        nElemSize = GDALGetDataTypeSize(eType) / 8;
+        nPixelSize = nElemSize * nBands;
+        nLineSize =  nPixelSize * nXSize;
+        pabyLine = (GByte *) CPLMalloc(nLineSize);
+
+        for( int iLine = 0; 
+             eErr == CE_None && iLine < nYSize; 
+             iLine++ )
+        {
+            eErr = 
+                poSrcDS->RasterIO( GF_Read, 0, iLine, nXSize, 1, 
+                                   pabyLine, nXSize, 1, eType, nBands, NULL, 
+                                   nPixelSize, nLineSize, nElemSize );
+
+            if( eErr == CE_None 
+                && TIFFWriteScanline( hTIFF, pabyLine, iLine, 0 ) == -1 )
+            {
+                CPLError( CE_Failure, CPLE_AppDefined,
+                          "TIFFWriteScanline failed." );
+                eErr = CE_Failure;
+            }
+
+            nLinesDone++;
+            if( eErr == CE_None 
+                && !pfnProgress( nLinesDone / ((double) nYSize), 
+                                 NULL, pProgressData ) )
+            {
+                eErr = CE_Failure;
+                CPLError( CE_Failure, CPLE_UserInterrupt, 
+                          "User terminated CreateCopy()" );
+            }
+        }
+
+        CPLFree( pabyLine );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    TIFFFlush( hTIFF );
+    XTIFFClose( hTIFF );
+
+    if( eErr != CE_None )
+    {
+        VSIUnlink( pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Re-open as a dataset and copy over missing metadata using       */
+/*      PAM facilities.                                                 */
+/* -------------------------------------------------------------------- */
+    GDALPamDataset *poDS;
+
+    poDS = (GDALPamDataset *) GDALOpen( pszFilename, GA_Update );
+    if( poDS == NULL )
+        poDS = (GDALPamDataset *) GDALOpen( pszFilename, GA_ReadOnly );
+
+    if( poDS != NULL && EQUAL(pszProfile,"GDALGeoTIFF") )
+    {
+        poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
+    }
+    
+    return poDS;
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *GTiffDataset::GetProjectionRef()
+
+{
+    if( nGCPCount == 0 )
+    {
+        if( EQUAL(pszProjection,"") )
+            return GDALPamDataset::GetProjectionRef();
+        else
+            return( pszProjection );
+    }
+    else
+        return "";
+}
+
+/************************************************************************/
+/*                           SetProjection()                            */
+/************************************************************************/
+
+CPLErr GTiffDataset::SetProjection( const char * pszNewProjection )
+
+{
+    if( !EQUALN(pszNewProjection,"GEOGCS",6)
+        && !EQUALN(pszNewProjection,"PROJCS",6)
+        && !EQUAL(pszNewProjection,"") )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                "Only OGC WKT Projections supported for writing to GeoTIFF.\n"
+                "%s not supported.",
+                  pszNewProjection );
+        
+        return CE_Failure;
+    }
+    
+    CPLFree( pszProjection );
+    pszProjection = CPLStrdup( pszNewProjection );
+
+    bGeoTIFFInfoChanged = TRUE;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr GTiffDataset::GetGeoTransform( double * padfTransform )
+
+{
+    memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
+    
+    if( !bGeoTransformValid )
+        return CE_Failure;
+    else
+        return CE_None;
+}
+
+/************************************************************************/
+/*                          SetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr GTiffDataset::SetGeoTransform( double * padfTransform )
+
+{
+    if( GetAccess() == GA_Update )
+    {
+        memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 );
+        bGeoTransformValid = TRUE;
+        bGeoTIFFInfoChanged = TRUE;
+
+        return( CE_None );
+    }
+    else
+    {
+        CPLError( CE_Failure, CPLE_NotSupported,
+      "SetGeoTransform() is only supported on newly created GeoTIFF files." );
+        return CE_Failure;
+    }
+}
+
+/************************************************************************/
+/*                            GetGCPCount()                             */
+/************************************************************************/
+
+int GTiffDataset::GetGCPCount()
+
+{
+    return nGCPCount;
+}
+
+/************************************************************************/
+/*                          GetGCPProjection()                          */
+/************************************************************************/
+
+const char *GTiffDataset::GetGCPProjection()
+
+{
+    if( nGCPCount > 0 )
+        return pszProjection;
+    else
+        return "";
+}
+
+/************************************************************************/
+/*                               GetGCPs()                              */
+/************************************************************************/
+
+const GDAL_GCP *GTiffDataset::GetGCPs()
+
+{
+    return pasGCPList;
+}
+
+/************************************************************************/
+/*                               SetGCPs()                              */
+/************************************************************************/
+
+CPLErr GTiffDataset::SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList,
+                              const char *pszGCPProjection )
+{
+    if( GetAccess() == GA_Update )
+    {
+        if( this->nGCPCount > 0 )
+        {
+            GDALDeinitGCPs( this->nGCPCount, this->pasGCPList );
+            CPLFree( this->pasGCPList );
+        }
+
+	this->nGCPCount = nGCPCount;
+	this->pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPList);
+
+        CPLFree( this->pszProjection );
+	this->pszProjection = CPLStrdup( pszGCPProjection );
+        bGeoTIFFInfoChanged = TRUE;
+
+	return CE_None;
+    }
+    else
+    {
+        CPLError( CE_Failure, CPLE_NotSupported,
+            "SetGCPs() is only supported on newly created GeoTIFF files." );
+        return CE_Failure;
+    }
+}
+
+/************************************************************************/
+/*                            SetMetadata()                             */
+/************************************************************************/
+CPLErr GTiffDataset::SetMetadata( char ** papszMD, const char *pszDomain )
+
+{
+    bMetadataChanged = TRUE;
+    return GDALPamDataset::SetMetadata( papszMD, pszDomain );
+}
+
+/************************************************************************/
+/*                          SetMetadataItem()                           */
+/************************************************************************/
+
+CPLErr GTiffDataset::SetMetadataItem( const char *pszName, 
+                                      const char *pszValue,
+                                      const char *pszDomain )
+
+{
+    bMetadataChanged = TRUE;
+    return GDALPamDataset::SetMetadataItem( pszName, pszValue, pszDomain );
+}
+
+/************************************************************************/
+/*                         GetInternalHandle()                          */
+/************************************************************************/
+
+void *GTiffDataset::GetInternalHandle( const char * /* pszHandleName */ )
+
+{
+    return hTIFF;
+}
+
+/************************************************************************/
+/*                       PrepareTIFFErrorFormat()                       */
+/*                                                                      */
+/*      sometimes the "module" has stuff in it that has special         */
+/*      meaning in a printf() style format, so we try to escape it.     */
+/*      For now we hope the only thing we have to escape is %'s.        */
+/************************************************************************/
+
+static char *PrepareTIFFErrorFormat( const char *module, const char *fmt )
+
+{
+    char      *pszModFmt;
+    int       iIn, iOut;
+
+    pszModFmt = (char *) CPLMalloc( strlen(module)*2 + strlen(fmt) + 2 );
+    for( iOut = 0, iIn = 0; module[iIn] != '\0'; iIn++ )
+    {
+        if( module[iIn] == '%' )
+        {
+            pszModFmt[iOut++] = '%';
+            pszModFmt[iOut++] = '%';
+        }
+        else
+            pszModFmt[iOut++] = module[iIn];
+    }
+    pszModFmt[iOut] = '\0';
+    strcat( pszModFmt, ":" );
+    strcat( pszModFmt, fmt );
+
+    return pszModFmt;
+}
+
+/************************************************************************/
+/*                        GTiffWarningHandler()                         */
+/************************************************************************/
+void
+GTiffWarningHandler(const char* module, const char* fmt, va_list ap )
+{
+    char	*pszModFmt;
+
+    if( strstr(fmt,"unknown field") != NULL )
+        return;
+
+    pszModFmt = PrepareTIFFErrorFormat( module, fmt );
+    CPLErrorV( CE_Warning, CPLE_AppDefined, pszModFmt, ap );
+    CPLFree( pszModFmt );
+}
+
+/************************************************************************/
+/*                        GTiffWarningHandler()                         */
+/************************************************************************/
+void
+GTiffErrorHandler(const char* module, const char* fmt, va_list ap )
+{
+    char *pszModFmt;
+
+    pszModFmt = PrepareTIFFErrorFormat( module, fmt );
+    CPLErrorV( CE_Failure, CPLE_AppDefined, pszModFmt, ap );
+    CPLFree( pszModFmt );
+}
+
+/************************************************************************/
+/*                          GTiffTagExtender()                          */
+/*                                                                      */
+/*      Install tags specially known to GDAL.                           */
+/************************************************************************/
+
+static TIFFExtendProc _ParentExtender = NULL;
+
+static void GTiffTagExtender(TIFF *tif)
+
+{
+    static const TIFFFieldInfo xtiffFieldInfo[] = {
+        { TIFFTAG_GDAL_METADATA,    -1,-1, TIFF_ASCII,	FIELD_CUSTOM,
+          TRUE,	FALSE,	"GDALMetadata" },
+        { TIFFTAG_GDAL_NODATA,	    -1,-1, TIFF_ASCII,	FIELD_CUSTOM,
+          TRUE,	FALSE,	"GDALNoDataValue" }
+    };
+
+    if (_ParentExtender) 
+        (*_ParentExtender)(tif);
+
+    TIFFMergeFieldInfo( tif, xtiffFieldInfo,
+		        sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]) );
+}
+
+/************************************************************************/
+/*                          GTiffOneTimeInit()                          */
+/*                                                                      */
+/*      This is stuff that is initialized for the TIFF library just     */
+/*      once.  We deliberately defer the initialization till the        */
+/*      first time we are likely to call into libtiff to avoid          */
+/*      unnecessary paging in of the library for GDAL apps that         */
+/*      don't use it.                                                   */
+/************************************************************************/
+
+static void GTiffOneTimeInit()
+
+{
+    static int bOneTimeInitDone = FALSE;
+    
+    if( bOneTimeInitDone )
+        return;
+
+    bOneTimeInitDone = TRUE;
+
+    _ParentExtender = TIFFSetTagExtender(GTiffTagExtender);
+
+    TIFFSetWarningHandler( GTiffWarningHandler );
+    TIFFSetErrorHandler( GTiffErrorHandler );
+
+    // This only really needed if we are linked to an external libgeotiff
+    // with its own (lame) file searching logic. 
+    SetCSVFilenameHook( GDALDefaultCSVFilename );
+}
+
+/************************************************************************/
+/*                        GDALDeregister_GTiff()                        */
+/************************************************************************/
+
+void GDALDeregister_GTiff( GDALDriver * )
+
+{
+    CPLDebug( "GDAL", "GDALDeregister_GTiff() called." );
+    CSVDeaccess( NULL );
+
+#if defined(LIBGEOTIFF_VERSION) && LIBGEOTIFF_VERSION > 1150
+    GTIFDeaccessCSV();
+#endif
+}
+
+/************************************************************************/
+/*                          GDALRegister_GTiff()                        */
+/************************************************************************/
+
+void GDALRegister_GTiff()
+
+{
+    if( GDALGetDriverByName( "GTiff" ) == NULL )
+    {
+        GDALDriver	*poDriver;
+        char szCreateOptions[2048];
+        char szOptionalCompressItems[500];
+
+        poDriver = new GDALDriver();
+        
+/* -------------------------------------------------------------------- */
+/*      Determine which compression codecs are available that we        */
+/*      want to advertise.  If we are using an old libtiff we won't     */
+/*      be able to find out so we just assume all are available.        */
+/* -------------------------------------------------------------------- */
+        strcpy( szOptionalCompressItems, 
+                "       <Value>NONE</Value>" );
+
+#if TIFFLIB_VERSION <= 20040919
+        strcat( szOptionalCompressItems, 
+                "       <Value>PACKBITS</Value>"
+                "       <Value>JPEG</Value>"
+                "       <Value>LZW</Value>"
+                "       <Value>DEFLATE</Value>" );
+#else
+        TIFFCodec	*c, *codecs = TIFFGetConfiguredCODECs();
+
+        for( c = codecs; c->name; c++ )
+        {
+            if( c->scheme == COMPRESSION_PACKBITS )
+                strcat( szOptionalCompressItems,
+                        "       <Value>PACKBITS</Value>" );
+            else if( c->scheme == COMPRESSION_JPEG )
+                strcat( szOptionalCompressItems,
+                        "       <Value>JPEG</Value>" );
+            else if( c->scheme == COMPRESSION_LZW )
+                strcat( szOptionalCompressItems,
+                        "       <Value>LZW</Value>" );
+            else if( c->scheme == COMPRESSION_ADOBE_DEFLATE )
+                strcat( szOptionalCompressItems,
+                        "       <Value>DEFLATE</Value>" );
+        }
+        _TIFFfree( codecs );
+#endif        
+
+/* -------------------------------------------------------------------- */
+/*      Build full creation option list.                                */
+/* -------------------------------------------------------------------- */
+        sprintf( szCreateOptions, "%s%s%s", 
+"<CreationOptionList>"
+"   <Option name='COMPRESS' type='string-select'>",
+                 szOptionalCompressItems,
+"   </Option>"
+"   <Option name='PREDICTOR' type='int' description='Predictor Type'/>"
+"   <Option name='JPEG_QUALITY' type='int' description='JPEG quality 1-100, default 75.'/>"
+"   <Option name='INTERLEAVE' type='string-select'>"
+"       <Value>BAND</Value>"
+"       <Value>PIXEL</Value>"
+"   </Option>"
+"   <Option name='TILED' type='boolean' description='Switch to tiled format'/>"
+"   <Option name='TFW' type='boolean' description='Write out world file'/>"
+"   <Option name='BLOCKXSIZE' type='int' description='Tile Width'/>"
+"   <Option name='BLOCKYSIZE' type='int' description='Tile/Strip Height'/>"
+"   <Option name='PHOTOMETRIC' type='string-select'>"
+"       <Value>MINISBLACK</Value>"
+"       <Value>MINISWHITE</value>"
+"       <Value>RGB</Value>"
+"       <Value>CMYK</Value>"
+"       <Value>YCBCR</Value>"
+"       <Value>CIELAB</Value>"
+"       <Value>ICCLAB</Value>"
+"       <Value>ITULAB</Value>"
+"   </Option>"
+"   <Option name='PROFILE' type='string-select'>"
+"       <Value>GDALGeoTIFF</Value>"
+"       <Value>GeoTIFF</value>"
+"       <Value>BASELINE</Value>"
+"   </Option>"
+"</CreationOptionList>" );
+                 
+/* -------------------------------------------------------------------- */
+/*      Set the driver details.                                         */
+/* -------------------------------------------------------------------- */
+        poDriver->SetDescription( "GTiff" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "GeoTIFF" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_gtiff.html" );
+        poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/tiff" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "tif" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte UInt16 Int16 UInt32 Int32 Float32 Float64 CInt16 CInt32 CFloat32 CFloat64" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, 
+                                   szCreateOptions );
+
+        poDriver->pfnOpen = GTiffDataset::Open;
+        poDriver->pfnCreate = GTiffDataset::Create;
+        poDriver->pfnCreateCopy = GTiffCreateCopy;
+        poDriver->pfnUnloadDriver = GDALDeregister_GTiff;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
diff --git a/Utilities/GDAL/frmts/gtiff/gt_overview.cpp b/Utilities/GDAL/frmts/gtiff/gt_overview.cpp
new file mode 100644
index 0000000000..9f0480e1a7
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/gt_overview.cpp
@@ -0,0 +1,403 @@
+/******************************************************************************
+ * $Id: gt_overview.cpp,v 1.14 2006/04/05 00:06:14 fwarmerdam Exp $
+ *
+ * Project:  GeoTIFF Driver
+ * Purpose:  Code to build overviews of external databases as a TIFF file. 
+ *           Only used by the GDALDefaultOverviews::BuildOverviews() method.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: gt_overview.cpp,v $
+ * Revision 1.14  2006/04/05 00:06:14  fwarmerdam
+ * fix contact info
+ *
+ * Revision 1.13  2005/05/22 16:58:12  dron
+ * Added PlanarConfiguration parameter to the TIFF_WriteOverview().
+ *
+ * Revision 1.12  2004/09/09 18:59:21  fwarmerdam
+ * Adjusted to properly support colormaps with up to 65536 entries.
+ *
+ * Revision 1.11  2004/04/14 09:51:36  dron
+ * Added support for COMPRESS_OVERVIEW option.
+ *
+ * Revision 1.10  2002/07/20 12:27:13  dron
+ * Fixed building with externally preinstalled TIFF library.
+ *
+ * Revision 1.9  2001/10/12 15:06:05  warmerda
+ * various build improvements to avoid internal/external conflicts
+ *
+ * Revision 1.8  2001/07/18 04:51:56  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.7  2001/06/25 14:44:15  warmerda
+ * Fixed to return success if no bands requested to be processed.
+ *
+ * Revision 1.6  2000/09/25 21:16:24  warmerda
+ * Avoid initialization warnings.
+ *
+ * Revision 1.5  2000/08/14 18:37:24  warmerda
+ * added (untested) support for writing palettes to overviews
+ *
+ * Revision 1.4  2000/07/17 17:09:30  warmerda
+ * added support for complex data
+ *
+ * Revision 1.3  2000/06/26 22:18:33  warmerda
+ * added scaled progress support
+ *
+ * Revision 1.2  2000/06/19 18:47:24  warmerda
+ * fixed header
+ *
+ * Revision 1.1  2000/04/21 21:53:15  warmerda
+ * New
+ *
+ */
+
+#include "gdal_priv.h"
+#define CPL_SERV_H_INCLUDED
+
+#include "tiffio.h"
+#include "xtiffio.h"
+#include "geotiff.h"
+#include "tif_ovrcache.h"
+
+CPL_CVSID("$Id: gt_overview.cpp,v 1.14 2006/04/05 00:06:14 fwarmerdam Exp $");
+
+/************************************************************************/
+/*                        GTIFFBuildOverviews()                         */
+/************************************************************************/
+
+CPLErr 
+GTIFFBuildOverviews( const char * pszFilename,
+                     int nBands, GDALRasterBand **papoBandList, 
+                     int nOverviews, int * panOverviewList,
+                     const char * pszResampling, 
+                     GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    TIFF    *hOTIFF;
+    int     nBitsPerPixel=0, nCompression=COMPRESSION_NONE, nPhotometric=0;
+    int     nSampleFormat=0, nPlanarConfig, iOverview, iBand;
+    int     nXSize=0, nYSize=0;
+
+    if( nBands == 0 || nOverviews == 0 )
+        return CE_None;
+
+/* -------------------------------------------------------------------- */
+/*      Verify that the list of bands is suitable for emitting in       */
+/*      TIFF file.                                                      */
+/* -------------------------------------------------------------------- */
+    for( iBand = 0; iBand < nBands; iBand++ )
+    {
+        int     nBandBits, nBandFormat;
+        GDALRasterBand *hBand = papoBandList[iBand];
+
+        switch( hBand->GetRasterDataType() )
+        {
+          case GDT_Byte:
+            nBandBits = 8;
+            nBandFormat = SAMPLEFORMAT_UINT;
+            break;
+
+          case GDT_UInt16:
+            nBandBits = 16;
+            nBandFormat = SAMPLEFORMAT_UINT;
+            break;
+
+          case GDT_Int16:
+            nBandBits = 16;
+            nBandFormat = SAMPLEFORMAT_INT;
+            break;
+
+          case GDT_UInt32:
+            nBandBits = 32;
+            nBandFormat = SAMPLEFORMAT_UINT;
+            break;
+
+          case GDT_Int32:
+            nBandBits = 32;
+            nBandFormat = SAMPLEFORMAT_INT;
+            break;
+
+          case GDT_Float32:
+            nBandBits = 32;
+            nBandFormat = SAMPLEFORMAT_IEEEFP;
+            break;
+
+          case GDT_Float64:
+            nBandBits = 64;
+            nBandFormat = SAMPLEFORMAT_IEEEFP;
+            break;
+
+          case GDT_CInt16:
+            nBandBits = 32;
+            nBandFormat = SAMPLEFORMAT_COMPLEXINT;
+            break;
+
+          case GDT_CFloat32:
+            nBandBits = 64;
+            nBandFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
+            break;
+
+          case GDT_CFloat64:
+            nBandBits = 128;
+            nBandFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
+            break;
+
+          default:
+            CPLAssert( FALSE );
+            return CE_Failure;
+        }
+
+        if( iBand == 0 )
+        {
+            nBitsPerPixel = nBandBits;
+            nSampleFormat = nBandFormat;
+            nXSize = hBand->GetXSize();
+            nYSize = hBand->GetYSize();
+        }
+        else if( nBitsPerPixel != nBandBits || nSampleFormat != nBandFormat )
+        {
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "GTIFFBuildOverviews() doesn't support a mixture of band"
+                      " data types." );
+            return CE_Failure;
+        }
+        else if( hBand->GetColorTable() != NULL )
+        {
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "GTIFFBuildOverviews() doesn't support building"
+                      " overviews of multiple colormapped bands." );
+            return CE_Failure;
+        }
+        else if( hBand->GetXSize() != nXSize 
+                 || hBand->GetYSize() != nYSize )
+        {
+            CPLError( CE_Failure, CPLE_NotSupported, 
+                      "GTIFFBuildOverviews() doesn't support building"
+                      " overviews of different sized bands." );
+            return CE_Failure;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Use specified compression method.                               */
+/* -------------------------------------------------------------------- */
+    const char *pszCompress = CPLGetConfigOption( "COMPRESS_OVERVIEW", NULL );
+
+    if( pszCompress )
+    {
+        if( EQUAL( pszCompress, "JPEG" ) )
+            nCompression = COMPRESSION_JPEG;
+        else if( EQUAL( pszCompress, "LZW" ) )
+            nCompression = COMPRESSION_LZW;
+        else if( EQUAL( pszCompress, "PACKBITS" ))
+            nCompression = COMPRESSION_PACKBITS;
+        else if( EQUAL( pszCompress, "DEFLATE" ) || EQUAL( pszCompress, "ZIP" ))
+            nCompression = COMPRESSION_ADOBE_DEFLATE;
+        else
+            CPLError( CE_Warning, CPLE_IllegalArg, 
+                      "COMPRESS_OVERVIEW=%s value not recognised, ignoring.",
+                      pszCompress );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Figure out the planar configuration to use.                     */
+/* -------------------------------------------------------------------- */
+    if( nBands == 1 )
+        nPlanarConfig = PLANARCONFIG_CONTIG;
+    else
+        nPlanarConfig = PLANARCONFIG_SEPARATE;
+
+/* -------------------------------------------------------------------- */
+/*      Figure out the photometric interpretation to use.               */
+/* -------------------------------------------------------------------- */
+    if( nBands == 3 )
+        nPhotometric = PHOTOMETRIC_RGB;
+    else if( papoBandList[0]->GetColorTable() != NULL )
+    {
+        nPhotometric = PHOTOMETRIC_PALETTE;
+        /* should set the colormap up at this point too! */
+    }
+    else
+        nPhotometric = PHOTOMETRIC_MINISBLACK;
+
+/* -------------------------------------------------------------------- */
+/*      Create the file, if it does not already exist.                  */
+/* -------------------------------------------------------------------- */
+    VSIStatBuf  sStatBuf;
+
+    if( VSIStat( pszFilename, &sStatBuf ) != 0 )
+    {
+        hOTIFF = XTIFFOpen( pszFilename, "w+" );
+        if( hOTIFF == NULL )
+        {
+            if( CPLGetLastErrorNo() == 0 )
+                CPLError( CE_Failure, CPLE_OpenFailed,
+                          "Attempt to create new tiff file `%s'\n"
+                          "failed in XTIFFOpen().\n",
+                          pszFilename );
+
+            return CE_Failure;
+        }
+    }
+/* -------------------------------------------------------------------- */
+/*      Otherwise just open it for update access.                       */
+/* -------------------------------------------------------------------- */
+    else 
+    {
+        hOTIFF = XTIFFOpen( pszFilename, "r+" );
+        if( hOTIFF == NULL )
+        {
+            if( CPLGetLastErrorNo() == 0 )
+                CPLError( CE_Failure, CPLE_OpenFailed,
+                          "Attempt to create new tiff file `%s'\n"
+                          "failed in XTIFFOpen().\n",
+                          pszFilename );
+
+            return CE_Failure;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Do we have a palette?  If so, create a TIFF compatible version. */
+/* -------------------------------------------------------------------- */
+    unsigned short	anTRed[65536], anTGreen[65536], anTBlue[65536];
+    unsigned short      *panRed=NULL, *panGreen=NULL, *panBlue=NULL;
+
+    if( nPhotometric == PHOTOMETRIC_PALETTE )
+    {
+        GDALColorTable *poCT = papoBandList[0]->GetColorTable();
+        int nColorCount = MIN(65536,poCT->GetColorEntryCount());
+
+        memset( anTRed, 0, 65536 * 2 );
+        memset( anTGreen, 0, 65536 * 2 );
+        memset( anTBlue, 0, 65536 * 2 );
+
+        for( int iColor = 0; iColor < nColorCount; iColor++ )
+        {
+            GDALColorEntry  sRGB;
+
+            poCT->GetColorEntryAsRGB( iColor, &sRGB );
+
+            anTRed[iColor] = (unsigned short) (256 * sRGB.c1);
+            anTGreen[iColor] = (unsigned short) (256 * sRGB.c2);
+            anTBlue[iColor] = (unsigned short) (256 * sRGB.c3);
+        }
+
+        panRed = anTRed;
+        panGreen = anTGreen;
+        panBlue = anTBlue;
+    }
+        
+/* -------------------------------------------------------------------- */
+/*      Loop, creating overviews.                                       */
+/* -------------------------------------------------------------------- */
+    for( iOverview = 0; iOverview < nOverviews; iOverview++ )
+    {
+        int    nOXSize, nOYSize;
+        uint32 nDirOffset;
+
+        nOXSize = (nXSize + panOverviewList[iOverview] - 1) 
+            / panOverviewList[iOverview];
+        nOYSize = (nYSize + panOverviewList[iOverview] - 1) 
+            / panOverviewList[iOverview];
+
+        nDirOffset = 
+            TIFF_WriteOverview( hOTIFF, nOXSize, nOYSize, nBitsPerPixel, 
+                                nPlanarConfig, nBands,
+                                128, 128, TRUE, nCompression,
+                                nPhotometric, nSampleFormat, 
+                                panRed, panGreen, panBlue, 
+                                FALSE );
+    }
+
+    XTIFFClose( hOTIFF );
+
+/* -------------------------------------------------------------------- */
+/*      Open the overview dataset so that we can get at the overview    */
+/*      bands.                                                          */
+/* -------------------------------------------------------------------- */
+    GDALDataset *hODS;
+
+    hODS = (GDALDataset *) GDALOpen( pszFilename, GA_Update );
+    if( hODS == NULL )
+        return CE_Failure;
+    
+/* -------------------------------------------------------------------- */
+/*      Loop writing overview data.                                     */
+/* -------------------------------------------------------------------- */
+    GDALRasterBand   **papoOverviews;
+
+    papoOverviews = (GDALRasterBand **) CPLCalloc(sizeof(void*),128);
+
+    for( iBand = 0; iBand < nBands; iBand++ )
+    {
+        GDALRasterBand    *hSrcBand = papoBandList[iBand];
+        GDALRasterBand    *hDstBand;
+        int               nDstOverviews;
+        CPLErr            eErr;
+
+        hDstBand = hODS->GetRasterBand( iBand+1 );
+        
+        papoOverviews[0] = hDstBand;
+        nDstOverviews = hDstBand->GetOverviewCount() + 1;
+        CPLAssert( nDstOverviews < 128 );
+        nDstOverviews = MIN(128,nDstOverviews);
+
+        for( int i = 0; i < nDstOverviews-1; i++ )
+        {
+            papoOverviews[i+1] = hDstBand->GetOverview(i);
+        }
+
+        void         *pScaledProgressData;
+
+        pScaledProgressData = 
+            GDALCreateScaledProgress( iBand / (double) nBands, 
+                                      (iBand+1) / (double) nBands,
+                                      pfnProgress, pProgressData );
+
+        eErr = 
+            GDALRegenerateOverviews( hSrcBand, nDstOverviews, papoOverviews, 
+                                     pszResampling,pfnProgress,pProgressData);
+
+        GDALDestroyScaledProgress( pScaledProgressData );
+
+        if( eErr != CE_None )
+        {
+            delete hODS;
+            return eErr;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    hODS->FlushCache();
+    delete hODS;
+
+    pfnProgress( 1.0, NULL, pProgressData );
+
+    return CE_None;
+}
+    
diff --git a/Utilities/GDAL/frmts/gtiff/gt_wkt_srs.cpp b/Utilities/GDAL/frmts/gtiff/gt_wkt_srs.cpp
new file mode 100644
index 0000000000..4270076534
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/gt_wkt_srs.cpp
@@ -0,0 +1,1884 @@
+/******************************************************************************
+ * $Id: gt_wkt_srs.cpp,v 1.63 2006/04/28 04:21:57 fwarmerdam Exp $
+ *
+ * Project:  GeoTIFF Driver
+ * Purpose:  Implements translation between GeoTIFF normalized projection
+ *           definitions and OpenGIS WKT SRS format.  This code is
+ *           deliberately GDAL free, and it is intended to be moved into
+ *           libgeotiff someday if possible.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: gt_wkt_srs.cpp,v $
+ * Revision 1.63  2006/04/28 04:21:57  fwarmerdam
+ * make sure pszInfo is dynamically allocated in gcps
+ *
+ * Revision 1.62  2006/04/21 12:31:29  fwarmerdam
+ * Ensure all of adfParms[] is initialized per:
+ * http://bugzilla.remotesensing.org/show_bug.cgi?id=1164
+ *
+ * Revision 1.61  2006/04/05 00:06:14  fwarmerdam
+ * fix contact info
+ *
+ * Revision 1.60  2005/12/22 04:14:59  fwarmerdam
+ * Added special method to massage citations coming from Imagine.
+ *
+ * Revision 1.59  2005/09/13 23:55:00  fwarmerdam
+ * fixed VSIUnlink prototype
+ *
+ * Revision 1.58  2005/09/12 17:06:28  fwarmerdam
+ * avoid dependence on tif_vsi.h
+ *
+ * Revision 1.57  2005/09/12 16:56:44  fwarmerdam
+ * fixup to avoid depending on cpl_vsi.h
+ *
+ * Revision 1.56  2005/09/12 00:28:28  fwarmerdam
+ * use vsi memory io instead of memio module
+ *
+ * Revision 1.55  2005/06/08 18:07:35  fwarmerdam
+ * Fixed for memory allocation mixup in GTIFGetOGISDefn() return value.
+ *
+ * Revision 1.54  2005/05/22 21:00:15  fwarmerdam
+ * ensure false easting/northing written out in meters
+ *
+ * Revision 1.53  2005/03/15 16:04:47  fwarmerdam
+ * Emit semimajor+semiminor if inv flattening is zero (sphere) instead of
+ * semimajor + 0 inv flattening.
+ *
+ * Revision 1.52  2005/03/09 17:04:44  fwarmerdam
+ * added CEA support
+ *
+ * Revision 1.51  2005/02/17 21:58:08  fwarmerdam
+ * Avoid leak of pszGeogName.
+ *
+ * Revision 1.50  2005/02/07 13:30:30  dron
+ * Memory leak removed.
+ *
+ * Revision 1.49  2004/10/18 21:12:44  fwarmerdam
+ * Always emit angular units as degrees.
+ *
+ * Revision 1.48  2004/07/10 05:02:57  warmerda
+ * Fixed improper projection parameters for false easting/northing for LCC.
+ *
+ * Revision 1.47  2004/04/29 19:58:43  warmerda
+ * export GTIFGetOGISDefn, and GTIFSetFromOGISDefn
+ *
+ * Revision 1.46  2004/04/29 18:10:35  warmerda
+ * try not to crash if GTIF is NULL
+ *
+ * Revision 1.45  2004/04/21 13:59:15  warmerda
+ * try to preserve PROJCS and GEOGCS names in citations
+ *
+ * Revision 1.44  2004/03/18 09:58:07  dron
+ * Use auxiliary functions from the libgeotiff instead of the ones from CPL.
+ */
+
+#include "cpl_serv.h"
+#include "geo_tiffp.h"
+#define _CPL_ERROR_H_INCLUDED_
+
+#include "geo_normalize.h"
+#include "geovalues.h"
+#include "ogr_spatialref.h"
+#include "gdal.h"
+#include "xtiffio.h"
+
+CPL_CVSID("$Id: gt_wkt_srs.cpp,v 1.63 2006/04/28 04:21:57 fwarmerdam Exp $");
+
+CPL_C_START
+int CPL_DLL VSIFCloseL( FILE * );
+int CPL_DLL VSIUnlink( const char * );
+FILE CPL_DLL *VSIFileFromMemBuffer( const char *pszFilename, 
+                                    GByte *pabyData, 
+                                    GUIntBig nDataLength,
+                                    int bTakeOwnership );
+GByte CPL_DLL *VSIGetMemFileBuffer( const char *pszFilename, 
+                                    GUIntBig *pnDataLength, 
+                                    int bUnlinkAndSeize );
+
+char CPL_DLL *  GTIFGetOGISDefn( GTIF *, GTIFDefn * );
+int  CPL_DLL   GTIFSetFromOGISDefn( GTIF *, const char * );
+
+CPLErr CPL_DLL GTIFMemBufFromWkt( const char *pszWKT, 
+                                  const double *padfGeoTransform,
+                                  int nGCPCount, const GDAL_GCP *pasGCPList,
+                                  int *pnSize, unsigned char **ppabyBuffer );
+CPLErr CPL_DLL GTIFWktFromMemBuf( int nSize, unsigned char *pabyBuffer, 
+                          char **ppszWKT, double *padfGeoTransform,
+                          int *pnGCPCount, GDAL_GCP **ppasGCPList );
+CPL_C_END
+
+TIFF* VSI_TIFFOpen(const char* name, const char* mode);
+
+static char *papszDatumEquiv[] =
+{
+    "Militar_Geographische_Institut",
+    "Militar_Geographische_Institute",
+    "World_Geodetic_System_1984",
+    "WGS_1984",
+    "WGS_72_Transit_Broadcast_Ephemeris",
+    "WGS_1972_Transit_Broadcast_Ephemeris",
+    "World_Geodetic_System_1972",
+    "WGS_1972",
+    "European_Terrestrial_Reference_System_89",
+    "European_Reference_System_1989",
+    NULL
+};
+
+// older libgeotiff's won't list this.
+#ifndef CT_CylindricalEqualArea
+# define CT_CylindricalEqualArea 28
+#endif
+
+/************************************************************************/
+/*                          WKTMassageDatum()                           */
+/*                                                                      */
+/*      Massage an EPSG datum name into WMT format.  Also transform     */
+/*      specific exception cases into WKT versions.                     */
+/************************************************************************/
+
+static void WKTMassageDatum( char ** ppszDatum )
+
+{
+    int		i, j;
+    char	*pszDatum;
+
+/* -------------------------------------------------------------------- */
+/*      First copy string and allocate with our CPLStrdup() to so we    */
+/*      know when we are done this function we will have a CPL          */
+/*      string, not a GTIF one.                                         */
+/* -------------------------------------------------------------------- */
+    pszDatum = CPLStrdup(*ppszDatum);
+    GTIFFreeMemory( *ppszDatum );
+    *ppszDatum = pszDatum;
+
+/* -------------------------------------------------------------------- */
+/*      Translate non-alphanumeric values to underscores.               */
+/* -------------------------------------------------------------------- */
+    for( i = 0; pszDatum[i] != '\0'; i++ )
+    {
+        if( !(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z')
+            && !(pszDatum[i] >= 'a' && pszDatum[i] <= 'z')
+            && !(pszDatum[i] >= '0' && pszDatum[i] <= '9') )
+        {
+            pszDatum[i] = '_';
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Remove repeated and trailing underscores.                       */
+/* -------------------------------------------------------------------- */
+    for( i = 1, j = 0; pszDatum[i] != '\0'; i++ )
+    {
+        if( pszDatum[j] == '_' && pszDatum[i] == '_' )
+            continue;
+
+        pszDatum[++j] = pszDatum[i];
+    }
+    if( pszDatum[j] == '_' )
+        pszDatum[j] = '\0';
+    else
+        pszDatum[j+1] = '\0';
+    
+/* -------------------------------------------------------------------- */
+/*      Search for datum equivelences.  Specific massaged names get     */
+/*      mapped to OpenGIS specified names.                              */
+/* -------------------------------------------------------------------- */
+    for( i = 0; papszDatumEquiv[i] != NULL; i += 2 )
+    {
+        if( EQUAL(*ppszDatum,papszDatumEquiv[i]) )
+        {
+            CPLFree( *ppszDatum );
+            *ppszDatum = CPLStrdup( papszDatumEquiv[i+1] );
+            return;
+        }
+    }
+}
+
+/************************************************************************/
+/*                      GTIFCleanupImageineNames()                      */
+/*                                                                      */
+/*      Erdas Imagine sometimes emits big copyright messages, and       */
+/*      other stuff into citations.  These can be pretty messy when     */
+/*      turned into WKT, so we try to trim and clean the strings        */
+/*      somewhat.                                                       */
+/************************************************************************/
+
+/* For example:
+   GTCitationGeoKey (Ascii,215): "IMAGINE GeoTIFF Support\nCopyright 1991 - 2001 by ERDAS, Inc. All Rights Reserved\n@(#)$RCSfile: gt_wkt_srs.cpp,v $ $Revision: 1.63 $ $Date: 2006/04/28 04:21:57 $\nProjection Name = UTM\nUnits = meters\nGeoTIFF Units = meters"
+
+   GeogCitationGeoKey (Ascii,267): "IMAGINE GeoTIFF Support\nCopyright 1991 - 2001 by ERDAS, Inc. All Rights Reserved\n@(#)$RCSfile: gt_wkt_srs.cpp,v $ $Revision: 1.63 $ $Date: 2006/04/28 04:21:57 $\nUnable to match Ellipsoid (Datum) to a GeographicTypeGeoKey value\nEllipsoid = Clarke 1866\nDatum = NAD27 (CONUS)"
+
+   PCSCitationGeoKey (Ascii,214): "IMAGINE GeoTIFF Support\nCopyright 1991 - 2001 by ERDAS, Inc. All Rights Reserved\n@(#)$RCSfile: gt_wkt_srs.cpp,v $ $Revision: 1.63 $ $Date: 2006/04/28 04:21:57 $\nUTM Zone 10N\nEllipsoid = Clarke 1866\nDatum = NAD27 (CONUS)"
+ 
+*/
+
+static void GTIFCleanupImagineNames( char *pszCitation )
+
+{
+    if( strstr(pszCitation,"IMAGINE GeoTIFF") == NULL )
+        return;
+
+/* -------------------------------------------------------------------- */
+/*      First, we skip past all the copyright, and RCS stuff.  We       */
+/*      assume that this will have a "$" at the end of it all.          */
+/* -------------------------------------------------------------------- */
+    char *pszSkip;
+    
+    for( pszSkip = pszCitation + strlen(pszCitation) - 1;
+         pszSkip != pszCitation && *pszSkip != '$'; 
+         pszSkip-- ) {}
+
+    if( *pszSkip == '$' )
+        pszSkip++;
+
+    memmove( pszCitation, pszSkip, strlen(pszSkip)+1 );
+
+/* -------------------------------------------------------------------- */
+/*      Convert any newlines into spaces, they really gum up the        */
+/*      WKT.                                                            */
+/* -------------------------------------------------------------------- */
+    int i;
+
+    for( i = 0; pszCitation[i] != '\0'; i++ )
+    {
+        if( pszCitation[i] == '\n' )
+            pszCitation[i] = ' ';
+    }
+}
+
+/************************************************************************/
+/*                          GTIFGetOGISDefn()                           */
+/************************************************************************/
+
+char *GTIFGetOGISDefn( GTIF *hGTIF, GTIFDefn * psDefn )
+
+{
+    OGRSpatialReference	oSRS;
+
+    if( psDefn->Model != ModelTypeProjected 
+        && psDefn->Model != ModelTypeGeographic )
+    {
+        char	*pszWKT;
+        
+        // We use this ackward alternative to return an empty string
+        // to ensure that it is allocated with GDAL's copy of VSIMalloc()
+        // instead of the one in libtiff as they sometime differ (ie. on
+        // win32 with external libtiff). 
+
+        oSRS.exportToWkt( &pszWKT );
+
+        return pszWKT;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      If this is a projected SRS we set the PROJCS keyword first      */
+/*      to ensure that the GEOGCS will be a child.                      */
+/* -------------------------------------------------------------------- */
+    if( psDefn->Model == ModelTypeProjected )
+    {
+        char	*pszPCSName = "unnamed";
+        int         bNeedFree = FALSE;
+
+        if( psDefn->PCS != KvUserDefined )
+        {
+
+            if( GTIFGetPCSInfo( psDefn->PCS, &pszPCSName, NULL, NULL, NULL ) )
+                bNeedFree = TRUE;
+            
+            oSRS.SetNode( "PROJCS", pszPCSName );
+            if( bNeedFree )
+                GTIFFreeMemory( pszPCSName );
+
+            oSRS.SetAuthority( "PROJCS", "EPSG", psDefn->PCS );
+        }
+        else
+        {
+            char szPCSName[300];
+            strcpy( szPCSName, "unnamed" );
+            if( hGTIF != NULL )
+            {
+                GTIFKeyGet( hGTIF, GTCitationGeoKey, szPCSName, 0, sizeof(szPCSName) );
+                GTIFCleanupImagineNames( szPCSName );
+            }
+            oSRS.SetNode( "PROJCS", szPCSName );
+        }
+    }
+    
+/* ==================================================================== */
+/*      Setup the GeogCS                                                */
+/* ==================================================================== */
+    char	*pszGeogName = NULL;
+    char	*pszDatumName = NULL;
+    char	*pszPMName = NULL;
+    char	*pszSpheroidName = NULL;
+    char	*pszAngularUnits = NULL;
+    double	dfInvFlattening, dfSemiMajor;
+    char        szGCSName[300];
+    
+    if( !GTIFGetGCSInfo( psDefn->GCS, &pszGeogName, NULL, NULL, NULL )
+        && hGTIF != NULL 
+        && GTIFKeyGet( hGTIF, GeogCitationGeoKey, szGCSName, 0, 
+                       sizeof(szGCSName)) )
+    {
+        GTIFCleanupImagineNames( szGCSName );
+        pszGeogName = CPLStrdup(szGCSName);
+    }
+
+    GTIFGetDatumInfo( psDefn->Datum, &pszDatumName, NULL );
+    GTIFGetPMInfo( psDefn->PM, &pszPMName, NULL );
+    GTIFGetEllipsoidInfo( psDefn->Ellipsoid, &pszSpheroidName, NULL, NULL );
+    
+    GTIFGetUOMAngleInfo( psDefn->UOMAngle, &pszAngularUnits, NULL );
+    if( pszAngularUnits == NULL )
+        pszAngularUnits = CPLStrdup("unknown");
+
+    if( pszDatumName != NULL )
+        WKTMassageDatum( &pszDatumName );
+
+    dfSemiMajor = psDefn->SemiMajor;
+    if( psDefn->SemiMajor == 0.0 )
+    {
+        pszSpheroidName = CPLStrdup("unretrievable - using WGS84");
+        dfSemiMajor = SRS_WGS84_SEMIMAJOR;
+        dfInvFlattening = SRS_WGS84_INVFLATTENING;
+    }
+    else if( (psDefn->SemiMinor / psDefn->SemiMajor) < 0.99999999999999999
+             || (psDefn->SemiMinor / psDefn->SemiMajor) > 1.00000000000000001 )
+        dfInvFlattening = -1.0 / (psDefn->SemiMinor/psDefn->SemiMajor - 1.0);
+    else
+        dfInvFlattening = 0.0; /* special flag for infinity */
+
+    oSRS.SetGeogCS( pszGeogName, pszDatumName, 
+                    pszSpheroidName, dfSemiMajor, dfInvFlattening,
+                    pszPMName,
+                    psDefn->PMLongToGreenwich / psDefn->UOMAngleInDegrees,
+                    pszAngularUnits,
+                    psDefn->UOMAngleInDegrees * 0.0174532925199433 );
+
+    if( psDefn->GCS != KvUserDefined )
+        oSRS.SetAuthority( "GEOGCS", "EPSG", psDefn->GCS );
+
+    if( psDefn->Datum != KvUserDefined )
+        oSRS.SetAuthority( "DATUM", "EPSG", psDefn->Datum );
+
+    if( psDefn->Ellipsoid != KvUserDefined )
+        oSRS.SetAuthority( "SPHEROID", "EPSG", psDefn->Ellipsoid );
+
+    CPLFree( pszGeogName );
+    CPLFree( pszDatumName );
+    GTIFFreeMemory( pszPMName );
+    GTIFFreeMemory( pszSpheroidName );
+    GTIFFreeMemory( pszAngularUnits );
+        
+/* ==================================================================== */
+/*      Handle projection parameters.                                   */
+/* ==================================================================== */
+    if( psDefn->Model == ModelTypeProjected )
+    {
+/* -------------------------------------------------------------------- */
+/*      Make a local copy of parms, and convert back into the           */
+/*      angular units of the GEOGCS and the linear units of the         */
+/*      projection.                                                     */
+/* -------------------------------------------------------------------- */
+        double		adfParm[10];
+        int		i;
+
+        for( i = 0; i < MIN(10,psDefn->nParms); i++ )
+            adfParm[i] = psDefn->ProjParm[i];
+
+        for( ; i < 10; i++ )
+            adfParm[i] = 0.0;
+
+        adfParm[0] /= psDefn->UOMAngleInDegrees;
+        adfParm[1] /= psDefn->UOMAngleInDegrees;
+        adfParm[2] /= psDefn->UOMAngleInDegrees;
+        adfParm[3] /= psDefn->UOMAngleInDegrees;
+        
+        adfParm[5] /= psDefn->UOMLengthInMeters;
+        adfParm[6] /= psDefn->UOMLengthInMeters;
+        
+/* -------------------------------------------------------------------- */
+/*      Translation the fundamental projection.                         */
+/* -------------------------------------------------------------------- */
+        switch( psDefn->CTProjection )
+        {
+          case CT_TransverseMercator:
+            oSRS.SetTM( adfParm[0], adfParm[1],
+                        adfParm[4],
+                        adfParm[5], adfParm[6] );
+            break;
+
+          case CT_TransvMercator_SouthOriented:
+            oSRS.SetTMSO( adfParm[0], adfParm[1],
+                          adfParm[4],
+                          adfParm[5], adfParm[6] );
+            break;
+
+          case CT_Mercator:
+            oSRS.SetMercator( adfParm[0], adfParm[1],
+                              adfParm[4],
+                              adfParm[5], adfParm[6] );
+            break;
+
+          case CT_ObliqueStereographic:
+            oSRS.SetOS( adfParm[0], adfParm[1],
+                        adfParm[4],
+                        adfParm[5], adfParm[6] );
+            break;
+
+          case CT_Stereographic:
+            oSRS.SetOS( adfParm[0], adfParm[1],
+                        adfParm[4],
+                        adfParm[5], adfParm[6] );
+            break;
+
+          case CT_ObliqueMercator: /* hotine */
+            oSRS.SetHOM( adfParm[0], adfParm[1],
+                         adfParm[2], adfParm[3],
+                         adfParm[4],
+                         adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_EquidistantConic: 
+            oSRS.SetEC( adfParm[0], adfParm[1],
+                        adfParm[2], adfParm[3],
+                        adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_CassiniSoldner:
+            oSRS.SetCS( adfParm[0], adfParm[1],
+                        adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_Polyconic:
+            oSRS.SetPolyconic( adfParm[0], adfParm[1],
+                               adfParm[5], adfParm[6] );
+            break;
+
+          case CT_AzimuthalEquidistant:
+            oSRS.SetAE( adfParm[0], adfParm[1],
+                        adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_MillerCylindrical:
+            oSRS.SetMC( adfParm[0], adfParm[1],
+                        adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_Equirectangular:
+            oSRS.SetEquirectangular( adfParm[0], adfParm[1],
+                                     adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_Gnomonic:
+            oSRS.SetGnomonic( adfParm[0], adfParm[1],
+                              adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_LambertAzimEqualArea:
+            oSRS.SetLAEA( adfParm[0], adfParm[1],
+                          adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_Orthographic:
+            oSRS.SetOrthographic( adfParm[0], adfParm[1],
+                                  adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_Robinson:
+            oSRS.SetRobinson( adfParm[1],
+                              adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_Sinusoidal:
+            oSRS.SetSinusoidal( adfParm[1],
+                                adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_VanDerGrinten:
+            oSRS.SetVDG( adfParm[1],
+                         adfParm[5], adfParm[6] );
+            break;
+
+          case CT_PolarStereographic:
+            oSRS.SetPS( adfParm[0], adfParm[1],
+                        adfParm[4],
+                        adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_LambertConfConic_2SP:
+            oSRS.SetLCC( adfParm[2], adfParm[3],
+                         adfParm[0], adfParm[1],
+                         adfParm[5], adfParm[6] );
+            break;
+
+          case CT_LambertConfConic_1SP:
+            oSRS.SetLCC1SP( adfParm[0], adfParm[1],
+                            adfParm[4],
+                            adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_AlbersEqualArea:
+            oSRS.SetACEA( adfParm[0], adfParm[1],
+                          adfParm[2], adfParm[3],
+                          adfParm[5], adfParm[6] );
+            break;
+
+          case CT_NewZealandMapGrid:
+            oSRS.SetNZMG( adfParm[0], adfParm[1],
+                          adfParm[5], adfParm[6] );
+            break;
+
+          case CT_CylindricalEqualArea:
+            oSRS.SetCEA( adfParm[0], adfParm[1],
+                         adfParm[5], adfParm[6] );
+            break;
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Set projection units.                                           */
+/* -------------------------------------------------------------------- */
+        char	*pszUnitsName = NULL;
+        
+        GTIFGetUOMLengthInfo( psDefn->UOMLength, &pszUnitsName, NULL );
+
+        if( pszUnitsName != NULL && psDefn->UOMLength != KvUserDefined )
+        {
+            oSRS.SetLinearUnits( pszUnitsName, psDefn->UOMLengthInMeters );
+            oSRS.SetAuthority( "PROJCS|UNIT", "EPSG", psDefn->UOMLength );
+        }
+        else
+            oSRS.SetLinearUnits( "unknown", psDefn->UOMLengthInMeters );
+
+        GTIFFreeMemory( pszUnitsName );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Return the WKT serialization of the object.                     */
+/* -------------------------------------------------------------------- */
+    char	*pszWKT;
+
+    oSRS.FixupOrdering();
+
+    if( oSRS.exportToWkt( &pszWKT ) == OGRERR_NONE )
+        return pszWKT;
+    else
+        return NULL;
+}
+
+/************************************************************************/
+/*                     OGCDatumName2EPSGDatumCode()                     */
+/************************************************************************/
+
+static int OGCDatumName2EPSGDatumCode( const char * pszOGCName )
+
+{
+    FILE	*fp;
+    char	**papszTokens;
+    int		nReturn = KvUserDefined;
+
+/* -------------------------------------------------------------------- */
+/*      Do we know it as a built in?                                    */
+/* -------------------------------------------------------------------- */
+    if( EQUAL(pszOGCName,"NAD27") 
+        || EQUAL(pszOGCName,"North_American_Datum_1927") )
+        return Datum_North_American_Datum_1927;
+    else if( EQUAL(pszOGCName,"NAD83") 
+        || EQUAL(pszOGCName,"North_American_Datum_1983") )
+        return Datum_North_American_Datum_1983;
+    else if( EQUAL(pszOGCName,"WGS84") || EQUAL(pszOGCName,"WGS_1984")
+             || EQUAL(pszOGCName,"WGS 84"))
+        return Datum_WGS84;
+    else if( EQUAL(pszOGCName,"WGS72") || EQUAL(pszOGCName,"WGS_1972") )
+        return Datum_WGS72;
+    
+/* -------------------------------------------------------------------- */
+/*      Open the table if possible.                                     */
+/* -------------------------------------------------------------------- */
+    fp = VSIFOpen( CSVFilename("gdal_datum.csv"), "r" );
+    if( fp == NULL )
+        fp = VSIFOpen( CSVFilename("datum.csv"), "r" );
+
+    if( fp == NULL )
+        return nReturn;
+
+/* -------------------------------------------------------------------- */
+/*	Discard the first line with field names.			*/
+/* -------------------------------------------------------------------- */
+    CSLDestroy( CSVReadParseLine( fp ) );
+
+/* -------------------------------------------------------------------- */
+/*      Read lines looking for our datum.                               */
+/* -------------------------------------------------------------------- */
+    for( papszTokens = CSVReadParseLine( fp );
+         CSLCount(papszTokens) > 2 && nReturn == KvUserDefined;
+         papszTokens = CSVReadParseLine( fp ) )
+    {
+        WKTMassageDatum( papszTokens + 1 );
+
+        if( EQUAL(papszTokens[1], pszOGCName) )
+            nReturn = atoi(papszTokens[0]);
+
+        CSLDestroy( papszTokens );
+    }
+
+    CSLDestroy( papszTokens );
+    VSIFClose( fp );
+
+    return nReturn;
+}
+
+/************************************************************************/
+/*                        GTIFSetFromOGISDefn()                         */
+/*                                                                      */
+/*      Write GeoTIFF projection tags from an OGC WKT definition.       */
+/************************************************************************/
+
+int GTIFSetFromOGISDefn( GTIF * psGTIF, const char *pszOGCWKT )
+
+{
+    OGRSpatialReference *poSRS;
+    int		nPCS = KvUserDefined;
+    OGRErr      eErr;
+
+    GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
+               RasterPixelIsArea);
+
+/* -------------------------------------------------------------------- */
+/*      Create an OGRSpatialReference object corresponding to the       */
+/*      string.                                                         */
+/* -------------------------------------------------------------------- */
+    poSRS = new OGRSpatialReference();
+
+    if( poSRS->importFromWkt((char **) &pszOGCWKT) != OGRERR_NONE )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Get the ellipsoid definition.                                   */
+/* -------------------------------------------------------------------- */
+    short nSpheroid = KvUserDefined;
+    double dfSemiMajor, dfInvFlattening;
+
+    if( poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM|SPHEROID") != NULL
+        && EQUAL(poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM|SPHEROID"),
+                 "EPSG")) 
+    {
+        nSpheroid = (short)
+            atoi(poSRS->GetAuthorityCode("PROJCS|GEOGCS|DATUM|SPHEROID"));
+    }
+    else if( poSRS->GetAuthorityName("GEOGCS|DATUM|SPHEROID") != NULL
+            && EQUAL(poSRS->GetAuthorityName("GEOGCS|DATUM|SPHEROID"),"EPSG")) 
+    {
+        nSpheroid = (short)
+            atoi(poSRS->GetAuthorityCode("GEOGCS|DATUM|SPHEROID"));
+    }
+    
+    dfSemiMajor = poSRS->GetSemiMajor( &eErr );
+    dfInvFlattening = poSRS->GetInvFlattening( &eErr );
+    if( eErr != OGRERR_NONE )
+    {
+        dfSemiMajor = 0.0;
+        dfInvFlattening = 0.0;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Get the Datum so we can special case a few PCS codes.           */
+/* -------------------------------------------------------------------- */
+    int		nDatum = KvUserDefined;
+
+    if( poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM") != NULL 
+        && EQUAL(poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM"),"EPSG") )
+        nDatum = atoi(poSRS->GetAuthorityCode("PROJCS|GEOGCS|DATUM"));
+    else if( poSRS->GetAuthorityName("GEOGCS|DATUM") != NULL 
+             && EQUAL(poSRS->GetAuthorityName("GEOGCS|DATUM"),"EPSG") )
+        nDatum = atoi(poSRS->GetAuthorityCode("GEOGCS|DATUM"));
+    else if( poSRS->GetAttrValue("DATUM") != NULL )
+        nDatum = OGCDatumName2EPSGDatumCode( poSRS->GetAttrValue("DATUM") );
+
+/* -------------------------------------------------------------------- */
+/*      Get the GCS if possible.                                        */
+/* -------------------------------------------------------------------- */
+    int         nGCS = KvUserDefined;
+
+    if( poSRS->GetAuthorityName("PROJCS|GEOGCS") != NULL 
+        && EQUAL(poSRS->GetAuthorityName("PROJCS|GEOGCS"),"EPSG") )
+        nGCS = atoi(poSRS->GetAuthorityCode("PROJCS|GEOGCS"));
+    else if( poSRS->GetAuthorityName("GEOGCS") != NULL 
+             && EQUAL(poSRS->GetAuthorityName("GEOGCS"),"EPSG") )
+        nGCS = atoi(poSRS->GetAuthorityCode("GEOGCS"));
+
+    if( nGCS > 32767 )
+        nGCS = KvUserDefined;
+
+/* -------------------------------------------------------------------- */
+/*      Get the linear units.                                           */
+/* -------------------------------------------------------------------- */
+    char        *pszLinearUOMName = NULL;
+    double	dfLinearUOM = poSRS->GetLinearUnits( &pszLinearUOMName );
+    int         nUOMLengthCode = 9001; /* meters */
+
+    if( (pszLinearUOMName != NULL
+         && EQUAL(pszLinearUOMName,SRS_UL_FOOT))
+        || dfLinearUOM == atof(SRS_UL_FOOT_CONV) )
+        nUOMLengthCode = 9002; /* international foot */
+    else if( (pszLinearUOMName != NULL
+         && EQUAL(pszLinearUOMName,SRS_UL_US_FOOT))
+             || ABS(dfLinearUOM-atof(SRS_UL_US_FOOT_CONV)) < 0.0000001 )
+        nUOMLengthCode = 9003; /* us survey foot */
+    else if( dfLinearUOM != 1.0 )
+        nUOMLengthCode = KvUserDefined;
+
+/* -------------------------------------------------------------------- */
+/*      Get some authority values.                                      */
+/* -------------------------------------------------------------------- */
+    if( poSRS->GetAuthorityName("PROJCS") != NULL 
+        && EQUAL(poSRS->GetAuthorityName("PROJCS"),"EPSG") )
+    {
+        nPCS = atoi(poSRS->GetAuthorityCode("PROJCS"));
+        if( nPCS > 32767 )
+            nPCS = KvUserDefined;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Handle the projection transformation.                           */
+/* -------------------------------------------------------------------- */
+    const char *pszProjection = poSRS->GetAttrValue( "PROJECTION" );
+
+    if( nPCS != KvUserDefined )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+        GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS );
+    }
+    else if( pszProjection == NULL )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeGeographic);
+    }
+
+    else if( EQUAL(pszProjection,SRS_PT_ALBERS_CONIC_EQUAL_AREA) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_AlbersEqualArea );
+
+        GTIFKeySet(psGTIF, ProjStdParallelGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+
+    else if( poSRS->GetUTMZone() != 0 )
+    {
+        int		bNorth, nZone, nProjection;
+
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+
+        nZone = poSRS->GetUTMZone( &bNorth );
+
+        if( nDatum == Datum_North_American_Datum_1983 && nZone >= 3
+            && nZone <= 22 && bNorth && nUOMLengthCode == 9001 )
+        {
+            nPCS = 26900 + nZone;
+
+            GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS );
+        }
+        else if( nDatum == Datum_North_American_Datum_1927 && nZone >= 3
+            && nZone <= 22 && bNorth && nUOMLengthCode == 9001 )
+        {
+            nPCS = 26700 + nZone;
+
+            GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS );
+        }
+        else if( nDatum == Datum_WGS84 && nUOMLengthCode == 9001 )
+        {
+            if( bNorth )
+                nPCS = 32600 + nZone;
+            else
+                nPCS = 32700 + nZone;
+
+            GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS );
+        }
+        else
+        {
+            if( bNorth )
+                nProjection = 16000 + nZone;
+            else
+                nProjection = 16100 + nZone;
+
+        
+            GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                       KvUserDefined );
+            
+            GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, nProjection );
+        }
+    }
+
+    else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_TransverseMercator );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_TransvMercator_SouthOriented );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_MERCATOR_2SP) 
+             || EQUAL(pszProjection,SRS_PT_MERCATOR_1SP) )
+
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_Mercator );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_OBLIQUE_STEREOGRAPHIC) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_ObliqueStereographic );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_STEREOGRAPHIC) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_Stereographic );
+
+        GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_POLAR_STEREOGRAPHIC) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_PolarStereographic );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjStraightVertPoleLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_HOTINE_OBLIQUE_MERCATOR) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_ObliqueMercator );
+
+        GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjAzimuthAngleGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_AZIMUTH, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjRectifiedGridAngleGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_RECTIFIED_GRID_ANGLE, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_CASSINI_SOLDNER) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_CassiniSoldner );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_EQUIDISTANT_CONIC) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_EquidistantConic );
+
+        GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_POLYCONIC) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_Polyconic );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_AZIMUTHAL_EQUIDISTANT) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_AzimuthalEquidistant );
+
+        GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_MILLER_CYLINDRICAL) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_MillerCylindrical );
+
+        GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_EQUIRECTANGULAR) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_Equirectangular );
+
+        GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_GNOMONIC) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_Gnomonic );
+
+        GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_LambertAzimEqualArea );
+
+        GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_ORTHOGRAPHIC) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_Orthographic );
+
+        GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_NEW_ZEALAND_MAP_GRID) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_NewZealandMapGrid );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_ROBINSON) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_Robinson );
+
+        GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_SINUSOIDAL) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_Sinusoidal );
+
+        GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_VANDERGRINTEN) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_VanDerGrinten );
+
+        GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_ALBERS_CONIC_EQUAL_AREA) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_AlbersEqualArea );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_LambertConfConic_2SP );
+
+        GTIFKeySet(psGTIF, ProjFalseOriginLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjFalseOriginLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseOriginEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseOriginNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_LambertConfConic_2SP );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+
+    else if( EQUAL(pszProjection,SRS_PT_CYLINDRICAL_EQUAL_AREA) )
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   ModelTypeProjected);
+	GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+	GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+
+	GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, 
+		   CT_CylindricalEqualArea );
+
+        GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ) );
+        
+        GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
+                   poSRS->GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) );
+    }
+    
+    else
+    {
+	GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
+                   KvUserDefined );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Write linear units information.                                 */
+/* -------------------------------------------------------------------- */
+    if( !poSRS->IsGeographic() )
+    {
+        GTIFKeySet(psGTIF, ProjLinearUnitsGeoKey, TYPE_SHORT, 1, 
+                   nUOMLengthCode );
+        if( nUOMLengthCode == KvUserDefined )
+            GTIFKeySet( psGTIF, ProjLinearUnitSizeGeoKey, TYPE_DOUBLE, 1, 
+                        dfLinearUOM);
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Write angular units.  Always Degrees for now.                   */
+/* -------------------------------------------------------------------- */
+    GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1, 
+               Angular_Degree );
+
+/* -------------------------------------------------------------------- */
+/*      Try to write a citation from the main coordinate system         */
+/*      name.                                                           */
+/* -------------------------------------------------------------------- */
+    if( poSRS->GetRoot() != NULL
+        && poSRS->GetRoot()->GetChild(0) != NULL 
+        && poSRS->IsProjected() )
+    {
+        GTIFKeySet( psGTIF, GTCitationGeoKey, TYPE_ASCII, 0, 
+                    poSRS->GetRoot()->GetChild(0)->GetValue() );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try to write a GCS citation.                                    */
+/* -------------------------------------------------------------------- */
+    OGR_SRSNode *poGCS = poSRS->GetAttrNode( "GEOGCS" );
+
+    if( poGCS != NULL && poGCS->GetChild(0) != NULL )
+    {
+        GTIFKeySet( psGTIF, GeogCitationGeoKey, TYPE_ASCII, 0, 
+                    poGCS->GetChild(0)->GetValue() );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try to identify the GCS/datum, scanning the EPSG datum file for */
+/*      a match.                                                        */
+/* -------------------------------------------------------------------- */
+    if( nPCS == KvUserDefined )
+    {
+        if( nGCS == KvUserDefined )
+        {
+            if( nDatum == Datum_North_American_Datum_1927 )
+                nGCS = GCS_NAD27;
+            else if( nDatum == Datum_North_American_Datum_1983 )
+                nGCS = GCS_NAD83;
+            else if( nDatum == Datum_WGS84 || nDatum == DatumE_WGS84 )
+                nGCS = GCS_WGS_84;
+        }
+            
+        if( nGCS != KvUserDefined )
+        {
+            GTIFKeySet( psGTIF, GeographicTypeGeoKey, TYPE_SHORT,
+                        1, nGCS );
+        }
+        else if( nDatum != KvUserDefined )
+        {
+            GTIFKeySet( psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1, 
+                        KvUserDefined );
+            GTIFKeySet( psGTIF, GeogGeodeticDatumGeoKey, TYPE_SHORT,
+                        1, nDatum );
+        }
+        else if( nSpheroid != KvUserDefined )
+        {
+            GTIFKeySet( psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1, 
+                        KvUserDefined );
+            GTIFKeySet( psGTIF, GeogGeodeticDatumGeoKey, TYPE_SHORT,
+                        1, KvUserDefined );
+            GTIFKeySet( psGTIF, GeogEllipsoidGeoKey, TYPE_SHORT, 1, 
+                        nSpheroid );
+        }
+        else if( dfSemiMajor != 0.0 )
+        {
+            GTIFKeySet( psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1, 
+                        KvUserDefined );
+            GTIFKeySet( psGTIF, GeogGeodeticDatumGeoKey, TYPE_SHORT,
+                        1, KvUserDefined );
+            GTIFKeySet( psGTIF, GeogEllipsoidGeoKey, TYPE_SHORT, 1, 
+                        KvUserDefined );
+            GTIFKeySet( psGTIF, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1,
+                        dfSemiMajor );
+            if( dfInvFlattening == 0.0 )
+                GTIFKeySet( psGTIF, GeogSemiMinorAxisGeoKey, TYPE_DOUBLE, 1,
+                            dfSemiMajor );
+            else
+                GTIFKeySet( psGTIF, GeogInvFlatteningGeoKey, TYPE_DOUBLE, 1,
+                            dfInvFlattening );
+        }
+        else if( poSRS->GetAttrValue("DATUM") != NULL
+                 && strstr(poSRS->GetAttrValue("DATUM"),"unknown") == NULL
+                 && strstr(poSRS->GetAttrValue("DATUM"),"unnamed") == NULL )
+                 
+        {
+            CPLError( CE_Warning, CPLE_AppDefined,
+                      "Couldn't translate `%s' to a GeoTIFF datum.\n",
+                      poSRS->GetAttrValue("DATUM") );
+        }
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    delete poSRS;
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                         GTIFWktFromMemBuf()                          */
+/************************************************************************/
+
+CPLErr GTIFWktFromMemBuf( int nSize, unsigned char *pabyBuffer, 
+                          char **ppszWKT, double *padfGeoTransform,
+                          int *pnGCPCount, GDAL_GCP **ppasGCPList )
+
+{
+    TIFF        *hTIFF;
+    GTIF 	*hGTIF;
+    GTIFDefn	sGTIFDefn;
+    const static char *pszFilename = "/vsimem/wkt_from_mem_buf.tif";
+
+/* -------------------------------------------------------------------- */
+/*      Create a memory file from the buffer.                           */
+/* -------------------------------------------------------------------- */
+    FILE *fp = VSIFileFromMemBuffer( pszFilename, pabyBuffer, nSize, FALSE );
+    if( fp == NULL )
+        return CE_Failure;
+    VSIFCloseL( fp );
+
+/* -------------------------------------------------------------------- */
+/*      Initialize access to the memory geotiff structure.              */
+/* -------------------------------------------------------------------- */
+    hTIFF = VSI_TIFFOpen( pszFilename, "r" );
+
+    if( hTIFF == NULL )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "TIFF/GeoTIFF structure is corrupt." );
+        return CE_Failure;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Get the projection definition.                                  */
+/* -------------------------------------------------------------------- */
+    hGTIF = GTIFNew(hTIFF);
+
+    if( hGTIF != NULL && GTIFGetDefn( hGTIF, &sGTIFDefn ) )
+        *ppszWKT = GTIFGetOGISDefn( hGTIF, &sGTIFDefn );
+    else
+        *ppszWKT = NULL;
+    
+    if( hGTIF )
+        GTIFFree( hGTIF );
+
+/* -------------------------------------------------------------------- */
+/*      Get geotransform or tiepoints.                                  */
+/* -------------------------------------------------------------------- */
+    double	*padfTiePoints, *padfScale, *padfMatrix;
+    int16	nCount;
+
+    padfGeoTransform[0] = 0.0;
+    padfGeoTransform[1] = 1.0;
+    padfGeoTransform[2] = 0.0;
+    padfGeoTransform[3] = 0.0;
+    padfGeoTransform[4] = 0.0;
+    padfGeoTransform[5] = 1.0;
+
+    *pnGCPCount = 0;
+    *ppasGCPList = NULL;
+    
+    if( TIFFGetField(hTIFF,TIFFTAG_GEOPIXELSCALE,&nCount,&padfScale )
+        && nCount >= 2 )
+    {
+        padfGeoTransform[1] = padfScale[0];
+        padfGeoTransform[5] = - ABS(padfScale[1]);
+
+        if( TIFFGetField(hTIFF,TIFFTAG_GEOTIEPOINTS,&nCount,&padfTiePoints )
+            && nCount >= 6 )
+        {
+            padfGeoTransform[0] =
+                padfTiePoints[3] - padfTiePoints[0] * padfGeoTransform[1];
+            padfGeoTransform[3] =
+                padfTiePoints[4] - padfTiePoints[1] * padfGeoTransform[5];
+        }
+    }
+
+    else if( TIFFGetField(hTIFF,TIFFTAG_GEOTIEPOINTS,&nCount,&padfTiePoints )
+            && nCount >= 6 )
+    {
+        *pnGCPCount = nCount / 6;
+        *ppasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),*pnGCPCount);
+        
+        for( int iGCP = 0; iGCP < *pnGCPCount; iGCP++ )
+        {
+            char	szID[32];
+            GDAL_GCP	*psGCP = *ppasGCPList + iGCP;
+
+            sprintf( szID, "%d", iGCP+1 );
+            psGCP->pszId = CPLStrdup( szID );
+            psGCP->pszInfo = CPLStrdup("");
+            psGCP->dfGCPPixel = padfTiePoints[iGCP*6+0];
+            psGCP->dfGCPLine = padfTiePoints[iGCP*6+1];
+            psGCP->dfGCPX = padfTiePoints[iGCP*6+3];
+            psGCP->dfGCPY = padfTiePoints[iGCP*6+4];
+            psGCP->dfGCPZ = padfTiePoints[iGCP*6+5];
+        }
+    }
+
+    else if( TIFFGetField(hTIFF,TIFFTAG_GEOTRANSMATRIX,&nCount,&padfMatrix ) 
+             && nCount == 16 )
+    {
+        padfGeoTransform[0] = padfMatrix[3];
+        padfGeoTransform[1] = padfMatrix[0];
+        padfGeoTransform[2] = padfMatrix[1];
+        padfGeoTransform[3] = padfMatrix[7];
+        padfGeoTransform[4] = padfMatrix[4];
+        padfGeoTransform[5] = padfMatrix[5];
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup.                                                        */
+/* -------------------------------------------------------------------- */
+    XTIFFClose( hTIFF );
+
+    VSIUnlink( pszFilename );
+
+    if( *ppszWKT == NULL )
+        return CE_Failure;
+    else
+        return CE_None;
+}
+
+/************************************************************************/
+/*                         GTIFMemBufFromWkt()                          */
+/************************************************************************/
+
+CPLErr GTIFMemBufFromWkt( const char *pszWKT, const double *padfGeoTransform,
+                          int nGCPCount, const GDAL_GCP *pasGCPList,
+                          int *pnSize, unsigned char **ppabyBuffer )
+
+{
+    TIFF        *hTIFF;
+    GTIF 	*hGTIF;
+    const static char *pszFilename = "/vsimem/wkt_from_mem_buf.tif";
+
+/* -------------------------------------------------------------------- */
+/*      Initialize access to the memory geotiff structure.              */
+/* -------------------------------------------------------------------- */
+    hTIFF = VSI_TIFFOpen( pszFilename, "w" );
+
+    if( hTIFF == NULL )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "TIFF/GeoTIFF structure is corrupt." );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Write some minimal set of image parameters.                     */
+/* -------------------------------------------------------------------- */
+    TIFFSetField( hTIFF, TIFFTAG_IMAGEWIDTH, 1 );
+    TIFFSetField( hTIFF, TIFFTAG_IMAGELENGTH, 1 );
+    TIFFSetField( hTIFF, TIFFTAG_BITSPERSAMPLE, 8 );
+    TIFFSetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, 1 );
+    TIFFSetField( hTIFF, TIFFTAG_ROWSPERSTRIP, 1 );
+    TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG );
+    TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK );
+    
+/* -------------------------------------------------------------------- */
+/*      Get the projection definition.                                  */
+/* -------------------------------------------------------------------- */
+
+    if( pszWKT != NULL )
+    {
+        hGTIF = GTIFNew(hTIFF);
+        GTIFSetFromOGISDefn( hGTIF, pszWKT );
+        GTIFWriteKeys( hGTIF );
+        GTIFFree( hGTIF );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Set the geotransform, or GCPs.                                  */
+/* -------------------------------------------------------------------- */
+    if( padfGeoTransform[0] != 0.0 || padfGeoTransform[1] != 1.0
+        || padfGeoTransform[2] != 0.0 || padfGeoTransform[3] != 0.0
+        || padfGeoTransform[4] != 0.0 || ABS(padfGeoTransform[5]) != 1.0 )
+    {
+
+        if( padfGeoTransform[2] == 0.0 && padfGeoTransform[4] == 0.0 )
+        {
+            double	adfPixelScale[3], adfTiePoints[6];
+
+            adfPixelScale[0] = padfGeoTransform[1];
+            adfPixelScale[1] = fabs(padfGeoTransform[5]);
+            adfPixelScale[2] = 0.0;
+
+            TIFFSetField( hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale );
+            
+            adfTiePoints[0] = 0.0;
+            adfTiePoints[1] = 0.0;
+            adfTiePoints[2] = 0.0;
+            adfTiePoints[3] = padfGeoTransform[0];
+            adfTiePoints[4] = padfGeoTransform[3];
+            adfTiePoints[5] = 0.0;
+        
+            TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints );
+        }
+        else
+        {
+            double	adfMatrix[16];
+
+            memset(adfMatrix,0,sizeof(double) * 16);
+
+            adfMatrix[0] = padfGeoTransform[1];
+            adfMatrix[1] = padfGeoTransform[2];
+            adfMatrix[3] = padfGeoTransform[0];
+            adfMatrix[4] = padfGeoTransform[4];
+            adfMatrix[5] = padfGeoTransform[5];
+            adfMatrix[7] = padfGeoTransform[3];
+            adfMatrix[15] = 1.0;
+
+            TIFFSetField( hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Otherwise write tiepoints if they are available.                */
+/* -------------------------------------------------------------------- */
+    else if( nGCPCount > 0 )
+    {
+        double	*padfTiePoints;
+
+        padfTiePoints = (double *) CPLMalloc(6*sizeof(double)*nGCPCount);
+
+        for( int iGCP = 0; iGCP < nGCPCount; iGCP++ )
+        {
+
+            padfTiePoints[iGCP*6+0] = pasGCPList[iGCP].dfGCPPixel;
+            padfTiePoints[iGCP*6+1] = pasGCPList[iGCP].dfGCPLine;
+            padfTiePoints[iGCP*6+2] = 0;
+            padfTiePoints[iGCP*6+3] = pasGCPList[iGCP].dfGCPX;
+            padfTiePoints[iGCP*6+4] = pasGCPList[iGCP].dfGCPY;
+            padfTiePoints[iGCP*6+5] = pasGCPList[iGCP].dfGCPZ;
+        }
+
+        TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 6*nGCPCount, padfTiePoints);
+        CPLFree( padfTiePoints );
+    } 
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup and return the created memory buffer.                   */
+/* -------------------------------------------------------------------- */
+    GByte bySmallImage = 0;
+
+    TIFFWriteEncodedStrip( hTIFF, 0, (char *) &bySmallImage, 1 );
+    TIFFWriteCheck( hTIFF, TIFFIsTiled(hTIFF), "GTIFMemBufFromWkt");
+    TIFFWriteDirectory( hTIFF );
+
+    XTIFFClose( hTIFF );
+
+/* -------------------------------------------------------------------- */
+/*      Read back from the memory buffer.  It would be preferrable      */
+/*      to be able to "steal" the memory buffer, but there isn't        */
+/*      currently any support for this.                                 */
+/* -------------------------------------------------------------------- */
+    GUIntBig nBigLength;
+
+    *ppabyBuffer = VSIGetMemFileBuffer( pszFilename, &nBigLength, TRUE );
+    *pnSize = (int) nBigLength;
+
+    return CE_None;
+}
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/GNUmakefile b/Utilities/GDAL/frmts/gtiff/libgeotiff/GNUmakefile
new file mode 100644
index 0000000000..806441a371
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/GNUmakefile
@@ -0,0 +1,61 @@
+
+include ../../../GDALmake.opt
+
+OBJ	= \
+	xtiff.o \
+	geo_free.o \
+	geo_get.o \
+	geo_names.o \
+	geo_new.o \
+	geo_print.o \
+	geo_set.o \
+	geo_tiffp.o \
+	geo_write.o \
+	geo_normalize.o \
+	geotiff_proj4.o \
+	geo_extra.o \
+	geo_trans.o
+
+O_OBJ	=	$(foreach file,$(OBJ),../../o/$(file))
+
+ALL_C_FLAGS	=	$(CPPFLAGS) $(CFLAGS) 
+
+ifeq ($(TIFF_SETTING),internal)
+ALL_C_FLAGS	:=	-I../libtiff $(ALL_C_FLAGS)
+endif
+
+
+default:	$(OBJ)
+
+clean:
+	rm -f $(O_OBJ) *.o *.a
+
+install-obj:	$(O_OBJ)
+
+../../o/%.o:	%.c
+	$(CC) -c -I../../port $(ALL_C_FLAGS) $< -o $@
+
+#
+# Updating to the latest libgeotiff involves copying all matching source
+# except for a few files that hook to GDALs own CPL services.
+#
+import:
+	@if test ! -d ~/geotiff ; then \
+	  echo reimport requires libgeotiff checked out ~/geotiff ; \
+	  exit 1; \
+	fi
+
+	rm -rf safe
+	mkdir safe
+	mv cpl_serv.h geo_config.h safe
+
+	copymatch.sh ~/geotiff/libgeotiff *.cpp *.c *.h *.inc
+	copymatch.sh ~/geotiff/libgeotiff/libxtiff xtiff*.c xtiffio.h
+
+	mv safe/* .
+	rm -rf safe
+
+	@echo
+	@echo 'Now do something like:'
+	@echo '% cvs commit -m "updated to libgeotiff 1.1.x"'
+	@echo
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/cpl_serv.h b/Utilities/GDAL/frmts/gtiff/libgeotiff/cpl_serv.h
new file mode 100644
index 0000000000..0cc010d728
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/cpl_serv.h
@@ -0,0 +1,41 @@
+/******************************************************************************
+ * Copyright (c) 1998, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * cpl_serv.h
+ *
+ * This include file derived and simplified from the GDAL Common Portability
+ * Library.
+ */
+
+#ifndef CPL_SERV_H_INCLUDED
+#define CPL_SERV_H_INCLUDED
+
+/* ==================================================================== */
+/*	Standard include files.						*/
+/* ==================================================================== */
+
+#include "geo_config.h"
+
+#include "cpl_string.h"
+#include "cpl_csv.h"
+
+#endif /* ndef CPL_SERV_H_INCLUDED */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_datum.inc b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_datum.inc
new file mode 100644
index 0000000000..198e78ff2a
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_datum.inc
@@ -0,0 +1,174 @@
+/*
+ *  EPSG/POSC Datum database -- GeoTIFF Rev. 0.2
+ */
+ 
+/* C database for Geotiff include files.   */
+/* the macro ValuePair() must be defined   */
+/* by the enclosing include file           */
+
+#ifdef INCLUDE_OLD_CODES
+#include old_datum.inc
+#endif /* OLD Codes */
+
+/* New datums */
+ValuePair(Datum_Dealul_Piscului_1970,6317)
+
+/* Datums for which only the ellipsoid is known */
+ValuePair(DatumE_Airy1830,	6001)
+ValuePair(DatumE_AiryModified1849,	6002)
+ValuePair(DatumE_AustralianNationalSpheroid,	6003)
+ValuePair(DatumE_Bessel1841,	6004)
+ValuePair(DatumE_BesselModified,	6005)
+ValuePair(DatumE_BesselNamibia,	6006)
+ValuePair(DatumE_Clarke1858,	6007)
+ValuePair(DatumE_Clarke1866,	6008)
+ValuePair(DatumE_Clarke1866Michigan,	6009)
+ValuePair(DatumE_Clarke1880_Benoit,	6010)
+ValuePair(DatumE_Clarke1880_IGN,	6011)
+ValuePair(DatumE_Clarke1880_RGS,	6012)
+ValuePair(DatumE_Clarke1880_Arc,	6013)
+ValuePair(DatumE_Clarke1880_SGA1922,	6014)
+ValuePair(DatumE_Everest1830_1937Adjustment,	6015)
+ValuePair(DatumE_Everest1830_1967Definition,	6016)
+ValuePair(DatumE_Everest1830_1975Definition,	6017)
+ValuePair(DatumE_Everest1830Modified,	6018)
+ValuePair(DatumE_GRS1980,	6019)
+ValuePair(DatumE_Helmert1906,	6020)
+ValuePair(DatumE_IndonesianNationalSpheroid,	6021)
+ValuePair(DatumE_International1924,	6022)
+ValuePair(DatumE_International1967,	6023)
+ValuePair(DatumE_Krassowsky1960,	6024)
+ValuePair(DatumE_NWL9D,	6025)
+ValuePair(DatumE_NWL10D,	6026)
+ValuePair(DatumE_Plessis1817,	6027)
+ValuePair(DatumE_Struve1860,	6028)
+ValuePair(DatumE_WarOffice,	6029)
+ValuePair(DatumE_WGS84,	6030)
+ValuePair(DatumE_GEM10C,	6031)
+ValuePair(DatumE_OSU86F,	6032)
+ValuePair(DatumE_OSU91A,	6033)
+ValuePair(DatumE_Clarke1880,	6034)
+ValuePair(DatumE_Sphere,	6035)
+
+/* standard datums */
+ValuePair(Datum_Adindan,	6201)
+ValuePair(Datum_Australian_Geodetic_Datum_1966,	6202)
+ValuePair(Datum_Australian_Geodetic_Datum_1984,	6203)
+ValuePair(Datum_Ain_el_Abd_1970,	6204)
+ValuePair(Datum_Afgooye,	6205)
+ValuePair(Datum_Agadez,	6206)
+ValuePair(Datum_Lisbon,	6207)
+ValuePair(Datum_Aratu,	6208)
+ValuePair(Datum_Arc_1950,	6209)
+ValuePair(Datum_Arc_1960,	6210)
+ValuePair(Datum_Batavia,	6211)
+ValuePair(Datum_Barbados,	6212)
+ValuePair(Datum_Beduaram,	6213)
+ValuePair(Datum_Beijing_1954,	6214)
+ValuePair(Datum_Reseau_National_Belge_1950,	6215)
+ValuePair(Datum_Bermuda_1957,	6216)
+ValuePair(Datum_Bern_1898,	6217)
+ValuePair(Datum_Bogota,	6218)
+ValuePair(Datum_Bukit_Rimpah,	6219)
+ValuePair(Datum_Camacupa,	6220)
+ValuePair(Datum_Campo_Inchauspe,	6221)
+ValuePair(Datum_Cape,	6222)
+ValuePair(Datum_Carthage,	6223)
+ValuePair(Datum_Chua,	6224)
+ValuePair(Datum_Corrego_Alegre,	6225)
+ValuePair(Datum_Cote_d_Ivoire,	6226)
+ValuePair(Datum_Deir_ez_Zor,	6227)
+ValuePair(Datum_Douala,	6228)
+ValuePair(Datum_Egypt_1907,	6229)
+ValuePair(Datum_European_Datum_1950,	6230)
+ValuePair(Datum_European_Datum_1987,	6231)
+ValuePair(Datum_Fahud,	6232)
+ValuePair(Datum_Gandajika_1970,	6233)
+ValuePair(Datum_Garoua,	6234)
+ValuePair(Datum_Guyane_Francaise,	6235)
+ValuePair(Datum_Hu_Tzu_Shan,	6236)
+ValuePair(Datum_Hungarian_Datum_1972,	6237)
+ValuePair(Datum_Indonesian_Datum_1974,	6238)
+ValuePair(Datum_Indian_1954,	6239)
+ValuePair(Datum_Indian_1975,	6240)
+ValuePair(Datum_Jamaica_1875,	6241)
+ValuePair(Datum_Jamaica_1969,	6242)
+ValuePair(Datum_Kalianpur,	6243)
+ValuePair(Datum_Kandawala,	6244)
+ValuePair(Datum_Kertau,	6245)
+ValuePair(Datum_Kuwait_Oil_Company,	6246)
+ValuePair(Datum_La_Canoa,	6247)
+ValuePair(Datum_Provisional_S_American_Datum_1956,	6248)
+ValuePair(Datum_Lake,	6249)
+ValuePair(Datum_Leigon,	6250)
+ValuePair(Datum_Liberia_1964,	6251)
+ValuePair(Datum_Lome,	6252)
+ValuePair(Datum_Luzon_1911,	6253)
+ValuePair(Datum_Hito_XVIII_1963,	6254)
+ValuePair(Datum_Herat_North,	6255)
+ValuePair(Datum_Mahe_1971,	6256)
+ValuePair(Datum_Makassar,	6257)
+ValuePair(Datum_European_Reference_System_1989,	6258)
+ValuePair(Datum_Malongo_1987,	6259)
+ValuePair(Datum_Manoca,	6260)
+ValuePair(Datum_Merchich,	6261)
+ValuePair(Datum_Massawa,	6262)
+ValuePair(Datum_Minna,	6263)
+ValuePair(Datum_Mhast,	6264)
+ValuePair(Datum_Monte_Mario,	6265)
+ValuePair(Datum_M_poraloko,	6266)
+ValuePair(Datum_North_American_Datum_1927,	6267)
+ValuePair(Datum_NAD_Michigan,	6268)
+ValuePair(Datum_North_American_Datum_1983,	6269)
+ValuePair(Datum_Nahrwan_1967,	6270)
+ValuePair(Datum_Naparima_1972,	6271)
+ValuePair(Datum_New_Zealand_Geodetic_Datum_1949,	6272)
+ValuePair(Datum_NGO_1948,	6273)
+ValuePair(Datum_Datum_73,	6274)
+ValuePair(Datum_Nouvelle_Triangulation_Francaise,	6275)
+ValuePair(Datum_NSWC_9Z_2,	6276)
+ValuePair(Datum_OSGB_1936,	6277)
+ValuePair(Datum_OSGB_1970_SN,	6278)
+ValuePair(Datum_OS_SN_1980,	6279)
+ValuePair(Datum_Padang_1884,	6280)
+ValuePair(Datum_Palestine_1923,	6281)
+ValuePair(Datum_Pointe_Noire,	6282)
+ValuePair(Datum_Geocentric_Datum_of_Australia_1994,	6283)
+ValuePair(Datum_Pulkovo_1942,	6284)
+ValuePair(Datum_Qatar,	6285)
+ValuePair(Datum_Qatar_1948,	6286)
+ValuePair(Datum_Qornoq,	6287)
+ValuePair(Datum_Loma_Quintana,	6288)
+ValuePair(Datum_Amersfoort,	6289)
+ValuePair(Datum_RT38,	6290)
+ValuePair(Datum_South_American_Datum_1969,	6291)
+ValuePair(Datum_Sapper_Hill_1943,	6292)
+ValuePair(Datum_Schwarzeck,	6293)
+ValuePair(Datum_Segora,	6294)
+ValuePair(Datum_Serindung,	6295)
+ValuePair(Datum_Sudan,	6296)
+ValuePair(Datum_Tananarive_1925,	6297)
+ValuePair(Datum_Timbalai_1948,	6298)
+ValuePair(Datum_TM65,	6299)
+ValuePair(Datum_TM75,	6300)
+ValuePair(Datum_Tokyo,	6301)
+ValuePair(Datum_Trinidad_1903,	6302)
+ValuePair(Datum_Trucial_Coast_1948,	6303)
+ValuePair(Datum_Voirol_1875,	6304)
+ValuePair(Datum_Voirol_Unifie_1960,	6305)
+ValuePair(Datum_Bern_1938,	6306)
+ValuePair(Datum_Nord_Sahara_1959,	6307)
+ValuePair(Datum_Stockholm_1938,	6308)
+ValuePair(Datum_Yacare,	6309)
+ValuePair(Datum_Yoff,	6310)
+ValuePair(Datum_Zanderij,	6311)
+ValuePair(Datum_Militar_Geographische_Institut,	6312)
+ValuePair(Datum_Reseau_National_Belge_1972,	6313)
+ValuePair(Datum_Deutsche_Hauptdreiecksnetz,	6314)
+ValuePair(Datum_Conakry_1905,	6315)
+ValuePair(Datum_WGS72,	6322)
+ValuePair(Datum_WGS72_Transit_Broadcast_Ephemeris,	6324)
+ValuePair(Datum_WGS84,	6326)
+ValuePair(Datum_Ancienne_Triangulation_Francaise,	6901)
+ValuePair(Datum_Nord_de_Guerre,	6902)
+/* end of list */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_ellipse.inc b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_ellipse.inc
new file mode 100644
index 0000000000..95c14321f7
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_ellipse.inc
@@ -0,0 +1,48 @@
+/*
+ *  GeoTIFF Rev. 0.2  Ellipsoids
+ */
+
+/* C database for Geotiff include files.   */
+/* the macro ValuePair() must be defined   */
+/* by the enclosing include file           */
+
+#ifdef INCLUDE_OLD_CODES
+#include old_ellipse.inc
+#endif /* OLD Codes */
+
+ValuePair(Ellipse_Airy_1830,	7001)
+ValuePair(Ellipse_Airy_Modified_1849,	7002)
+ValuePair(Ellipse_Australian_National_Spheroid,	7003)
+ValuePair(Ellipse_Bessel_1841,	7004)
+ValuePair(Ellipse_Bessel_Modified,	7005)
+ValuePair(Ellipse_Bessel_Namibia,	7006)
+ValuePair(Ellipse_Clarke_1858,	7007)
+ValuePair(Ellipse_Clarke_1866,	7008)
+ValuePair(Ellipse_Clarke_1866_Michigan,	7009)
+ValuePair(Ellipse_Clarke_1880_Benoit,	7010)
+ValuePair(Ellipse_Clarke_1880_IGN,	7011)
+ValuePair(Ellipse_Clarke_1880_RGS,	7012)
+ValuePair(Ellipse_Clarke_1880_Arc,	7013)
+ValuePair(Ellipse_Clarke_1880_SGA_1922,	7014)
+ValuePair(Ellipse_Everest_1830_1937_Adjustment,	7015)
+ValuePair(Ellipse_Everest_1830_1967_Definition,	7016)
+ValuePair(Ellipse_Everest_1830_1975_Definition,	7017)
+ValuePair(Ellipse_Everest_1830_Modified,	7018)
+ValuePair(Ellipse_GRS_1980,	7019)
+ValuePair(Ellipse_Helmert_1906,	7020)
+ValuePair(Ellipse_Indonesian_National_Spheroid,	7021)
+ValuePair(Ellipse_International_1924,	7022)
+ValuePair(Ellipse_International_1967,	7023)
+ValuePair(Ellipse_Krassowsky_1940,	7024)
+ValuePair(Ellipse_NWL_9D,	7025)
+ValuePair(Ellipse_NWL_10D,	7026)
+ValuePair(Ellipse_Plessis_1817,	7027)
+ValuePair(Ellipse_Struve_1860,	7028)
+ValuePair(Ellipse_War_Office,	7029)
+ValuePair(Ellipse_WGS_84,	7030)
+ValuePair(Ellipse_GEM_10C,	7031)
+ValuePair(Ellipse_OSU86F,	7032)
+ValuePair(Ellipse_OSU91A,	7033)
+ValuePair(Ellipse_Clarke_1880,	7034)
+ValuePair(Ellipse_Sphere,	7035)
+/* end of list */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_gcs.inc b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_gcs.inc
new file mode 100644
index 0000000000..4917964d82
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_gcs.inc
@@ -0,0 +1,193 @@
+/*
+ *  EPSG/POSC GCS Codes -- GeoTIFF Rev. 0.2
+ */
+
+/* C database for Geotiff include files.   */
+/* the macro ValuePair() must be defined   */
+/* by the enclosing include file           */
+
+#ifdef INCLUDE_OLD_CODES
+#include old_gcs.inc
+#endif /* OLD Codes */
+
+/* Unspecified GCS based on ellipsoid */
+ValuePair(GCSE_Airy1830,	4001)
+ValuePair(GCSE_AiryModified1849,	4002)
+ValuePair(GCSE_AustralianNationalSpheroid,	4003)
+ValuePair(GCSE_Bessel1841,	4004)
+ValuePair(GCSE_BesselModified,	4005)
+ValuePair(GCSE_BesselNamibia,	4006)
+ValuePair(GCSE_Clarke1858,	4007)
+ValuePair(GCSE_Clarke1866,	4008)
+ValuePair(GCSE_Clarke1866Michigan,	4009)
+ValuePair(GCSE_Clarke1880_Benoit,	4010)
+ValuePair(GCSE_Clarke1880_IGN,	4011)
+ValuePair(GCSE_Clarke1880_RGS,	4012)
+ValuePair(GCSE_Clarke1880_Arc,	4013)
+ValuePair(GCSE_Clarke1880_SGA1922,	4014)
+ValuePair(GCSE_Everest1830_1937Adjustment,	4015)
+ValuePair(GCSE_Everest1830_1967Definition,	4016)
+ValuePair(GCSE_Everest1830_1975Definition,	4017)
+ValuePair(GCSE_Everest1830Modified,	4018)
+ValuePair(GCSE_GRS1980,	4019)
+ValuePair(GCSE_Helmert1906,	4020)
+ValuePair(GCSE_IndonesianNationalSpheroid,	4021)
+ValuePair(GCSE_International1924,	4022)
+ValuePair(GCSE_International1967,	4023)
+ValuePair(GCSE_Krassowsky1940,	4024)
+ValuePair(GCSE_NWL9D,	4025)
+ValuePair(GCSE_NWL10D,	4026)
+ValuePair(GCSE_Plessis1817,	4027)
+ValuePair(GCSE_Struve1860,	4028)
+ValuePair(GCSE_WarOffice,	4029)
+ValuePair(GCSE_WGS84,	4030)
+ValuePair(GCSE_GEM10C,	4031)
+ValuePair(GCSE_OSU86F,	4032)
+ValuePair(GCSE_OSU91A,	4033)
+ValuePair(GCSE_Clarke1880,	4034)
+ValuePair(GCSE_Sphere,	4035)
+
+/* New GCS */
+ValuePair(GCS_Greek,4120)
+ValuePair(GCS_GGRS87,4121)
+ValuePair(GCS_KKJ,4123)
+ValuePair(GCS_RT90,4124)
+ValuePair(GCS_EST92,4133)
+ValuePair(GCS_Dealul_Piscului_1970,4317)
+ValuePair(GCS_Greek_Athens,4815)
+
+/* Standard GCS */
+ValuePair(GCS_Adindan,	4201)
+ValuePair(GCS_AGD66,	4202)
+ValuePair(GCS_AGD84,	4203)
+ValuePair(GCS_Ain_el_Abd,	4204)
+ValuePair(GCS_Afgooye,	4205)
+ValuePair(GCS_Agadez,	4206)
+ValuePair(GCS_Lisbon,	4207)
+ValuePair(GCS_Aratu,	4208)
+ValuePair(GCS_Arc_1950,	4209)
+ValuePair(GCS_Arc_1960,	4210)
+ValuePair(GCS_Batavia,	4211)
+ValuePair(GCS_Barbados,	4212)
+ValuePair(GCS_Beduaram,	4213)
+ValuePair(GCS_Beijing_1954,	4214)
+ValuePair(GCS_Belge_1950,	4215)
+ValuePair(GCS_Bermuda_1957,	4216)
+ValuePair(GCS_Bern_1898,	4217)
+ValuePair(GCS_Bogota,	4218)
+ValuePair(GCS_Bukit_Rimpah,	4219)
+ValuePair(GCS_Camacupa,	4220)
+ValuePair(GCS_Campo_Inchauspe,	4221)
+ValuePair(GCS_Cape,	4222)
+ValuePair(GCS_Carthage,	4223)
+ValuePair(GCS_Chua,	4224)
+ValuePair(GCS_Corrego_Alegre,	4225)
+ValuePair(GCS_Cote_d_Ivoire,	4226)
+ValuePair(GCS_Deir_ez_Zor,	4227)
+ValuePair(GCS_Douala,	4228)
+ValuePair(GCS_Egypt_1907,	4229)
+ValuePair(GCS_ED50,	4230)
+ValuePair(GCS_ED87,	4231)
+ValuePair(GCS_Fahud,	4232)
+ValuePair(GCS_Gandajika_1970,	4233)
+ValuePair(GCS_Garoua,	4234)
+ValuePair(GCS_Guyane_Francaise,	4235)
+ValuePair(GCS_Hu_Tzu_Shan,	4236)
+ValuePair(GCS_HD72,	4237)
+ValuePair(GCS_ID74,	4238)
+ValuePair(GCS_Indian_1954,	4239)
+ValuePair(GCS_Indian_1975,	4240)
+ValuePair(GCS_Jamaica_1875,	4241)
+ValuePair(GCS_JAD69,	4242)
+ValuePair(GCS_Kalianpur,	4243)
+ValuePair(GCS_Kandawala,	4244)
+ValuePair(GCS_Kertau,	4245)
+ValuePair(GCS_KOC,	4246)
+ValuePair(GCS_La_Canoa,	4247)
+ValuePair(GCS_PSAD56,	4248)
+ValuePair(GCS_Lake,	4249)
+ValuePair(GCS_Leigon,	4250)
+ValuePair(GCS_Liberia_1964,	4251)
+ValuePair(GCS_Lome,	4252)
+ValuePair(GCS_Luzon_1911,	4253)
+ValuePair(GCS_Hito_XVIII_1963,	4254)
+ValuePair(GCS_Herat_North,	4255)
+ValuePair(GCS_Mahe_1971,	4256)
+ValuePair(GCS_Makassar,	4257)
+ValuePair(GCS_EUREF89,	4258)
+ValuePair(GCS_Malongo_1987,	4259)
+ValuePair(GCS_Manoca,	4260)
+ValuePair(GCS_Merchich,	4261)
+ValuePair(GCS_Massawa,	4262)
+ValuePair(GCS_Minna,	4263)
+ValuePair(GCS_Mhast,	4264)
+ValuePair(GCS_Monte_Mario,	4265)
+ValuePair(GCS_M_poraloko,	4266)
+ValuePair(GCS_NAD27,	4267)
+ValuePair(GCS_NAD_Michigan,	4268)
+ValuePair(GCS_NAD83,	4269)
+ValuePair(GCS_Nahrwan_1967,	4270)
+ValuePair(GCS_Naparima_1972,	4271)
+ValuePair(GCS_GD49,	4272)
+ValuePair(GCS_NGO_1948,	4273)
+ValuePair(GCS_Datum_73,	4274)
+ValuePair(GCS_NTF,	4275)
+ValuePair(GCS_NSWC_9Z_2,	4276)
+ValuePair(GCS_OSGB_1936,	4277)
+ValuePair(GCS_OSGB70,	4278)
+ValuePair(GCS_OS_SN80,	4279)
+ValuePair(GCS_Padang,	4280)
+ValuePair(GCS_Palestine_1923,	4281)
+ValuePair(GCS_Pointe_Noire,	4282)
+ValuePair(GCS_GDA94,	4283)
+ValuePair(GCS_Pulkovo_1942,	4284)
+ValuePair(GCS_Qatar,	4285)
+ValuePair(GCS_Qatar_1948,	4286)
+ValuePair(GCS_Qornoq,	4287)
+ValuePair(GCS_Loma_Quintana,	4288)
+ValuePair(GCS_Amersfoort,	4289)
+ValuePair(GCS_RT38,	4290)
+ValuePair(GCS_SAD69,	4291)
+ValuePair(GCS_Sapper_Hill_1943,	4292)
+ValuePair(GCS_Schwarzeck,	4293)
+ValuePair(GCS_Segora,	4294)
+ValuePair(GCS_Serindung,	4295)
+ValuePair(GCS_Sudan,	4296)
+ValuePair(GCS_Tananarive,	4297)
+ValuePair(GCS_Timbalai_1948,	4298)
+ValuePair(GCS_TM65,	4299)
+ValuePair(GCS_TM75,	4300)
+ValuePair(GCS_Tokyo,	4301)
+ValuePair(GCS_Trinidad_1903,	4302)
+ValuePair(GCS_TC_1948,	4303)
+ValuePair(GCS_Voirol_1875,	4304)
+ValuePair(GCS_Voirol_Unifie,	4305)
+ValuePair(GCS_Bern_1938,	4306)
+ValuePair(GCS_Nord_Sahara_1959,	4307)
+ValuePair(GCS_Stockholm_1938,	4308)
+ValuePair(GCS_Yacare,	4309)
+ValuePair(GCS_Yoff,	4310)
+ValuePair(GCS_Zanderij,	4311)
+ValuePair(GCS_MGI,	4312)
+ValuePair(GCS_Belge_1972,	4313)
+ValuePair(GCS_DHDN,	4314)
+ValuePair(GCS_Conakry_1905,	4315)
+ValuePair(GCS_WGS_72,	4322)
+ValuePair(GCS_WGS_72BE,	4324)
+ValuePair(GCS_WGS_84,	4326)
+ValuePair(GCS_Bern_1898_Bern,	4801)
+ValuePair(GCS_Bogota_Bogota,	4802)
+ValuePair(GCS_Lisbon_Lisbon,	4803)
+ValuePair(GCS_Makassar_Jakarta,	4804)
+ValuePair(GCS_MGI_Ferro,	4805)
+ValuePair(GCS_Monte_Mario_Rome,	4806)
+ValuePair(GCS_NTF_Paris,	4807)
+ValuePair(GCS_Padang_Jakarta,	4808)
+ValuePair(GCS_Belge_1950_Brussels,	4809)
+ValuePair(GCS_Tananarive_Paris,	4810)
+ValuePair(GCS_Voirol_1875_Paris,	4811)
+ValuePair(GCS_Voirol_Unifie_Paris,	4812)
+ValuePair(GCS_Batavia_Jakarta,	4813)
+ValuePair(GCS_ATF_Paris,	4901)
+ValuePair(GCS_NDG_Paris,	4902)
+/* End of list */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_pcs.inc b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_pcs.inc
new file mode 100644
index 0000000000..e8a5ef50a4
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_pcs.inc
@@ -0,0 +1,1012 @@
+/*
+ *  EPSG PCS Codes - GeoTIFF Rev 0.2
+ */
+
+/* C database for Geotiff include files.   */
+/* the macro ValuePair() must be defined   */
+/* by the enclosing include file           */
+
+#ifdef INCLUDE_OLD_CODES
+#include old_pcs.inc
+#endif /* OLD Codes */
+
+/* Newer PCS */
+ValuePair(PCS_Hjorsey_1955_Lambert, 3053)
+ValuePair(PCS_ISN93_Lambert_1993, 3057)
+ValuePair(PCS_ETRS89_Poland_CS2000_zone_5,2176)
+ValuePair(PCS_ETRS89_Poland_CS2000_zone_6,2177)
+ValuePair(PCS_ETRS89_Poland_CS2000_zone_7,2177)
+ValuePair(PCS_ETRS89_Poland_CS2000_zone_8,2178)
+ValuePair(PCS_ETRS89_Poland_CS92,2180)
+
+/* New PCS */
+ValuePair(PCS_GGRS87_Greek_Grid,2100)
+ValuePair(PCS_KKJ_Finland_zone_1,2391)
+ValuePair(PCS_KKJ_Finland_zone_2,2392)
+ValuePair(PCS_KKJ_Finland_zone_3,2393)
+ValuePair(PCS_KKJ_Finland_zone_4,2394)
+ValuePair(PCS_RT90_2_5_gon_W,2400)
+ValuePair(PCS_Lietuvos_Koordinoei_Sistema_1994,2600)
+ValuePair(PCS_Estonian_Coordinate_System_of_1992,3300)
+ValuePair(PCS_HD72_EOV,23700)
+ValuePair(PCS_Dealul_Piscului_1970_Stereo_70,31700)
+
+ValuePair(PCS_Adindan_UTM_zone_37N,	20137)
+ValuePair(PCS_Adindan_UTM_zone_38N,	20138)
+ValuePair(PCS_AGD66_AMG_zone_48,	20248)
+ValuePair(PCS_AGD66_AMG_zone_49,	20249)
+ValuePair(PCS_AGD66_AMG_zone_50,	20250)
+ValuePair(PCS_AGD66_AMG_zone_51,	20251)
+ValuePair(PCS_AGD66_AMG_zone_52,	20252)
+ValuePair(PCS_AGD66_AMG_zone_53,	20253)
+ValuePair(PCS_AGD66_AMG_zone_54,	20254)
+ValuePair(PCS_AGD66_AMG_zone_55,	20255)
+ValuePair(PCS_AGD66_AMG_zone_56,	20256)
+ValuePair(PCS_AGD66_AMG_zone_57,	20257)
+ValuePair(PCS_AGD66_AMG_zone_58,	20258)
+ValuePair(PCS_AGD84_AMG_zone_48,	20348)
+ValuePair(PCS_AGD84_AMG_zone_49,	20349)
+ValuePair(PCS_AGD84_AMG_zone_50,	20350)
+ValuePair(PCS_AGD84_AMG_zone_51,	20351)
+ValuePair(PCS_AGD84_AMG_zone_52,	20352)
+ValuePair(PCS_AGD84_AMG_zone_53,	20353)
+ValuePair(PCS_AGD84_AMG_zone_54,	20354)
+ValuePair(PCS_AGD84_AMG_zone_55,	20355)
+ValuePair(PCS_AGD84_AMG_zone_56,	20356)
+ValuePair(PCS_AGD84_AMG_zone_57,	20357)
+ValuePair(PCS_AGD84_AMG_zone_58,	20358)
+ValuePair(PCS_Ain_el_Abd_UTM_zone_37N,	20437)
+ValuePair(PCS_Ain_el_Abd_UTM_zone_38N,	20438)
+ValuePair(PCS_Ain_el_Abd_UTM_zone_39N,	20439)
+ValuePair(PCS_Ain_el_Abd_Bahrain_Grid,	20499)
+ValuePair(PCS_Afgooye_UTM_zone_38N,	20538)
+ValuePair(PCS_Afgooye_UTM_zone_39N,	20539)
+ValuePair(PCS_Lisbon_Portugese_Grid,	20700)
+ValuePair(PCS_Aratu_UTM_zone_22S,	20822)
+ValuePair(PCS_Aratu_UTM_zone_23S,	20823)
+ValuePair(PCS_Aratu_UTM_zone_24S,	20824)
+ValuePair(PCS_Arc_1950_Lo13,	20973)
+ValuePair(PCS_Arc_1950_Lo15,	20975)
+ValuePair(PCS_Arc_1950_Lo17,	20977)
+ValuePair(PCS_Arc_1950_Lo19,	20979)
+ValuePair(PCS_Arc_1950_Lo21,	20981)
+ValuePair(PCS_Arc_1950_Lo23,	20983)
+ValuePair(PCS_Arc_1950_Lo25,	20985)
+ValuePair(PCS_Arc_1950_Lo27,	20987)
+ValuePair(PCS_Arc_1950_Lo29,	20989)
+ValuePair(PCS_Arc_1950_Lo31,	20991)
+ValuePair(PCS_Arc_1950_Lo33,	20993)
+ValuePair(PCS_Arc_1950_Lo35,	20995)
+ValuePair(PCS_Batavia_NEIEZ,	21100)
+ValuePair(PCS_Batavia_UTM_zone_48S,	21148)
+ValuePair(PCS_Batavia_UTM_zone_49S,	21149)
+ValuePair(PCS_Batavia_UTM_zone_50S,	21150)
+ValuePair(PCS_Beijing_Gauss_zone_13,	21413)
+ValuePair(PCS_Beijing_Gauss_zone_14,	21414)
+ValuePair(PCS_Beijing_Gauss_zone_15,	21415)
+ValuePair(PCS_Beijing_Gauss_zone_16,	21416)
+ValuePair(PCS_Beijing_Gauss_zone_17,	21417)
+ValuePair(PCS_Beijing_Gauss_zone_18,	21418)
+ValuePair(PCS_Beijing_Gauss_zone_19,	21419)
+ValuePair(PCS_Beijing_Gauss_zone_20,	21420)
+ValuePair(PCS_Beijing_Gauss_zone_21,	21421)
+ValuePair(PCS_Beijing_Gauss_zone_22,	21422)
+ValuePair(PCS_Beijing_Gauss_zone_23,	21423)
+ValuePair(PCS_Beijing_Gauss_13N,	21473)
+ValuePair(PCS_Beijing_Gauss_14N,	21474)
+ValuePair(PCS_Beijing_Gauss_15N,	21475)
+ValuePair(PCS_Beijing_Gauss_16N,	21476)
+ValuePair(PCS_Beijing_Gauss_17N,	21477)
+ValuePair(PCS_Beijing_Gauss_18N,	21478)
+ValuePair(PCS_Beijing_Gauss_19N,	21479)
+ValuePair(PCS_Beijing_Gauss_20N,	21480)
+ValuePair(PCS_Beijing_Gauss_21N,	21481)
+ValuePair(PCS_Beijing_Gauss_22N,	21482)
+ValuePair(PCS_Beijing_Gauss_23N,	21483)
+ValuePair(PCS_Belge_Lambert_50,	21500)
+ValuePair(PCS_Bern_1898_Swiss_Old,	21790)
+ValuePair(PCS_Bogota_UTM_zone_17N,	21817)
+ValuePair(PCS_Bogota_UTM_zone_18N,	21818)
+ValuePair(PCS_Bogota_Colombia_3W,	21891)
+ValuePair(PCS_Bogota_Colombia_Bogota,	21892)
+ValuePair(PCS_Bogota_Colombia_3E,	21893)
+ValuePair(PCS_Bogota_Colombia_6E,	21894)
+ValuePair(PCS_Camacupa_UTM_32S,	22032)
+ValuePair(PCS_Camacupa_UTM_33S,	22033)
+ValuePair(PCS_C_Inchauspe_Argentina_1,	22191)
+ValuePair(PCS_C_Inchauspe_Argentina_2,	22192)
+ValuePair(PCS_C_Inchauspe_Argentina_3,	22193)
+ValuePair(PCS_C_Inchauspe_Argentina_4,	22194)
+ValuePair(PCS_C_Inchauspe_Argentina_5,	22195)
+ValuePair(PCS_C_Inchauspe_Argentina_6,	22196)
+ValuePair(PCS_C_Inchauspe_Argentina_7,	22197)
+ValuePair(PCS_Carthage_UTM_zone_32N,	22332)
+ValuePair(PCS_Carthage_Nord_Tunisie,	22391)
+ValuePair(PCS_Carthage_Sud_Tunisie,	22392)
+ValuePair(PCS_Corrego_Alegre_UTM_23S,	22523)
+ValuePair(PCS_Corrego_Alegre_UTM_24S,	22524)
+ValuePair(PCS_Douala_UTM_zone_32N,	22832)
+ValuePair(PCS_Egypt_1907_Red_Belt,	22992)
+ValuePair(PCS_Egypt_1907_Purple_Belt,	22993)
+ValuePair(PCS_Egypt_1907_Ext_Purple,	22994)
+ValuePair(PCS_ED50_UTM_zone_28N,	23028)
+ValuePair(PCS_ED50_UTM_zone_29N,	23029)
+ValuePair(PCS_ED50_UTM_zone_30N,	23030)
+ValuePair(PCS_ED50_UTM_zone_31N,	23031)
+ValuePair(PCS_ED50_UTM_zone_32N,	23032)
+ValuePair(PCS_ED50_UTM_zone_33N,	23033)
+ValuePair(PCS_ED50_UTM_zone_34N,	23034)
+ValuePair(PCS_ED50_UTM_zone_35N,	23035)
+ValuePair(PCS_ED50_UTM_zone_36N,	23036)
+ValuePair(PCS_ED50_UTM_zone_37N,	23037)
+ValuePair(PCS_ED50_UTM_zone_38N,	23038)
+ValuePair(PCS_Fahud_UTM_zone_39N,	23239)
+ValuePair(PCS_Fahud_UTM_zone_40N,	23240)
+ValuePair(PCS_Garoua_UTM_zone_33N,	23433)
+ValuePair(PCS_ID74_UTM_zone_46N,	23846)
+ValuePair(PCS_ID74_UTM_zone_47N,	23847)
+ValuePair(PCS_ID74_UTM_zone_48N,	23848)
+ValuePair(PCS_ID74_UTM_zone_49N,	23849)
+ValuePair(PCS_ID74_UTM_zone_50N,	23850)
+ValuePair(PCS_ID74_UTM_zone_51N,	23851)
+ValuePair(PCS_ID74_UTM_zone_52N,	23852)
+ValuePair(PCS_ID74_UTM_zone_53N,	23853)
+ValuePair(PCS_ID74_UTM_zone_46S,	23886)
+ValuePair(PCS_ID74_UTM_zone_47S,	23887)
+ValuePair(PCS_ID74_UTM_zone_48S,	23888)
+ValuePair(PCS_ID74_UTM_zone_49S,	23889)
+ValuePair(PCS_ID74_UTM_zone_50S,	23890)
+ValuePair(PCS_ID74_UTM_zone_51S,	23891)
+ValuePair(PCS_ID74_UTM_zone_52S,	23892)
+ValuePair(PCS_ID74_UTM_zone_53S,	23893)
+ValuePair(PCS_ID74_UTM_zone_54S,	23894)
+ValuePair(PCS_Indian_1954_UTM_47N,	23947)
+ValuePair(PCS_Indian_1954_UTM_48N,	23948)
+ValuePair(PCS_Indian_1975_UTM_47N,	24047)
+ValuePair(PCS_Indian_1975_UTM_48N,	24048)
+ValuePair(PCS_Jamaica_1875_Old_Grid,	24100)
+ValuePair(PCS_JAD69_Jamaica_Grid,	24200)
+ValuePair(PCS_Kalianpur_India_0,	24370)
+ValuePair(PCS_Kalianpur_India_I,	24371)
+ValuePair(PCS_Kalianpur_India_IIa,	24372)
+ValuePair(PCS_Kalianpur_India_IIIa,	24373)
+ValuePair(PCS_Kalianpur_India_IVa,	24374)
+ValuePair(PCS_Kalianpur_India_IIb,	24382)
+ValuePair(PCS_Kalianpur_India_IIIb,	24383)
+ValuePair(PCS_Kalianpur_India_IVb,	24384)
+ValuePair(PCS_Kertau_Singapore_Grid,	24500)
+ValuePair(PCS_Kertau_UTM_zone_47N,	24547)
+ValuePair(PCS_Kertau_UTM_zone_48N,	24548)
+ValuePair(PCS_La_Canoa_UTM_zone_20N,	24720)
+ValuePair(PCS_La_Canoa_UTM_zone_21N,	24721)
+ValuePair(PCS_PSAD56_UTM_zone_18N,	24818)
+ValuePair(PCS_PSAD56_UTM_zone_19N,	24819)
+ValuePair(PCS_PSAD56_UTM_zone_20N,	24820)
+ValuePair(PCS_PSAD56_UTM_zone_21N,	24821)
+ValuePair(PCS_PSAD56_UTM_zone_17S,	24877)
+ValuePair(PCS_PSAD56_UTM_zone_18S,	24878)
+ValuePair(PCS_PSAD56_UTM_zone_19S,	24879)
+ValuePair(PCS_PSAD56_UTM_zone_20S,	24880)
+ValuePair(PCS_PSAD56_Peru_west_zone,	24891)
+ValuePair(PCS_PSAD56_Peru_central,	24892)
+ValuePair(PCS_PSAD56_Peru_east_zone,	24893)
+ValuePair(PCS_Leigon_Ghana_Grid,	25000)
+ValuePair(PCS_Lome_UTM_zone_31N,	25231)
+ValuePair(PCS_Luzon_Philippines_I,	25391)
+ValuePair(PCS_Luzon_Philippines_II,	25392)
+ValuePair(PCS_Luzon_Philippines_III,	25393)
+ValuePair(PCS_Luzon_Philippines_IV,	25394)
+ValuePair(PCS_Luzon_Philippines_V,	25395)
+ValuePair(PCS_Makassar_NEIEZ,	25700)
+ValuePair(PCS_Malongo_1987_UTM_32S,	25932)
+ValuePair(PCS_Merchich_Nord_Maroc,	26191)
+ValuePair(PCS_Merchich_Sud_Maroc,	26192)
+ValuePair(PCS_Merchich_Sahara,	26193)
+ValuePair(PCS_Massawa_UTM_zone_37N,	26237)
+ValuePair(PCS_Minna_UTM_zone_31N,	26331)
+ValuePair(PCS_Minna_UTM_zone_32N,	26332)
+ValuePair(PCS_Minna_Nigeria_West,	26391)
+ValuePair(PCS_Minna_Nigeria_Mid_Belt,	26392)
+ValuePair(PCS_Minna_Nigeria_East,	26393)
+ValuePair(PCS_Mhast_UTM_zone_32S,	26432)
+ValuePair(PCS_Monte_Mario_Italy_1,	26591)
+ValuePair(PCS_Monte_Mario_Italy_2,	26592)
+ValuePair(PCS_M_poraloko_UTM_32N,	26632)
+ValuePair(PCS_M_poraloko_UTM_32S,	26692)
+ValuePair(PCS_NAD27_UTM_zone_3N,	26703)
+ValuePair(PCS_NAD27_UTM_zone_4N,	26704)
+ValuePair(PCS_NAD27_UTM_zone_5N,	26705)
+ValuePair(PCS_NAD27_UTM_zone_6N,	26706)
+ValuePair(PCS_NAD27_UTM_zone_7N,	26707)
+ValuePair(PCS_NAD27_UTM_zone_8N,	26708)
+ValuePair(PCS_NAD27_UTM_zone_9N,	26709)
+ValuePair(PCS_NAD27_UTM_zone_10N,	26710)
+ValuePair(PCS_NAD27_UTM_zone_11N,	26711)
+ValuePair(PCS_NAD27_UTM_zone_12N,	26712)
+ValuePair(PCS_NAD27_UTM_zone_13N,	26713)
+ValuePair(PCS_NAD27_UTM_zone_14N,	26714)
+ValuePair(PCS_NAD27_UTM_zone_15N,	26715)
+ValuePair(PCS_NAD27_UTM_zone_16N,	26716)
+ValuePair(PCS_NAD27_UTM_zone_17N,	26717)
+ValuePair(PCS_NAD27_UTM_zone_18N,	26718)
+ValuePair(PCS_NAD27_UTM_zone_19N,	26719)
+ValuePair(PCS_NAD27_UTM_zone_20N,	26720)
+ValuePair(PCS_NAD27_UTM_zone_21N,	26721)
+ValuePair(PCS_NAD27_UTM_zone_22N,	26722)
+ValuePair(PCS_NAD27_Alabama_East,	26729)
+ValuePair(PCS_NAD27_Alabama_West,	26730)
+ValuePair(PCS_NAD27_Alaska_zone_1,	26731)
+ValuePair(PCS_NAD27_Alaska_zone_2,	26732)
+ValuePair(PCS_NAD27_Alaska_zone_3,	26733)
+ValuePair(PCS_NAD27_Alaska_zone_4,	26734)
+ValuePair(PCS_NAD27_Alaska_zone_5,	26735)
+ValuePair(PCS_NAD27_Alaska_zone_6,	26736)
+ValuePair(PCS_NAD27_Alaska_zone_7,	26737)
+ValuePair(PCS_NAD27_Alaska_zone_8,	26738)
+ValuePair(PCS_NAD27_Alaska_zone_9,	26739)
+ValuePair(PCS_NAD27_Alaska_zone_10,	26740)
+ValuePair(PCS_NAD27_California_I,	26741)
+ValuePair(PCS_NAD27_California_II,	26742)
+ValuePair(PCS_NAD27_California_III,	26743)
+ValuePair(PCS_NAD27_California_IV,	26744)
+ValuePair(PCS_NAD27_California_V,	26745)
+ValuePair(PCS_NAD27_California_VI,	26746)
+ValuePair(PCS_NAD27_California_VII,	26747)
+ValuePair(PCS_NAD27_Arizona_East,	26748)
+ValuePair(PCS_NAD27_Arizona_Central,	26749)
+ValuePair(PCS_NAD27_Arizona_West,	26750)
+ValuePair(PCS_NAD27_Arkansas_North,	26751)
+ValuePair(PCS_NAD27_Arkansas_South,	26752)
+ValuePair(PCS_NAD27_Colorado_North,	26753)
+ValuePair(PCS_NAD27_Colorado_Central,	26754)
+ValuePair(PCS_NAD27_Colorado_South,	26755)
+ValuePair(PCS_NAD27_Connecticut,	26756)
+ValuePair(PCS_NAD27_Delaware,	26757)
+ValuePair(PCS_NAD27_Florida_East,	26758)
+ValuePair(PCS_NAD27_Florida_West,	26759)
+ValuePair(PCS_NAD27_Florida_North,	26760)
+ValuePair(PCS_NAD27_Hawaii_zone_1,	26761)
+ValuePair(PCS_NAD27_Hawaii_zone_2,	26762)
+ValuePair(PCS_NAD27_Hawaii_zone_3,	26763)
+ValuePair(PCS_NAD27_Hawaii_zone_4,	26764)
+ValuePair(PCS_NAD27_Hawaii_zone_5,	26765)
+ValuePair(PCS_NAD27_Georgia_East,	26766)
+ValuePair(PCS_NAD27_Georgia_West,	26767)
+ValuePair(PCS_NAD27_Idaho_East,	26768)
+ValuePair(PCS_NAD27_Idaho_Central,	26769)
+ValuePair(PCS_NAD27_Idaho_West,	26770)
+ValuePair(PCS_NAD27_Illinois_East,	26771)
+ValuePair(PCS_NAD27_Illinois_West,	26772)
+ValuePair(PCS_NAD27_Indiana_East,	26773)
+ValuePair(PCS_NAD27_BLM_14N_feet,	26774)
+ValuePair(PCS_NAD27_Indiana_West,	26774)
+ValuePair(PCS_NAD27_BLM_15N_feet,	26775)
+ValuePair(PCS_NAD27_Iowa_North,	26775)
+ValuePair(PCS_NAD27_BLM_16N_feet,	26776)
+ValuePair(PCS_NAD27_Iowa_South,	26776)
+ValuePair(PCS_NAD27_BLM_17N_feet,	26777)
+ValuePair(PCS_NAD27_Kansas_North,	26777)
+ValuePair(PCS_NAD27_Kansas_South,	26778)
+ValuePair(PCS_NAD27_Kentucky_North,	26779)
+ValuePair(PCS_NAD27_Kentucky_South,	26780)
+ValuePair(PCS_NAD27_Louisiana_North,	26781)
+ValuePair(PCS_NAD27_Louisiana_South,	26782)
+ValuePair(PCS_NAD27_Maine_East,	26783)
+ValuePair(PCS_NAD27_Maine_West,	26784)
+ValuePair(PCS_NAD27_Maryland,	26785)
+ValuePair(PCS_NAD27_Massachusetts,	26786)
+ValuePair(PCS_NAD27_Massachusetts_Is,	26787)
+ValuePair(PCS_NAD27_Michigan_North,	26788)
+ValuePair(PCS_NAD27_Michigan_Central,	26789)
+ValuePair(PCS_NAD27_Michigan_South,	26790)
+ValuePair(PCS_NAD27_Minnesota_North,	26791)
+ValuePair(PCS_NAD27_Minnesota_Cent,	26792)
+ValuePair(PCS_NAD27_Minnesota_South,	26793)
+ValuePair(PCS_NAD27_Mississippi_East,	26794)
+ValuePair(PCS_NAD27_Mississippi_West,	26795)
+ValuePair(PCS_NAD27_Missouri_East,	26796)
+ValuePair(PCS_NAD27_Missouri_Central,	26797)
+ValuePair(PCS_NAD27_Missouri_West,	26798)
+ValuePair(PCS_NAD_Michigan_Michigan_East,	26801)
+ValuePair(PCS_NAD_Michigan_Michigan_Old_Central,	26802)
+ValuePair(PCS_NAD_Michigan_Michigan_West,	26803)
+ValuePair(PCS_NAD83_UTM_zone_3N,	26903)
+ValuePair(PCS_NAD83_UTM_zone_4N,	26904)
+ValuePair(PCS_NAD83_UTM_zone_5N,	26905)
+ValuePair(PCS_NAD83_UTM_zone_6N,	26906)
+ValuePair(PCS_NAD83_UTM_zone_7N,	26907)
+ValuePair(PCS_NAD83_UTM_zone_8N,	26908)
+ValuePair(PCS_NAD83_UTM_zone_9N,	26909)
+ValuePair(PCS_NAD83_UTM_zone_10N,	26910)
+ValuePair(PCS_NAD83_UTM_zone_11N,	26911)
+ValuePair(PCS_NAD83_UTM_zone_12N,	26912)
+ValuePair(PCS_NAD83_UTM_zone_13N,	26913)
+ValuePair(PCS_NAD83_UTM_zone_14N,	26914)
+ValuePair(PCS_NAD83_UTM_zone_15N,	26915)
+ValuePair(PCS_NAD83_UTM_zone_16N,	26916)
+ValuePair(PCS_NAD83_UTM_zone_17N,	26917)
+ValuePair(PCS_NAD83_UTM_zone_18N,	26918)
+ValuePair(PCS_NAD83_UTM_zone_19N,	26919)
+ValuePair(PCS_NAD83_UTM_zone_20N,	26920)
+ValuePair(PCS_NAD83_UTM_zone_21N,	26921)
+ValuePair(PCS_NAD83_UTM_zone_22N,	26922)
+ValuePair(PCS_NAD83_UTM_zone_23N,	26923)
+ValuePair(PCS_NAD83_Alabama_East,	26929)
+ValuePair(PCS_NAD83_Alabama_West,	26930)
+ValuePair(PCS_NAD83_Alaska_zone_1,	26931)
+ValuePair(PCS_NAD83_Alaska_zone_2,	26932)
+ValuePair(PCS_NAD83_Alaska_zone_3,	26933)
+ValuePair(PCS_NAD83_Alaska_zone_4,	26934)
+ValuePair(PCS_NAD83_Alaska_zone_5,	26935)
+ValuePair(PCS_NAD83_Alaska_zone_6,	26936)
+ValuePair(PCS_NAD83_Alaska_zone_7,	26937)
+ValuePair(PCS_NAD83_Alaska_zone_8,	26938)
+ValuePair(PCS_NAD83_Alaska_zone_9,	26939)
+ValuePair(PCS_NAD83_Alaska_zone_10,	26940)
+ValuePair(PCS_NAD83_California_1,	26941)
+ValuePair(PCS_NAD83_California_2,	26942)
+ValuePair(PCS_NAD83_California_3,	26943)
+ValuePair(PCS_NAD83_California_4,	26944)
+ValuePair(PCS_NAD83_California_5,	26945)
+ValuePair(PCS_NAD83_California_6,	26946)
+ValuePair(PCS_NAD83_Arizona_East,	26948)
+ValuePair(PCS_NAD83_Arizona_Central,	26949)
+ValuePair(PCS_NAD83_Arizona_West,	26950)
+ValuePair(PCS_NAD83_Arkansas_North,	26951)
+ValuePair(PCS_NAD83_Arkansas_South,	26952)
+ValuePair(PCS_NAD83_Colorado_North,	26953)
+ValuePair(PCS_NAD83_Colorado_Central,	26954)
+ValuePair(PCS_NAD83_Colorado_South,	26955)
+ValuePair(PCS_NAD83_Connecticut,	26956)
+ValuePair(PCS_NAD83_Delaware,	26957)
+ValuePair(PCS_NAD83_Florida_East,	26958)
+ValuePair(PCS_NAD83_Florida_West,	26959)
+ValuePair(PCS_NAD83_Florida_North,	26960)
+ValuePair(PCS_NAD83_Hawaii_zone_1,	26961)
+ValuePair(PCS_NAD83_Hawaii_zone_2,	26962)
+ValuePair(PCS_NAD83_Hawaii_zone_3,	26963)
+ValuePair(PCS_NAD83_Hawaii_zone_4,	26964)
+ValuePair(PCS_NAD83_Hawaii_zone_5,	26965)
+ValuePair(PCS_NAD83_Georgia_East,	26966)
+ValuePair(PCS_NAD83_Georgia_West,	26967)
+ValuePair(PCS_NAD83_Idaho_East,	26968)
+ValuePair(PCS_NAD83_Idaho_Central,	26969)
+ValuePair(PCS_NAD83_Idaho_West,	26970)
+ValuePair(PCS_NAD83_Illinois_East,	26971)
+ValuePair(PCS_NAD83_Illinois_West,	26972)
+ValuePair(PCS_NAD83_Indiana_East,	26973)
+ValuePair(PCS_NAD83_Indiana_West,	26974)
+ValuePair(PCS_NAD83_Iowa_North,	26975)
+ValuePair(PCS_NAD83_Iowa_South,	26976)
+ValuePair(PCS_NAD83_Kansas_North,	26977)
+ValuePair(PCS_NAD83_Kansas_South,	26978)
+ValuePair(PCS_NAD83_Kentucky_North,	2205)
+ValuePair(PCS_NAD83_Kentucky_South,	26980)
+ValuePair(PCS_NAD83_Louisiana_North,	26981)
+ValuePair(PCS_NAD83_Louisiana_South,	26982)
+ValuePair(PCS_NAD83_Maine_East,	26983)
+ValuePair(PCS_NAD83_Maine_West,	26984)
+ValuePair(PCS_NAD83_Maryland,	26985)
+ValuePair(PCS_NAD83_Massachusetts,	26986)
+ValuePair(PCS_NAD83_Massachusetts_Is,	26987)
+ValuePair(PCS_NAD83_Michigan_North,	26988)
+ValuePair(PCS_NAD83_Michigan_Central,	26989)
+ValuePair(PCS_NAD83_Michigan_South,	26990)
+ValuePair(PCS_NAD83_Minnesota_North,	26991)
+ValuePair(PCS_NAD83_Minnesota_Cent,	26992)
+ValuePair(PCS_NAD83_Minnesota_South,	26993)
+ValuePair(PCS_NAD83_Mississippi_East,	26994)
+ValuePair(PCS_NAD83_Mississippi_West,	26995)
+ValuePair(PCS_NAD83_Missouri_East,	26996)
+ValuePair(PCS_NAD83_Missouri_Central,	26997)
+ValuePair(PCS_NAD83_Missouri_West,	26998)
+ValuePair(PCS_Nahrwan_1967_UTM_38N,	27038)
+ValuePair(PCS_Nahrwan_1967_UTM_39N,	27039)
+ValuePair(PCS_Nahrwan_1967_UTM_40N,	27040)
+ValuePair(PCS_Naparima_UTM_20N,	27120)
+ValuePair(PCS_GD49_NZ_Map_Grid,	27200)
+ValuePair(PCS_GD49_North_Island_Grid,	27291)
+ValuePair(PCS_GD49_South_Island_Grid,	27292)
+ValuePair(PCS_Datum_73_UTM_zone_29N,	27429)
+ValuePair(PCS_ATF_Nord_de_Guerre,	27500)
+ValuePair(PCS_NTF_France_I,	27581)
+ValuePair(PCS_NTF_France_II,	27582)
+ValuePair(PCS_NTF_France_III,	27583)
+ValuePair(PCS_NTF_Nord_France,	27591)
+ValuePair(PCS_NTF_Centre_France,	27592)
+ValuePair(PCS_NTF_Sud_France,	27593)
+ValuePair(PCS_British_National_Grid,	27700)
+ValuePair(PCS_Point_Noire_UTM_32S,	28232)
+ValuePair(PCS_GDA94_MGA_zone_48,	28348)
+ValuePair(PCS_GDA94_MGA_zone_49,	28349)
+ValuePair(PCS_GDA94_MGA_zone_50,	28350)
+ValuePair(PCS_GDA94_MGA_zone_51,	28351)
+ValuePair(PCS_GDA94_MGA_zone_52,	28352)
+ValuePair(PCS_GDA94_MGA_zone_53,	28353)
+ValuePair(PCS_GDA94_MGA_zone_54,	28354)
+ValuePair(PCS_GDA94_MGA_zone_55,	28355)
+ValuePair(PCS_GDA94_MGA_zone_56,	28356)
+ValuePair(PCS_GDA94_MGA_zone_57,	28357)
+ValuePair(PCS_GDA94_MGA_zone_58,	28358)
+ValuePair(PCS_Pulkovo_Gauss_zone_4,	28404)
+ValuePair(PCS_Pulkovo_Gauss_zone_5,	28405)
+ValuePair(PCS_Pulkovo_Gauss_zone_6,	28406)
+ValuePair(PCS_Pulkovo_Gauss_zone_7,	28407)
+ValuePair(PCS_Pulkovo_Gauss_zone_8,	28408)
+ValuePair(PCS_Pulkovo_Gauss_zone_9,	28409)
+ValuePair(PCS_Pulkovo_Gauss_zone_10,	28410)
+ValuePair(PCS_Pulkovo_Gauss_zone_11,	28411)
+ValuePair(PCS_Pulkovo_Gauss_zone_12,	28412)
+ValuePair(PCS_Pulkovo_Gauss_zone_13,	28413)
+ValuePair(PCS_Pulkovo_Gauss_zone_14,	28414)
+ValuePair(PCS_Pulkovo_Gauss_zone_15,	28415)
+ValuePair(PCS_Pulkovo_Gauss_zone_16,	28416)
+ValuePair(PCS_Pulkovo_Gauss_zone_17,	28417)
+ValuePair(PCS_Pulkovo_Gauss_zone_18,	28418)
+ValuePair(PCS_Pulkovo_Gauss_zone_19,	28419)
+ValuePair(PCS_Pulkovo_Gauss_zone_20,	28420)
+ValuePair(PCS_Pulkovo_Gauss_zone_21,	28421)
+ValuePair(PCS_Pulkovo_Gauss_zone_22,	28422)
+ValuePair(PCS_Pulkovo_Gauss_zone_23,	28423)
+ValuePair(PCS_Pulkovo_Gauss_zone_24,	28424)
+ValuePair(PCS_Pulkovo_Gauss_zone_25,	28425)
+ValuePair(PCS_Pulkovo_Gauss_zone_26,	28426)
+ValuePair(PCS_Pulkovo_Gauss_zone_27,	28427)
+ValuePair(PCS_Pulkovo_Gauss_zone_28,	28428)
+ValuePair(PCS_Pulkovo_Gauss_zone_29,	28429)
+ValuePair(PCS_Pulkovo_Gauss_zone_30,	28430)
+ValuePair(PCS_Pulkovo_Gauss_zone_31,	28431)
+ValuePair(PCS_Pulkovo_Gauss_zone_32,	28432)
+ValuePair(PCS_Pulkovo_Gauss_4N,	28464)
+ValuePair(PCS_Pulkovo_Gauss_5N,	28465)
+ValuePair(PCS_Pulkovo_Gauss_6N,	28466)
+ValuePair(PCS_Pulkovo_Gauss_7N,	28467)
+ValuePair(PCS_Pulkovo_Gauss_8N,	28468)
+ValuePair(PCS_Pulkovo_Gauss_9N,	28469)
+ValuePair(PCS_Pulkovo_Gauss_10N,	28470)
+ValuePair(PCS_Pulkovo_Gauss_11N,	28471)
+ValuePair(PCS_Pulkovo_Gauss_12N,	28472)
+ValuePair(PCS_Pulkovo_Gauss_13N,	28473)
+ValuePair(PCS_Pulkovo_Gauss_14N,	28474)
+ValuePair(PCS_Pulkovo_Gauss_15N,	28475)
+ValuePair(PCS_Pulkovo_Gauss_16N,	28476)
+ValuePair(PCS_Pulkovo_Gauss_17N,	28477)
+ValuePair(PCS_Pulkovo_Gauss_18N,	28478)
+ValuePair(PCS_Pulkovo_Gauss_19N,	28479)
+ValuePair(PCS_Pulkovo_Gauss_20N,	28480)
+ValuePair(PCS_Pulkovo_Gauss_21N,	28481)
+ValuePair(PCS_Pulkovo_Gauss_22N,	28482)
+ValuePair(PCS_Pulkovo_Gauss_23N,	28483)
+ValuePair(PCS_Pulkovo_Gauss_24N,	28484)
+ValuePair(PCS_Pulkovo_Gauss_25N,	28485)
+ValuePair(PCS_Pulkovo_Gauss_26N,	28486)
+ValuePair(PCS_Pulkovo_Gauss_27N,	28487)
+ValuePair(PCS_Pulkovo_Gauss_28N,	28488)
+ValuePair(PCS_Pulkovo_Gauss_29N,	28489)
+ValuePair(PCS_Pulkovo_Gauss_30N,	28490)
+ValuePair(PCS_Pulkovo_Gauss_31N,	28491)
+ValuePair(PCS_Pulkovo_Gauss_32N,	28492)
+ValuePair(PCS_Qatar_National_Grid,	28600)
+ValuePair(PCS_RD_Netherlands_Old,	28991)
+ValuePair(PCS_RD_Netherlands_New,	28992)
+ValuePair(PCS_SAD69_UTM_zone_18N,	29118)
+ValuePair(PCS_SAD69_UTM_zone_19N,	29119)
+ValuePair(PCS_SAD69_UTM_zone_20N,	29120)
+ValuePair(PCS_SAD69_UTM_zone_21N,	29121)
+ValuePair(PCS_SAD69_UTM_zone_22N,	29122)
+ValuePair(PCS_SAD69_UTM_zone_17S,	29177)
+ValuePair(PCS_SAD69_UTM_zone_18S,	29178)
+ValuePair(PCS_SAD69_UTM_zone_19S,	29179)
+ValuePair(PCS_SAD69_UTM_zone_20S,	29180)
+ValuePair(PCS_SAD69_UTM_zone_21S,	29181)
+ValuePair(PCS_SAD69_UTM_zone_22S,	29182)
+ValuePair(PCS_SAD69_UTM_zone_23S,	29183)
+ValuePair(PCS_SAD69_UTM_zone_24S,	29184)
+ValuePair(PCS_SAD69_UTM_zone_25S,	29185)
+ValuePair(PCS_Sapper_Hill_UTM_20S,	29220)
+ValuePair(PCS_Sapper_Hill_UTM_21S,	29221)
+ValuePair(PCS_Schwarzeck_UTM_33S,	29333)
+ValuePair(PCS_Sudan_UTM_zone_35N,	29635)
+ValuePair(PCS_Sudan_UTM_zone_36N,	29636)
+ValuePair(PCS_Tananarive_Laborde,	29700)
+ValuePair(PCS_Tananarive_UTM_38S,	29738)
+ValuePair(PCS_Tananarive_UTM_39S,	29739)
+ValuePair(PCS_Timbalai_1948_Borneo,	29800)
+ValuePair(PCS_Timbalai_1948_UTM_49N,	29849)
+ValuePair(PCS_Timbalai_1948_UTM_50N,	29850)
+ValuePair(PCS_TM65_Irish_Nat_Grid,	29900)
+ValuePair(PCS_Trinidad_1903_Trinidad,	30200)
+ValuePair(PCS_TC_1948_UTM_zone_39N,	30339)
+ValuePair(PCS_TC_1948_UTM_zone_40N,	30340)
+ValuePair(PCS_Voirol_N_Algerie_ancien,	30491)
+ValuePair(PCS_Voirol_S_Algerie_ancien,	30492)
+ValuePair(PCS_Voirol_Unifie_N_Algerie,	30591)
+ValuePair(PCS_Voirol_Unifie_S_Algerie,	30592)
+ValuePair(PCS_Bern_1938_Swiss_New,	30600)
+ValuePair(PCS_Nord_Sahara_UTM_29N,	30729)
+ValuePair(PCS_Nord_Sahara_UTM_30N,	30730)
+ValuePair(PCS_Nord_Sahara_UTM_31N,	30731)
+ValuePair(PCS_Nord_Sahara_UTM_32N,	30732)
+ValuePair(PCS_Yoff_UTM_zone_28N,	31028)
+ValuePair(PCS_Zanderij_UTM_zone_21N,	31121)
+ValuePair(PCS_MGI_Austria_West,	31291)
+ValuePair(PCS_MGI_Austria_Central,	31292)
+ValuePair(PCS_MGI_Austria_East,	31293)
+ValuePair(PCS_Belge_Lambert_72,	31300)
+ValuePair(PCS_DHDN_Germany_zone_1,	31491)
+ValuePair(PCS_DHDN_Germany_zone_2,	31492)
+ValuePair(PCS_DHDN_Germany_zone_3,	31493)
+ValuePair(PCS_DHDN_Germany_zone_4,	31494)
+ValuePair(PCS_DHDN_Germany_zone_5,	31495)
+ValuePair(PCS_NAD27_Montana_North,	32001)
+ValuePair(PCS_NAD27_Montana_Central,	32002)
+ValuePair(PCS_NAD27_Montana_South,	32003)
+ValuePair(PCS_NAD27_Nebraska_North,	32005)
+ValuePair(PCS_NAD27_Nebraska_South,	32006)
+ValuePair(PCS_NAD27_Nevada_East,	32007)
+ValuePair(PCS_NAD27_Nevada_Central,	32008)
+ValuePair(PCS_NAD27_Nevada_West,	32009)
+ValuePair(PCS_NAD27_New_Hampshire,	32010)
+ValuePair(PCS_NAD27_New_Jersey,	32011)
+ValuePair(PCS_NAD27_New_Mexico_East,	32012)
+ValuePair(PCS_NAD27_New_Mexico_Cent,	32013)
+ValuePair(PCS_NAD27_New_Mexico_West,	32014)
+ValuePair(PCS_NAD27_New_York_East,	32015)
+ValuePair(PCS_NAD27_New_York_Central,	32016)
+ValuePair(PCS_NAD27_New_York_West,	32017)
+ValuePair(PCS_NAD27_New_York_Long_Is,	32018)
+ValuePair(PCS_NAD27_North_Carolina,	32019)
+ValuePair(PCS_NAD27_North_Dakota_N,	32020)
+ValuePair(PCS_NAD27_North_Dakota_S,	32021)
+ValuePair(PCS_NAD27_Ohio_North,	32022)
+ValuePair(PCS_NAD27_Ohio_South,	32023)
+ValuePair(PCS_NAD27_Oklahoma_North,	32024)
+ValuePair(PCS_NAD27_Oklahoma_South,	32025)
+ValuePair(PCS_NAD27_Oregon_North,	32026)
+ValuePair(PCS_NAD27_Oregon_South,	32027)
+ValuePair(PCS_NAD27_Pennsylvania_N,	32028)
+ValuePair(PCS_NAD27_Pennsylvania_S,	32029)
+ValuePair(PCS_NAD27_Rhode_Island,	32030)
+ValuePair(PCS_NAD27_South_Carolina_N,	32031)
+ValuePair(PCS_NAD27_South_Carolina_S,	32033)
+ValuePair(PCS_NAD27_South_Dakota_N,	32034)
+ValuePair(PCS_NAD27_South_Dakota_S,	32035)
+ValuePair(PCS_NAD27_Tennessee,		2204)
+ValuePair(PCS_NAD27_Texas_North,	32037)
+ValuePair(PCS_NAD27_Texas_North_Cen,	32038)
+ValuePair(PCS_NAD27_Texas_Central,	32039)
+ValuePair(PCS_NAD27_Texas_South_Cen,	32040)
+ValuePair(PCS_NAD27_Texas_South,	32041)
+ValuePair(PCS_NAD27_Utah_North,	32042)
+ValuePair(PCS_NAD27_Utah_Central,	32043)
+ValuePair(PCS_NAD27_Utah_South,	32044)
+ValuePair(PCS_NAD27_Vermont,	32045)
+ValuePair(PCS_NAD27_Virginia_North,	32046)
+ValuePair(PCS_NAD27_Virginia_South,	32047)
+ValuePair(PCS_NAD27_Washington_North,	32048)
+ValuePair(PCS_NAD27_Washington_South,	32049)
+ValuePair(PCS_NAD27_West_Virginia_N,	32050)
+ValuePair(PCS_NAD27_West_Virginia_S,	32051)
+ValuePair(PCS_NAD27_Wisconsin_North,	32052)
+ValuePair(PCS_NAD27_Wisconsin_Cen,	32053)
+ValuePair(PCS_NAD27_Wisconsin_South,	32054)
+ValuePair(PCS_NAD27_Wyoming_East,	32055)
+ValuePair(PCS_NAD27_Wyoming_E_Cen,	32056)
+ValuePair(PCS_NAD27_Wyoming_W_Cen,	32057)
+ValuePair(PCS_NAD27_Wyoming_West,	32058)
+ValuePair(PCS_NAD27_Puerto_Rico,	32059)
+ValuePair(PCS_NAD27_St_Croix,	32060)
+ValuePair(PCS_NAD83_Montana,	32100)
+ValuePair(PCS_NAD83_Nebraska,	32104)
+ValuePair(PCS_NAD83_Nevada_East,	32107)
+ValuePair(PCS_NAD83_Nevada_Central,	32108)
+ValuePair(PCS_NAD83_Nevada_West,	32109)
+ValuePair(PCS_NAD83_New_Hampshire,	32110)
+ValuePair(PCS_NAD83_New_Jersey,	32111)
+ValuePair(PCS_NAD83_New_Mexico_East,	32112)
+ValuePair(PCS_NAD83_New_Mexico_Cent,	32113)
+ValuePair(PCS_NAD83_New_Mexico_West,	32114)
+ValuePair(PCS_NAD83_New_York_East,	32115)
+ValuePair(PCS_NAD83_New_York_Central,	32116)
+ValuePair(PCS_NAD83_New_York_West,	32117)
+ValuePair(PCS_NAD83_New_York_Long_Is,	32118)
+ValuePair(PCS_NAD83_North_Carolina,	32119)
+ValuePair(PCS_NAD83_North_Dakota_N,	32120)
+ValuePair(PCS_NAD83_North_Dakota_S,	32121)
+ValuePair(PCS_NAD83_Ohio_North,	32122)
+ValuePair(PCS_NAD83_Ohio_South,	32123)
+ValuePair(PCS_NAD83_Oklahoma_North,	32124)
+ValuePair(PCS_NAD83_Oklahoma_South,	32125)
+ValuePair(PCS_NAD83_Oregon_North,	32126)
+ValuePair(PCS_NAD83_Oregon_South,	32127)
+ValuePair(PCS_NAD83_Pennsylvania_N,	32128)
+ValuePair(PCS_NAD83_Pennsylvania_S,	32129)
+ValuePair(PCS_NAD83_Rhode_Island,	32130)
+ValuePair(PCS_NAD83_South_Carolina,	32133)
+ValuePair(PCS_NAD83_South_Dakota_N,	32134)
+ValuePair(PCS_NAD83_South_Dakota_S,	32135)
+ValuePair(PCS_NAD83_Tennessee,	32136)
+ValuePair(PCS_NAD83_Texas_North,	32137)
+ValuePair(PCS_NAD83_Texas_North_Cen,	32138)
+ValuePair(PCS_NAD83_Texas_Central,	32139)
+ValuePair(PCS_NAD83_Texas_South_Cen,	32140)
+ValuePair(PCS_NAD83_Texas_South,	32141)
+ValuePair(PCS_NAD83_Utah_North,	32142)
+ValuePair(PCS_NAD83_Utah_Central,	32143)
+ValuePair(PCS_NAD83_Utah_South,	32144)
+ValuePair(PCS_NAD83_Vermont,	32145)
+ValuePair(PCS_NAD83_Virginia_North,	32146)
+ValuePair(PCS_NAD83_Virginia_South,	32147)
+ValuePair(PCS_NAD83_Washington_North,	32148)
+ValuePair(PCS_NAD83_Washington_South,	32149)
+ValuePair(PCS_NAD83_West_Virginia_N,	32150)
+ValuePair(PCS_NAD83_West_Virginia_S,	32151)
+ValuePair(PCS_NAD83_Wisconsin_North,	32152)
+ValuePair(PCS_NAD83_Wisconsin_Cen,	32153)
+ValuePair(PCS_NAD83_Wisconsin_South,	32154)
+ValuePair(PCS_NAD83_Wyoming_East,	32155)
+ValuePair(PCS_NAD83_Wyoming_E_Cen,	32156)
+ValuePair(PCS_NAD83_Wyoming_W_Cen,	32157)
+ValuePair(PCS_NAD83_Wyoming_West,	32158)
+ValuePair(PCS_NAD83_Puerto_Rico_Virgin_Is,	32161)
+ValuePair(PCS_WGS72_UTM_zone_1N,	32201)
+ValuePair(PCS_WGS72_UTM_zone_2N,	32202)
+ValuePair(PCS_WGS72_UTM_zone_3N,	32203)
+ValuePair(PCS_WGS72_UTM_zone_4N,	32204)
+ValuePair(PCS_WGS72_UTM_zone_5N,	32205)
+ValuePair(PCS_WGS72_UTM_zone_6N,	32206)
+ValuePair(PCS_WGS72_UTM_zone_7N,	32207)
+ValuePair(PCS_WGS72_UTM_zone_8N,	32208)
+ValuePair(PCS_WGS72_UTM_zone_9N,	32209)
+ValuePair(PCS_WGS72_UTM_zone_10N,	32210)
+ValuePair(PCS_WGS72_UTM_zone_11N,	32211)
+ValuePair(PCS_WGS72_UTM_zone_12N,	32212)
+ValuePair(PCS_WGS72_UTM_zone_13N,	32213)
+ValuePair(PCS_WGS72_UTM_zone_14N,	32214)
+ValuePair(PCS_WGS72_UTM_zone_15N,	32215)
+ValuePair(PCS_WGS72_UTM_zone_16N,	32216)
+ValuePair(PCS_WGS72_UTM_zone_17N,	32217)
+ValuePair(PCS_WGS72_UTM_zone_18N,	32218)
+ValuePair(PCS_WGS72_UTM_zone_19N,	32219)
+ValuePair(PCS_WGS72_UTM_zone_20N,	32220)
+ValuePair(PCS_WGS72_UTM_zone_21N,	32221)
+ValuePair(PCS_WGS72_UTM_zone_22N,	32222)
+ValuePair(PCS_WGS72_UTM_zone_23N,	32223)
+ValuePair(PCS_WGS72_UTM_zone_24N,	32224)
+ValuePair(PCS_WGS72_UTM_zone_25N,	32225)
+ValuePair(PCS_WGS72_UTM_zone_26N,	32226)
+ValuePair(PCS_WGS72_UTM_zone_27N,	32227)
+ValuePair(PCS_WGS72_UTM_zone_28N,	32228)
+ValuePair(PCS_WGS72_UTM_zone_29N,	32229)
+ValuePair(PCS_WGS72_UTM_zone_30N,	32230)
+ValuePair(PCS_WGS72_UTM_zone_31N,	32231)
+ValuePair(PCS_WGS72_UTM_zone_32N,	32232)
+ValuePair(PCS_WGS72_UTM_zone_33N,	32233)
+ValuePair(PCS_WGS72_UTM_zone_34N,	32234)
+ValuePair(PCS_WGS72_UTM_zone_35N,	32235)
+ValuePair(PCS_WGS72_UTM_zone_36N,	32236)
+ValuePair(PCS_WGS72_UTM_zone_37N,	32237)
+ValuePair(PCS_WGS72_UTM_zone_38N,	32238)
+ValuePair(PCS_WGS72_UTM_zone_39N,	32239)
+ValuePair(PCS_WGS72_UTM_zone_40N,	32240)
+ValuePair(PCS_WGS72_UTM_zone_41N,	32241)
+ValuePair(PCS_WGS72_UTM_zone_42N,	32242)
+ValuePair(PCS_WGS72_UTM_zone_43N,	32243)
+ValuePair(PCS_WGS72_UTM_zone_44N,	32244)
+ValuePair(PCS_WGS72_UTM_zone_45N,	32245)
+ValuePair(PCS_WGS72_UTM_zone_46N,	32246)
+ValuePair(PCS_WGS72_UTM_zone_47N,	32247)
+ValuePair(PCS_WGS72_UTM_zone_48N,	32248)
+ValuePair(PCS_WGS72_UTM_zone_49N,	32249)
+ValuePair(PCS_WGS72_UTM_zone_50N,	32250)
+ValuePair(PCS_WGS72_UTM_zone_51N,	32251)
+ValuePair(PCS_WGS72_UTM_zone_52N,	32252)
+ValuePair(PCS_WGS72_UTM_zone_53N,	32253)
+ValuePair(PCS_WGS72_UTM_zone_54N,	32254)
+ValuePair(PCS_WGS72_UTM_zone_55N,	32255)
+ValuePair(PCS_WGS72_UTM_zone_56N,	32256)
+ValuePair(PCS_WGS72_UTM_zone_57N,	32257)
+ValuePair(PCS_WGS72_UTM_zone_58N,	32258)
+ValuePair(PCS_WGS72_UTM_zone_59N,	32259)
+ValuePair(PCS_WGS72_UTM_zone_60N,	32260)
+ValuePair(PCS_WGS72_UTM_zone_1S,	32301)
+ValuePair(PCS_WGS72_UTM_zone_2S,	32302)
+ValuePair(PCS_WGS72_UTM_zone_3S,	32303)
+ValuePair(PCS_WGS72_UTM_zone_4S,	32304)
+ValuePair(PCS_WGS72_UTM_zone_5S,	32305)
+ValuePair(PCS_WGS72_UTM_zone_6S,	32306)
+ValuePair(PCS_WGS72_UTM_zone_7S,	32307)
+ValuePair(PCS_WGS72_UTM_zone_8S,	32308)
+ValuePair(PCS_WGS72_UTM_zone_9S,	32309)
+ValuePair(PCS_WGS72_UTM_zone_10S,	32310)
+ValuePair(PCS_WGS72_UTM_zone_11S,	32311)
+ValuePair(PCS_WGS72_UTM_zone_12S,	32312)
+ValuePair(PCS_WGS72_UTM_zone_13S,	32313)
+ValuePair(PCS_WGS72_UTM_zone_14S,	32314)
+ValuePair(PCS_WGS72_UTM_zone_15S,	32315)
+ValuePair(PCS_WGS72_UTM_zone_16S,	32316)
+ValuePair(PCS_WGS72_UTM_zone_17S,	32317)
+ValuePair(PCS_WGS72_UTM_zone_18S,	32318)
+ValuePair(PCS_WGS72_UTM_zone_19S,	32319)
+ValuePair(PCS_WGS72_UTM_zone_20S,	32320)
+ValuePair(PCS_WGS72_UTM_zone_21S,	32321)
+ValuePair(PCS_WGS72_UTM_zone_22S,	32322)
+ValuePair(PCS_WGS72_UTM_zone_23S,	32323)
+ValuePair(PCS_WGS72_UTM_zone_24S,	32324)
+ValuePair(PCS_WGS72_UTM_zone_25S,	32325)
+ValuePair(PCS_WGS72_UTM_zone_26S,	32326)
+ValuePair(PCS_WGS72_UTM_zone_27S,	32327)
+ValuePair(PCS_WGS72_UTM_zone_28S,	32328)
+ValuePair(PCS_WGS72_UTM_zone_29S,	32329)
+ValuePair(PCS_WGS72_UTM_zone_30S,	32330)
+ValuePair(PCS_WGS72_UTM_zone_31S,	32331)
+ValuePair(PCS_WGS72_UTM_zone_32S,	32332)
+ValuePair(PCS_WGS72_UTM_zone_33S,	32333)
+ValuePair(PCS_WGS72_UTM_zone_34S,	32334)
+ValuePair(PCS_WGS72_UTM_zone_35S,	32335)
+ValuePair(PCS_WGS72_UTM_zone_36S,	32336)
+ValuePair(PCS_WGS72_UTM_zone_37S,	32337)
+ValuePair(PCS_WGS72_UTM_zone_38S,	32338)
+ValuePair(PCS_WGS72_UTM_zone_39S,	32339)
+ValuePair(PCS_WGS72_UTM_zone_40S,	32340)
+ValuePair(PCS_WGS72_UTM_zone_41S,	32341)
+ValuePair(PCS_WGS72_UTM_zone_42S,	32342)
+ValuePair(PCS_WGS72_UTM_zone_43S,	32343)
+ValuePair(PCS_WGS72_UTM_zone_44S,	32344)
+ValuePair(PCS_WGS72_UTM_zone_45S,	32345)
+ValuePair(PCS_WGS72_UTM_zone_46S,	32346)
+ValuePair(PCS_WGS72_UTM_zone_47S,	32347)
+ValuePair(PCS_WGS72_UTM_zone_48S,	32348)
+ValuePair(PCS_WGS72_UTM_zone_49S,	32349)
+ValuePair(PCS_WGS72_UTM_zone_50S,	32350)
+ValuePair(PCS_WGS72_UTM_zone_51S,	32351)
+ValuePair(PCS_WGS72_UTM_zone_52S,	32352)
+ValuePair(PCS_WGS72_UTM_zone_53S,	32353)
+ValuePair(PCS_WGS72_UTM_zone_54S,	32354)
+ValuePair(PCS_WGS72_UTM_zone_55S,	32355)
+ValuePair(PCS_WGS72_UTM_zone_56S,	32356)
+ValuePair(PCS_WGS72_UTM_zone_57S,	32357)
+ValuePair(PCS_WGS72_UTM_zone_58S,	32358)
+ValuePair(PCS_WGS72_UTM_zone_59S,	32359)
+ValuePair(PCS_WGS72_UTM_zone_60S,	32360)
+ValuePair(PCS_WGS72BE_UTM_zone_1N,	32401)
+ValuePair(PCS_WGS72BE_UTM_zone_2N,	32402)
+ValuePair(PCS_WGS72BE_UTM_zone_3N,	32403)
+ValuePair(PCS_WGS72BE_UTM_zone_4N,	32404)
+ValuePair(PCS_WGS72BE_UTM_zone_5N,	32405)
+ValuePair(PCS_WGS72BE_UTM_zone_6N,	32406)
+ValuePair(PCS_WGS72BE_UTM_zone_7N,	32407)
+ValuePair(PCS_WGS72BE_UTM_zone_8N,	32408)
+ValuePair(PCS_WGS72BE_UTM_zone_9N,	32409)
+ValuePair(PCS_WGS72BE_UTM_zone_10N,	32410)
+ValuePair(PCS_WGS72BE_UTM_zone_11N,	32411)
+ValuePair(PCS_WGS72BE_UTM_zone_12N,	32412)
+ValuePair(PCS_WGS72BE_UTM_zone_13N,	32413)
+ValuePair(PCS_WGS72BE_UTM_zone_14N,	32414)
+ValuePair(PCS_WGS72BE_UTM_zone_15N,	32415)
+ValuePair(PCS_WGS72BE_UTM_zone_16N,	32416)
+ValuePair(PCS_WGS72BE_UTM_zone_17N,	32417)
+ValuePair(PCS_WGS72BE_UTM_zone_18N,	32418)
+ValuePair(PCS_WGS72BE_UTM_zone_19N,	32419)
+ValuePair(PCS_WGS72BE_UTM_zone_20N,	32420)
+ValuePair(PCS_WGS72BE_UTM_zone_21N,	32421)
+ValuePair(PCS_WGS72BE_UTM_zone_22N,	32422)
+ValuePair(PCS_WGS72BE_UTM_zone_23N,	32423)
+ValuePair(PCS_WGS72BE_UTM_zone_24N,	32424)
+ValuePair(PCS_WGS72BE_UTM_zone_25N,	32425)
+ValuePair(PCS_WGS72BE_UTM_zone_26N,	32426)
+ValuePair(PCS_WGS72BE_UTM_zone_27N,	32427)
+ValuePair(PCS_WGS72BE_UTM_zone_28N,	32428)
+ValuePair(PCS_WGS72BE_UTM_zone_29N,	32429)
+ValuePair(PCS_WGS72BE_UTM_zone_30N,	32430)
+ValuePair(PCS_WGS72BE_UTM_zone_31N,	32431)
+ValuePair(PCS_WGS72BE_UTM_zone_32N,	32432)
+ValuePair(PCS_WGS72BE_UTM_zone_33N,	32433)
+ValuePair(PCS_WGS72BE_UTM_zone_34N,	32434)
+ValuePair(PCS_WGS72BE_UTM_zone_35N,	32435)
+ValuePair(PCS_WGS72BE_UTM_zone_36N,	32436)
+ValuePair(PCS_WGS72BE_UTM_zone_37N,	32437)
+ValuePair(PCS_WGS72BE_UTM_zone_38N,	32438)
+ValuePair(PCS_WGS72BE_UTM_zone_39N,	32439)
+ValuePair(PCS_WGS72BE_UTM_zone_40N,	32440)
+ValuePair(PCS_WGS72BE_UTM_zone_41N,	32441)
+ValuePair(PCS_WGS72BE_UTM_zone_42N,	32442)
+ValuePair(PCS_WGS72BE_UTM_zone_43N,	32443)
+ValuePair(PCS_WGS72BE_UTM_zone_44N,	32444)
+ValuePair(PCS_WGS72BE_UTM_zone_45N,	32445)
+ValuePair(PCS_WGS72BE_UTM_zone_46N,	32446)
+ValuePair(PCS_WGS72BE_UTM_zone_47N,	32447)
+ValuePair(PCS_WGS72BE_UTM_zone_48N,	32448)
+ValuePair(PCS_WGS72BE_UTM_zone_49N,	32449)
+ValuePair(PCS_WGS72BE_UTM_zone_50N,	32450)
+ValuePair(PCS_WGS72BE_UTM_zone_51N,	32451)
+ValuePair(PCS_WGS72BE_UTM_zone_52N,	32452)
+ValuePair(PCS_WGS72BE_UTM_zone_53N,	32453)
+ValuePair(PCS_WGS72BE_UTM_zone_54N,	32454)
+ValuePair(PCS_WGS72BE_UTM_zone_55N,	32455)
+ValuePair(PCS_WGS72BE_UTM_zone_56N,	32456)
+ValuePair(PCS_WGS72BE_UTM_zone_57N,	32457)
+ValuePair(PCS_WGS72BE_UTM_zone_58N,	32458)
+ValuePair(PCS_WGS72BE_UTM_zone_59N,	32459)
+ValuePair(PCS_WGS72BE_UTM_zone_60N,	32460)
+ValuePair(PCS_WGS72BE_UTM_zone_1S,	32501)
+ValuePair(PCS_WGS72BE_UTM_zone_2S,	32502)
+ValuePair(PCS_WGS72BE_UTM_zone_3S,	32503)
+ValuePair(PCS_WGS72BE_UTM_zone_4S,	32504)
+ValuePair(PCS_WGS72BE_UTM_zone_5S,	32505)
+ValuePair(PCS_WGS72BE_UTM_zone_6S,	32506)
+ValuePair(PCS_WGS72BE_UTM_zone_7S,	32507)
+ValuePair(PCS_WGS72BE_UTM_zone_8S,	32508)
+ValuePair(PCS_WGS72BE_UTM_zone_9S,	32509)
+ValuePair(PCS_WGS72BE_UTM_zone_10S,	32510)
+ValuePair(PCS_WGS72BE_UTM_zone_11S,	32511)
+ValuePair(PCS_WGS72BE_UTM_zone_12S,	32512)
+ValuePair(PCS_WGS72BE_UTM_zone_13S,	32513)
+ValuePair(PCS_WGS72BE_UTM_zone_14S,	32514)
+ValuePair(PCS_WGS72BE_UTM_zone_15S,	32515)
+ValuePair(PCS_WGS72BE_UTM_zone_16S,	32516)
+ValuePair(PCS_WGS72BE_UTM_zone_17S,	32517)
+ValuePair(PCS_WGS72BE_UTM_zone_18S,	32518)
+ValuePair(PCS_WGS72BE_UTM_zone_19S,	32519)
+ValuePair(PCS_WGS72BE_UTM_zone_20S,	32520)
+ValuePair(PCS_WGS72BE_UTM_zone_21S,	32521)
+ValuePair(PCS_WGS72BE_UTM_zone_22S,	32522)
+ValuePair(PCS_WGS72BE_UTM_zone_23S,	32523)
+ValuePair(PCS_WGS72BE_UTM_zone_24S,	32524)
+ValuePair(PCS_WGS72BE_UTM_zone_25S,	32525)
+ValuePair(PCS_WGS72BE_UTM_zone_26S,	32526)
+ValuePair(PCS_WGS72BE_UTM_zone_27S,	32527)
+ValuePair(PCS_WGS72BE_UTM_zone_28S,	32528)
+ValuePair(PCS_WGS72BE_UTM_zone_29S,	32529)
+ValuePair(PCS_WGS72BE_UTM_zone_30S,	32530)
+ValuePair(PCS_WGS72BE_UTM_zone_31S,	32531)
+ValuePair(PCS_WGS72BE_UTM_zone_32S,	32532)
+ValuePair(PCS_WGS72BE_UTM_zone_33S,	32533)
+ValuePair(PCS_WGS72BE_UTM_zone_34S,	32534)
+ValuePair(PCS_WGS72BE_UTM_zone_35S,	32535)
+ValuePair(PCS_WGS72BE_UTM_zone_36S,	32536)
+ValuePair(PCS_WGS72BE_UTM_zone_37S,	32537)
+ValuePair(PCS_WGS72BE_UTM_zone_38S,	32538)
+ValuePair(PCS_WGS72BE_UTM_zone_39S,	32539)
+ValuePair(PCS_WGS72BE_UTM_zone_40S,	32540)
+ValuePair(PCS_WGS72BE_UTM_zone_41S,	32541)
+ValuePair(PCS_WGS72BE_UTM_zone_42S,	32542)
+ValuePair(PCS_WGS72BE_UTM_zone_43S,	32543)
+ValuePair(PCS_WGS72BE_UTM_zone_44S,	32544)
+ValuePair(PCS_WGS72BE_UTM_zone_45S,	32545)
+ValuePair(PCS_WGS72BE_UTM_zone_46S,	32546)
+ValuePair(PCS_WGS72BE_UTM_zone_47S,	32547)
+ValuePair(PCS_WGS72BE_UTM_zone_48S,	32548)
+ValuePair(PCS_WGS72BE_UTM_zone_49S,	32549)
+ValuePair(PCS_WGS72BE_UTM_zone_50S,	32550)
+ValuePair(PCS_WGS72BE_UTM_zone_51S,	32551)
+ValuePair(PCS_WGS72BE_UTM_zone_52S,	32552)
+ValuePair(PCS_WGS72BE_UTM_zone_53S,	32553)
+ValuePair(PCS_WGS72BE_UTM_zone_54S,	32554)
+ValuePair(PCS_WGS72BE_UTM_zone_55S,	32555)
+ValuePair(PCS_WGS72BE_UTM_zone_56S,	32556)
+ValuePair(PCS_WGS72BE_UTM_zone_57S,	32557)
+ValuePair(PCS_WGS72BE_UTM_zone_58S,	32558)
+ValuePair(PCS_WGS72BE_UTM_zone_59S,	32559)
+ValuePair(PCS_WGS72BE_UTM_zone_60S,	32560)
+ValuePair(PCS_WGS84_UTM_zone_1N,	32601)
+ValuePair(PCS_WGS84_UTM_zone_2N,	32602)
+ValuePair(PCS_WGS84_UTM_zone_3N,	32603)
+ValuePair(PCS_WGS84_UTM_zone_4N,	32604)
+ValuePair(PCS_WGS84_UTM_zone_5N,	32605)
+ValuePair(PCS_WGS84_UTM_zone_6N,	32606)
+ValuePair(PCS_WGS84_UTM_zone_7N,	32607)
+ValuePair(PCS_WGS84_UTM_zone_8N,	32608)
+ValuePair(PCS_WGS84_UTM_zone_9N,	32609)
+ValuePair(PCS_WGS84_UTM_zone_10N,	32610)
+ValuePair(PCS_WGS84_UTM_zone_11N,	32611)
+ValuePair(PCS_WGS84_UTM_zone_12N,	32612)
+ValuePair(PCS_WGS84_UTM_zone_13N,	32613)
+ValuePair(PCS_WGS84_UTM_zone_14N,	32614)
+ValuePair(PCS_WGS84_UTM_zone_15N,	32615)
+ValuePair(PCS_WGS84_UTM_zone_16N,	32616)
+ValuePair(PCS_WGS84_UTM_zone_17N,	32617)
+ValuePair(PCS_WGS84_UTM_zone_18N,	32618)
+ValuePair(PCS_WGS84_UTM_zone_19N,	32619)
+ValuePair(PCS_WGS84_UTM_zone_20N,	32620)
+ValuePair(PCS_WGS84_UTM_zone_21N,	32621)
+ValuePair(PCS_WGS84_UTM_zone_22N,	32622)
+ValuePair(PCS_WGS84_UTM_zone_23N,	32623)
+ValuePair(PCS_WGS84_UTM_zone_24N,	32624)
+ValuePair(PCS_WGS84_UTM_zone_25N,	32625)
+ValuePair(PCS_WGS84_UTM_zone_26N,	32626)
+ValuePair(PCS_WGS84_UTM_zone_27N,	32627)
+ValuePair(PCS_WGS84_UTM_zone_28N,	32628)
+ValuePair(PCS_WGS84_UTM_zone_29N,	32629)
+ValuePair(PCS_WGS84_UTM_zone_30N,	32630)
+ValuePair(PCS_WGS84_UTM_zone_31N,	32631)
+ValuePair(PCS_WGS84_UTM_zone_32N,	32632)
+ValuePair(PCS_WGS84_UTM_zone_33N,	32633)
+ValuePair(PCS_WGS84_UTM_zone_34N,	32634)
+ValuePair(PCS_WGS84_UTM_zone_35N,	32635)
+ValuePair(PCS_WGS84_UTM_zone_36N,	32636)
+ValuePair(PCS_WGS84_UTM_zone_37N,	32637)
+ValuePair(PCS_WGS84_UTM_zone_38N,	32638)
+ValuePair(PCS_WGS84_UTM_zone_39N,	32639)
+ValuePair(PCS_WGS84_UTM_zone_40N,	32640)
+ValuePair(PCS_WGS84_UTM_zone_41N,	32641)
+ValuePair(PCS_WGS84_UTM_zone_42N,	32642)
+ValuePair(PCS_WGS84_UTM_zone_43N,	32643)
+ValuePair(PCS_WGS84_UTM_zone_44N,	32644)
+ValuePair(PCS_WGS84_UTM_zone_45N,	32645)
+ValuePair(PCS_WGS84_UTM_zone_46N,	32646)
+ValuePair(PCS_WGS84_UTM_zone_47N,	32647)
+ValuePair(PCS_WGS84_UTM_zone_48N,	32648)
+ValuePair(PCS_WGS84_UTM_zone_49N,	32649)
+ValuePair(PCS_WGS84_UTM_zone_50N,	32650)
+ValuePair(PCS_WGS84_UTM_zone_51N,	32651)
+ValuePair(PCS_WGS84_UTM_zone_52N,	32652)
+ValuePair(PCS_WGS84_UTM_zone_53N,	32653)
+ValuePair(PCS_WGS84_UTM_zone_54N,	32654)
+ValuePair(PCS_WGS84_UTM_zone_55N,	32655)
+ValuePair(PCS_WGS84_UTM_zone_56N,	32656)
+ValuePair(PCS_WGS84_UTM_zone_57N,	32657)
+ValuePair(PCS_WGS84_UTM_zone_58N,	32658)
+ValuePair(PCS_WGS84_UTM_zone_59N,	32659)
+ValuePair(PCS_WGS84_UTM_zone_60N,	32660)
+ValuePair(PCS_WGS84_UTM_zone_1S,	32701)
+ValuePair(PCS_WGS84_UTM_zone_2S,	32702)
+ValuePair(PCS_WGS84_UTM_zone_3S,	32703)
+ValuePair(PCS_WGS84_UTM_zone_4S,	32704)
+ValuePair(PCS_WGS84_UTM_zone_5S,	32705)
+ValuePair(PCS_WGS84_UTM_zone_6S,	32706)
+ValuePair(PCS_WGS84_UTM_zone_7S,	32707)
+ValuePair(PCS_WGS84_UTM_zone_8S,	32708)
+ValuePair(PCS_WGS84_UTM_zone_9S,	32709)
+ValuePair(PCS_WGS84_UTM_zone_10S,	32710)
+ValuePair(PCS_WGS84_UTM_zone_11S,	32711)
+ValuePair(PCS_WGS84_UTM_zone_12S,	32712)
+ValuePair(PCS_WGS84_UTM_zone_13S,	32713)
+ValuePair(PCS_WGS84_UTM_zone_14S,	32714)
+ValuePair(PCS_WGS84_UTM_zone_15S,	32715)
+ValuePair(PCS_WGS84_UTM_zone_16S,	32716)
+ValuePair(PCS_WGS84_UTM_zone_17S,	32717)
+ValuePair(PCS_WGS84_UTM_zone_18S,	32718)
+ValuePair(PCS_WGS84_UTM_zone_19S,	32719)
+ValuePair(PCS_WGS84_UTM_zone_20S,	32720)
+ValuePair(PCS_WGS84_UTM_zone_21S,	32721)
+ValuePair(PCS_WGS84_UTM_zone_22S,	32722)
+ValuePair(PCS_WGS84_UTM_zone_23S,	32723)
+ValuePair(PCS_WGS84_UTM_zone_24S,	32724)
+ValuePair(PCS_WGS84_UTM_zone_25S,	32725)
+ValuePair(PCS_WGS84_UTM_zone_26S,	32726)
+ValuePair(PCS_WGS84_UTM_zone_27S,	32727)
+ValuePair(PCS_WGS84_UTM_zone_28S,	32728)
+ValuePair(PCS_WGS84_UTM_zone_29S,	32729)
+ValuePair(PCS_WGS84_UTM_zone_30S,	32730)
+ValuePair(PCS_WGS84_UTM_zone_31S,	32731)
+ValuePair(PCS_WGS84_UTM_zone_32S,	32732)
+ValuePair(PCS_WGS84_UTM_zone_33S,	32733)
+ValuePair(PCS_WGS84_UTM_zone_34S,	32734)
+ValuePair(PCS_WGS84_UTM_zone_35S,	32735)
+ValuePair(PCS_WGS84_UTM_zone_36S,	32736)
+ValuePair(PCS_WGS84_UTM_zone_37S,	32737)
+ValuePair(PCS_WGS84_UTM_zone_38S,	32738)
+ValuePair(PCS_WGS84_UTM_zone_39S,	32739)
+ValuePair(PCS_WGS84_UTM_zone_40S,	32740)
+ValuePair(PCS_WGS84_UTM_zone_41S,	32741)
+ValuePair(PCS_WGS84_UTM_zone_42S,	32742)
+ValuePair(PCS_WGS84_UTM_zone_43S,	32743)
+ValuePair(PCS_WGS84_UTM_zone_44S,	32744)
+ValuePair(PCS_WGS84_UTM_zone_45S,	32745)
+ValuePair(PCS_WGS84_UTM_zone_46S,	32746)
+ValuePair(PCS_WGS84_UTM_zone_47S,	32747)
+ValuePair(PCS_WGS84_UTM_zone_48S,	32748)
+ValuePair(PCS_WGS84_UTM_zone_49S,	32749)
+ValuePair(PCS_WGS84_UTM_zone_50S,	32750)
+ValuePair(PCS_WGS84_UTM_zone_51S,	32751)
+ValuePair(PCS_WGS84_UTM_zone_52S,	32752)
+ValuePair(PCS_WGS84_UTM_zone_53S,	32753)
+ValuePair(PCS_WGS84_UTM_zone_54S,	32754)
+ValuePair(PCS_WGS84_UTM_zone_55S,	32755)
+ValuePair(PCS_WGS84_UTM_zone_56S,	32756)
+ValuePair(PCS_WGS84_UTM_zone_57S,	32757)
+ValuePair(PCS_WGS84_UTM_zone_58S,	32758)
+ValuePair(PCS_WGS84_UTM_zone_59S,	32759)
+ValuePair(PCS_WGS84_UTM_zone_60S,	32760)
+/* end of list */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_pm.inc b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_pm.inc
new file mode 100644
index 0000000000..1e41598819
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_pm.inc
@@ -0,0 +1,22 @@
+/* EPSG/GeoTIFF Rev 0.2 Prime Meridian Database */
+
+/* C database for Geotiff include files.   */
+/* the macro ValuePair() must be defined   */
+/* by the enclosing include file           */
+
+#ifdef INCLUDE_OLD_CODES
+#include old_pm.inc
+#endif /* OLD Codes */
+
+ValuePair(PM_Greenwich,	8901)
+ValuePair(PM_Lisbon,	8902)
+ValuePair(PM_Paris,	8903)
+ValuePair(PM_Bogota,	8904)
+ValuePair(PM_Madrid,	8905)
+ValuePair(PM_Rome,	8906)
+ValuePair(PM_Bern,	8907)
+ValuePair(PM_Jakarta,	8908)
+ValuePair(PM_Ferro,	8909)
+ValuePair(PM_Brussels,	8910)
+ValuePair(PM_Stockholm,	8911)
+/* end of list */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_proj.inc b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_proj.inc
new file mode 100644
index 0000000000..d6c8791556
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_proj.inc
@@ -0,0 +1,443 @@
+/*
+ *  EPSG/POSC Projection Codes - GeoTIFF Rev 0.2
+ */
+
+/* C database for Geotiff include files.   */
+/* the macro ValuePair() must be defined   */
+/* by the enclosing include file           */
+
+#ifdef INCLUDE_OLD_CODES
+#include old_proj.inc
+#endif /* OLD Codes */
+
+/* New codes */
+
+ValuePair(Proj_Stereo_70,19926)
+
+/* old codes */
+
+ValuePair(Proj_Alabama_CS27_East,	10101)
+ValuePair(Proj_Alabama_CS27_West,	10102)
+ValuePair(Proj_Alabama_CS83_East,	10131)
+ValuePair(Proj_Alabama_CS83_West,	10132)
+ValuePair(Proj_Arizona_Coordinate_System_east,	10201)
+ValuePair(Proj_Arizona_Coordinate_System_Central,	10202)
+ValuePair(Proj_Arizona_Coordinate_System_west,	10203)
+ValuePair(Proj_Arizona_CS83_east,	10231)
+ValuePair(Proj_Arizona_CS83_Central,	10232)
+ValuePair(Proj_Arizona_CS83_west,	10233)
+ValuePair(Proj_Arkansas_CS27_North,	10301)
+ValuePair(Proj_Arkansas_CS27_South,	10302)
+ValuePair(Proj_Arkansas_CS83_North,	10331)
+ValuePair(Proj_Arkansas_CS83_South,	10332)
+ValuePair(Proj_California_CS27_I,	10401)
+ValuePair(Proj_California_CS27_II,	10402)
+ValuePair(Proj_California_CS27_III,	10403)
+ValuePair(Proj_California_CS27_IV,	10404)
+ValuePair(Proj_California_CS27_V,	10405)
+ValuePair(Proj_California_CS27_VI,	10406)
+ValuePair(Proj_California_CS27_VII,	10407)
+ValuePair(Proj_California_CS83_1,	10431)
+ValuePair(Proj_California_CS83_2,	10432)
+ValuePair(Proj_California_CS83_3,	10433)
+ValuePair(Proj_California_CS83_4,	10434)
+ValuePair(Proj_California_CS83_5,	10435)
+ValuePair(Proj_California_CS83_6,	10436)
+ValuePair(Proj_Colorado_CS27_North,	10501)
+ValuePair(Proj_Colorado_CS27_Central,	10502)
+ValuePair(Proj_Colorado_CS27_South,	10503)
+ValuePair(Proj_Colorado_CS83_North,	10531)
+ValuePair(Proj_Colorado_CS83_Central,	10532)
+ValuePair(Proj_Colorado_CS83_South,	10533)
+ValuePair(Proj_Connecticut_CS27,	10600)
+ValuePair(Proj_Connecticut_CS83,	10630)
+ValuePair(Proj_Delaware_CS27,	10700)
+ValuePair(Proj_Delaware_CS83,	10730)
+ValuePair(Proj_Florida_CS27_East,	10901)
+ValuePair(Proj_Florida_CS27_West,	10902)
+ValuePair(Proj_Florida_CS27_North,	10903)
+ValuePair(Proj_Florida_CS83_East,	10931)
+ValuePair(Proj_Florida_CS83_West,	10932)
+ValuePair(Proj_Florida_CS83_North,	10933)
+ValuePair(Proj_Georgia_CS27_East,	11001)
+ValuePair(Proj_Georgia_CS27_West,	11002)
+ValuePair(Proj_Georgia_CS83_East,	11031)
+ValuePair(Proj_Georgia_CS83_West,	11032)
+ValuePair(Proj_Idaho_CS27_East,	11101)
+ValuePair(Proj_Idaho_CS27_Central,	11102)
+ValuePair(Proj_Idaho_CS27_West,	11103)
+ValuePair(Proj_Idaho_CS83_East,	11131)
+ValuePair(Proj_Idaho_CS83_Central,	11132)
+ValuePair(Proj_Idaho_CS83_West,	11133)
+ValuePair(Proj_Illinois_CS27_East,	11201)
+ValuePair(Proj_Illinois_CS27_West,	11202)
+ValuePair(Proj_Illinois_CS83_East,	11231)
+ValuePair(Proj_Illinois_CS83_West,	11232)
+ValuePair(Proj_Indiana_CS27_East,	11301)
+ValuePair(Proj_Indiana_CS27_West,	11302)
+ValuePair(Proj_Indiana_CS83_East,	11331)
+ValuePair(Proj_Indiana_CS83_West,	11332)
+ValuePair(Proj_Iowa_CS27_North,	11401)
+ValuePair(Proj_Iowa_CS27_South,	11402)
+ValuePair(Proj_Iowa_CS83_North,	11431)
+ValuePair(Proj_Iowa_CS83_South,	11432)
+ValuePair(Proj_Kansas_CS27_North,	11501)
+ValuePair(Proj_Kansas_CS27_South,	11502)
+ValuePair(Proj_Kansas_CS83_North,	11531)
+ValuePair(Proj_Kansas_CS83_South,	11532)
+ValuePair(Proj_Kentucky_CS27_North,	11601)
+ValuePair(Proj_Kentucky_CS27_South,	11602)
+ValuePair(Proj_Kentucky_CS83_North,	15303)
+ValuePair(Proj_Kentucky_CS83_South,	11632)
+ValuePair(Proj_Louisiana_CS27_North,	11701)
+ValuePair(Proj_Louisiana_CS27_South,	11702)
+ValuePair(Proj_Louisiana_CS83_North,	11731)
+ValuePair(Proj_Louisiana_CS83_South,	11732)
+ValuePair(Proj_Maine_CS27_East,	11801)
+ValuePair(Proj_Maine_CS27_West,	11802)
+ValuePair(Proj_Maine_CS83_East,	11831)
+ValuePair(Proj_Maine_CS83_West,	11832)
+ValuePair(Proj_Maryland_CS27,	11900)
+ValuePair(Proj_Maryland_CS83,	11930)
+ValuePair(Proj_Massachusetts_CS27_Mainland,	12001)
+ValuePair(Proj_Massachusetts_CS27_Island,	12002)
+ValuePair(Proj_Massachusetts_CS83_Mainland,	12031)
+ValuePair(Proj_Massachusetts_CS83_Island,	12032)
+ValuePair(Proj_Michigan_State_Plane_East,	12101)
+ValuePair(Proj_Michigan_State_Plane_Old_Central,	12102)
+ValuePair(Proj_Michigan_State_Plane_West,	12103)
+ValuePair(Proj_Michigan_CS27_North,	12111)
+ValuePair(Proj_Michigan_CS27_Central,	12112)
+ValuePair(Proj_Michigan_CS27_South,	12113)
+ValuePair(Proj_Michigan_CS83_North,	12141)
+ValuePair(Proj_Michigan_CS83_Central,	12142)
+ValuePair(Proj_Michigan_CS83_South,	12143)
+ValuePair(Proj_Minnesota_CS27_North,	12201)
+ValuePair(Proj_Minnesota_CS27_Central,	12202)
+ValuePair(Proj_Minnesota_CS27_South,	12203)
+ValuePair(Proj_Minnesota_CS83_North,	12231)
+ValuePair(Proj_Minnesota_CS83_Central,	12232)
+ValuePair(Proj_Minnesota_CS83_South,	12233)
+ValuePair(Proj_Mississippi_CS27_East,	12301)
+ValuePair(Proj_Mississippi_CS27_West,	12302)
+ValuePair(Proj_Mississippi_CS83_East,	12331)
+ValuePair(Proj_Mississippi_CS83_West,	12332)
+ValuePair(Proj_Missouri_CS27_East,	12401)
+ValuePair(Proj_Missouri_CS27_Central,	12402)
+ValuePair(Proj_Missouri_CS27_West,	12403)
+ValuePair(Proj_Missouri_CS83_East,	12431)
+ValuePair(Proj_Missouri_CS83_Central,	12432)
+ValuePair(Proj_Missouri_CS83_West,	12433)
+ValuePair(Proj_Montana_CS27_North,	12501)
+ValuePair(Proj_Montana_CS27_Central,	12502)
+ValuePair(Proj_Montana_CS27_South,	12503)
+ValuePair(Proj_Montana_CS83,	12530)
+ValuePair(Proj_Nebraska_CS27_North,	12601)
+ValuePair(Proj_Nebraska_CS27_South,	12602)
+ValuePair(Proj_Nebraska_CS83,	12630)
+ValuePair(Proj_Nevada_CS27_East,	12701)
+ValuePair(Proj_Nevada_CS27_Central,	12702)
+ValuePair(Proj_Nevada_CS27_West,	12703)
+ValuePair(Proj_Nevada_CS83_East,	12731)
+ValuePair(Proj_Nevada_CS83_Central,	12732)
+ValuePair(Proj_Nevada_CS83_West,	12733)
+ValuePair(Proj_New_Hampshire_CS27,	12800)
+ValuePair(Proj_New_Hampshire_CS83,	12830)
+ValuePair(Proj_New_Jersey_CS27,	12900)
+ValuePair(Proj_New_Jersey_CS83,	12930)
+ValuePair(Proj_New_Mexico_CS27_East,	13001)
+ValuePair(Proj_New_Mexico_CS27_Central,	13002)
+ValuePair(Proj_New_Mexico_CS27_West,	13003)
+ValuePair(Proj_New_Mexico_CS83_East,	13031)
+ValuePair(Proj_New_Mexico_CS83_Central,	13032)
+ValuePair(Proj_New_Mexico_CS83_West,	13033)
+ValuePair(Proj_New_York_CS27_East,	13101)
+ValuePair(Proj_New_York_CS27_Central,	13102)
+ValuePair(Proj_New_York_CS27_West,	13103)
+ValuePair(Proj_New_York_CS27_Long_Island,	13104)
+ValuePair(Proj_New_York_CS83_East,	13131)
+ValuePair(Proj_New_York_CS83_Central,	13132)
+ValuePair(Proj_New_York_CS83_West,	13133)
+ValuePair(Proj_New_York_CS83_Long_Island,	13134)
+ValuePair(Proj_North_Carolina_CS27,	13200)
+ValuePair(Proj_North_Carolina_CS83,	13230)
+ValuePair(Proj_North_Dakota_CS27_North,	13301)
+ValuePair(Proj_North_Dakota_CS27_South,	13302)
+ValuePair(Proj_North_Dakota_CS83_North,	13331)
+ValuePair(Proj_North_Dakota_CS83_South,	13332)
+ValuePair(Proj_Ohio_CS27_North,	13401)
+ValuePair(Proj_Ohio_CS27_South,	13402)
+ValuePair(Proj_Ohio_CS83_North,	13431)
+ValuePair(Proj_Ohio_CS83_South,	13432)
+ValuePair(Proj_Oklahoma_CS27_North,	13501)
+ValuePair(Proj_Oklahoma_CS27_South,	13502)
+ValuePair(Proj_Oklahoma_CS83_North,	13531)
+ValuePair(Proj_Oklahoma_CS83_South,	13532)
+ValuePair(Proj_Oregon_CS27_North,	13601)
+ValuePair(Proj_Oregon_CS27_South,	13602)
+ValuePair(Proj_Oregon_CS83_North,	13631)
+ValuePair(Proj_Oregon_CS83_South,	13632)
+ValuePair(Proj_Pennsylvania_CS27_North,	13701)
+ValuePair(Proj_Pennsylvania_CS27_South,	13702)
+ValuePair(Proj_Pennsylvania_CS83_North,	13731)
+ValuePair(Proj_Pennsylvania_CS83_South,	13732)
+ValuePair(Proj_Rhode_Island_CS27,	13800)
+ValuePair(Proj_Rhode_Island_CS83,	13830)
+ValuePair(Proj_South_Carolina_CS27_North,	13901)
+ValuePair(Proj_South_Carolina_CS27_South,	13902)
+ValuePair(Proj_South_Carolina_CS83,	13930)
+ValuePair(Proj_South_Dakota_CS27_North,	14001)
+ValuePair(Proj_South_Dakota_CS27_South,	14002)
+ValuePair(Proj_South_Dakota_CS83_North,	14031)
+ValuePair(Proj_South_Dakota_CS83_South,	14032)
+ValuePair(Proj_Tennessee_CS27,	15302)
+ValuePair(Proj_Tennessee_CS83,	14130)
+ValuePair(Proj_Texas_CS27_North,	14201)
+ValuePair(Proj_Texas_CS27_North_Central,	14202)
+ValuePair(Proj_Texas_CS27_Central,	14203)
+ValuePair(Proj_Texas_CS27_South_Central,	14204)
+ValuePair(Proj_Texas_CS27_South,	14205)
+ValuePair(Proj_Texas_CS83_North,	14231)
+ValuePair(Proj_Texas_CS83_North_Central,	14232)
+ValuePair(Proj_Texas_CS83_Central,	14233)
+ValuePair(Proj_Texas_CS83_South_Central,	14234)
+ValuePair(Proj_Texas_CS83_South,	14235)
+ValuePair(Proj_Utah_CS27_North,	14301)
+ValuePair(Proj_Utah_CS27_Central,	14302)
+ValuePair(Proj_Utah_CS27_South,	14303)
+ValuePair(Proj_Utah_CS83_North,	14331)
+ValuePair(Proj_Utah_CS83_Central,	14332)
+ValuePair(Proj_Utah_CS83_South,	14333)
+ValuePair(Proj_Vermont_CS27,	14400)
+ValuePair(Proj_Vermont_CS83,	14430)
+ValuePair(Proj_Virginia_CS27_North,	14501)
+ValuePair(Proj_Virginia_CS27_South,	14502)
+ValuePair(Proj_Virginia_CS83_North,	14531)
+ValuePair(Proj_Virginia_CS83_South,	14532)
+ValuePair(Proj_Washington_CS27_North,	14601)
+ValuePair(Proj_Washington_CS27_South,	14602)
+ValuePair(Proj_Washington_CS83_North,	14631)
+ValuePair(Proj_Washington_CS83_South,	14632)
+ValuePair(Proj_West_Virginia_CS27_North,	14701)
+ValuePair(Proj_West_Virginia_CS27_South,	14702)
+ValuePair(Proj_West_Virginia_CS83_North,	14731)
+ValuePair(Proj_West_Virginia_CS83_South,	14732)
+ValuePair(Proj_Wisconsin_CS27_North,	14801)
+ValuePair(Proj_Wisconsin_CS27_Central,	14802)
+ValuePair(Proj_Wisconsin_CS27_South,	14803)
+ValuePair(Proj_Wisconsin_CS83_North,	14831)
+ValuePair(Proj_Wisconsin_CS83_Central,	14832)
+ValuePair(Proj_Wisconsin_CS83_South,	14833)
+ValuePair(Proj_Wyoming_CS27_East,	14901)
+ValuePair(Proj_Wyoming_CS27_East_Central,	14902)
+ValuePair(Proj_Wyoming_CS27_West_Central,	14903)
+ValuePair(Proj_Wyoming_CS27_West,	14904)
+ValuePair(Proj_Wyoming_CS83_East,	14931)
+ValuePair(Proj_Wyoming_CS83_East_Central,	14932)
+ValuePair(Proj_Wyoming_CS83_West_Central,	14933)
+ValuePair(Proj_Wyoming_CS83_West,	14934)
+ValuePair(Proj_Alaska_CS27_1,	15001)
+ValuePair(Proj_Alaska_CS27_2,	15002)
+ValuePair(Proj_Alaska_CS27_3,	15003)
+ValuePair(Proj_Alaska_CS27_4,	15004)
+ValuePair(Proj_Alaska_CS27_5,	15005)
+ValuePair(Proj_Alaska_CS27_6,	15006)
+ValuePair(Proj_Alaska_CS27_7,	15007)
+ValuePair(Proj_Alaska_CS27_8,	15008)
+ValuePair(Proj_Alaska_CS27_9,	15009)
+ValuePair(Proj_Alaska_CS27_10,	15010)
+ValuePair(Proj_Alaska_CS83_1,	15031)
+ValuePair(Proj_Alaska_CS83_2,	15032)
+ValuePair(Proj_Alaska_CS83_3,	15033)
+ValuePair(Proj_Alaska_CS83_4,	15034)
+ValuePair(Proj_Alaska_CS83_5,	15035)
+ValuePair(Proj_Alaska_CS83_6,	15036)
+ValuePair(Proj_Alaska_CS83_7,	15037)
+ValuePair(Proj_Alaska_CS83_8,	15038)
+ValuePair(Proj_Alaska_CS83_9,	15039)
+ValuePair(Proj_Alaska_CS83_10,	15040)
+ValuePair(Proj_Hawaii_CS27_1,	15101)
+ValuePair(Proj_Hawaii_CS27_2,	15102)
+ValuePair(Proj_Hawaii_CS27_3,	15103)
+ValuePair(Proj_Hawaii_CS27_4,	15104)
+ValuePair(Proj_Hawaii_CS27_5,	15105)
+ValuePair(Proj_Hawaii_CS83_1,	15131)
+ValuePair(Proj_Hawaii_CS83_2,	15132)
+ValuePair(Proj_Hawaii_CS83_3,	15133)
+ValuePair(Proj_Hawaii_CS83_4,	15134)
+ValuePair(Proj_Hawaii_CS83_5,	15135)
+ValuePair(Proj_Puerto_Rico_CS27,	15201)
+ValuePair(Proj_St_Croix,	15202)
+ValuePair(Proj_Puerto_Rico_Virgin_Is,	15230)
+ValuePair(Proj_BLM_14N_feet,	15914)
+ValuePair(Proj_BLM_15N_feet,	15915)
+ValuePair(Proj_BLM_16N_feet,	15916)
+ValuePair(Proj_BLM_17N_feet,	15917)
+ValuePair(Proj_UTM_zone_1N,  16001)
+ValuePair(Proj_UTM_zone_2N,  16002)
+ValuePair(Proj_UTM_zone_3N,  16003)
+ValuePair(Proj_UTM_zone_4N,  16004)
+ValuePair(Proj_UTM_zone_5N,  16005)
+ValuePair(Proj_UTM_zone_6N,  16006)
+ValuePair(Proj_UTM_zone_7N,  16007)
+ValuePair(Proj_UTM_zone_8N,  16008)
+ValuePair(Proj_UTM_zone_9N,  16009)
+ValuePair(Proj_UTM_zone_10N, 16010)
+ValuePair(Proj_UTM_zone_11N, 16011)
+ValuePair(Proj_UTM_zone_12N, 16012)
+ValuePair(Proj_UTM_zone_13N, 16013)
+ValuePair(Proj_UTM_zone_14N, 16014)
+ValuePair(Proj_UTM_zone_15N, 16015)
+ValuePair(Proj_UTM_zone_16N, 16016)
+ValuePair(Proj_UTM_zone_17N, 16017)
+ValuePair(Proj_UTM_zone_18N, 16018)
+ValuePair(Proj_UTM_zone_19N, 16019)
+ValuePair(Proj_UTM_zone_20N, 16020)
+ValuePair(Proj_UTM_zone_21N, 16021)
+ValuePair(Proj_UTM_zone_22N, 16022)
+ValuePair(Proj_UTM_zone_23N, 16023)
+ValuePair(Proj_UTM_zone_24N, 16024)
+ValuePair(Proj_UTM_zone_25N, 16025)
+ValuePair(Proj_UTM_zone_26N, 16026)
+ValuePair(Proj_UTM_zone_27N, 16027)
+ValuePair(Proj_UTM_zone_28N, 16028)
+ValuePair(Proj_UTM_zone_29N, 16029)
+ValuePair(Proj_UTM_zone_30N, 16030)
+ValuePair(Proj_UTM_zone_31N, 16031)
+ValuePair(Proj_UTM_zone_32N, 16032)
+ValuePair(Proj_UTM_zone_33N, 16033)
+ValuePair(Proj_UTM_zone_34N, 16034)
+ValuePair(Proj_UTM_zone_35N, 16035)
+ValuePair(Proj_UTM_zone_36N, 16036)
+ValuePair(Proj_UTM_zone_37N, 16037)
+ValuePair(Proj_UTM_zone_38N, 16038)
+ValuePair(Proj_UTM_zone_39N, 16039)
+ValuePair(Proj_UTM_zone_40N, 16040)
+ValuePair(Proj_UTM_zone_41N, 16041)
+ValuePair(Proj_UTM_zone_42N, 16042)
+ValuePair(Proj_UTM_zone_43N, 16043)
+ValuePair(Proj_UTM_zone_44N, 16044)
+ValuePair(Proj_UTM_zone_45N, 16045)
+ValuePair(Proj_UTM_zone_46N, 16046)
+ValuePair(Proj_UTM_zone_47N, 16047)
+ValuePair(Proj_UTM_zone_48N, 16048)
+ValuePair(Proj_UTM_zone_49N, 16049)
+ValuePair(Proj_UTM_zone_50N, 16050)
+ValuePair(Proj_UTM_zone_51N, 16051)
+ValuePair(Proj_UTM_zone_52N, 16052)
+ValuePair(Proj_UTM_zone_53N, 16053)
+ValuePair(Proj_UTM_zone_54N, 16054)
+ValuePair(Proj_UTM_zone_55N, 16055)
+ValuePair(Proj_UTM_zone_56N, 16056)
+ValuePair(Proj_UTM_zone_57N, 16057)
+ValuePair(Proj_UTM_zone_58N, 16058)
+ValuePair(Proj_UTM_zone_59N, 16059)
+ValuePair(Proj_UTM_zone_60N, 16060)
+ValuePair(Proj_UTM_zone_1S,  16101)
+ValuePair(Proj_UTM_zone_2S,  16102)
+ValuePair(Proj_UTM_zone_3S,  16103)
+ValuePair(Proj_UTM_zone_4S,  16104)
+ValuePair(Proj_UTM_zone_5S,  16105)
+ValuePair(Proj_UTM_zone_6S,  16106)
+ValuePair(Proj_UTM_zone_7S,  16107)
+ValuePair(Proj_UTM_zone_8S,  16108)
+ValuePair(Proj_UTM_zone_9S,  16109)
+ValuePair(Proj_UTM_zone_10S, 16110)
+ValuePair(Proj_UTM_zone_11S, 16111)
+ValuePair(Proj_UTM_zone_12S, 16112)
+ValuePair(Proj_UTM_zone_13S, 16113)
+ValuePair(Proj_UTM_zone_14S, 16114)
+ValuePair(Proj_UTM_zone_15S, 16115)
+ValuePair(Proj_UTM_zone_16S, 16116)
+ValuePair(Proj_UTM_zone_17S, 16117)
+ValuePair(Proj_UTM_zone_18S, 16118)
+ValuePair(Proj_UTM_zone_19S, 16119)
+ValuePair(Proj_UTM_zone_20S, 16120)
+ValuePair(Proj_UTM_zone_21S, 16121)
+ValuePair(Proj_UTM_zone_22S, 16122)
+ValuePair(Proj_UTM_zone_23S, 16123)
+ValuePair(Proj_UTM_zone_24S, 16124)
+ValuePair(Proj_UTM_zone_25S, 16125)
+ValuePair(Proj_UTM_zone_26S, 16126)
+ValuePair(Proj_UTM_zone_27S, 16127)
+ValuePair(Proj_UTM_zone_28S, 16128)
+ValuePair(Proj_UTM_zone_29S, 16129)
+ValuePair(Proj_UTM_zone_30S, 16130)
+ValuePair(Proj_UTM_zone_31S, 16131)
+ValuePair(Proj_UTM_zone_32S, 16132)
+ValuePair(Proj_UTM_zone_33S, 16133)
+ValuePair(Proj_UTM_zone_34S, 16134)
+ValuePair(Proj_UTM_zone_35S, 16135)
+ValuePair(Proj_UTM_zone_36S, 16136)
+ValuePair(Proj_UTM_zone_37S, 16137)
+ValuePair(Proj_UTM_zone_38S, 16138)
+ValuePair(Proj_UTM_zone_39S, 16139)
+ValuePair(Proj_UTM_zone_40S, 16140)
+ValuePair(Proj_UTM_zone_41S, 16141)
+ValuePair(Proj_UTM_zone_42S, 16142)
+ValuePair(Proj_UTM_zone_43S, 16143)
+ValuePair(Proj_UTM_zone_44S, 16144)
+ValuePair(Proj_UTM_zone_45S, 16145)
+ValuePair(Proj_UTM_zone_46S, 16146)
+ValuePair(Proj_UTM_zone_47S, 16147)
+ValuePair(Proj_UTM_zone_48S, 16148)
+ValuePair(Proj_UTM_zone_49S, 16149)
+ValuePair(Proj_UTM_zone_50S, 16150)
+ValuePair(Proj_UTM_zone_51S, 16151)
+ValuePair(Proj_UTM_zone_52S, 16152)
+ValuePair(Proj_UTM_zone_53S, 16153)
+ValuePair(Proj_UTM_zone_54S, 16154)
+ValuePair(Proj_UTM_zone_55S, 16155)
+ValuePair(Proj_UTM_zone_56S, 16156)
+ValuePair(Proj_UTM_zone_57S, 16157)
+ValuePair(Proj_UTM_zone_58S, 16158)
+ValuePair(Proj_UTM_zone_59S, 16159)
+ValuePair(Proj_UTM_zone_60S, 16160)
+ValuePair(Proj_Gauss_Kruger_zone_0, 16200)
+ValuePair(Proj_Gauss_Kruger_zone_1, 16201)
+ValuePair(Proj_Gauss_Kruger_zone_2, 16202)
+ValuePair(Proj_Gauss_Kruger_zone_3, 16203)
+ValuePair(Proj_Gauss_Kruger_zone_4, 16204)
+ValuePair(Proj_Gauss_Kruger_zone_5, 16205)
+ValuePair(Proj_Map_Grid_of_Australia_48,	17348)
+ValuePair(Proj_Map_Grid_of_Australia_49,	17349)
+ValuePair(Proj_Map_Grid_of_Australia_50,	17350)
+ValuePair(Proj_Map_Grid_of_Australia_51,	17351)
+ValuePair(Proj_Map_Grid_of_Australia_52,	17352)
+ValuePair(Proj_Map_Grid_of_Australia_53,	17353)
+ValuePair(Proj_Map_Grid_of_Australia_54,	17354)
+ValuePair(Proj_Map_Grid_of_Australia_55,	17355)
+ValuePair(Proj_Map_Grid_of_Australia_56,	17356)
+ValuePair(Proj_Map_Grid_of_Australia_57,	17357)
+ValuePair(Proj_Map_Grid_of_Australia_58,	17358)
+ValuePair(Proj_Australian_Map_Grid_48,	17448)
+ValuePair(Proj_Australian_Map_Grid_49,	17449)
+ValuePair(Proj_Australian_Map_Grid_50,	17450)
+ValuePair(Proj_Australian_Map_Grid_51,	17451)
+ValuePair(Proj_Australian_Map_Grid_52,	17452)
+ValuePair(Proj_Australian_Map_Grid_53,	17453)
+ValuePair(Proj_Australian_Map_Grid_54,	17454)
+ValuePair(Proj_Australian_Map_Grid_55,	17455)
+ValuePair(Proj_Australian_Map_Grid_56,	17456)
+ValuePair(Proj_Australian_Map_Grid_57,	17457)
+ValuePair(Proj_Australian_Map_Grid_58,	17458)
+ValuePair(Proj_Argentina_1,	18031)
+ValuePair(Proj_Argentina_2,	18032)
+ValuePair(Proj_Argentina_3,	18033)
+ValuePair(Proj_Argentina_4,	18034)
+ValuePair(Proj_Argentina_5,	18035)
+ValuePair(Proj_Argentina_6,	18036)
+ValuePair(Proj_Argentina_7,	18037)
+ValuePair(Proj_Colombia_3W,	18051)
+ValuePair(Proj_Colombia_Bogota,	18052)
+ValuePair(Proj_Colombia_3E,	18053)
+ValuePair(Proj_Colombia_6E,	18054)
+ValuePair(Proj_Egypt_Red_Belt,	18072)
+ValuePair(Proj_Egypt_Purple_Belt,	18073)
+ValuePair(Proj_Extended_Purple_Belt,	18074)
+ValuePair(Proj_New_Zealand_North_Island_Nat_Grid,	18141)
+ValuePair(Proj_New_Zealand_South_Island_Nat_Grid,	18142)
+ValuePair(Proj_Bahrain_Grid,	19900)
+ValuePair(Proj_Netherlands_E_Indies_Equatorial,	19905)
+ValuePair(Proj_RSO_Borneo,	19912)
+/* end of list */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_units.inc b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_units.inc
new file mode 100644
index 0000000000..fe1b5db7bf
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_units.inc
@@ -0,0 +1,35 @@
+/*
+ * Rev. 0.2 EPSG/POSC Units Database.
+ */
+ 
+#ifdef INCLUDE_OLD_CODES
+#include geo_units.inc
+#endif /* OLD Codes */
+
+ValuePair(Linear_Meter,	9001)
+ValuePair(Linear_Foot,	9002)
+ValuePair(Linear_Foot_US_Survey,	9003)
+ValuePair(Linear_Foot_Modified_American,	9004)
+ValuePair(Linear_Foot_Clarke,	9005)
+ValuePair(Linear_Foot_Indian,	9006)
+ValuePair(Linear_Link,	9007)
+ValuePair(Linear_Link_Benoit,	9008)
+ValuePair(Linear_Link_Sears,	9009)
+ValuePair(Linear_Chain_Benoit,	9010)
+ValuePair(Linear_Chain_Sears,	9011)
+ValuePair(Linear_Yard_Sears,	9012)
+ValuePair(Linear_Yard_Indian,	9013)
+ValuePair(Linear_Fathom,	9014)
+ValuePair(Linear_Mile_International_Nautical,	9015)
+/*
+ *  Angular Units
+ */
+ValuePair(Angular_Radian,	9101)
+ValuePair(Angular_Degree,	9102)
+ValuePair(Angular_Arc_Minute,	9103)
+ValuePair(Angular_Arc_Second,	9104)
+ValuePair(Angular_Grad,	9105)
+ValuePair(Angular_Gon,	9106)
+ValuePair(Angular_DMS,	9107)
+ValuePair(Angular_DMS_Hemisphere,	9108)
+/* end of list */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_vertcs.inc b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_vertcs.inc
new file mode 100644
index 0000000000..d6b6791cb3
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/epsg_vertcs.inc
@@ -0,0 +1,46 @@
+/*
+ *  EPSG/POSC Ellipsoid-referenced Vertical CS
+ *   Note: these should correspond exactly with the Ellipsoid database.
+ */
+ValuePair(VertCS_Airy_1830_ellipsoid,	5001)
+ValuePair(VertCS_Airy_Modified_1849_ellipsoid,	5002)
+ValuePair(VertCS_ANS_ellipsoid,	5003)
+ValuePair(VertCS_Bessel_1841_ellipsoid,	5004)
+ValuePair(VertCS_Bessel_Modified_ellipsoid,	5005)
+ValuePair(VertCS_Bessel_Namibia_ellipsoid,	5006)
+ValuePair(VertCS_Clarke_1858_ellipsoid,	5007)
+ValuePair(VertCS_Clarke_1866_ellipsoid,	5008)
+ValuePair(VertCS_Clarke_1880_Benoit_ellipsoid,	5010)
+ValuePair(VertCS_Clarke_1880_IGN_ellipsoid,	5011)
+ValuePair(VertCS_Clarke_1880_RGS_ellipsoid,	5012)
+ValuePair(VertCS_Clarke_1880_Arc_ellipsoid,	5013)
+ValuePair(VertCS_Clarke_1880_SGA_1922_ellipsoid,	5014)
+ValuePair(VertCS_Everest_1830_1937_Adjustment_ellipsoid,	5015)
+ValuePair(VertCS_Everest_1830_1967_Definition_ellipsoid,	5016)
+ValuePair(VertCS_Everest_1830_1975_Definition_ellipsoid,	5017)
+ValuePair(VertCS_Everest_1830_Modified_ellipsoid,	5018)
+ValuePair(VertCS_GRS_1980_ellipsoid,	5019)
+ValuePair(VertCS_Helmert_1906_ellipsoid,	5020)
+ValuePair(VertCS_INS_ellipsoid,	5021)
+ValuePair(VertCS_International_1924_ellipsoid,	5022)
+ValuePair(VertCS_International_1967_ellipsoid,	5023)
+ValuePair(VertCS_Krassowsky_1940_ellipsoid,	5024)
+ValuePair(VertCS_NWL_9D_ellipsoid,	5025)
+ValuePair(VertCS_NWL_10D_ellipsoid,	5026)
+ValuePair(VertCS_Plessis_1817_ellipsoid,	5027)
+ValuePair(VertCS_Struve_1860_ellipsoid,	5028)
+ValuePair(VertCS_War_Office_ellipsoid,	5029)
+ValuePair(VertCS_WGS_84_ellipsoid,	5030)
+ValuePair(VertCS_GEM_10C_ellipsoid,	5031)
+ValuePair(VertCS_OSU86F_ellipsoid,	5032)
+ValuePair(VertCS_OSU91A_ellipsoid,	5033)
+/*
+ *  Other established Vertical CS
+ */
+ValuePair(VertCS_Newlyn,	5101)
+ValuePair(VertCS_North_American_Vertical_Datum_1929,	5102)
+ValuePair(VertCS_North_American_Vertical_Datum_1988,	5103)
+ValuePair(VertCS_Yellow_Sea_1956,	5104)
+ValuePair(VertCS_Baltic_Sea,	5105)
+ValuePair(VertCS_Caspian_Sea,	5106)
+/* end of list */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_config.h b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_config.h
new file mode 100644
index 0000000000..3a19fb4fe1
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_config.h
@@ -0,0 +1,7 @@
+/* geo_config.h.  Generated automatically by configure.  */
+#ifndef GEO_CONFIG_H
+#define GEO_CONFIG_H
+
+#include "cpl_config.h"
+
+#endif /* ndef GEO_CONFIG_H */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_ctrans.inc b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_ctrans.inc
new file mode 100644
index 0000000000..5ebc643dc9
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_ctrans.inc
@@ -0,0 +1,91 @@
+/******************************************************************************
+ * $Id: geo_ctrans.inc,v 1.3 2005/03/04 03:59:11 fwarmerdam Exp $
+ *
+ * Project:  libgeotiff
+ * Purpose:  GeoTIFF Projection Method codes. 
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: geo_ctrans.inc,v $
+ * Revision 1.3  2005/03/04 03:59:11  fwarmerdam
+ * Added header.
+ *
+ */
+
+/* C database for Geotiff include files.   */
+/* the macro ValuePair() must be defined   */
+/* by the enclosing include file           */
+
+/*
+ *  Revised 12 Jul 1995   NDR -- changed South Oriented to a code 
+ *  Revised 28 Sep 1995   NDR -- Added Rev. 1.0 aliases. 
+ */
+
+ValuePair(CT_TransverseMercator,	1)
+ValuePair(CT_TransvMercator_Modified_Alaska, 2)
+ValuePair(CT_ObliqueMercator,	3)
+ValuePair(CT_ObliqueMercator_Laborde,	4)
+ValuePair(CT_ObliqueMercator_Rosenmund,	5)
+ValuePair(CT_ObliqueMercator_Spherical,	6)   /* not advisable */
+ValuePair(CT_Mercator,	7)
+ValuePair(CT_LambertConfConic_2SP,	8)
+ValuePair(CT_LambertConfConic,CT_LambertConfConic_2SP)         /* Alias */
+ValuePair(CT_LambertConfConic_1SP,	9)
+ValuePair(CT_LambertConfConic_Helmert,CT_LambertConfConic_1SP) /* alias */
+ValuePair(CT_LambertAzimEqualArea,	10)
+ValuePair(CT_AlbersEqualArea,	11)
+ValuePair(CT_AzimuthalEquidistant,	12)
+ValuePair(CT_EquidistantConic,	13)
+ValuePair(CT_Stereographic,	14)
+ValuePair(CT_PolarStereographic,	15)
+ValuePair(CT_ObliqueStereographic,	16)   /* not advisable */
+ValuePair(CT_Equirectangular,	17)
+ValuePair(CT_CassiniSoldner,	18)
+ValuePair(CT_Gnomonic,	19)
+ValuePair(CT_MillerCylindrical,	20)
+ValuePair(CT_Orthographic,	21)
+ValuePair(CT_Polyconic,	22)
+ValuePair(CT_Robinson,	23)
+ValuePair(CT_Sinusoidal,	24)
+ValuePair(CT_VanDerGrinten,	25)
+ValuePair(CT_NewZealandMapGrid,	26)
+/* Added for 1.0 */
+ValuePair(CT_TransvMercator_SouthOrientated, 27)
+
+/* Added Feb 2005 */
+ValuePair(CT_CylindricalEqualArea, 28)
+
+
+/* Aliases */
+
+ValuePair(CT_SouthOrientedGaussConformal,CT_TransvMercator_SouthOrientated)
+ValuePair(CT_AlaskaConformal,	CT_TransvMercator_Modified_Alaska)
+ValuePair(CT_TransvEquidistCylindrical,	CT_CassiniSoldner)
+ValuePair(CT_ObliqueMercator_Hotine,	CT_ObliqueMercator)
+ValuePair(CT_SwissObliqueCylindrical,	CT_ObliqueMercator_Rosenmund)
+ValuePair(CT_GaussBoaga,	CT_TransverseMercator)
+ValuePair(CT_GaussKruger,	CT_TransverseMercator)
+ValuePair(CT_TransvMercator_SouthOriented, CT_TransvMercator_SouthOrientated)
+
+
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_extra.c b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_extra.c
new file mode 100644
index 0000000000..75729595d7
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_extra.c
@@ -0,0 +1,747 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  libgeotiff
+ * Purpose:  Code to normalize a few common PCS values without use of CSV
+ *           files.
+ * Author:   Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: geo_extra.c,v $
+ * Revision 1.4  2002/12/01 23:44:34  warmerda
+ * Fixed typo in last fix.
+ *
+ * Revision 1.3  2002/12/01 23:42:06  warmerda
+ * added overrides for two deprecated stateplane zones
+ *
+ * Revision 1.2  1999/05/04 03:09:33  warmerda
+ * avoid warnings
+ *
+ * Revision 1.1  1999/04/28 20:01:29  warmerda
+ * new
+ *
+ */
+
+/*
+#include "geotiff.h"
+#include "geo_tiffp.h"
+#include "geo_keyp.h"
+*/
+
+#include "geo_normalize.h"
+#include "geovalues.h"
+
+static int StatePlaneTable[] = 
+{
+    PCS_NAD83_Alabama_East,		Proj_Alabama_CS83_East,
+    PCS_NAD83_Alabama_West,		Proj_Alabama_CS83_West,
+
+    PCS_NAD83_Alaska_zone_1,		Proj_Alaska_CS83_1,
+    PCS_NAD83_Alaska_zone_2,		Proj_Alaska_CS83_2,
+    PCS_NAD83_Alaska_zone_3,		Proj_Alaska_CS83_3,
+    PCS_NAD83_Alaska_zone_4,		Proj_Alaska_CS83_4,
+    PCS_NAD83_Alaska_zone_5,		Proj_Alaska_CS83_5,
+    PCS_NAD83_Alaska_zone_6,		Proj_Alaska_CS83_6,
+    PCS_NAD83_Alaska_zone_7,		Proj_Alaska_CS83_7,
+    PCS_NAD83_Alaska_zone_8,		Proj_Alaska_CS83_8,
+    PCS_NAD83_Alaska_zone_9,		Proj_Alaska_CS83_9,
+    PCS_NAD83_Alaska_zone_10,		Proj_Alaska_CS83_10,
+
+    PCS_NAD83_California_1,		Proj_California_CS83_1,
+    PCS_NAD83_California_2,		Proj_California_CS83_2,
+    PCS_NAD83_California_3,		Proj_California_CS83_3,
+    PCS_NAD83_California_4,		Proj_California_CS83_4,
+    PCS_NAD83_California_5,		Proj_California_CS83_5,
+    PCS_NAD83_California_6,		Proj_California_CS83_6,
+
+    PCS_NAD83_Arizona_East,		Proj_Arizona_CS83_east,
+    PCS_NAD83_Arizona_Central,		Proj_Arizona_CS83_Central,
+    PCS_NAD83_Arizona_West,		Proj_Arizona_CS83_west,
+
+    PCS_NAD83_Arkansas_North,		Proj_Arkansas_CS83_North,
+    PCS_NAD83_Arkansas_South,		Proj_Arkansas_CS83_South,
+
+    PCS_NAD83_Colorado_North,		Proj_Colorado_CS83_North,
+    PCS_NAD83_Colorado_Central,		Proj_Colorado_CS83_Central,
+    PCS_NAD83_Colorado_South,		Proj_Colorado_CS83_South,
+
+    PCS_NAD83_Connecticut,		Proj_Connecticut_CS83,
+
+    PCS_NAD83_Delaware,			Proj_Delaware_CS83,
+
+    PCS_NAD83_Florida_East,		Proj_Florida_CS83_East,
+    PCS_NAD83_Florida_North,		Proj_Florida_CS83_North,
+    PCS_NAD83_Florida_West,		Proj_Florida_CS83_West,
+
+    PCS_NAD83_Hawaii_zone_1,		Proj_Hawaii_CS83_1,
+    PCS_NAD83_Hawaii_zone_2,		Proj_Hawaii_CS83_2,
+    PCS_NAD83_Hawaii_zone_3,		Proj_Hawaii_CS83_3,
+    PCS_NAD83_Hawaii_zone_4,		Proj_Hawaii_CS83_4,
+    PCS_NAD83_Hawaii_zone_5,		Proj_Hawaii_CS83_5,
+
+    PCS_NAD83_Georgia_East,		Proj_Georgia_CS83_East,
+    PCS_NAD83_Georgia_West,		Proj_Georgia_CS83_West,
+
+    PCS_NAD83_Idaho_East,		Proj_Idaho_CS83_East,
+    PCS_NAD83_Idaho_Central,		Proj_Idaho_CS83_Central,
+    PCS_NAD83_Idaho_West,		Proj_Idaho_CS83_West,
+
+    PCS_NAD83_Illinois_East,		Proj_Illinois_CS83_East,
+    PCS_NAD83_Illinois_West,		Proj_Illinois_CS83_West,
+
+    PCS_NAD83_Indiana_East,		Proj_Indiana_CS83_East,
+    PCS_NAD83_Indiana_West,		Proj_Indiana_CS83_West,
+
+    PCS_NAD83_Iowa_North,      		Proj_Iowa_CS83_North,
+    PCS_NAD83_Iowa_South,      		Proj_Iowa_CS83_South,
+
+    PCS_NAD83_Kansas_North,		Proj_Kansas_CS83_North,
+    PCS_NAD83_Kansas_South,		Proj_Kansas_CS83_South,
+
+    PCS_NAD83_Kentucky_North,		Proj_Kentucky_CS83_North,
+    PCS_NAD83_Kentucky_South,		Proj_Kentucky_CS83_South,
+
+    PCS_NAD83_Louisiana_North,		Proj_Louisiana_CS83_North,
+    PCS_NAD83_Louisiana_South,		Proj_Louisiana_CS83_South,
+
+    PCS_NAD83_Maine_East,		Proj_Maine_CS83_East,
+    PCS_NAD83_Maine_West,		Proj_Maine_CS83_West,
+
+    PCS_NAD83_Maryland,			Proj_Maryland_CS83,
+
+    PCS_NAD83_Massachusetts,		Proj_Massachusetts_CS83_Mainland,
+    PCS_NAD83_Massachusetts_Is,		Proj_Massachusetts_CS83_Island,
+
+    PCS_NAD83_Michigan_North,		Proj_Michigan_CS83_North,
+    PCS_NAD83_Michigan_Central,		Proj_Michigan_CS83_Central,
+    PCS_NAD83_Michigan_South,		Proj_Michigan_CS83_South,
+
+    PCS_NAD83_Minnesota_North,		Proj_Minnesota_CS83_North,
+    PCS_NAD83_Minnesota_Cent,		Proj_Minnesota_CS83_Central,
+    PCS_NAD83_Minnesota_South,		Proj_Minnesota_CS83_South,
+
+    PCS_NAD83_Mississippi_East,		Proj_Mississippi_CS83_East,
+    PCS_NAD83_Mississippi_West,		Proj_Mississippi_CS83_West,
+
+    PCS_NAD83_Missouri_East,		Proj_Missouri_CS83_East,
+    PCS_NAD83_Missouri_Central,		Proj_Missouri_CS83_Central,
+    PCS_NAD83_Missouri_West,		Proj_Missouri_CS83_West,
+
+    PCS_NAD83_Montana,			Proj_Montana_CS83,
+
+    PCS_NAD83_Nebraska,			Proj_Nebraska_CS83,
+
+    PCS_NAD83_Nevada_East,		Proj_Nevada_CS83_East,
+    PCS_NAD83_Nevada_Central,		Proj_Nevada_CS83_Central,
+    PCS_NAD83_Nevada_West,		Proj_Nevada_CS83_West,
+
+    PCS_NAD83_New_Hampshire,		Proj_New_Hampshire_CS83,
+
+    PCS_NAD83_New_Jersey,		Proj_New_Jersey_CS83,
+
+    PCS_NAD83_New_Mexico_East,		Proj_New_Mexico_CS83_East,
+    PCS_NAD83_New_Mexico_Cent,		Proj_New_Mexico_CS83_Central,
+    PCS_NAD83_New_Mexico_West,		Proj_New_Mexico_CS83_West,
+
+    PCS_NAD83_New_York_East,		Proj_New_York_CS83_East,
+    PCS_NAD83_New_York_Central,		Proj_New_York_CS83_Central,
+    PCS_NAD83_New_York_West,		Proj_New_York_CS83_West,
+    PCS_NAD83_New_York_Long_Is,		Proj_New_York_CS83_Long_Island,
+
+    PCS_NAD83_North_Carolina,	       	Proj_North_Carolina_CS83,
+
+    PCS_NAD83_North_Dakota_N,		Proj_North_Dakota_CS83_North,
+    PCS_NAD83_North_Dakota_S,		Proj_North_Dakota_CS83_South,
+
+    PCS_NAD83_Ohio_North,		Proj_Ohio_CS83_North,
+    PCS_NAD83_Ohio_South,		Proj_Ohio_CS83_South,
+
+    PCS_NAD83_Oklahoma_North,		Proj_Oklahoma_CS83_North,
+    PCS_NAD83_Oklahoma_South,		Proj_Oklahoma_CS83_South,
+
+    PCS_NAD83_Oregon_North,		Proj_Oregon_CS83_North,
+    PCS_NAD83_Oregon_South,		Proj_Oregon_CS83_South,
+
+    PCS_NAD83_Pennsylvania_N,		Proj_Pennsylvania_CS83_North,
+    PCS_NAD83_Pennsylvania_S,		Proj_Pennsylvania_CS83_South,
+
+    PCS_NAD83_Rhode_Island,		Proj_Rhode_Island_CS83,
+
+    PCS_NAD83_South_Carolina,		Proj_South_Carolina_CS83,
+
+    PCS_NAD83_South_Dakota_N,		Proj_South_Dakota_CS83_North,
+    PCS_NAD83_South_Dakota_S,		Proj_South_Dakota_CS83_South,
+
+    PCS_NAD83_Tennessee,		Proj_Tennessee_CS83,
+
+    PCS_NAD83_Texas_North,		Proj_Texas_CS83_North,
+    PCS_NAD83_Texas_North_Cen,		Proj_Texas_CS83_North_Central,
+    PCS_NAD83_Texas_Central,		Proj_Texas_CS83_Central,
+    PCS_NAD83_Texas_South_Cen,		Proj_Texas_CS83_South_Central,
+    PCS_NAD83_Texas_South,		Proj_Texas_CS83_South,
+
+    PCS_NAD83_Utah_North,		Proj_Utah_CS83_North,
+    PCS_NAD83_Utah_Central,		Proj_Utah_CS83_Central,
+    PCS_NAD83_Utah_South,		Proj_Utah_CS83_South,
+
+    PCS_NAD83_Vermont,			Proj_Vermont_CS83,
+
+    PCS_NAD83_Virginia_North,		Proj_Virginia_CS83_North,
+    PCS_NAD83_Virginia_South,		Proj_Virginia_CS83_South,
+
+    PCS_NAD83_Washington_North,		Proj_Washington_CS83_North,
+    PCS_NAD83_Washington_South,		Proj_Washington_CS83_South,
+
+    PCS_NAD83_West_Virginia_N,		Proj_West_Virginia_CS83_North,
+    PCS_NAD83_West_Virginia_S,		Proj_West_Virginia_CS83_South,
+
+    PCS_NAD83_Wisconsin_North,		Proj_Wisconsin_CS83_North,
+    PCS_NAD83_Wisconsin_Cen,		Proj_Wisconsin_CS83_Central,
+    PCS_NAD83_Wisconsin_South,		Proj_Wisconsin_CS83_South,
+
+    PCS_NAD83_Wyoming_East,		Proj_Wyoming_CS83_East,
+    PCS_NAD83_Wyoming_E_Cen,		Proj_Wyoming_CS83_East_Central,
+    PCS_NAD83_Wyoming_W_Cen,		Proj_Wyoming_CS83_West_Central,
+    PCS_NAD83_Wyoming_West,		Proj_Wyoming_CS83_West,
+    
+    PCS_NAD83_Puerto_Rico_Virgin_Is,	Proj_Puerto_Rico_Virgin_Is,
+
+    PCS_NAD27_Alabama_East,		Proj_Alabama_CS27_East,
+    PCS_NAD27_Alabama_West,		Proj_Alabama_CS27_West,
+
+    PCS_NAD27_Alaska_zone_1,		Proj_Alaska_CS27_1,
+    PCS_NAD27_Alaska_zone_2,		Proj_Alaska_CS27_2,
+    PCS_NAD27_Alaska_zone_3,		Proj_Alaska_CS27_3,
+    PCS_NAD27_Alaska_zone_4,		Proj_Alaska_CS27_4,
+    PCS_NAD27_Alaska_zone_5,		Proj_Alaska_CS27_5,
+    PCS_NAD27_Alaska_zone_6,		Proj_Alaska_CS27_6,
+    PCS_NAD27_Alaska_zone_7,		Proj_Alaska_CS27_7,
+    PCS_NAD27_Alaska_zone_8,		Proj_Alaska_CS27_8,
+    PCS_NAD27_Alaska_zone_9,		Proj_Alaska_CS27_9,
+    PCS_NAD27_Alaska_zone_10,		Proj_Alaska_CS27_10,
+
+    PCS_NAD27_California_I,		Proj_California_CS27_I,
+    PCS_NAD27_California_II,		Proj_California_CS27_II,
+    PCS_NAD27_California_III,		Proj_California_CS27_III,
+    PCS_NAD27_California_IV,		Proj_California_CS27_IV,
+    PCS_NAD27_California_V,		Proj_California_CS27_V,
+    PCS_NAD27_California_VI,		Proj_California_CS27_VI,
+    PCS_NAD27_California_VII,		Proj_California_CS27_VII,
+
+    PCS_NAD27_Arizona_East,		Proj_Arizona_Coordinate_System_east,
+    PCS_NAD27_Arizona_Central,		Proj_Arizona_Coordinate_System_Central,
+    PCS_NAD27_Arizona_West,		Proj_Arizona_Coordinate_System_west,
+
+    PCS_NAD27_Arkansas_North,		Proj_Arkansas_CS27_North,
+    PCS_NAD27_Arkansas_South,		Proj_Arkansas_CS27_South,
+
+    PCS_NAD27_Colorado_North,		Proj_Colorado_CS27_North,
+    PCS_NAD27_Colorado_Central,		Proj_Colorado_CS27_Central,
+    PCS_NAD27_Colorado_South,		Proj_Colorado_CS27_South,
+
+    PCS_NAD27_Connecticut,		Proj_Connecticut_CS27,
+
+    PCS_NAD27_Delaware,			Proj_Delaware_CS27,
+
+    PCS_NAD27_Florida_East,		Proj_Florida_CS27_East,
+    PCS_NAD27_Florida_North,		Proj_Florida_CS27_North,
+    PCS_NAD27_Florida_West,		Proj_Florida_CS27_West,
+
+    PCS_NAD27_Hawaii_zone_1,		Proj_Hawaii_CS27_1,
+    PCS_NAD27_Hawaii_zone_2,		Proj_Hawaii_CS27_2,
+    PCS_NAD27_Hawaii_zone_3,		Proj_Hawaii_CS27_3,
+    PCS_NAD27_Hawaii_zone_4,		Proj_Hawaii_CS27_4,
+    PCS_NAD27_Hawaii_zone_5,		Proj_Hawaii_CS27_5,
+
+    PCS_NAD27_Georgia_East,		Proj_Georgia_CS27_East,
+    PCS_NAD27_Georgia_West,		Proj_Georgia_CS27_West,
+
+    PCS_NAD27_Idaho_East,		Proj_Idaho_CS27_East,
+    PCS_NAD27_Idaho_Central,		Proj_Idaho_CS27_Central,
+    PCS_NAD27_Idaho_West,		Proj_Idaho_CS27_West,
+
+    PCS_NAD27_Illinois_East,		Proj_Illinois_CS27_East,
+    PCS_NAD27_Illinois_West,		Proj_Illinois_CS27_West,
+
+    PCS_NAD27_Indiana_East,		Proj_Indiana_CS27_East,
+    PCS_NAD27_Indiana_West,		Proj_Indiana_CS27_West,
+
+    PCS_NAD27_Iowa_North,      		Proj_Iowa_CS27_North,
+    PCS_NAD27_Iowa_South,      		Proj_Iowa_CS27_South,
+
+    PCS_NAD27_Kansas_North,		Proj_Kansas_CS27_North,
+    PCS_NAD27_Kansas_South,		Proj_Kansas_CS27_South,
+
+    PCS_NAD27_Kentucky_North,		Proj_Kentucky_CS27_North,
+    PCS_NAD27_Kentucky_South,		Proj_Kentucky_CS27_South,
+
+    PCS_NAD27_Louisiana_North,		Proj_Louisiana_CS27_North,
+    PCS_NAD27_Louisiana_South,		Proj_Louisiana_CS27_South,
+
+    PCS_NAD27_Maine_East,		Proj_Maine_CS27_East,
+    PCS_NAD27_Maine_West,		Proj_Maine_CS27_West,
+
+    PCS_NAD27_Maryland,			Proj_Maryland_CS27,
+
+    PCS_NAD27_Massachusetts,		Proj_Massachusetts_CS27_Mainland,
+    PCS_NAD27_Massachusetts_Is,		Proj_Massachusetts_CS27_Island,
+
+    PCS_NAD27_Michigan_North,		Proj_Michigan_CS27_North,
+    PCS_NAD27_Michigan_Central,		Proj_Michigan_CS27_Central,
+    PCS_NAD27_Michigan_South,		Proj_Michigan_CS27_South,
+
+    PCS_NAD27_Minnesota_North,		Proj_Minnesota_CS27_North,
+    PCS_NAD27_Minnesota_Cent,		Proj_Minnesota_CS27_Central,
+    PCS_NAD27_Minnesota_South,		Proj_Minnesota_CS27_South,
+
+    PCS_NAD27_Mississippi_East,		Proj_Mississippi_CS27_East,
+    PCS_NAD27_Mississippi_West,		Proj_Mississippi_CS27_West,
+
+    PCS_NAD27_Missouri_East,		Proj_Missouri_CS27_East,
+    PCS_NAD27_Missouri_Central,		Proj_Missouri_CS27_Central,
+    PCS_NAD27_Missouri_West,		Proj_Missouri_CS27_West,
+
+    PCS_NAD27_Montana_North,		Proj_Montana_CS27_North,
+    PCS_NAD27_Montana_Central,		Proj_Montana_CS27_Central,
+    PCS_NAD27_Montana_South,		Proj_Montana_CS27_South,
+
+    PCS_NAD27_Nebraska_North,		Proj_Nebraska_CS27_North,
+    PCS_NAD27_Nebraska_South,		Proj_Nebraska_CS27_South,
+
+    PCS_NAD27_Nevada_East,		Proj_Nevada_CS27_East,
+    PCS_NAD27_Nevada_Central,		Proj_Nevada_CS27_Central,
+    PCS_NAD27_Nevada_West,		Proj_Nevada_CS27_West,
+
+    PCS_NAD27_New_Hampshire,		Proj_New_Hampshire_CS27,
+
+    PCS_NAD27_New_Jersey,		Proj_New_Jersey_CS27,
+
+    PCS_NAD27_New_Mexico_East,		Proj_New_Mexico_CS27_East,
+    PCS_NAD27_New_Mexico_Cent,		Proj_New_Mexico_CS27_Central,
+    PCS_NAD27_New_Mexico_West,		Proj_New_Mexico_CS27_West,
+
+    PCS_NAD27_New_York_East,		Proj_New_York_CS27_East,
+    PCS_NAD27_New_York_Central,		Proj_New_York_CS27_Central,
+    PCS_NAD27_New_York_West,		Proj_New_York_CS27_West,
+    PCS_NAD27_New_York_Long_Is,		Proj_New_York_CS27_Long_Island,
+
+    PCS_NAD27_North_Carolina,	       	Proj_North_Carolina_CS27,
+
+    PCS_NAD27_North_Dakota_N,		Proj_North_Dakota_CS27_North,
+    PCS_NAD27_North_Dakota_S,		Proj_North_Dakota_CS27_South,
+
+    PCS_NAD27_Ohio_North,		Proj_Ohio_CS27_North,
+    PCS_NAD27_Ohio_South,		Proj_Ohio_CS27_South,
+
+    PCS_NAD27_Oklahoma_North,		Proj_Oklahoma_CS27_North,
+    PCS_NAD27_Oklahoma_South,		Proj_Oklahoma_CS27_South,
+
+    PCS_NAD27_Oregon_North,		Proj_Oregon_CS27_North,
+    PCS_NAD27_Oregon_South,		Proj_Oregon_CS27_South,
+
+    PCS_NAD27_Pennsylvania_N,		Proj_Pennsylvania_CS27_North,
+    PCS_NAD27_Pennsylvania_S,		Proj_Pennsylvania_CS27_South,
+
+    PCS_NAD27_Rhode_Island,		Proj_Rhode_Island_CS27,
+
+    PCS_NAD27_South_Carolina_N,		Proj_South_Carolina_CS27_North,
+    PCS_NAD27_South_Carolina_S,		Proj_South_Carolina_CS27_South,
+
+    PCS_NAD27_South_Dakota_N,		Proj_South_Dakota_CS27_North,
+    PCS_NAD27_South_Dakota_S,		Proj_South_Dakota_CS27_South,
+
+    PCS_NAD27_Tennessee,		Proj_Tennessee_CS27,
+
+    PCS_NAD27_Texas_North,		Proj_Texas_CS27_North,
+    PCS_NAD27_Texas_North_Cen,		Proj_Texas_CS27_North_Central,
+    PCS_NAD27_Texas_Central,		Proj_Texas_CS27_Central,
+    PCS_NAD27_Texas_South_Cen,		Proj_Texas_CS27_South_Central,
+    PCS_NAD27_Texas_South,		Proj_Texas_CS27_South,
+
+    PCS_NAD27_Utah_North,		Proj_Utah_CS27_North,
+    PCS_NAD27_Utah_Central,		Proj_Utah_CS27_Central,
+    PCS_NAD27_Utah_South,		Proj_Utah_CS27_South,
+
+    PCS_NAD27_Vermont,			Proj_Vermont_CS27,
+
+    PCS_NAD27_Virginia_North,		Proj_Virginia_CS27_North,
+    PCS_NAD27_Virginia_South,		Proj_Virginia_CS27_South,
+
+    PCS_NAD27_Washington_North,		Proj_Washington_CS27_North,
+    PCS_NAD27_Washington_South,		Proj_Washington_CS27_South,
+
+    PCS_NAD27_West_Virginia_N,		Proj_West_Virginia_CS27_North,
+    PCS_NAD27_West_Virginia_S,		Proj_West_Virginia_CS27_South,
+
+    PCS_NAD27_Wisconsin_North,		Proj_Wisconsin_CS27_North,
+    PCS_NAD27_Wisconsin_Cen,		Proj_Wisconsin_CS27_Central,
+    PCS_NAD27_Wisconsin_South,		Proj_Wisconsin_CS27_South,
+
+    PCS_NAD27_Wyoming_East,		Proj_Wyoming_CS27_East,
+    PCS_NAD27_Wyoming_E_Cen,		Proj_Wyoming_CS27_East_Central,
+    PCS_NAD27_Wyoming_W_Cen,		Proj_Wyoming_CS27_West_Central,
+    PCS_NAD27_Wyoming_West,		Proj_Wyoming_CS27_West,
+    
+    PCS_NAD27_Puerto_Rico,		Proj_Puerto_Rico_CS27,
+
+    KvUserDefined
+};
+
+/************************************************************************/
+/*                          GTIFMapSysToPCS()                           */
+/*                                                                      */
+/*      Given a Datum, MapSys and zone value generate the best PCS      */
+/*      code possible.                                                  */
+/************************************************************************/
+
+int	GTIFMapSysToPCS( int MapSys, int Datum, int nZone )
+
+{
+    int		PCSCode = KvUserDefined;
+
+    if( MapSys == MapSys_UTM_North )
+    {
+	if( Datum == GCS_NAD27 )
+	    PCSCode = PCS_NAD27_UTM_zone_3N + nZone - 3;
+	else if( Datum == GCS_NAD83 )
+	    PCSCode = PCS_NAD83_UTM_zone_3N + nZone - 3;
+	else if( Datum == GCS_WGS_72 )
+	    PCSCode = PCS_WGS72_UTM_zone_1N + nZone - 1;
+	else if( Datum == GCS_WGS_72BE )
+	    PCSCode = PCS_WGS72BE_UTM_zone_1N + nZone - 1;
+	else if( Datum == GCS_WGS_84 )
+	    PCSCode = PCS_WGS84_UTM_zone_1N + nZone - 1;
+    }
+    else if( MapSys == MapSys_UTM_South )
+    {
+	if( Datum == GCS_WGS_72 )
+	    PCSCode = PCS_WGS72_UTM_zone_1S + nZone - 1;
+	else if( Datum == GCS_WGS_72BE )
+	    PCSCode = PCS_WGS72BE_UTM_zone_1S + nZone - 1;
+	else if( Datum == GCS_WGS_84 )
+	    PCSCode = PCS_WGS84_UTM_zone_1S + nZone - 1;
+    }
+    else if( MapSys == MapSys_State_Plane_27 )
+    {
+	int		i;
+
+        PCSCode = 10000 + nZone;
+	for( i = 0; StatePlaneTable[i] != KvUserDefined; i += 2 )
+	{
+	    if( StatePlaneTable[i+1] == PCSCode )
+	        PCSCode = StatePlaneTable[i];
+	}
+
+        /* Old EPSG code was in error for Tennesse CS27, override */
+        if( nZone == 4100 )
+            PCSCode = 2204;
+    }
+    else if( MapSys == MapSys_State_Plane_83 )
+    {
+	int		i;
+
+        PCSCode = 10000 + nZone + 30;
+
+	for( i = 0; StatePlaneTable[i] != KvUserDefined; i += 2 )
+	{
+	    if( StatePlaneTable[i+1] == PCSCode )
+	        PCSCode = StatePlaneTable[i];
+	}
+
+        /* Old EPSG code was in error for Kentucky North CS83, override */
+        if( nZone == 1601 )
+            PCSCode = 2205;
+    }
+
+    return( PCSCode );
+}
+
+/************************************************************************/
+/*                          GTIFMapSysToProj()                          */
+/*                                                                      */
+/*      Given a MapSys and zone value generate the best Proj_           */
+/*      code possible.                                                  */
+/************************************************************************/
+
+int	GTIFMapSysToProj( int MapSys, int nZone )
+
+{
+    int		ProjCode = KvUserDefined;
+
+    if( MapSys == MapSys_UTM_North )
+    {
+        ProjCode = Proj_UTM_zone_1N + nZone - 1;
+    }
+    else if( MapSys == MapSys_UTM_South )
+    {
+        ProjCode = Proj_UTM_zone_1S + nZone - 1;
+    }
+    else if( MapSys == MapSys_State_Plane_27 )
+    {
+        ProjCode = 10000 + nZone;
+
+        /* Tennesse override */
+        if( nZone == 4100 )
+            ProjCode = 15302;
+    }
+    else if( MapSys == MapSys_State_Plane_83 )
+    {
+        ProjCode = 10000 + nZone + 30;
+
+        /* Kentucky North override */
+        if( nZone == 1601 )
+            ProjCode = 15303;
+    }
+
+    return( ProjCode );
+}
+
+/************************************************************************/
+/*                          GTIFPCSToMapSys()                           */
+/************************************************************************/
+
+/**
+ * Translate a PCS_ code into a UTM or State Plane map system, a datum,
+ * and a zone if possible.
+ *
+ * @param PCSCode The projection code (PCS_*) as would be stored in the
+ * ProjectedCSTypeGeoKey of a GeoTIFF file.
+ *
+ * @param pDatum Pointer to an integer into which the datum code (GCS_*)
+ * is put if the function succeeds.
+ *
+ * @param pZone Pointer to an integer into which the zone will be placed
+ * if the function is successful.
+ *
+ * @return Returns either MapSys_UTM_North, MapSys_UTM_South,
+ * MapSys_State_Plane_83, MapSys_State_Plane_27 or KvUserDefined.
+ * KvUserDefined indicates that the
+ * function failed to recognise the projection as UTM or State Plane.
+ *
+ * The zone value is only set if the return code is other than KvUserDefined.
+ * For utm map system the returned zone will be between 1 and 60.  For
+ * State Plane, the USGS state plane zone number is returned.  For instance,
+ * Alabama East is zone 101.
+ *
+ * The datum (really this is the GCS) is set to a GCS_ value such as GCS_NAD27.
+ *
+ * This function is useful to recognise (most) UTM and State Plane coordinate
+ * systems, even if CSV files aren't available to translate them automatically.
+ * It is used as a fallback mechanism by GTIFGetDefn() for normalization when
+ * CSV files aren't found. 
+ */
+
+int GTIFPCSToMapSys( int PCSCode, int * pDatum, int * pZone )
+
+{
+    int		Datum = KvUserDefined, Proj = KvUserDefined;
+    int		nZone = KvUserDefined, i;
+
+/* -------------------------------------------------------------------- */
+/*      UTM with various datums.  Note there are lots of PCS UTM        */
+/*      codes not done yet which use strange datums.                    */
+/* -------------------------------------------------------------------- */
+    if( PCSCode >= PCS_NAD27_UTM_zone_3N && PCSCode <= PCS_NAD27_UTM_zone_22N )
+    {
+	Datum = GCS_NAD27;
+	Proj = MapSys_UTM_North;
+	nZone = PCSCode - PCS_NAD27_UTM_zone_3N + 3;
+    }
+    else if( PCSCode >= PCS_NAD83_UTM_zone_3N 
+	     && PCSCode <= PCS_NAD83_UTM_zone_23N )
+    {
+	Datum = GCS_NAD83;
+	Proj = MapSys_UTM_North;
+	nZone = PCSCode - PCS_NAD83_UTM_zone_3N + 3;
+    }
+
+    else if( PCSCode >= PCS_WGS72_UTM_zone_1N
+	     && PCSCode <= PCS_WGS72_UTM_zone_60N )
+    {
+	Datum = GCS_WGS_72;
+	Proj = MapSys_UTM_North;
+	nZone = PCSCode - PCS_WGS72_UTM_zone_1N + 1;
+    }
+    else if( PCSCode >= PCS_WGS72_UTM_zone_1S
+	     && PCSCode <= PCS_WGS72_UTM_zone_60S )
+    {
+	Datum = GCS_WGS_72;
+	Proj = MapSys_UTM_South;
+	nZone = PCSCode - PCS_WGS72_UTM_zone_1S + 1;
+    }
+
+    else if( PCSCode >= PCS_WGS72BE_UTM_zone_1N
+	     && PCSCode <= PCS_WGS72BE_UTM_zone_60N )
+    {
+	Datum = GCS_WGS_72BE;
+	Proj = MapSys_UTM_North;
+	nZone = PCSCode - PCS_WGS72BE_UTM_zone_1N + 1;
+    }
+    else if( PCSCode >= PCS_WGS72BE_UTM_zone_1S
+	     && PCSCode <= PCS_WGS72BE_UTM_zone_60S )
+    {
+	Datum = GCS_WGS_72BE;
+	Proj = MapSys_UTM_South;
+	nZone = PCSCode - PCS_WGS72BE_UTM_zone_1S + 1;
+    }
+
+    else if( PCSCode >= PCS_WGS84_UTM_zone_1N
+	     && PCSCode <= PCS_WGS84_UTM_zone_60N )
+    {
+	Datum = GCS_WGS_84;
+	Proj = MapSys_UTM_North;
+	nZone = PCSCode - PCS_WGS84_UTM_zone_1N + 1;
+    }
+    else if( PCSCode >= PCS_WGS84_UTM_zone_1S
+	     && PCSCode <= PCS_WGS84_UTM_zone_60S )
+    {
+	Datum = GCS_WGS_84;
+	Proj = MapSys_UTM_South;
+	nZone = PCSCode - PCS_WGS84_UTM_zone_1S + 1;
+    }
+    else if( PCSCode >= PCS_SAD69_UTM_zone_18N 
+	     && PCSCode <= PCS_SAD69_UTM_zone_22N )
+    {
+	Datum = KvUserDefined;
+	Proj = MapSys_UTM_North;
+	nZone = PCSCode - PCS_SAD69_UTM_zone_18N + 18;
+    }
+    else if( PCSCode >= PCS_SAD69_UTM_zone_17S
+	     && PCSCode <= PCS_SAD69_UTM_zone_25S )
+    {
+	Datum = KvUserDefined;
+	Proj = MapSys_UTM_South;
+	nZone = PCSCode - PCS_SAD69_UTM_zone_17S + 17;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      State Plane zones, first we translate any PCS_ codes to		*/
+/*	a Proj_ code that we can get a handle on.			*/
+/* -------------------------------------------------------------------- */
+    for( i = 0; StatePlaneTable[i] != KvUserDefined; i += 2 )
+    {
+	if( StatePlaneTable[i] == PCSCode )
+	    PCSCode = StatePlaneTable[i+1];
+    }
+
+    if( PCSCode <= 15900 && PCSCode >= 10000 )
+    {
+	if( (PCSCode % 100) >= 30 )
+        {
+            Proj = MapSys_State_Plane_83;
+	    Datum = GCS_NAD83;
+        }
+	else
+        {
+            Proj = MapSys_State_Plane_27;
+	    Datum = GCS_NAD27;
+        }
+	
+	nZone = PCSCode - 10000;
+	if( Datum == GCS_NAD83 )
+	    nZone -= 30;
+    }
+
+    if( pDatum != NULL )
+        *pDatum = Datum;
+
+    if( pZone != NULL )
+        *pZone = nZone;
+
+    return( Proj );
+}
+
+/************************************************************************/
+/*                          GTIFProjToMapSys()                          */
+/************************************************************************/
+
+/**
+ * Translate a Proj_ code into a UTM or State Plane map system, and a zone
+ * if possible.
+ *
+ * @param ProjCode The projection code (Proj_*) as would be stored in the
+ * ProjectionGeoKey of a GeoTIFF file.
+ * @param pZone Pointer to an integer into which the zone will be placed
+ * if the function is successful.
+ *
+ * @return Returns either MapSys_UTM_North, MapSys_UTM_South,
+ * MapSys_State_Plane_27, MapSys_State_Plane_83 or KvUserDefined.
+ * KvUserDefined indicates that the
+ * function failed to recognise the projection as UTM or State Plane.
+ *
+ * The zone value is only set if the return code is other than KvUserDefined.
+ * For utm map system the returned zone will be between 1 and 60.  For
+ * State Plane, the USGS state plane zone number is returned.  For instance,
+ * Alabama East is zone 101.
+ *
+ * This function is useful to recognise UTM and State Plane coordinate
+ * systems, and to extract zone numbers so the projections can be
+ * represented as UTM rather than as the underlying projection method such
+ * Transverse Mercator for instance.
+ */
+
+int GTIFProjToMapSys( int ProjCode, int * pZone )
+
+{
+    int		nZone = KvUserDefined;
+    int		MapSys = KvUserDefined;
+
+/* -------------------------------------------------------------------- */
+/*      Handle UTM.                                                     */
+/* -------------------------------------------------------------------- */
+    if( ProjCode >= Proj_UTM_zone_1N && ProjCode <= Proj_UTM_zone_60N )
+    {
+	MapSys = MapSys_UTM_North;
+	nZone = ProjCode - Proj_UTM_zone_1N + 1;
+    }
+    else if( ProjCode >= Proj_UTM_zone_1S && ProjCode <= Proj_UTM_zone_60S )
+    {
+	MapSys = MapSys_UTM_South;
+	nZone = ProjCode - Proj_UTM_zone_1S + 1;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Handle State Plane.  I think there are some anomolies in        */
+/*      here, so this is a bit risky.                                   */
+/* -------------------------------------------------------------------- */
+    else if( ProjCode >= 10101 && ProjCode <= 15299 )
+    {
+        if( ProjCode % 100 >= 30 )
+        {
+            MapSys = MapSys_State_Plane_83;
+            nZone = ProjCode - 10000 - 30;
+        }
+        else
+        {
+            MapSys = MapSys_State_Plane_27;
+            nZone = ProjCode - 10000;
+        }
+    }
+    
+    if( pZone != NULL )
+        *pZone = nZone;
+
+    return( MapSys );
+}
+
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_free.c b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_free.c
new file mode 100644
index 0000000000..a43fcad354
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_free.c
@@ -0,0 +1,62 @@
+/**********************************************************************
+ *
+ *  geo_free.c  -- Public routines for GEOTIFF GeoKey access.
+ *
+ *    Written By: Niles D. Ritter.
+ *
+ *  copyright (c) 1995   Niles D. Ritter
+ *
+ *  Permission granted to use this software, so long as this copyright
+ *  notice accompanies any products derived therefrom.
+ *
+ **********************************************************************/
+
+#include "geotiff.h"   /* public interface        */
+#include "geo_tiffp.h" /* external TIFF interface */
+#include "geo_keyp.h"  /* private interface       */
+
+
+/**********************************************************************
+ *
+ *                        Public Routines
+ *
+ **********************************************************************/
+
+/**
+  
+This function deallocates an existing GeoTIFF access handle previously
+created with GTIFNew().  If the handle was
+used to write GeoTIFF keys to the TIFF file, the 
+GTIFWriteKeys() function should be used
+to flush results to the file before calling GTIFFree().  GTIFFree()
+should be called before XTIFFClose() is
+called on the corresponding TIFF file handle.<p>
+
+*/
+
+void GTIFFree(GTIF* gtif)
+{
+    int     i;
+	
+    if (!gtif) return;
+	
+    /* Free parameter arrays */
+    if (gtif->gt_double) _GTIFFree (gtif->gt_double);
+    if (gtif->gt_short) _GTIFFree (gtif->gt_short);
+	
+    /* Free GeoKey arrays */
+    if (gtif->gt_keys)
+    {
+        for (i = 0; i < MAX_KEYS; i++)
+        {
+            if (gtif->gt_keys[i].gk_type == TYPE_ASCII)
+            {
+                _GTIFFree (gtif->gt_keys[i].gk_data);
+            }
+        }
+        _GTIFFree (gtif->gt_keys);
+    }
+    if (gtif->gt_keyindex) _GTIFFree (gtif->gt_keyindex);
+	
+    _GTIFFree (gtif);
+}
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_get.c b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_get.c
new file mode 100644
index 0000000000..f027bd3aa2
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_get.c
@@ -0,0 +1,176 @@
+/**********************************************************************
+ *
+ *  geo_get.c  -- Public routines for GEOTIFF GeoKey access.
+ *
+ *    Written By: Niles D. Ritter.
+ *
+ *  copyright (c) 1995   Niles D. Ritter
+ *
+ *  Permission granted to use this software, so long as this copyright
+ *  notice accompanies any products derived therefrom.
+ *
+ *  Revision History;
+ *
+ *    20 June, 1995      Niles D. Ritter         New
+ *    3 July,  1995      Greg Martin             Fix strings and index
+ *    6 July,  1995      Niles D. Ritter         Unfix indexing.
+ *
+ **********************************************************************/
+
+#include "geotiff.h"   /* public interface        */
+#include "geo_tiffp.h" /* external TIFF interface */
+#include "geo_keyp.h"  /* private interface       */
+
+/* return the Header info of this geotiff file */
+
+void GTIFDirectoryInfo(GTIF *gtif, int version[3], int *keycount)
+{
+        if (version)
+        {
+                version[0]  = gtif->gt_version;
+                version[1]  = gtif->gt_rev_major;
+                version[2]  = gtif->gt_rev_minor;
+        }
+        if (keycount) *keycount = gtif->gt_num_keys;
+}
+
+
+int GTIFKeyInfo(GTIF *gtif, geokey_t key, int *size, tagtype_t* type)
+{
+        int index = gtif->gt_keyindex[ key ];
+        GeoKey *keyptr;
+
+        if (!index) return 0;
+
+        keyptr = gtif->gt_keys + index;
+        if (size) *size = (int) keyptr->gk_size;
+        if (type) *type = keyptr->gk_type;
+
+        return keyptr->gk_count;
+}
+
+/** 
+
+This function reads the value of a single GeoKey from a GeoTIFF file.
+
+@param gtif The geotiff information handle from GTIFNew().
+
+@param thekey The geokey_t name (such as ProjectedCSTypeGeoKey).
+This must come from the list of legal geokey_t values
+(an enumeration) listed below.
+
+@param val The <b>val</b> argument is a pointer to the
+variable into which the value should be read.  The type of the variable
+varies depending on the geokey_t given.  While there is no ready mapping
+of geokey_t values onto types, in general code values are of type <i>short</i>,
+citations are strings, and everything else is of type <i>double</i>.  Note
+that pointer's to <i>int</i> should never be passed to GTIFKeyGet() for
+integer values as they will be shorts, and the int's may not be properly
+initialized (and will be grossly wrong on MSB systems).
+
+@param index Indicates how far into the list of values
+for this geokey to offset. Should normally be zero.
+
+@param count Indicates how many values
+to read.  At this time all keys except for strings have only one value,
+so <b>index</b> should be zero, and <b>count</b> should be one.
+
+@return The GTIFKeyGet() function returns the number of values read.  Normally
+this would be one if successful or zero if the key doesn't exist for this
+file.
+
+From geokeys.inc we see the following geokey_t values are possible:<p>
+
+<pre>
+-- 6.2.1 GeoTIFF Configuration Keys --
+
+ValuePair(  GTModelTypeGeoKey,	1024) -- Section 6.3.1.1 Codes       --
+ValuePair(  GTRasterTypeGeoKey,	1025) -- Section 6.3.1.2 Codes       --
+ValuePair(  GTCitationGeoKey,	1026) -- documentation --
+
+-- 6.2.2 Geographic CS Parameter Keys --
+
+ValuePair(  GeographicTypeGeoKey,	2048) -- Section 6.3.2.1 Codes     --
+ValuePair(  GeogCitationGeoKey,	2049) -- documentation             --
+ValuePair(  GeogGeodeticDatumGeoKey,	2050) -- Section 6.3.2.2 Codes     --
+ValuePair(  GeogPrimeMeridianGeoKey,	2051) -- Section 6.3.2.4 codes     --
+ValuePair(  GeogLinearUnitsGeoKey,	2052) -- Section 6.3.1.3 Codes     --
+ValuePair(  GeogLinearUnitSizeGeoKey,	2053) -- meters                    --
+ValuePair(  GeogAngularUnitsGeoKey,	2054) -- Section 6.3.1.4 Codes     --
+ValuePair(  GeogAngularUnitSizeGeoKey,	2055) -- radians                   --
+ValuePair(  GeogEllipsoidGeoKey,	2056) -- Section 6.3.2.3 Codes     --
+ValuePair(  GeogSemiMajorAxisGeoKey,	2057) -- GeogLinearUnits           --
+ValuePair(  GeogSemiMinorAxisGeoKey,	2058) -- GeogLinearUnits           --
+ValuePair(  GeogInvFlatteningGeoKey,	2059) -- ratio                     --
+ValuePair(  GeogAzimuthUnitsGeoKey,	2060) -- Section 6.3.1.4 Codes     --
+ValuePair(  GeogPrimeMeridianLongGeoKey,	2061) -- GeoAngularUnit            --
+
+-- 6.2.3 Projected CS Parameter Keys --
+--    Several keys have been renamed,--
+--    and the deprecated names aliased for backward compatibility --
+
+ValuePair(  ProjectedCSTypeGeoKey,	3072)     -- Section 6.3.3.1 codes   --
+ValuePair(  PCSCitationGeoKey,	3073)     -- documentation           --
+ValuePair(  ProjectionGeoKey,	3074)     -- Section 6.3.3.2 codes   --
+ValuePair(  ProjCoordTransGeoKey,	3075)     -- Section 6.3.3.3 codes   --
+ValuePair(  ProjLinearUnitsGeoKey,	3076)     -- Section 6.3.1.3 codes   --
+ValuePair(  ProjLinearUnitSizeGeoKey,	3077)     -- meters                  --
+ValuePair(  ProjStdParallel1GeoKey,	3078)     -- GeogAngularUnit --
+ValuePair(  ProjStdParallelGeoKey,ProjStdParallel1GeoKey) -- ** alias **   --
+ValuePair(  ProjStdParallel2GeoKey,	3079)     -- GeogAngularUnit --
+ValuePair(  ProjNatOriginLongGeoKey,	3080)     -- GeogAngularUnit --
+ValuePair(  ProjOriginLongGeoKey,ProjNatOriginLongGeoKey) -- ** alias **     --
+ValuePair(  ProjNatOriginLatGeoKey,	3081)     -- GeogAngularUnit --
+ValuePair(  ProjOriginLatGeoKey,ProjNatOriginLatGeoKey)   -- ** alias **     --
+ValuePair(  ProjFalseEastingGeoKey,	3082)     -- ProjLinearUnits --
+ValuePair(  ProjFalseNorthingGeoKey,	3083)     -- ProjLinearUnits --
+ValuePair(  ProjFalseOriginLongGeoKey,	3084)     -- GeogAngularUnit --
+ValuePair(  ProjFalseOriginLatGeoKey,	3085)     -- GeogAngularUnit --
+ValuePair(  ProjFalseOriginEastingGeoKey,	3086)     -- ProjLinearUnits --
+ValuePair(  ProjFalseOriginNorthingGeoKey,	3087)     -- ProjLinearUnits --
+ValuePair(  ProjCenterLongGeoKey,	3088)     -- GeogAngularUnit --
+ValuePair(  ProjCenterLatGeoKey,	3089)     -- GeogAngularUnit --
+ValuePair(  ProjCenterEastingGeoKey,	3090)     -- ProjLinearUnits --
+ValuePair(  ProjCenterNorthingGeoKey,	3091)     -- ProjLinearUnits --
+ValuePair(  ProjScaleAtNatOriginGeoKey,	3092)     -- ratio   --
+ValuePair(  ProjScaleAtOriginGeoKey,ProjScaleAtNatOriginGeoKey)  -- ** alias **   --
+ValuePair(  ProjScaleAtCenterGeoKey,	3093)     -- ratio   --
+ValuePair(  ProjAzimuthAngleGeoKey,	3094)     -- GeogAzimuthUnit --
+ValuePair(  ProjStraightVertPoleLongGeoKey,	3095)     -- GeogAngularUnit --
+
+ 6.2.4 Vertical CS Keys 
+   
+ValuePair(  VerticalCSTypeGeoKey,	4096)  -- Section 6.3.4.1 codes   --
+ValuePair(  VerticalCitationGeoKey,	4097)  -- documentation --
+ValuePair(  VerticalDatumGeoKey,	4098)  -- Section 6.3.4.2 codes   --
+ValuePair(  VerticalUnitsGeoKey,	4099)  -- Section 6.3.1 (.x) codes   --
+</pre>
+*/
+
+int GTIFKeyGet(GTIF *gtif, geokey_t thekey, void *val, int index, int count)
+{
+        int kindex = gtif->gt_keyindex[ thekey ];
+        GeoKey *key;
+        gsize_t size;
+        char *data;
+        tagtype_t type;
+
+        if (!kindex) return 0;
+
+        key = gtif->gt_keys+kindex;
+        if (!count) count = key->gk_count - index;
+        if (count <=0) return 0;
+        if (count > key->gk_count) count = key->gk_count;
+        size = key->gk_size;
+        type = key->gk_type;
+
+        if (count==1 && type==TYPE_SHORT) data = (char *)&key->gk_data;
+        else data = key->gk_data;
+
+        _GTIFmemcpy( val, data + index*size, count*size );
+
+        if (type==TYPE_ASCII)
+           ((char *)val)[count-1] = '\0'; /* replace last char with NULL */
+
+        return count;
+}
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_keyp.h b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_keyp.h
new file mode 100644
index 0000000000..140bf403ac
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_keyp.h
@@ -0,0 +1,98 @@
+/**********************************************************************
+ *
+ *  geo_keyp.h - private interface for GeoTIFF geokey tag parsing
+ *
+ *     Written by: Niles D. Ritter
+ *
+ **********************************************************************/
+
+#ifndef __geo_keyp_h_
+#define __geo_keyp_h_
+
+#include <stdlib.h> /* for size_t */
+
+/*
+ * This structure contains the internal program
+ * representation of the key entry.
+ */
+struct GeoKey {
+	int       gk_key;    /* GeoKey ID        */
+	size_t    gk_size;   /* data byte size   */
+	tagtype_t gk_type;   /* TIFF data type   */
+	long      gk_count;  /* number of values */
+	char*     gk_data;   /* pointer to data, or value */
+};
+typedef struct GeoKey GeoKey;
+
+/*
+ *  This structure represents the file-organization of
+ *  the key entry. Note that it assumes that short entries
+ *  are aligned along 2-byte boundaries.
+ */
+struct KeyEntry {
+	pinfo_t ent_key;        /* GeoKey ID            */
+	pinfo_t ent_location;   /* TIFF Tag ID or 0     */
+	pinfo_t ent_count;      /* GeoKey value count   */
+	pinfo_t ent_val_offset; /* value or tag offset  */
+};
+typedef struct KeyEntry KeyEntry;
+
+/*
+ * This is the header of the CoordSystemInfoTag. The 'Version'
+ *  will only change if the CoorSystemInfoTag structure changes;
+ *  The Major Revision will be incremented whenever a new set of
+ *  Keys is added or changed, while the Minor revision will be
+ *  incremented when only the set of Key-values is increased.
+ */
+struct KeyHeader{
+	pinfo_t hdr_version;      /* GeoTIFF Version          */
+	pinfo_t hdr_rev_major;    /* GeoKey Major Revision #  */
+	pinfo_t hdr_rev_minor;    /* GeoKey Minor Revision #  */
+	pinfo_t hdr_num_keys;     /* Number of GeoKeys        */
+};
+typedef struct KeyHeader KeyHeader;
+
+/*
+ * This structure holds temporary data while reading or writing
+ *  the tags.
+ */
+struct TempKeyData {
+    char   *tk_asciiParams;
+    int     tk_asciiParamsLength;
+    int     tk_asciiParamsOffset;
+};
+typedef struct TempKeyData TempKeyData;
+
+
+struct gtiff {
+   tiff_t*    gt_tif;      /* TIFF file descriptor  */
+   TIFFMethod gt_methods;  /* TIFF i/o methods      */
+   int        gt_flags;    /* file flags            */
+   
+   pinfo_t    gt_version;  /* GeoTIFF Version       */
+   pinfo_t    gt_rev_major;/* GeoKey Key Revision   */
+   pinfo_t    gt_rev_minor;/* GeoKey Code Revision  */
+   
+   int        gt_num_keys; /* number of keys        */
+   GeoKey*    gt_keys;     /* array of keys         */
+   int*       gt_keyindex; /* index of a key, if set*/
+   int        gt_keymin;   /* smallest key set      */
+   int        gt_keymax;   /* largest key set       */
+   
+   pinfo_t*   gt_short;    /* array of SHORT vals   */
+   double*    gt_double;   /* array of DOUBLE vals  */
+   int        gt_nshorts;  /* number of SHORT vals  */
+   int        gt_ndoubles; /* number of DOUBLE vals */
+};  
+
+typedef enum {
+	FLAG_FILE_OPEN=1,
+	FLAG_FILE_MODIFIED=2
+} gtiff_flags;
+
+#define MAX_KEYINDEX 65535   /* largest possible key    */
+#define MAX_KEYS 100         /* maximum keys in a file  */
+#define MAX_VALUES 1000      /* maximum values in a tag */
+
+#endif /* __geo_keyp_h_ */
+
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_names.c b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_names.c
new file mode 100644
index 0000000000..de58ca2a63
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_names.c
@@ -0,0 +1,175 @@
+/*
+ * geo_names.c
+ *
+ *  This encapsulates all of the value-naming mechanism of 
+ *  libgeotiff. 
+ *
+ *  Written By: Niles Ritter
+ */
+
+#include "geotiffio.h"
+#include "geonames.h"
+#include "geo_tiffp.h" /* for tag names */
+
+static KeyInfo _formatInfo[] =  {
+   {TYPE_BYTE,    "Byte"},
+   {TYPE_SHORT,   "Short"},
+   {TYPE_LONG,    "Long"},
+   {TYPE_RATIONAL,"Rational"},
+   {TYPE_ASCII,   "Ascii"},
+   {TYPE_FLOAT,   "Float"},
+   {TYPE_DOUBLE,  "Double"},
+   {TYPE_SBYTE,   "SignedByte"},
+   {TYPE_SSHORT,  "SignedShort"},
+   {TYPE_SLONG,  "SignedLong"},
+   {TYPE_UNKNOWN, "Unknown"},
+    END_LIST
+};
+
+static KeyInfo _tagInfo[] =  {
+    {GTIFF_PIXELSCALE,  "ModelPixelScaleTag"},
+    {GTIFF_TRANSMATRIX, "ModelTransformationTag"},
+    {GTIFF_TIEPOINTS,   "ModelTiepointTag"},
+     /* This alias maps the Intergraph symbol to the current tag */
+    {GTIFF_TRANSMATRIX, "IntergraphMatrixTag"},
+    END_LIST
+};
+
+static char *FindName(KeyInfo *info,int key)
+{
+   static char errmsg[80];
+   
+   while (info->ki_key>=0 && info->ki_key != key) info++;
+
+   if (info->ki_key<0)
+   {
+	   sprintf(errmsg,"Unknown-%d", key );
+	   return errmsg;
+   }
+   return info->ki_name;
+}
+
+char *GTIFKeyName(geokey_t key)
+{
+   return FindName( &_keyInfo[0],key);
+}
+
+char *GTIFTypeName(tagtype_t type)
+{
+   return FindName( &_formatInfo[0],type);
+}
+
+char *GTIFTagName(int tag)
+{
+   return FindName( &_tagInfo[0],tag);
+}
+
+char *GTIFValueName(geokey_t key, int value)
+{
+   KeyInfo *info;
+   
+   switch (key)
+   {
+	/* All codes using linear/angular/whatever units */
+	case GeogLinearUnitsGeoKey: 
+	case ProjLinearUnitsGeoKey: 
+	case GeogAngularUnitsGeoKey: 
+	case GeogAzimuthUnitsGeoKey: 
+		                      info=_geounitsValue; break;
+
+   	/* put other key-dependent lists here */
+	case GTModelTypeGeoKey:       info=_modeltypeValue; break;
+	case GTRasterTypeGeoKey:      info=_rastertypeValue; break;
+	case GeographicTypeGeoKey:    info=_geographicValue; break;
+	case GeogGeodeticDatumGeoKey: info=_geodeticdatumValue; break;
+	case GeogEllipsoidGeoKey:     info=_ellipsoidValue; break;
+	case GeogPrimeMeridianGeoKey: info=_primemeridianValue; break;
+	case ProjectedCSTypeGeoKey:   info=_pcstypeValue; break;
+	case ProjectionGeoKey:        info=_projectionValue; break;
+	case ProjCoordTransGeoKey:    info=_coordtransValue; break;
+	case VerticalCSTypeGeoKey:    info=_vertcstypeValue; break;
+	case VerticalDatumGeoKey:     info=_vdatumValue; break;
+
+	/* And if all else fails... */
+   	default:                      info = _csdefaultValue;break;
+   }
+   
+   return FindName( info,value);
+}
+
+/* 
+ * Inverse Utilities (name->code) 
+ */
+
+
+static int FindCode(KeyInfo *info,char *key)
+{
+   while (info->ki_key>=0 && strcmp(info->ki_name,key) ) info++;
+
+   if (info->ki_key<0)
+   {
+	/* not a registered key; might be generic code */
+	if (!strncmp(key,"Unknown-",8))
+	{
+		int code=-1;
+		sscanf(key,"Unknown-%d",&code);
+		return code;
+	}
+	else return -1;
+   }
+   return info->ki_key;
+}
+
+int GTIFKeyCode(char *key)
+{
+   return FindCode( &_keyInfo[0],key);
+}
+
+int GTIFTypeCode(char *type)
+{
+   return FindCode( &_formatInfo[0],type);
+}
+
+int GTIFTagCode(char *tag)
+{
+   return FindCode( &_tagInfo[0],tag);
+}
+
+
+/*
+ *  The key must be determined with GTIFKeyCode() before
+ *  the name can be encoded.
+ */
+int GTIFValueCode(geokey_t key, char *name)
+{
+   KeyInfo *info;
+   
+   switch (key)
+   {
+	/* All codes using linear/angular/whatever units */
+	case GeogLinearUnitsGeoKey: 
+	case ProjLinearUnitsGeoKey: 
+	case GeogAngularUnitsGeoKey: 
+	case GeogAzimuthUnitsGeoKey: 
+		                      info=_geounitsValue; break;
+
+   	/* put other key-dependent lists here */
+	case GTModelTypeGeoKey:       info=_modeltypeValue; break;
+	case GTRasterTypeGeoKey:      info=_rastertypeValue; break;
+	case GeographicTypeGeoKey:    info=_geographicValue; break;
+	case GeogGeodeticDatumGeoKey: info=_geodeticdatumValue; break;
+	case GeogEllipsoidGeoKey:     info=_ellipsoidValue; break;
+	case GeogPrimeMeridianGeoKey: info=_primemeridianValue; break;
+	case ProjectedCSTypeGeoKey:   info=_pcstypeValue; break;
+	case ProjectionGeoKey:        info=_projectionValue; break;
+	case ProjCoordTransGeoKey:    info=_coordtransValue; break;
+	case VerticalCSTypeGeoKey:    info=_vertcstypeValue; break;
+	case VerticalDatumGeoKey:     info=_vdatumValue; break;
+
+	/* And if all else fails... */
+   	default:                      info = _csdefaultValue;break;
+   }
+   
+   return FindCode( info,name);
+}
+
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_new.c b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_new.c
new file mode 100644
index 0000000000..423effd628
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_new.c
@@ -0,0 +1,242 @@
+/**********************************************************************
+ *
+ *  geo_new.c  -- Public routines for GEOTIFF GeoKey access.
+ *
+ *    Written By: Niles D. Ritter.
+ *
+ *  copyright (c) 1995   Niles D. Ritter
+ *
+ *  Permission granted to use this software, so long as this copyright
+ *  notice accompanies any products derived therefrom.
+ *
+ *    20 June, 1995      Niles D. Ritter         New
+ *    7 July,  1995      Greg Martin             Fix index
+ *
+ * $Log: geo_new.c,v $
+ * Revision 1.11  2004/04/27 21:32:08  warmerda
+ * Allow GTIFNew(NULL) to work
+ *
+ * Revision 1.10  2003/09/02 13:52:17  warmerda
+ * various hacks to support improperly terminated asciiparms
+ *
+ * Revision 1.9  2003/06/19 20:04:11  warmerda
+ * fix memory underwrite if ascii parameter string is zero length
+ *
+ * Revision 1.8  2003/06/05 14:20:45  warmerda
+ * cosmetic formatting changes
+ *
+ **********************************************************************/
+
+#include "geotiffio.h"   /* public interface        */
+#include "geo_tiffp.h" /* external TIFF interface */
+#include "geo_keyp.h"  /* private interface       */
+
+/* private local routines */
+static int ReadKey(GTIF* gt, TempKeyData* tempData,
+                   KeyEntry* entptr, GeoKey* keyptr);
+
+
+/**********************************************************************
+ *
+ *                        Public Routines
+ *
+ **********************************************************************/
+
+
+/**
+ * Given an open TIFF file, look for GTIF keys and 
+ *  values and return GTIF structure.
+
+This function creates a GeoTIFF information interpretation handle
+(GTIF *) based on a passed in TIFF handle originally from 
+XTIFFOpen().  Even though the argument 
+(<b>tif</b>) is shown as type <tt>void *</tt>, it is really normally
+of type <tt>TIFF *</tt>.<p>
+
+The returned GTIF handle can be used to read or write GeoTIFF tags 
+using the various GTIF functions.  The handle should be destroyed using
+GTIFFree() before the file is closed with TIFFClose().<p>
+
+If the file accessed has no GeoTIFF keys, an valid (but empty) GTIF is
+still returned.  GTIFNew() is used both for existing files being read, and
+for new TIFF files that will have GeoTIFF tags written to them.<p>
+
+ */
+ 
+GTIF* GTIFNew(void *tif)
+{
+    GTIF* gt=(GTIF*)0;
+    int count,bufcount,index;
+    GeoKey *keyptr;
+    pinfo_t *data;
+    KeyEntry *entptr;
+    KeyHeader *header;
+    TempKeyData tempData;
+	
+    gt = (GTIF*)_GTIFcalloc( sizeof(GTIF));
+    if (!gt) goto failure;	
+	
+    /* install TIFF file and I/O methods */
+    gt->gt_tif = (tiff_t *)tif;
+    _GTIFSetDefaultTIFF(&gt->gt_methods);
+
+    tempData.tk_asciiParams = 0;
+    tempData.tk_asciiParamsLength = 0;
+    tempData.tk_asciiParamsOffset = 0;
+	
+    /* since this is an array, GTIF will allocate the memory */
+    if ( tif == NULL 
+         || !(gt->gt_methods.get)(tif, GTIFF_GEOKEYDIRECTORY, &gt->gt_nshorts, &data ))
+    {
+        /* No ProjectionInfo, create a blank one */
+        data=(pinfo_t*)_GTIFcalloc((4+MAX_VALUES)*sizeof(pinfo_t));
+        if (!data) goto failure;	
+        header = (KeyHeader *)data;
+        header->hdr_version = GvCurrentVersion;
+        header->hdr_rev_major = GvCurrentRevision;
+        header->hdr_rev_minor = GvCurrentMinorRev;
+        gt->gt_nshorts=sizeof(KeyHeader)/sizeof(pinfo_t);
+    }
+    gt->gt_short = data;
+    header = (KeyHeader *)data;
+	
+    if (header->hdr_version > GvCurrentVersion) goto failure;
+    if (header->hdr_rev_major > GvCurrentRevision)
+    {
+        /* issue warning */
+    }
+	
+    /* If we got here, then the geokey can be parsed */
+    count = header->hdr_num_keys;
+    gt->gt_num_keys = count;
+    gt->gt_version  = header->hdr_version;
+    gt->gt_rev_major  = header->hdr_rev_major;
+    gt->gt_rev_minor  = header->hdr_rev_minor;
+
+    bufcount = count+MAX_KEYS; /* allow for expansion */
+
+    /* Get the PARAMS Tags, if any */
+    if (tif == NULL
+        || !(gt->gt_methods.get)(tif, GTIFF_DOUBLEPARAMS,
+                                 &gt->gt_ndoubles, &gt->gt_double ))
+    {
+        gt->gt_double=(double*)_GTIFcalloc(MAX_VALUES*sizeof(double));
+        if (!gt->gt_double) goto failure;	
+    }
+    if ( tif == NULL
+         || !(gt->gt_methods.get)(tif, GTIFF_ASCIIPARAMS,
+                                  &tempData.tk_asciiParamsLength,
+                                  &tempData.tk_asciiParams ))
+    {
+        tempData.tk_asciiParams         = 0;
+        tempData.tk_asciiParamsLength   = 0;
+    }
+    else
+    {
+        /* last NULL doesn't count; "|" used for delimiter */
+        --tempData.tk_asciiParamsLength;
+    }
+
+    /* allocate space for GeoKey array and its index */
+    gt->gt_keys = (GeoKey *)_GTIFcalloc( sizeof(GeoKey)*bufcount);
+    if (!gt->gt_keys) goto failure;
+    gt->gt_keyindex = (int *)_GTIFcalloc( sizeof(int)*(MAX_KEYINDEX+1));
+    if (!gt->gt_keyindex) goto failure;
+	
+    /*  Loop to get all GeoKeys */
+    entptr = ((KeyEntry *)data) + 1;
+    keyptr = gt->gt_keys;
+    gt->gt_keymin = MAX_KEYINDEX;
+    gt->gt_keymax = 0;
+    for (index=1; index<=count; index++,entptr++)
+    {
+        if (!ReadKey(gt, &tempData, entptr, ++keyptr))
+            goto failure;
+			
+        /* Set up the index (start at 1, since 0=unset) */
+        gt->gt_keyindex[entptr->ent_key] = index;		
+    }
+
+    if( tempData.tk_asciiParams != NULL )
+        _GTIFFree( tempData.tk_asciiParams );
+	
+    return gt;
+	
+  failure:
+    /* Notify of error */
+    GTIFFree (gt);
+    return (GTIF *)0;
+}
+
+/**********************************************************************
+ *
+ *                        Private Routines
+ *
+ **********************************************************************/
+
+/*
+ * Given KeyEntry, read in the GeoKey value location and set up
+ *  the Key structure, returning 0 if failure.
+ */
+
+static int ReadKey(GTIF* gt, TempKeyData* tempData,
+                   KeyEntry* entptr, GeoKey* keyptr)
+{
+    int offset,count;
+	
+    keyptr->gk_key = entptr->ent_key;
+    keyptr->gk_count = entptr->ent_count;
+    count = entptr->ent_count;
+    offset = entptr->ent_val_offset;
+    if (gt->gt_keymin > keyptr->gk_key)  gt->gt_keymin=keyptr->gk_key;
+    if (gt->gt_keymax < keyptr->gk_key)  gt->gt_keymax=keyptr->gk_key;
+	
+    if (entptr->ent_location)
+        keyptr->gk_type = (gt->gt_methods.type)(gt->gt_tif,entptr->ent_location);
+    else
+        keyptr->gk_type = (gt->gt_methods.type)(gt->gt_tif,GTIFF_GEOKEYDIRECTORY);
+	  
+    switch (entptr->ent_location)
+    {
+        case GTIFF_LOCAL:
+            /* store value into data value */
+            *(pinfo_t *)(&keyptr->gk_data) = entptr->ent_val_offset;
+            break;
+        case GTIFF_GEOKEYDIRECTORY:
+            keyptr->gk_data = (char *)(gt->gt_short+offset);
+            if (gt->gt_nshorts < offset+count)
+                gt->gt_nshorts = offset+count;
+            break;
+        case GTIFF_DOUBLEPARAMS:
+            keyptr->gk_data = (char *)(gt->gt_double+offset);
+            if (gt->gt_ndoubles < offset+count)
+                gt->gt_ndoubles = offset+count;
+            break;
+        case GTIFF_ASCIIPARAMS:
+            if( offset + count == tempData->tk_asciiParamsLength + 1 
+                && count > 0 )
+            {
+                /* some vendors seem to feel they should not use the 
+                   terminating '|' char, but do include a terminating '\0'
+                   which we lose in the low level reading code.  
+                   If this is the case, drop the extra character */
+                count--;
+            }
+            else if (offset + count > tempData->tk_asciiParamsLength)
+                return (0);
+
+            keyptr->gk_data = (char *) _GTIFcalloc (MAX(1,count+1));
+            _GTIFmemcpy (keyptr->gk_data,
+                         tempData->tk_asciiParams + offset, count);
+            if( keyptr->gk_data[MAX(0,count-1)] == '|' )
+                keyptr->gk_data[MAX(0,count-1)] = '\0';
+            else
+                keyptr->gk_data[MAX(0,count)] = '\0';
+            break;
+        default:
+            return 0; /* failure */
+    }
+    keyptr->gk_size = _gtiff_size[keyptr->gk_type];
+	
+    return 1; /* success */
+}
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_normalize.c b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_normalize.c
new file mode 100644
index 0000000000..d1a6ce984a
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_normalize.c
@@ -0,0 +1,2402 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  libgeotiff
+ * Purpose:  Code to normalize PCS and other composite codes in a GeoTIFF file.
+ * Author:   Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: geo_normalize.c,v $
+ * Revision 1.45  2005/03/15 16:01:18  fwarmerdam
+ * zero inv flattening interpreted as sphere
+ *
+ * Revision 1.44  2005/03/04 04:32:37  fwarmerdam
+ * added cylindricalequalarea support
+ *
+ * Revision 1.43  2005/03/04 04:02:40  fwarmerdam
+ * Fixed initialization of dfStdParallel2 for AEA and EC.
+ *
+ * Revision 1.42  2005/02/17 01:21:38  fwarmerdam
+ * fixed handling of ProjFalseOrigin{Easting,Northing}GeoKey
+ *
+ * Revision 1.41  2004/12/01 22:06:42  fwarmerdam
+ * bug 698: GTIFGetGCSInfo should not fail on missing pm if pm info not req.
+ *
+ * Revision 1.40  2004/07/09 17:27:37  warmerda
+ * Added 9122 as an alias for simple degrees.
+ *
+ * Revision 1.39  2004/06/07 12:57:13  warmerda
+ * fallback to using gdal_datum.csv if datum.csv not found
+ *
+ * Revision 1.38  2004/03/19 12:20:40  dron
+ * Initialize projection parameters in GTIFFetchProjParms() before using.
+ *
+ * Revision 1.37  2003/07/08 17:31:30  warmerda
+ * cleanup various warnings
+ *
+ * Revision 1.36  2003/01/28 18:31:58  warmerda
+ * Default dfInDegrees in GTIFAngleToDD().
+ *
+ * Revision 1.35  2003/01/15 04:39:16  warmerda
+ * Added GTIFDeaccessCSV
+ *
+ * Revision 1.34  2003/01/15 03:37:40  warmerda
+ * added GTIFFreeMemory()
+ *
+ * Revision 1.33  2002/12/05 19:21:01  warmerda
+ * fixed dfInDegrees to actually be in degrees, not radians!
+ *
+ * Revision 1.32  2002/11/30 16:01:11  warmerda
+ * fixed some problems in GTIFGetUOMAngleInfo
+ *
+ * Revision 1.31  2002/11/30 15:44:35  warmerda
+ * fixed GetCTParms EPSG code mappings
+ *
+ * Revision 1.30  2002/11/28 22:27:42  warmerda
+ * preliminary upgrade to EPSG 6.2.2 tables
+ *
+ * Revision 1.29  2002/06/19 03:51:15  warmerda
+ * migrated cpl_csv.h into cpl_serv.h
+ *
+ * Revision 1.28  2002/01/03 21:28:25  warmerda
+ * call CSVDeaccess(NULL) at end of GTIFPrintDefn()
+ *
+ * Revision 1.27  2001/04/17 13:41:10  warmerda
+ * fix memory leaks in GTIFPrintDefn()
+ *
+ * Revision 1.26  2001/04/17 13:23:07  warmerda
+ * added support for reading custom ellipsoid definitions
+ *
+ * Revision 1.25  2001/03/05 04:55:26  warmerda
+ * CVSDeaccess at end of GTIFGetDefn to avoid file leak
+ *
+ * Revision 1.24  2001/03/05 03:26:29  warmerda
+ * fixed memory leaks in GTIFPrintDefn()
+ *
+ * Revision 1.23  2001/02/23 13:49:48  warmerda
+ * Fixed GTIFPrintDefn() to use fprintf( fp ), instead of printf().
+ *
+ * Revision 1.22  2000/10/13 14:30:57  warmerda
+ * fixed LCC parm order when parameters read directly from geotiff file
+ *
+ * Revision 1.21  2000/09/15 19:30:14  warmerda
+ * report units of linear proj parms
+ *
+ * Revision 1.20  2000/09/15 18:21:07  warmerda
+ * Fixed order of parameters for LCC 2SP.  When parameters
+ * were read from EPSG CSV files the standard parallels and origin
+ * were mixed up.  This affects alot of state plane zones!
+ *
+ * Revision 1.19  2000/06/09 14:05:43  warmerda
+ * added default knowledge of NAD27/NAD83/WGS72/WGS84
+ *
+ * Revision 1.18  1999/12/10 21:28:12  warmerda
+ * fixed Stereographic to look for ProjCenterLat/Long
+ *
+ * Revision 1.17  1999/12/10 20:06:58  warmerda
+ * fixed up scale geokey used for a couple of projections
+ *
+ * Revision 1.16  1999/12/10 19:50:21  warmerda
+ * Added EquidistantConic support, fixed return of StdParallel2GeoKey for
+ * LCC2, and Albers.
+ *
+ * Revision 1.15  1999/12/10 19:39:26  warmerda
+ * Fixed bug setting the false northing for files with
+ * ProjCenterNorthingGeoKey set in GTIFGetDefn().
+ *
+ * Revision 1.14  1999/09/17 14:58:37  warmerda
+ * Added ProjRectifiedGridAngleGeoKey(3096) and support for it's
+ * use with Oblique Mercator in geo_normalize.c.
+ *
+ * Revision 1.13  1999/09/17 00:55:26  warmerda
+ * added GTIFGetUOMAngleInfo(), and UOMAngle in GTIFDefn
+ *
+ * Revision 1.12  1999/09/15 18:51:31  warmerda
+ * Map 9808 to TM South Oriented, not TM Modified Alaska.
+ *
+ * Revision 1.11  1999/09/15 16:44:06  warmerda
+ * Change meter to metre to match EPSG database in GTIFGetUOMLengthInfo()
+ * shortcut.
+ *
+ * Revision 1.10  1999/09/15 16:35:15  warmerda
+ * Fixed the fractions of second handling properly in GTIFAngleStringToDD().
+ *
+ * Revision 1.9  1999/09/15 14:24:17  warmerda
+ * Fixed serious bug in geo_normalize.c with translation of
+ * DD.MMSSsss values.  Return value was seriously off if any
+ * fraction of a second was included in the string.
+ *
+ * Revision 1.8  1999/07/13 03:12:52  warmerda
+ * Make scale a parameter of CT_Stereographic.
+ *
+ * Revision 1.7  1999/05/04 03:13:22  warmerda
+ * fixed a serious bug in parsing DMSmmss.sss values, and a bug in forming DMS strings
+ *
+ * Revision 1.6  1999/05/03 17:50:31  warmerda
+ * avoid warnings on IRIX
+ *
+ * Revision 1.5  1999/04/28 20:04:51  warmerda
+ * Added doxygen style documentation.
+ * Use GTIFPCSToMapSys() and related functions to partially normalize
+ * projections when we don't have the CSV files.
+ *
+ * Revision 1.4  1999/03/18 21:34:59  geotiff
+ * added GTIFDecToDMS
+ *
+ * Revision 1.3  1999/03/17 19:53:15  geotiff
+ * sys includes moved to cpl_serv.h
+ *
+ * Revision 1.2  1999/03/10 18:24:06  geotiff
+ * corrected to use int'
+ *
+ * Revision 1.1  1999/03/09 15:57:04  geotiff
+ * New
+ *
+ * Revision 1.4  1999/03/03 02:29:38  warmerda
+ * Define PI if not already defined.
+ *
+ * Revision 1.3  1999/03/02 21:10:57  warmerda
+ * added lots of projections
+ *
+ * Revision 1.2  1999/02/24 16:24:15  warmerda
+ * Continuing to evolve
+ *
+ * Revision 1.1  1999/02/22 18:51:08  warmerda
+ * New
+ *
+ */
+ 
+#include "cpl_serv.h"
+#include "geo_tiffp.h"
+#include "geovalues.h"
+#include "geo_normalize.h"
+
+#ifndef KvUserDefined
+#  define KvUserDefined 32767
+#endif
+
+#ifndef PI
+#  define PI 3.14159265358979323846
+#endif
+
+/* EPSG Codes for projection parameters.  Unfortunately, these bear no
+   relationship to the GeoTIFF codes even though the names are so similar. */
+
+#define EPSGNatOriginLat         8801
+#define EPSGNatOriginLong        8802
+#define EPSGNatOriginScaleFactor 8805
+#define EPSGFalseEasting         8806
+#define EPSGFalseNorthing        8807
+#define EPSGProjCenterLat        8811
+#define EPSGProjCenterLong       8812
+#define EPSGAzimuth              8813
+#define EPSGAngleRectifiedToSkewedGrid 8814
+#define EPSGInitialLineScaleFactor 8815
+#define EPSGProjCenterEasting    8816
+#define EPSGProjCenterNorthing   8817
+#define EPSGPseudoStdParallelLat 8818
+#define EPSGPseudoStdParallelScaleFactor 8819
+#define EPSGFalseOriginLat       8821
+#define EPSGFalseOriginLong      8822
+#define EPSGStdParallel1Lat      8823
+#define EPSGStdParallel2Lat      8824
+#define EPSGFalseOriginEasting   8826
+#define EPSGFalseOriginNorthing  8827
+#define EPSGSphericalOriginLat   8828
+#define EPSGSphericalOriginLong  8829
+#define EPSGInitialLongitude     8830
+#define EPSGZoneWidth            8831
+
+/************************************************************************/
+/*                           GTIFGetPCSInfo()                           */
+/************************************************************************/
+
+int GTIFGetPCSInfo( int nPCSCode, char **ppszEPSGName, 
+                    short *pnProjOp, short *pnUOMLengthCode, 
+                    short *pnGeogCS )
+
+{
+    char	**papszRecord;
+    char	szSearchKey[24];
+    const char	*pszFilename = CSVFilename( "pcs.csv" );
+    
+/* -------------------------------------------------------------------- */
+/*      Search the units database for this unit.  If we don't find      */
+/*      it return failure.                                              */
+/* -------------------------------------------------------------------- */
+    sprintf( szSearchKey, "%d", nPCSCode );
+    papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE",
+                                     szSearchKey, CC_Integer );
+
+    if( papszRecord == NULL )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Get the name, if requested.                                     */
+/* -------------------------------------------------------------------- */
+    if( ppszEPSGName != NULL )
+    {
+        *ppszEPSGName =
+            CPLStrdup( CSLGetField( papszRecord,
+                                    CSVGetFileFieldId(pszFilename,
+                                                      "COORD_REF_SYS_NAME") ));
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get the UOM Length code, if requested.                          */
+/* -------------------------------------------------------------------- */
+    if( pnUOMLengthCode != NULL )
+    {
+        const char	*pszValue;
+
+        pszValue =
+            CSLGetField( papszRecord,
+                         CSVGetFileFieldId(pszFilename,"UOM_CODE"));
+        if( atoi(pszValue) > 0 )
+            *pnUOMLengthCode = (short) atoi(pszValue);
+        else
+            *pnUOMLengthCode = KvUserDefined;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get the UOM Length code, if requested.                          */
+/* -------------------------------------------------------------------- */
+    if( pnProjOp != NULL )
+    {
+        const char	*pszValue;
+
+        pszValue =
+            CSLGetField( papszRecord,
+                         CSVGetFileFieldId(pszFilename,"COORD_OP_CODE"));
+        if( atoi(pszValue) > 0 )
+            *pnProjOp = (short) atoi(pszValue);
+        else
+            *pnUOMLengthCode = KvUserDefined;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get the GeogCS (Datum with PM) code, if requested.		*/
+/* -------------------------------------------------------------------- */
+    if( pnGeogCS != NULL )
+    {
+        const char	*pszValue;
+
+        pszValue =
+            CSLGetField( papszRecord,
+                         CSVGetFileFieldId(pszFilename,"SOURCE_GEOGCRS_CODE"));
+        if( atoi(pszValue) > 0 )
+            *pnGeogCS = (short) atoi(pszValue);
+        else
+            *pnGeogCS = KvUserDefined;
+    }
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                           GTIFAngleToDD()                            */
+/*                                                                      */
+/*      Convert a numeric angle to decimal degress.                     */
+/************************************************************************/
+
+double GTIFAngleToDD( double dfAngle, int nUOMAngle )
+
+{
+    if( nUOMAngle == 9110 )		/* DDD.MMSSsss */
+    {
+        char	szAngleString[32];
+
+        sprintf( szAngleString, "%12.7f", dfAngle );
+        dfAngle = GTIFAngleStringToDD( szAngleString, nUOMAngle );
+    }
+    else
+    {
+        double		dfInDegrees = 1.0;
+        
+        GTIFGetUOMAngleInfo( nUOMAngle, NULL, &dfInDegrees );
+        dfAngle = dfAngle * dfInDegrees;
+    }
+
+    return( dfAngle );
+}
+
+/************************************************************************/
+/*                        GTIFAngleStringToDD()                         */
+/*                                                                      */
+/*      Convert an angle in the specified units to decimal degrees.     */
+/************************************************************************/
+
+double GTIFAngleStringToDD( const char * pszAngle, int nUOMAngle )
+
+{
+    double	dfAngle;
+    
+    if( nUOMAngle == 9110 )		/* DDD.MMSSsss */
+    {
+        char	*pszDecimal;
+        
+        dfAngle = ABS(atoi(pszAngle));
+        pszDecimal = strchr(pszAngle,'.');
+        if( pszDecimal != NULL && strlen(pszDecimal) > 1 )
+        {
+            char	szMinutes[3];
+            char	szSeconds[64];
+
+            szMinutes[0] = pszDecimal[1];
+            if( pszDecimal[2] >= '0' && pszDecimal[2] <= '9' )
+                szMinutes[1] = pszDecimal[2];
+            else
+                szMinutes[1] = '0';
+            
+            szMinutes[2] = '\0';
+            dfAngle += atoi(szMinutes) / 60.0;
+
+            if( strlen(pszDecimal) > 3 )
+            {
+                szSeconds[0] = pszDecimal[3];
+                if( pszDecimal[4] >= '0' && pszDecimal[4] <= '9' )
+                {
+                    szSeconds[1] = pszDecimal[4];
+                    szSeconds[2] = '.';
+                    strcpy( szSeconds+3, pszDecimal + 5 );
+                }
+                else
+                {
+                    szSeconds[1] = '0';
+                    szSeconds[2] = '\0';
+                }
+                dfAngle += atof(szSeconds) / 3600.0;
+            }
+        }
+
+        if( pszAngle[0] == '-' )
+            dfAngle *= -1;
+    }
+    else if( nUOMAngle == 9105 || nUOMAngle == 9106 )	/* grad */
+    {
+        dfAngle = 180 * (atof(pszAngle ) / 200);
+    }
+    else if( nUOMAngle == 9101 )			/* radians */
+    {
+        dfAngle = 180 * (atof(pszAngle ) / PI);
+    }
+    else if( nUOMAngle == 9103 )			/* arc-minute */
+    {
+        dfAngle = atof(pszAngle) / 60;
+    }
+    else if( nUOMAngle == 9104 )			/* arc-second */
+    {
+        dfAngle = atof(pszAngle) / 3600;
+    }
+    else /* decimal degrees ... some cases missing but seeminly never used */
+    {
+        CPLAssert( nUOMAngle == 9102 || nUOMAngle == KvUserDefined
+                   || nUOMAngle == 0 );
+        
+        dfAngle = atof(pszAngle );
+    }
+
+    return( dfAngle );
+}
+
+/************************************************************************/
+/*                           GTIFGetGCSInfo()                           */
+/*                                                                      */
+/*      Fetch the datum, and prime meridian related to a particular     */
+/*      GCS.                                                            */
+/************************************************************************/
+
+int GTIFGetGCSInfo( int nGCSCode, char ** ppszName,
+                    short * pnDatum, short * pnPM, short *pnUOMAngle )
+
+{
+    char	szSearchKey[24];
+    int		nDatum, nPM, nUOMAngle;
+
+/* -------------------------------------------------------------------- */
+/*      Search the database for the corresponding datum code.           */
+/* -------------------------------------------------------------------- */
+    sprintf( szSearchKey, "%d", nGCSCode );
+
+    nDatum = atoi(CSVGetField( CSVFilename("gcs.csv" ),
+                               "COORD_REF_SYS_CODE", szSearchKey, CC_Integer,
+                               "DATUM_CODE" ) );
+
+/* -------------------------------------------------------------------- */
+/*      Handle some "well known" GCS codes directly if the table        */
+/*      wasn't found.                                                   */
+/* -------------------------------------------------------------------- */
+    if( nDatum < 1 )
+    {
+        const char * pszName = NULL;
+        nPM = PM_Greenwich;
+        nUOMAngle = Angular_DMS_Hemisphere; 
+        if( nGCSCode == GCS_NAD27 )
+        {
+            nDatum = Datum_North_American_Datum_1927;
+            pszName = "NAD27";
+        }
+        else if( nGCSCode == GCS_NAD83 )
+        {
+            nDatum = Datum_North_American_Datum_1983;
+            pszName = "NAD83";
+        }
+        else if( nGCSCode == GCS_WGS_84 )
+        {
+            nDatum = Datum_WGS84;
+            pszName = "WGS 84";
+        }
+        else if( nGCSCode == GCS_WGS_72 )
+        {
+            nDatum = Datum_WGS72;
+            pszName = "WGS 82";
+        }
+        else
+            return FALSE;
+
+        if( ppszName != NULL )
+            *ppszName = CPLStrdup( pszName );
+        if( pnDatum != NULL )
+            *pnDatum = (short) nDatum;
+        if( pnPM != NULL )
+            *pnPM = (short) nPM;
+        if( pnUOMAngle != NULL )
+            *pnUOMAngle = (short) nUOMAngle;
+
+        return TRUE;
+    }
+
+    if( pnDatum != NULL )
+        *pnDatum = (short) nDatum;
+    
+/* -------------------------------------------------------------------- */
+/*      Get the PM.                                                     */
+/* -------------------------------------------------------------------- */
+    if( pnPM != NULL )
+    {
+        nPM = atoi(CSVGetField( CSVFilename("gcs.csv" ),
+                            "COORD_REF_SYS_CODE", szSearchKey, CC_Integer,
+                            "PRIME_MERIDIAN_CODE" ) );
+
+        if( nPM < 1 )
+            return FALSE;
+
+        *pnPM = (short) nPM;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get the angular units.                                          */
+/* -------------------------------------------------------------------- */
+    nUOMAngle = atoi(CSVGetField( CSVFilename("gcs.csv" ),
+                                  "COORD_REF_SYS_CODE",szSearchKey, CC_Integer,
+                                  "UOM_CODE" ) );
+
+    if( nUOMAngle < 1 )
+        return FALSE;
+
+    if( pnUOMAngle != NULL )
+        *pnUOMAngle = (short) nUOMAngle;
+
+/* -------------------------------------------------------------------- */
+/*      Get the name, if requested.                                     */
+/* -------------------------------------------------------------------- */
+    if( ppszName != NULL )
+        *ppszName =
+            CPLStrdup(CSVGetField( CSVFilename("gcs.csv" ),
+                                   "COORD_REF_SYS_CODE",szSearchKey,CC_Integer,
+                                   "COORD_REF_SYS_NAME" ));
+    
+    return( TRUE );
+}
+
+/************************************************************************/
+/*                        GTIFGetEllipsoidInfo()                        */
+/*                                                                      */
+/*      Fetch info about an ellipsoid.  Axes are always returned in     */
+/*      meters.  SemiMajor computed based on inverse flattening         */
+/*      where that is provided.                                         */
+/************************************************************************/
+
+int GTIFGetEllipsoidInfo( int nEllipseCode, char ** ppszName,
+                          double * pdfSemiMajor, double * pdfSemiMinor )
+
+{
+    char	szSearchKey[24];
+    double	dfSemiMajor, dfToMeters = 1.0;
+    int		nUOMLength;
+    
+/* -------------------------------------------------------------------- */
+/*      Get the semi major axis.                                        */
+/* -------------------------------------------------------------------- */
+    sprintf( szSearchKey, "%d", nEllipseCode );
+
+    dfSemiMajor =
+        atof(CSVGetField( CSVFilename("ellipsoid.csv" ),
+                          "ELLIPSOID_CODE", szSearchKey, CC_Integer,
+                          "SEMI_MAJOR_AXIS" ) );
+
+/* -------------------------------------------------------------------- */
+/*      Try some well known ellipsoids.                                 */
+/* -------------------------------------------------------------------- */
+    if( dfSemiMajor == 0.0 )
+    {
+        double     dfInvFlattening, dfSemiMinor;
+        const char *pszName = NULL;
+        
+        if( nEllipseCode == Ellipse_Clarke_1866 )
+        {
+            pszName = "Clarke 1866";
+            dfSemiMajor = 6378206.4;
+            dfSemiMinor = 6356583.8;
+            dfInvFlattening = 0.0;
+        }
+        else if( nEllipseCode == Ellipse_GRS_1980 )
+        {
+            pszName = "GRS 1980";
+            dfSemiMajor = 6378137.0;
+            dfSemiMinor = 0.0;
+            dfInvFlattening = 298.257222101;
+        }
+        else if( nEllipseCode == Ellipse_WGS_84 )
+        {
+            pszName = "WGS 84";
+            dfSemiMajor = 6378137.0;
+            dfSemiMinor = 0.0;
+            dfInvFlattening = 298.257223563;
+        }
+        else if( nEllipseCode == 7043 )
+        {
+            pszName = "WGS 72";
+            dfSemiMajor = 6378135.0;
+            dfSemiMinor = 0.0;
+            dfInvFlattening = 298.26;
+        }
+        else
+            return FALSE;
+
+        if( dfSemiMinor == 0.0 )
+            dfSemiMinor = dfSemiMajor * (1 - 1.0/dfInvFlattening);
+
+        if( pdfSemiMinor != NULL )
+            *pdfSemiMinor = dfSemiMinor;
+        if( pdfSemiMajor != NULL )
+            *pdfSemiMajor = dfSemiMajor;
+        if( ppszName != NULL )
+            *ppszName = CPLStrdup( pszName );
+
+        return TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*	Get the translation factor into meters.				*/
+/* -------------------------------------------------------------------- */
+    nUOMLength = atoi(CSVGetField( CSVFilename("ellipsoid.csv" ),
+                                   "ELLIPSOID_CODE", szSearchKey, CC_Integer,
+                                   "UOM_CODE" ));
+    GTIFGetUOMLengthInfo( nUOMLength, NULL, &dfToMeters );
+
+    dfSemiMajor *= dfToMeters;
+    
+    if( pdfSemiMajor != NULL )
+        *pdfSemiMajor = dfSemiMajor;
+    
+/* -------------------------------------------------------------------- */
+/*      Get the semi-minor if requested.  If the Semi-minor axis        */
+/*      isn't available, compute it based on the inverse flattening.    */
+/* -------------------------------------------------------------------- */
+    if( pdfSemiMinor != NULL )
+    {
+        *pdfSemiMinor =
+            atof(CSVGetField( CSVFilename("ellipsoid.csv" ),
+                              "ELLIPSOID_CODE", szSearchKey, CC_Integer,
+                              "SEMI_MINOR_AXIS" )) * dfToMeters;
+
+        if( *pdfSemiMinor == 0.0 )
+        {
+            double	dfInvFlattening;
+            
+            dfInvFlattening = 
+                atof(CSVGetField( CSVFilename("ellipsoid.csv" ),
+                                  "ELLIPSOID_CODE", szSearchKey, CC_Integer,
+                                  "INV_FLATTENING" ));
+            *pdfSemiMinor = dfSemiMajor * (1 - 1.0/dfInvFlattening);
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get the name, if requested.                                     */
+/* -------------------------------------------------------------------- */
+    if( ppszName != NULL )
+        *ppszName =
+            CPLStrdup(CSVGetField( CSVFilename("ellipsoid.csv" ),
+                                   "ELLIPSOID_CODE", szSearchKey, CC_Integer,
+                                   "ELLIPSOID_NAME" ));
+    
+    return( TRUE );
+}
+
+/************************************************************************/
+/*                           GTIFGetPMInfo()                            */
+/*                                                                      */
+/*      Get the offset between a given prime meridian and Greenwich     */
+/*      in degrees.                                                     */
+/************************************************************************/
+
+int GTIFGetPMInfo( int nPMCode, char ** ppszName, double *pdfOffset )
+
+{
+    char	szSearchKey[24];
+    int		nUOMAngle;
+    const char *pszFilename = CSVFilename("prime_meridian.csv");
+
+/* -------------------------------------------------------------------- */
+/*      Use a special short cut for Greenwich, since it is so common.   */
+/* -------------------------------------------------------------------- */
+    if( nPMCode == PM_Greenwich )
+    {
+        if( pdfOffset != NULL )
+            *pdfOffset = 0.0;
+        if( ppszName != NULL )
+            *ppszName = CPLStrdup( "Greenwich" );
+        return TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Search the database for the corresponding datum code.           */
+/* -------------------------------------------------------------------- */
+    sprintf( szSearchKey, "%d", nPMCode );
+
+    nUOMAngle =
+        atoi(CSVGetField( pszFilename, 
+                          "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer,
+                          "UOM_CODE" ) );
+    if( nUOMAngle < 1 )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Get the PM offset.                                              */
+/* -------------------------------------------------------------------- */
+    if( pdfOffset != NULL )
+    {
+        *pdfOffset =
+            GTIFAngleStringToDD(
+                CSVGetField( pszFilename, 
+                             "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer,
+                             "GREENWICH_LONGITUDE" ),
+                nUOMAngle );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Get the name, if requested.                                     */
+/* -------------------------------------------------------------------- */
+    if( ppszName != NULL )
+        *ppszName =
+            CPLStrdup(
+                CSVGetField( pszFilename, 
+                             "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer,
+                             "PRIME_MERIDIAN_NAME" ));
+    
+    return( TRUE );
+}
+
+/************************************************************************/
+/*                          GTIFGetDatumInfo()                          */
+/*                                                                      */
+/*      Fetch the ellipsoid, and name for a datum.                      */
+/************************************************************************/
+
+int GTIFGetDatumInfo( int nDatumCode, char ** ppszName, short * pnEllipsoid )
+
+{
+    char	szSearchKey[24];
+    int		nEllipsoid;
+    const char *pszFilename = CSVFilename( "datum.csv" );
+    FILE       *fp;
+
+/* -------------------------------------------------------------------- */
+/*      If we can't find datum.csv then gdal_datum.csv is an            */
+/*      acceptable fallback.  Mostly this is for GDAL.                  */
+/* -------------------------------------------------------------------- */
+    if( (fp = VSIFOpen(pszFilename,"r")) == NULL )
+        pszFilename = CSVFilename( "gdal_datum.csv" );
+    else
+        VSIFClose( fp );
+
+/* -------------------------------------------------------------------- */
+/*      Search the database for the corresponding datum code.           */
+/* -------------------------------------------------------------------- */
+    sprintf( szSearchKey, "%d", nDatumCode );
+
+    nEllipsoid = atoi(CSVGetField( pszFilename,
+                                   "DATUM_CODE", szSearchKey, CC_Integer,
+                                   "ELLIPSOID_CODE" ) );
+
+    if( pnEllipsoid != NULL )
+        *pnEllipsoid = (short) nEllipsoid;
+    
+/* -------------------------------------------------------------------- */
+/*      Handle a few built-in datums.                                   */
+/* -------------------------------------------------------------------- */
+    if( nEllipsoid < 1 )
+    {
+        const char *pszName = NULL;
+        
+        if( nDatumCode == Datum_North_American_Datum_1927 )
+        {
+            nEllipsoid = Ellipse_Clarke_1866;
+            pszName = "North American Datum 1927";
+        }
+        else if( nDatumCode == Datum_North_American_Datum_1983 )
+        {
+            nEllipsoid = Ellipse_GRS_1980;
+            pszName = "North American Datum 1983";
+        }
+        else if( nDatumCode == Datum_WGS84 )
+        {
+            nEllipsoid = Ellipse_WGS_84;
+            pszName = "World Geodetic System 1984";
+        }
+        else if( nDatumCode == Datum_WGS72 )
+        {
+            nEllipsoid = 7043; /* WGS7 */
+            pszName = "World Geodetic System 1972";
+        }
+        else
+            return FALSE;
+
+        if( pnEllipsoid != NULL )
+            *pnEllipsoid = (short) nEllipsoid;
+
+        if( ppszName != NULL )
+            *ppszName = CPLStrdup( pszName );
+
+        return TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get the name, if requested.                                     */
+/* -------------------------------------------------------------------- */
+    if( ppszName != NULL )
+        *ppszName =
+            CPLStrdup(CSVGetField( pszFilename,
+                                   "DATUM_CODE", szSearchKey, CC_Integer,
+                                   "DATUM_NAME" ));
+    
+    return( TRUE );
+}
+
+
+/************************************************************************/
+/*                        GTIFGetUOMLengthInfo()                        */
+/*                                                                      */
+/*      Note: This function should eventually also know how to          */
+/*      lookup length aliases in the UOM_LE_ALIAS table.                */
+/************************************************************************/
+
+int GTIFGetUOMLengthInfo( int nUOMLengthCode,
+                          char **ppszUOMName,
+                          double * pdfInMeters )
+
+{
+    char	**papszUnitsRecord;
+    char	szSearchKey[24];
+    int		iNameField;
+    const char *pszFilename;
+
+/* -------------------------------------------------------------------- */
+/*      We short cut meter to save work in the most common case.        */
+/* -------------------------------------------------------------------- */
+    if( nUOMLengthCode == 9001 )
+    {
+        if( ppszUOMName != NULL )
+            *ppszUOMName = CPLStrdup( "metre" );
+        if( pdfInMeters != NULL )
+            *pdfInMeters = 1.0;
+
+        return TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Search the units database for this unit.  If we don't find      */
+/*      it return failure.                                              */
+/* -------------------------------------------------------------------- */
+    pszFilename = CSVFilename( "unit_of_measure.csv" );
+
+    sprintf( szSearchKey, "%d", nUOMLengthCode );
+    papszUnitsRecord =
+        CSVScanFileByName( pszFilename,
+                           "UOM_CODE", szSearchKey, CC_Integer );
+
+    if( papszUnitsRecord == NULL )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Get the name, if requested.                                     */
+/* -------------------------------------------------------------------- */
+    if( ppszUOMName != NULL )
+    {
+        iNameField = CSVGetFileFieldId( pszFilename,
+                                        "UNIT_OF_MEAS_NAME" );
+        *ppszUOMName = CPLStrdup( CSLGetField(papszUnitsRecord, iNameField) );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Get the A and B factor fields, and create the multiplicative    */
+/*      factor.                                                         */
+/* -------------------------------------------------------------------- */
+    if( pdfInMeters != NULL )
+    {
+        int	iBFactorField, iCFactorField;
+        
+        iBFactorField = CSVGetFileFieldId( pszFilename, "FACTOR_B" );
+        iCFactorField = CSVGetFileFieldId( pszFilename, "FACTOR_C" );
+
+        if( atof(CSLGetField(papszUnitsRecord, iCFactorField)) > 0.0 )
+            *pdfInMeters = atof(CSLGetField(papszUnitsRecord, iBFactorField))
+                / atof(CSLGetField(papszUnitsRecord, iCFactorField));
+        else
+            *pdfInMeters = 0.0;
+    }
+    
+    return( TRUE );
+}
+
+/************************************************************************/
+/*                        GTIFGetUOMAngleInfo()                         */
+/************************************************************************/
+
+int GTIFGetUOMAngleInfo( int nUOMAngleCode,
+                         char **ppszUOMName,
+                         double * pdfInDegrees )
+
+{
+    const char	*pszUOMName = NULL;
+    double	dfInDegrees = 1.0;
+    const char *pszFilename = CSVFilename( "unit_of_measure.csv" );
+    char	szSearchKey[24];
+
+    sprintf( szSearchKey, "%d", nUOMAngleCode );
+    pszUOMName = CSVGetField( pszFilename,
+                              "UOM_CODE", szSearchKey, CC_Integer,
+                              "UNIT_OF_MEAS_NAME" );
+
+/* -------------------------------------------------------------------- */
+/*      If the file is found, read from there.  Note that FactorC is    */
+/*      an empty field for any of the DMS style formats, and in this    */
+/*      case we really want to return the default InDegrees value       */
+/*      (1.0) from above.                                               */
+/* -------------------------------------------------------------------- */
+    if( pszUOMName != NULL )
+    {
+        double dfFactorB, dfFactorC, dfInRadians;
+        
+        dfFactorB = 
+            atof(CSVGetField( pszFilename,
+                              "UOM_CODE", szSearchKey, CC_Integer,
+                              "FACTOR_B" ));
+        
+        dfFactorC = 
+            atof(CSVGetField( pszFilename,
+                              "UOM_CODE", szSearchKey, CC_Integer,
+                              "FACTOR_C" ));
+
+        if( dfFactorC != 0.0 )
+        {
+            dfInRadians = (dfFactorB / dfFactorC);
+            dfInDegrees = dfInRadians * 180.0 / PI;
+        }
+                          
+
+        /* We do a special override of some of the DMS formats name */
+        if( nUOMAngleCode == 9102 || nUOMAngleCode == 9107
+            || nUOMAngleCode == 9108 || nUOMAngleCode == 9110
+            || nUOMAngleCode == 9122 )
+        {
+            dfInDegrees = 1.0;
+            pszUOMName = "degree";
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Otherwise handle a few well known units directly.               */
+/* -------------------------------------------------------------------- */
+    else
+    {
+        switch( nUOMAngleCode )
+        {
+          case 9101:
+            pszUOMName = "radian";
+            dfInDegrees = 180.0 / PI;
+            break;
+        
+          case 9102:
+          case 9107:
+          case 9108:
+          case 9110:
+            pszUOMName = "degree";
+            dfInDegrees = 1.0;
+            break;
+
+          case 9103:
+            pszUOMName = "arc-minute";
+            dfInDegrees = 1 / 60.0;
+            break;
+
+          case 9104:
+            pszUOMName = "arc-second";
+            dfInDegrees = 1 / 3600.0;
+            break;
+        
+          case 9105:
+            pszUOMName = "grad";
+            dfInDegrees = 180.0 / 200.0;
+            break;
+
+          case 9106:
+            pszUOMName = "gon";
+            dfInDegrees = 180.0 / 200.0;
+            break;
+        
+          case 9109:
+            pszUOMName = "microradian";
+            dfInDegrees = 180.0 / (PI * 1000000.0);
+            break;
+
+          default:
+            return FALSE;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Return to caller.                                               */
+/* -------------------------------------------------------------------- */
+    if( ppszUOMName != NULL )
+    {
+        if( pszUOMName != NULL )
+            *ppszUOMName = CPLStrdup( pszUOMName );
+        else
+            *ppszUOMName = NULL;
+    }
+
+    if( pdfInDegrees != NULL )
+        *pdfInDegrees = dfInDegrees;
+
+    return( TRUE );
+}
+
+/************************************************************************/
+/*                    EPSGProjMethodToCTProjMethod()                    */
+/*                                                                      */
+/*      Convert between the EPSG enumeration for projection methods,    */
+/*      and the GeoTIFF CT codes.                                       */
+/************************************************************************/
+
+static int EPSGProjMethodToCTProjMethod( int nEPSG )
+
+{
+    /* see trf_method.csv for list of EPSG codes */
+    
+    switch( nEPSG )
+    {
+      case 9801:
+        return( CT_LambertConfConic_1SP );
+
+      case 9802:
+        return( CT_LambertConfConic_2SP );
+
+      case 9803:
+        return( CT_LambertConfConic_2SP ); /* Belgian variant not supported */
+
+      case 9804:
+        return( CT_Mercator );  /* 1SP and 2SP not differentiated */
+
+      case 9805:
+        return( CT_Mercator );  /* 1SP and 2SP not differentiated */
+
+      case 9806:
+        return( CT_CassiniSoldner );
+
+      case 9807:
+        return( CT_TransverseMercator );
+
+      case 9808:
+        return( CT_TransvMercator_SouthOriented );
+
+      case 9809:
+        return( CT_ObliqueStereographic );
+
+      case 9810:
+        return( CT_PolarStereographic );
+
+      case 9811:
+        return( CT_NewZealandMapGrid );
+
+      case 9812:
+        return( CT_ObliqueMercator ); /* is hotine actually different? */
+
+      case 9813:
+        return( CT_ObliqueMercator_Laborde );
+
+      case 9814:
+        return( CT_ObliqueMercator_Rosenmund ); /* swiss  */
+
+      case 9815:
+        return( CT_ObliqueMercator );
+
+      case 9816: /* tunesia mining grid has no counterpart */
+        return( KvUserDefined );
+    }
+
+    return( KvUserDefined );
+}
+
+/************************************************************************/
+/*                            SetGTParmIds()                            */
+/*                                                                      */
+/*      This is hardcoded logic to set the GeoTIFF parmaeter            */
+/*      identifiers for all the EPSG supported projections.  As the     */
+/*      trf_method.csv table grows with new projections, this code      */
+/*      will need to be updated.                                        */
+/************************************************************************/
+
+static int SetGTParmIds( int nCTProjection, 
+                         int *panProjParmId, 
+                         int *panEPSGCodes )
+
+{
+    int anWorkingDummy[7];
+
+    if( panEPSGCodes == NULL )
+        panEPSGCodes = anWorkingDummy;
+    if( panProjParmId == NULL )
+        panProjParmId = anWorkingDummy;
+
+    memset( panEPSGCodes, 0, sizeof(int) * 7 );
+
+    /* psDefn->nParms = 7; */
+    
+    switch( nCTProjection )
+    {
+      case CT_CassiniSoldner:
+      case CT_NewZealandMapGrid:
+        panProjParmId[0] = ProjNatOriginLatGeoKey;
+        panProjParmId[1] = ProjNatOriginLongGeoKey;
+        panProjParmId[5] = ProjFalseEastingGeoKey;
+        panProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        panEPSGCodes[0] = EPSGNatOriginLat;
+        panEPSGCodes[1] = EPSGNatOriginLong;
+        panEPSGCodes[5] = EPSGFalseEasting;
+        panEPSGCodes[6] = EPSGFalseNorthing;
+        return TRUE;
+
+      case CT_ObliqueMercator:
+        panProjParmId[0] = ProjCenterLatGeoKey;
+        panProjParmId[1] = ProjCenterLongGeoKey;
+        panProjParmId[2] = ProjAzimuthAngleGeoKey;
+        panProjParmId[3] = ProjRectifiedGridAngleGeoKey;
+        panProjParmId[4] = ProjScaleAtCenterGeoKey;
+        panProjParmId[5] = ProjFalseEastingGeoKey;
+        panProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        panEPSGCodes[0] = EPSGProjCenterLat;
+        panEPSGCodes[1] = EPSGProjCenterLong;
+        panEPSGCodes[2] = EPSGAzimuth;
+        panEPSGCodes[3] = EPSGAngleRectifiedToSkewedGrid;
+        panEPSGCodes[4] = EPSGInitialLineScaleFactor;
+        panEPSGCodes[5] = EPSGProjCenterEasting;
+        panEPSGCodes[6] = EPSGProjCenterNorthing;
+        return TRUE;
+
+      case CT_ObliqueMercator_Laborde:
+        panProjParmId[0] = ProjCenterLatGeoKey;
+        panProjParmId[1] = ProjCenterLongGeoKey;
+        panProjParmId[2] = ProjAzimuthAngleGeoKey;
+        panProjParmId[4] = ProjScaleAtCenterGeoKey;
+        panProjParmId[5] = ProjFalseEastingGeoKey;
+        panProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        panEPSGCodes[0] = EPSGProjCenterLat;
+        panEPSGCodes[1] = EPSGProjCenterLong;
+        panEPSGCodes[2] = EPSGAzimuth;
+        panEPSGCodes[4] = EPSGInitialLineScaleFactor;
+        panEPSGCodes[5] = EPSGProjCenterEasting;
+        panEPSGCodes[6] = EPSGProjCenterNorthing;
+        return TRUE;
+        
+      case CT_LambertConfConic_1SP:
+      case CT_Mercator:
+      case CT_ObliqueStereographic:
+      case CT_PolarStereographic:
+      case CT_TransverseMercator:
+      case CT_TransvMercator_SouthOriented:
+        panProjParmId[0] = ProjNatOriginLatGeoKey;
+        panProjParmId[1] = ProjNatOriginLongGeoKey;
+        panProjParmId[4] = ProjScaleAtNatOriginGeoKey;
+        panProjParmId[5] = ProjFalseEastingGeoKey;
+        panProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        panEPSGCodes[0] = EPSGNatOriginLat;
+        panEPSGCodes[1] = EPSGNatOriginLong;
+        panEPSGCodes[4] = EPSGNatOriginScaleFactor;
+        panEPSGCodes[5] = EPSGFalseEasting;
+        panEPSGCodes[6] = EPSGFalseNorthing;
+        return TRUE;
+
+      case CT_LambertConfConic_2SP:
+        panProjParmId[0] = ProjFalseOriginLatGeoKey;
+        panProjParmId[1] = ProjFalseOriginLongGeoKey;
+        panProjParmId[2] = ProjStdParallel1GeoKey;
+        panProjParmId[3] = ProjStdParallel2GeoKey;
+        panProjParmId[5] = ProjFalseEastingGeoKey;
+        panProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        panEPSGCodes[0] = EPSGFalseOriginLat;
+        panEPSGCodes[1] = EPSGFalseOriginLong;
+        panEPSGCodes[2] = EPSGStdParallel1Lat;
+        panEPSGCodes[3] = EPSGStdParallel2Lat;
+        panEPSGCodes[5] = EPSGFalseOriginEasting;
+        panEPSGCodes[6] = EPSGFalseOriginNorthing;
+        return TRUE;
+
+      case CT_SwissObliqueCylindrical:
+        panProjParmId[0] = ProjCenterLatGeoKey;
+        panProjParmId[1] = ProjCenterLongGeoKey;
+        panProjParmId[5] = ProjFalseEastingGeoKey;
+        panProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        /* EPSG codes? */
+        return TRUE;
+
+      default:
+        return( FALSE );
+    }
+}
+
+/************************************************************************/
+/*                         GTIFGetProjTRFInfo()                         */
+/*                                                                      */
+/*      Transform a PROJECTION_TRF_CODE into a projection method,       */
+/*      and a set of parameters.  The parameters identify will          */
+/*      depend on the returned method, but they will all have been      */
+/*      normalized into degrees and meters.                             */
+/************************************************************************/
+
+int GTIFGetProjTRFInfo( /* COORD_OP_CODE from coordinate_operation.csv */
+                        int nProjTRFCode, 
+                        char **ppszProjTRFName,
+                        short * pnProjMethod,
+                        double * padfProjParms )
+
+{
+    int		nProjMethod, i, anEPSGCodes[7];
+    double	adfProjParms[7];
+    char	szTRFCode[16];
+    int         nCTProjMethod;
+    char       *pszFilename = CPLStrdup(CSVFilename("projop_wparm.csv"));
+
+/* -------------------------------------------------------------------- */
+/*      Get the proj method.  If this fails to return a meaningful      */
+/*      number, then the whole function fails.                          */
+/* -------------------------------------------------------------------- */
+    sprintf( szTRFCode, "%d", nProjTRFCode );
+    nProjMethod =
+        atoi( CSVGetField( pszFilename,
+                           "COORD_OP_CODE", szTRFCode, CC_Integer,
+                           "COORD_OP_METHOD_CODE" ) );
+    if( nProjMethod == 0 )
+    {
+        CPLFree( pszFilename );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize a definition of what EPSG codes need to be loaded    */
+/*      into what fields in adfProjParms.                               */
+/* -------------------------------------------------------------------- */
+    nCTProjMethod = EPSGProjMethodToCTProjMethod( nProjMethod );
+    SetGTParmIds( nCTProjMethod, NULL, anEPSGCodes );
+
+/* -------------------------------------------------------------------- */
+/*      Get the parameters for this projection.  For the time being     */
+/*      I am assuming the first four parameters are angles, the         */
+/*      fifth is unitless (normally scale), and the remainder are       */
+/*      linear measures.  This works fine for the existing              */
+/*      projections, but is a pretty fragile approach.                  */
+/* -------------------------------------------------------------------- */
+
+    for( i = 0; i < 7; i++ )
+    {
+        char    szParamUOMID[32], szParamValueID[32], szParamCodeID[32];
+        const char *pszValue;
+        int     nUOM;
+        int     nEPSGCode = anEPSGCodes[i];
+        int     iEPSG;
+
+        /* Establish default */
+        if( nEPSGCode == EPSGAngleRectifiedToSkewedGrid )
+            adfProjParms[i] = 90.0;
+        else if( nEPSGCode == EPSGNatOriginScaleFactor
+                 || nEPSGCode == EPSGInitialLineScaleFactor
+                 || nEPSGCode == EPSGPseudoStdParallelScaleFactor )
+            adfProjParms[i] = 1.0;
+        else
+            adfProjParms[i] = 0.0;
+
+        /* If there is no parameter, skip */
+        if( nEPSGCode == 0 )
+            continue;
+
+        /* Find the matching parameter */
+        for( iEPSG = 0; iEPSG < 7; iEPSG++ )
+        {
+            sprintf( szParamCodeID, "PARAMETER_CODE_%d", iEPSG+1 );
+
+            if( atoi(CSVGetField( pszFilename,
+                                  "COORD_OP_CODE", szTRFCode, CC_Integer, 
+                                  szParamCodeID )) == nEPSGCode )
+                break;
+        }
+
+        /* not found, accept the default */
+        if( iEPSG == 7 )
+            continue;
+
+        /* Get the value, and UOM */
+        sprintf( szParamUOMID, "PARAMETER_UOM_%d", iEPSG+1 );
+        sprintf( szParamValueID, "PARAMETER_VALUE_%d", iEPSG+1 );
+
+        nUOM = atoi(CSVGetField( pszFilename,
+                                 "COORD_OP_CODE", szTRFCode, CC_Integer, 
+                                 szParamUOMID ));
+        pszValue = CSVGetField( pszFilename,
+                                "COORD_OP_CODE", szTRFCode, CC_Integer, 
+                                szParamValueID );
+
+        /* Transform according to the UOM */
+        if( nUOM >= 9100 && nUOM < 9200 )
+            adfProjParms[i] = GTIFAngleStringToDD( pszValue, nUOM );
+        else if( nUOM > 9000 && nUOM < 9100 )
+        {
+            double dfInMeters;
+
+            if( !GTIFGetUOMLengthInfo( nUOM, NULL, &dfInMeters ) )
+                dfInMeters = 1.0;
+            adfProjParms[i] = atof(pszValue) * dfInMeters;
+        }
+        else
+            adfProjParms[i] = atof(pszValue);
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get the name, if requested.                                     */
+/* -------------------------------------------------------------------- */
+    if( ppszProjTRFName != NULL )
+    {
+        *ppszProjTRFName =
+            CPLStrdup(CSVGetField( pszFilename,
+                                   "COORD_OP_CODE", szTRFCode, CC_Integer,
+                                   "COORD_OP_NAME" ));
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Transfer requested data into passed variables.                  */
+/* -------------------------------------------------------------------- */
+    if( pnProjMethod != NULL )
+        *pnProjMethod = (short) nProjMethod;
+
+    if( padfProjParms != NULL )
+    {
+        for( i = 0; i < 7; i++ )
+            padfProjParms[i] = adfProjParms[i];
+    }
+
+    CPLFree( pszFilename );
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                         GTIFFetchProjParms()                         */
+/*                                                                      */
+/*      Fetch the projection parameters for a particular projection     */
+/*      from a GeoTIFF file, and fill the GTIFDefn structure out        */
+/*      with them.                                                      */
+/************************************************************************/
+
+static void GTIFFetchProjParms( GTIF * psGTIF, GTIFDefn * psDefn )
+
+{
+    double dfNatOriginLong = 0.0, dfNatOriginLat = 0.0, dfRectGridAngle = 0.0;
+    double dfFalseEasting = 0.0, dfFalseNorthing = 0.0, dfNatOriginScale = 1.0;
+    double dfStdParallel1 = 0.0, dfStdParallel2 = 0.0, dfAzimuth = 0.0;
+
+/* -------------------------------------------------------------------- */
+/*      Get the false easting, and northing if available.               */
+/* -------------------------------------------------------------------- */
+    if( !GTIFKeyGet(psGTIF, ProjFalseEastingGeoKey, &dfFalseEasting, 0, 1)
+        && !GTIFKeyGet(psGTIF, ProjCenterEastingGeoKey,
+                       &dfFalseEasting, 0, 1) 
+        && !GTIFKeyGet(psGTIF, ProjFalseOriginEastingGeoKey,
+                       &dfFalseEasting, 0, 1) )
+        dfFalseEasting = 0.0;
+        
+    if( !GTIFKeyGet(psGTIF, ProjFalseNorthingGeoKey, &dfFalseNorthing,0,1)
+        && !GTIFKeyGet(psGTIF, ProjCenterNorthingGeoKey,
+                       &dfFalseNorthing, 0, 1)
+        && !GTIFKeyGet(psGTIF, ProjFalseOriginNorthingGeoKey,
+                       &dfFalseNorthing, 0, 1) )
+        dfFalseNorthing = 0.0;
+        
+    switch( psDefn->CTProjection )
+    {
+/* -------------------------------------------------------------------- */
+      case CT_Stereographic:
+/* -------------------------------------------------------------------- */
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey, 
+                       &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey, 
+                       &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjScaleAtNatOriginGeoKey,
+                       &dfNatOriginScale, 0, 1 ) == 0 )
+            dfNatOriginScale = 1.0;
+            
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfNatOriginLat;
+        psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
+        psDefn->ProjParm[4] = dfNatOriginScale;
+        psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_LambertConfConic_1SP:
+      case CT_Mercator:
+      case CT_ObliqueStereographic:
+      case CT_TransverseMercator:
+      case CT_TransvMercator_SouthOriented:
+/* -------------------------------------------------------------------- */
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey, 
+                       &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey, 
+                       &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjScaleAtNatOriginGeoKey,
+                       &dfNatOriginScale, 0, 1 ) == 0 )
+            dfNatOriginScale = 1.0;
+            
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfNatOriginLat;
+        psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
+        psDefn->ProjParm[4] = dfNatOriginScale;
+        psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_ObliqueMercator: /* hotine */
+/* -------------------------------------------------------------------- */
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey, 
+                       &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey, 
+                       &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjAzimuthAngleGeoKey, 
+                       &dfAzimuth, 0, 1 ) == 0 )
+            dfAzimuth = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjRectifiedGridAngleGeoKey,
+                       &dfRectGridAngle, 0, 1 ) == 0 )
+            dfRectGridAngle = 90.0;
+
+        if( GTIFKeyGet(psGTIF, ProjScaleAtNatOriginGeoKey,
+                       &dfNatOriginScale, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjScaleAtCenterGeoKey,
+                          &dfNatOriginScale, 0, 1 ) == 0 )
+            dfNatOriginScale = 1.0;
+            
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfNatOriginLat;
+        psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
+        psDefn->ProjParm[2] = dfAzimuth;
+        psDefn->ProjParmId[2] = ProjAzimuthAngleGeoKey;
+        psDefn->ProjParm[3] = dfRectGridAngle;
+        psDefn->ProjParmId[3] = ProjRectifiedGridAngleGeoKey;
+        psDefn->ProjParm[4] = dfNatOriginScale;
+        psDefn->ProjParmId[4] = ProjScaleAtCenterGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_CassiniSoldner:
+      case CT_Polyconic:
+/* -------------------------------------------------------------------- */
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey, 
+                       &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey, 
+                       &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjScaleAtNatOriginGeoKey,
+                       &dfNatOriginScale, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjScaleAtCenterGeoKey,
+                          &dfNatOriginScale, 0, 1 ) == 0 )
+            dfNatOriginScale = 1.0;
+            
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfNatOriginLat;
+        psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
+        psDefn->ProjParm[4] = dfNatOriginScale;
+        psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_AzimuthalEquidistant:
+      case CT_MillerCylindrical:
+      case CT_Equirectangular:
+      case CT_Gnomonic:
+      case CT_LambertAzimEqualArea:
+      case CT_Orthographic:
+/* -------------------------------------------------------------------- */
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey, 
+                       &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey, 
+                       &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfNatOriginLat;
+        psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_Robinson:
+      case CT_Sinusoidal:
+      case CT_VanDerGrinten:
+/* -------------------------------------------------------------------- */
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey, 
+                       &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_PolarStereographic:
+/* -------------------------------------------------------------------- */
+        if( GTIFKeyGet(psGTIF, ProjStraightVertPoleLongGeoKey, 
+                       &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey, 
+                       &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjScaleAtNatOriginGeoKey,
+                       &dfNatOriginScale, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjScaleAtCenterGeoKey,
+                          &dfNatOriginScale, 0, 1 ) == 0 )
+            dfNatOriginScale = 1.0;
+            
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfNatOriginLat;
+        psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjStraightVertPoleLongGeoKey;
+        psDefn->ProjParm[4] = dfNatOriginScale;
+        psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_LambertConfConic_2SP:
+/* -------------------------------------------------------------------- */
+        if( GTIFKeyGet(psGTIF, ProjStdParallel1GeoKey, 
+                       &dfStdParallel1, 0, 1 ) == 0 )
+            dfStdParallel1 = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjStdParallel2GeoKey, 
+                       &dfStdParallel2, 0, 1 ) == 0 )
+            dfStdParallel1 = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey, 
+                       &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey, 
+                       &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfNatOriginLat;
+        psDefn->ProjParmId[0] = ProjFalseOriginLatGeoKey;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjFalseOriginLongGeoKey;
+        psDefn->ProjParm[2] = dfStdParallel1;
+        psDefn->ProjParmId[2] = ProjStdParallel1GeoKey;
+        psDefn->ProjParm[3] = dfStdParallel2;
+        psDefn->ProjParmId[3] = ProjStdParallel2GeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_AlbersEqualArea:
+      case CT_EquidistantConic:
+/* -------------------------------------------------------------------- */
+        if( GTIFKeyGet(psGTIF, ProjStdParallel1GeoKey, 
+                       &dfStdParallel1, 0, 1 ) == 0 )
+            dfStdParallel1 = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjStdParallel2GeoKey, 
+                       &dfStdParallel2, 0, 1 ) == 0 )
+            dfStdParallel2 = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey, 
+                       &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey, 
+                       &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey, 
+                          &dfNatOriginLat, 0, 1 ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfStdParallel1;
+        psDefn->ProjParmId[0] = ProjStdParallel1GeoKey;
+        psDefn->ProjParm[1] = dfStdParallel2;
+        psDefn->ProjParmId[1] = ProjStdParallel2GeoKey;
+        psDefn->ProjParm[2] = dfNatOriginLat;
+        psDefn->ProjParmId[2] = ProjNatOriginLatGeoKey;
+        psDefn->ProjParm[3] = dfNatOriginLong;
+        psDefn->ProjParmId[3] = ProjNatOriginLongGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_CylindricalEqualArea:
+/* -------------------------------------------------------------------- */
+        if( GTIFKeyGet(psGTIF, ProjStdParallel1GeoKey, 
+                       &dfStdParallel1, 0, 1 ) == 0 )
+            dfStdParallel1 = 0.0;
+
+        if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey, 
+                       &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0
+            && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey, 
+                          &dfNatOriginLong, 0, 1 ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfStdParallel1;
+        psDefn->ProjParmId[0] = ProjStdParallel1GeoKey;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+    }
+}
+
+/************************************************************************/
+/*                            GTIFGetDefn()                             */
+/************************************************************************/
+
+/**
+@param psGTIF GeoTIFF information handle as returned by GTIFNew.
+@param psDefn Pointer to an existing GTIFDefn structure.  This structure
+does not need to have been pre-initialized at all.
+
+@return TRUE if the function has been successful, otherwise FALSE.
+
+This function reads the coordinate system definition from a GeoTIFF file,
+and <i>normalizes</i> it into a set of component information using 
+definitions from CSV (Comma Seperated Value ASCII) files derived from 
+EPSG tables.  This function is intended to simplify correct support for
+reading files with defined PCS (Projected Coordinate System) codes that
+wouldn't otherwise be directly known by application software by reducing
+it to the underlying projection method, parameters, datum, ellipsoid, 
+prime meridian and units.<p>
+
+The application should pass a pointer to an existing uninitialized 
+GTIFDefn structure, and GTIFGetDefn() will fill it in.  The fuction 
+currently always returns TRUE but in the future will return FALSE if 
+CSV files are not found.  In any event, all geokeys actually found in the
+file will be copied into the GTIFDefn.  However, if the CSV files aren't
+found codes implied by other codes will not be set properly.<p>
+
+GTIFGetDefn() will not generally work if the EPSG derived CSV files cannot
+be found.  By default a modest attempt will be made to find them, but 
+in general it is necessary for the calling application to override the
+logic to find them.  This can be done by calling the 
+SetCSVFilenameHook() function to
+override the search method based on application knowledge of where they are
+found.<p>
+
+The normalization methodology operates by fetching tags from the GeoTIFF
+file, and then setting all other tags implied by them in the structure.  The
+implied relationships are worked out by reading definitions from the 
+various EPSG derived CSV tables.<p>
+
+For instance, if a PCS (ProjectedCSTypeGeoKey) is found in the GeoTIFF file
+this code is used to lookup a record in the <tt>horiz_cs.csv</tt> CSV
+file.  For example given the PCS 26746 we can find the name
+(NAD27 / California zone VI), the GCS 4257 (NAD27), and the ProjectionCode
+10406 (California CS27 zone VI).  The GCS, and ProjectionCode can in turn
+be looked up in other tables until all the details of units, ellipsoid, 
+prime meridian, datum, projection (LambertConfConic_2SP) and projection
+parameters are established.  A full listgeo dump of a file 
+for this result might look like the following, all based on a single PCS
+value:<p>
+
+<pre>
+% listgeo -norm ~/data/geotiff/pci_eg/spaf27.tif
+Geotiff_Information:
+   Version: 1
+   Key_Revision: 1.0
+   Tagged_Information:
+      ModelTiepointTag (2,3):
+         0                0                0                
+         1577139.71       634349.176       0                
+      ModelPixelScaleTag (1,3):
+         195.509321       198.32184        0                
+      End_Of_Tags.
+   Keyed_Information:
+      GTModelTypeGeoKey (Short,1): ModelTypeProjected
+      GTRasterTypeGeoKey (Short,1): RasterPixelIsArea
+      ProjectedCSTypeGeoKey (Short,1): PCS_NAD27_California_VI
+      End_Of_Keys.
+   End_Of_Geotiff.
+
+PCS = 26746 (NAD27 / California zone VI)
+Projection = 10406 (California CS27 zone VI)
+Projection Method: CT_LambertConfConic_2SP
+   ProjStdParallel1GeoKey: 33.883333
+   ProjStdParallel2GeoKey: 32.766667
+   ProjFalseOriginLatGeoKey: 32.166667
+   ProjFalseOriginLongGeoKey: -116.233333
+   ProjFalseEastingGeoKey: 609601.219202
+   ProjFalseNorthingGeoKey: 0.000000
+GCS: 4267/NAD27
+Datum: 6267/North American Datum 1927
+Ellipsoid: 7008/Clarke 1866 (6378206.40,6356583.80)
+Prime Meridian: 8901/Greenwich (0.000000)
+Projection Linear Units: 9003/US survey foot (0.304801m)
+</pre>
+
+Note that GTIFGetDefn() does not inspect or return the tiepoints and scale.
+This must be handled seperately as it normally would.  It is intended to
+simplify capture and normalization of the coordinate system definition.  
+Note that GTIFGetDefn() also does the following things:
+
+<ol>
+<li> Convert all angular values to decimal degrees.
+<li> Convert all linear values to meters. 
+<li> Return the linear units and conversion to meters for the tiepoints and
+scale (though the tiepoints and scale remain in their native units). 
+<li> When reading projection parameters a variety of differences between
+different GeoTIFF generators are handled, and a normalized set of parameters
+for each projection are always returned.
+</ol>
+
+Code fields in the GTIFDefn are filled with KvUserDefined if there is not
+value to assign.  The parameter lists for each of the underlying projection
+transform methods can be found at the
+<a href="http://www.remotesensing.org/geotiff/proj_list">Projections</a>
+page.  Note that nParms will be set based on the maximum parameter used.
+Some of the parameters may not be used in which case the
+GTIFDefn::ProjParmId[] will
+be zero.  This is done to retain correspondence to the EPSG parameter
+numbering scheme.<p>
+
+The 
+<a href="http://www.remotesensing.org/cgi-bin/cvsweb.cgi/~checkout~/osrs/geotiff/libgeotiff/geotiff_proj4.c">geotiff_proj4.c</a> module distributed with libgeotiff can 
+be used as an example of code that converts a GTIFDefn into another projection
+system.<p>
+
+@see GTIFKeySet(), SetCSVFilenameHook()
+
+*/
+
+int GTIFGetDefn( GTIF * psGTIF, GTIFDefn * psDefn )
+
+{
+    int		i;
+    short	nGeogUOMLinear;
+    double	dfInvFlattening;
+    
+/* -------------------------------------------------------------------- */
+/*      Initially we default all the information we can.                */
+/* -------------------------------------------------------------------- */
+    psDefn->Model = KvUserDefined;
+    psDefn->PCS = KvUserDefined;
+    psDefn->GCS = KvUserDefined;
+    psDefn->UOMLength = KvUserDefined;
+    psDefn->UOMLengthInMeters = 1.0;
+    psDefn->UOMAngle = KvUserDefined;
+    psDefn->UOMAngleInDegrees = 1.0;
+    psDefn->Datum = KvUserDefined;
+    psDefn->Ellipsoid = KvUserDefined;
+    psDefn->SemiMajor = 0.0;
+    psDefn->SemiMinor = 0.0;
+    psDefn->PM = KvUserDefined;
+    psDefn->PMLongToGreenwich = 0.0;
+
+    psDefn->ProjCode = KvUserDefined;
+    psDefn->Projection = KvUserDefined;
+    psDefn->CTProjection = KvUserDefined;
+
+    psDefn->nParms = 0;
+    for( i = 0; i < MAX_GTIF_PROJPARMS; i++ )
+    {
+        psDefn->ProjParm[i] = 0.0;
+        psDefn->ProjParmId[i] = 0;
+    }
+
+    psDefn->MapSys = KvUserDefined;
+    psDefn->Zone = 0;
+
+/* -------------------------------------------------------------------- */
+/*	Try to get the overall model type.				*/
+/* -------------------------------------------------------------------- */
+    GTIFKeyGet(psGTIF,GTModelTypeGeoKey,&(psDefn->Model),0,1);
+
+/* -------------------------------------------------------------------- */
+/*	Extract the Geog units.  					*/
+/* -------------------------------------------------------------------- */
+    nGeogUOMLinear = 9001; /* Linear_Meter */
+    GTIFKeyGet(psGTIF, GeogLinearUnitsGeoKey, &nGeogUOMLinear, 0, 1 );
+
+/* -------------------------------------------------------------------- */
+/*      Try to get a PCS.                                               */
+/* -------------------------------------------------------------------- */
+    if( GTIFKeyGet(psGTIF,ProjectedCSTypeGeoKey, &(psDefn->PCS),0,1) == 1
+        && psDefn->PCS != KvUserDefined )
+    {
+        /*
+         * Translate this into useful information.
+         */
+        GTIFGetPCSInfo( psDefn->PCS, NULL, &(psDefn->ProjCode),
+                        &(psDefn->UOMLength), &(psDefn->GCS) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*       If we have the PCS code, but didn't find it in the CSV files   */
+/*      (likely because we can't find them) we will try some ``jiffy    */
+/*      rules'' for UTM and state plane.                                */
+/* -------------------------------------------------------------------- */
+    if( psDefn->PCS != KvUserDefined && psDefn->ProjCode == KvUserDefined )
+    {
+        int	nMapSys, nZone;
+        int	nGCS = psDefn->GCS;
+
+        nMapSys = GTIFPCSToMapSys( psDefn->PCS, &nGCS, &nZone );
+        if( nMapSys != KvUserDefined )
+        {
+            psDefn->ProjCode = (short) GTIFMapSysToProj( nMapSys, nZone );
+            psDefn->GCS = (short) nGCS;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If the Proj_ code is specified directly, use that.              */
+/* -------------------------------------------------------------------- */
+    if( psDefn->ProjCode == KvUserDefined )
+        GTIFKeyGet(psGTIF, ProjectionGeoKey, &(psDefn->ProjCode), 0, 1 );
+    
+    if( psDefn->ProjCode != KvUserDefined )
+    {
+        /*
+         * We have an underlying projection transformation value.  Look
+         * this up.  For a PCS of ``WGS 84 / UTM 11'' the transformation
+         * would be Transverse Mercator, with a particular set of options.
+         * The nProjTRFCode itself would correspond to the name
+         * ``UTM zone 11N'', and doesn't include datum info.
+         */
+        GTIFGetProjTRFInfo( psDefn->ProjCode, NULL, &(psDefn->Projection),
+                            psDefn->ProjParm );
+        
+        /*
+         * Set the GeoTIFF identity of the parameters.
+         */
+        psDefn->CTProjection = (short) 
+            EPSGProjMethodToCTProjMethod( psDefn->Projection );
+
+        SetGTParmIds( psDefn->CTProjection, psDefn->ProjParmId, NULL);
+        psDefn->nParms = 7;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try to get a GCS.  If found, it will override any implied by    */
+/*      the PCS.                                                        */
+/* -------------------------------------------------------------------- */
+    GTIFKeyGet(psGTIF, GeographicTypeGeoKey, &(psDefn->GCS), 0, 1 );
+
+/* -------------------------------------------------------------------- */
+/*      Derive the datum, and prime meridian from the GCS.              */
+/* -------------------------------------------------------------------- */
+    if( psDefn->GCS != KvUserDefined )
+    {
+        GTIFGetGCSInfo( psDefn->GCS, NULL, &(psDefn->Datum), &(psDefn->PM),
+                        &(psDefn->UOMAngle) );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Handle the GCS angular units.  GeogAngularUnitsGeoKey           */
+/*      overrides the GCS or PCS setting.                               */
+/* -------------------------------------------------------------------- */
+    GTIFKeyGet(psGTIF, GeogAngularUnitsGeoKey, &(psDefn->UOMAngle), 0, 1 );
+    if( psDefn->UOMAngle != KvUserDefined )
+    {
+        GTIFGetUOMAngleInfo( psDefn->UOMAngle, NULL,
+                             &(psDefn->UOMAngleInDegrees) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for a datum setting, and then use the datum to derive     */
+/*      an ellipsoid.                                                   */
+/* -------------------------------------------------------------------- */
+    GTIFKeyGet(psGTIF, GeogGeodeticDatumGeoKey, &(psDefn->Datum), 0, 1 );
+
+    if( psDefn->Datum != KvUserDefined )
+    {
+        GTIFGetDatumInfo( psDefn->Datum, NULL, &(psDefn->Ellipsoid) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for an explicit ellipsoid.  Use the ellipsoid to          */
+/*      derive the ellipsoid characteristics, if possible.              */
+/* -------------------------------------------------------------------- */
+    GTIFKeyGet(psGTIF, GeogEllipsoidGeoKey, &(psDefn->Ellipsoid), 0, 1 );
+
+    if( psDefn->Ellipsoid != KvUserDefined )
+    {
+        GTIFGetEllipsoidInfo( psDefn->Ellipsoid, NULL,
+                              &(psDefn->SemiMajor), &(psDefn->SemiMinor) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for overridden ellipsoid parameters.  It would be nice    */
+/*      to warn if they conflict with provided information, but for     */
+/*      now we just override.                                           */
+/* -------------------------------------------------------------------- */
+    GTIFKeyGet(psGTIF, GeogSemiMajorAxisGeoKey, &(psDefn->SemiMajor), 0, 1 );
+    GTIFKeyGet(psGTIF, GeogSemiMinorAxisGeoKey, &(psDefn->SemiMinor), 0, 1 );
+    
+    if( GTIFKeyGet(psGTIF, GeogInvFlatteningGeoKey, &dfInvFlattening, 
+                   0, 1 ) == 1 )
+    {
+        if( dfInvFlattening != 0.0 )
+            psDefn->SemiMinor = 
+                psDefn->SemiMajor * (1 - 1.0/dfInvFlattening);
+        else
+            psDefn->SemiMinor = psDefn->SemiMajor;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Get the prime meridian info.                                    */
+/* -------------------------------------------------------------------- */
+    GTIFKeyGet(psGTIF, GeogPrimeMeridianGeoKey, &(psDefn->PM), 0, 1 );
+
+    if( psDefn->PM != KvUserDefined )
+    {
+        GTIFGetPMInfo( psDefn->PM, NULL, &(psDefn->PMLongToGreenwich) );
+    }
+    else
+    {
+        GTIFKeyGet(psGTIF, GeogPrimeMeridianLongGeoKey,
+                   &(psDefn->PMLongToGreenwich), 0, 1 );
+
+        psDefn->PMLongToGreenwich =
+            GTIFAngleToDD( psDefn->PMLongToGreenwich,
+                           psDefn->UOMAngle );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Have the projection units of measure been overridden?  We       */
+/*      should likely be doing something about angular units too,       */
+/*      but these are very rarely not decimal degrees for actual        */
+/*      file coordinates.                                               */
+/* -------------------------------------------------------------------- */
+    GTIFKeyGet(psGTIF,ProjLinearUnitsGeoKey,&(psDefn->UOMLength),0,1);
+
+    if( psDefn->UOMLength != KvUserDefined )
+    {
+        GTIFGetUOMLengthInfo( psDefn->UOMLength, NULL,
+                              &(psDefn->UOMLengthInMeters) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Handle a variety of user defined transform types.               */
+/* -------------------------------------------------------------------- */
+    if( GTIFKeyGet(psGTIF,ProjCoordTransGeoKey,
+                   &(psDefn->CTProjection),0,1) == 1)
+    {
+        GTIFFetchProjParms( psGTIF, psDefn );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try to set the zoned map system information.                    */
+/* -------------------------------------------------------------------- */
+    psDefn->MapSys = GTIFProjToMapSys( psDefn->ProjCode, &(psDefn->Zone) );
+
+/* -------------------------------------------------------------------- */
+/*      If this is UTM, and we were unable to extract the projection    */
+/*      parameters from the CSV file, just set them directly now,       */
+/*      since it's pretty easy, and a common case.                      */
+/* -------------------------------------------------------------------- */
+    if( (psDefn->MapSys == MapSys_UTM_North
+         || psDefn->MapSys == MapSys_UTM_South)
+        && psDefn->CTProjection == KvUserDefined )
+    {
+        psDefn->CTProjection = CT_TransverseMercator;
+        psDefn->nParms = 7;
+        psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
+        psDefn->ProjParm[0] = 0.0;
+            
+        psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
+        psDefn->ProjParm[1] = psDefn->Zone*6 - 183.0;
+        
+        psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
+        psDefn->ProjParm[4] = 0.9996;
+        
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[5] = 500000.0;
+        
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        if( psDefn->MapSys == MapSys_UTM_North )
+            psDefn->ProjParm[6] = 0.0;
+        else
+            psDefn->ProjParm[6] = 10000000.0;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      For now we forceable deaccess all CSV files to reduce the       */
+/*      chance of "leakage".  Really, this should be application        */
+/*      controlled.                                                     */
+/* -------------------------------------------------------------------- */
+    CSVDeaccess( NULL );
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                            GTIFDecToDMS()                            */
+/*                                                                      */
+/*      Convenient function to translate decimal degrees to DMS         */
+/*      format for reporting to a user.                                 */
+/************************************************************************/
+
+const char *GTIFDecToDMS( double dfAngle, const char * pszAxis,
+                          int nPrecision )
+
+{
+    int		nDegrees, nMinutes;
+    double	dfSeconds;
+    char	szFormat[30];
+    static char szBuffer[50];
+    const char	*pszHemisphere = NULL;
+    double	dfRound;
+    int		i;
+
+    dfRound = 0.5/60;
+    for( i = 0; i < nPrecision; i++ )
+        dfRound = dfRound * 0.1;
+
+    nDegrees = (int) ABS(dfAngle);
+    nMinutes = (int) ((ABS(dfAngle) - nDegrees) * 60 + dfRound);
+    dfSeconds = ABS((ABS(dfAngle) * 3600 - nDegrees*3600 - nMinutes*60));
+
+    if( EQUAL(pszAxis,"Long") && dfAngle < 0.0 )
+        pszHemisphere = "W";
+    else if( EQUAL(pszAxis,"Long") )
+        pszHemisphere = "E";
+    else if( dfAngle < 0.0 )
+        pszHemisphere = "S";
+    else
+        pszHemisphere = "N";
+
+    sprintf( szFormat, "%%3dd%%2d\'%%%d.%df\"%s",
+             nPrecision+3, nPrecision, pszHemisphere );
+    sprintf( szBuffer, szFormat, nDegrees, nMinutes, dfSeconds );
+
+    return( szBuffer );
+}
+
+/************************************************************************/
+/*                           GTIFPrintDefn()                            */
+/*                                                                      */
+/*      Report the contents of a GTIFDefn structure ... mostly for      */
+/*      debugging.                                                      */
+/************************************************************************/
+
+void GTIFPrintDefn( GTIFDefn * psDefn, FILE * fp )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Get the PCS name if possible.                                   */
+/* -------------------------------------------------------------------- */
+    if( psDefn->PCS != KvUserDefined )
+    {
+        char	*pszPCSName = NULL;
+    
+        GTIFGetPCSInfo( psDefn->PCS, &pszPCSName, NULL, NULL, NULL );
+        if( pszPCSName == NULL )
+            pszPCSName = CPLStrdup("name unknown");
+        
+        fprintf( fp, "PCS = %d (%s)\n", psDefn->PCS, pszPCSName );
+        CPLFree( pszPCSName );
+    }
+
+/* -------------------------------------------------------------------- */
+/*	Dump the projection code if possible.				*/
+/* -------------------------------------------------------------------- */
+    if( psDefn->ProjCode != KvUserDefined )
+    {
+        char	*pszTRFName = NULL;
+
+        GTIFGetProjTRFInfo( psDefn->ProjCode, &pszTRFName, NULL, NULL );
+        if( pszTRFName == NULL )
+            pszTRFName = CPLStrdup("");
+                
+        fprintf( fp, "Projection = %d (%s)\n",
+                 psDefn->ProjCode, pszTRFName );
+
+        CPLFree( pszTRFName );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try to dump the projection method name, and parameters if possible.*/
+/* -------------------------------------------------------------------- */
+    if( psDefn->CTProjection != KvUserDefined )
+    {
+        char	*pszName = GTIFValueName(ProjCoordTransGeoKey,
+                                         psDefn->CTProjection);
+        int     i;
+
+        if( pszName == NULL )
+            pszName = "(unknown)";
+            
+        fprintf( fp, "Projection Method: %s\n", pszName );
+
+        for( i = 0; i < psDefn->nParms; i++ )
+        {
+            if( psDefn->ProjParmId[i] == 0 )
+                continue;
+
+            pszName = GTIFKeyName((geokey_t) psDefn->ProjParmId[i]);
+            if( pszName == NULL )
+                pszName = "(unknown)";
+
+            if( i < 4 )
+            {
+                char	*pszAxisName;
+                
+                if( strstr(pszName,"Long") != NULL )
+                    pszAxisName = "Long";
+                else if( strstr(pszName,"Lat") != NULL )
+                    pszAxisName = "Lat";
+                else
+                    pszAxisName = "?";
+                
+                fprintf( fp, "   %s: %f (%s)\n",
+                         pszName, psDefn->ProjParm[i],
+                         GTIFDecToDMS( psDefn->ProjParm[i], pszAxisName, 2 ) );
+            }
+            else if( i == 4 )
+                fprintf( fp, "   %s: %f\n", pszName, psDefn->ProjParm[i] );
+            else
+                fprintf( fp, "   %s: %f m\n", pszName, psDefn->ProjParm[i] );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Report the GCS name, and number.                                */
+/* -------------------------------------------------------------------- */
+    if( psDefn->GCS != KvUserDefined )
+    {
+        char	*pszName = NULL;
+
+        GTIFGetGCSInfo( psDefn->GCS, &pszName, NULL, NULL, NULL );
+        if( pszName == NULL )
+            pszName = CPLStrdup("(unknown)");
+        
+        fprintf( fp, "GCS: %d/%s\n", psDefn->GCS, pszName );
+        CPLFree( pszName );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Report the datum name.                                          */
+/* -------------------------------------------------------------------- */
+    if( psDefn->Datum != KvUserDefined )
+    {
+        char	*pszName = NULL;
+
+        GTIFGetDatumInfo( psDefn->Datum, &pszName, NULL );
+        if( pszName == NULL )
+            pszName = CPLStrdup("(unknown)");
+        
+        fprintf( fp, "Datum: %d/%s\n", psDefn->Datum, pszName );
+        CPLFree( pszName );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Report the ellipsoid.                                           */
+/* -------------------------------------------------------------------- */
+    if( psDefn->Ellipsoid != KvUserDefined )
+    {
+        char	*pszName = NULL;
+
+        GTIFGetEllipsoidInfo( psDefn->Ellipsoid, &pszName, NULL, NULL );
+        if( pszName == NULL )
+            pszName = CPLStrdup("(unknown)");
+        
+        fprintf( fp, "Ellipsoid: %d/%s (%.2f,%.2f)\n",
+                 psDefn->Ellipsoid, pszName,
+                 psDefn->SemiMajor, psDefn->SemiMinor );
+        CPLFree( pszName );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Report the prime meridian.                                      */
+/* -------------------------------------------------------------------- */
+    if( psDefn->PM != KvUserDefined )
+    {
+        char	*pszName = NULL;
+
+        GTIFGetPMInfo( psDefn->PM, &pszName, NULL );
+
+        if( pszName == NULL )
+            pszName = CPLStrdup("(unknown)");
+        
+        fprintf( fp, "Prime Meridian: %d/%s (%f/%s)\n",
+                 psDefn->PM, pszName,
+                 psDefn->PMLongToGreenwich,
+                 GTIFDecToDMS( psDefn->PMLongToGreenwich, "Long", 2 ) );
+        CPLFree( pszName );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Report the projection units of measure (currently just          */
+/*      linear).                                                        */
+/* -------------------------------------------------------------------- */
+    if( psDefn->UOMLength != KvUserDefined )
+    {
+        char	*pszName = NULL;
+
+        GTIFGetUOMLengthInfo( psDefn->UOMLength, &pszName, NULL );
+        if( pszName == NULL )
+            pszName = CPLStrdup( "(unknown)" );
+        
+        fprintf( fp, "Projection Linear Units: %d/%s (%fm)\n",
+                 psDefn->UOMLength, pszName, psDefn->UOMLengthInMeters );
+        CPLFree( pszName );
+    }
+
+    CSVDeaccess( NULL );
+}
+
+/************************************************************************/
+/*                           GTIFFreeMemory()                           */
+/*                                                                      */
+/*      Externally visible function to free memory allocated within     */
+/*      geo_normalize.c.                                                */
+/************************************************************************/
+
+void GTIFFreeMemory( char * pMemory )
+
+{
+    if( pMemory != NULL )
+        VSIFree( pMemory );
+}
+
+/************************************************************************/
+/*                          GTIFDeaccessCSV()                           */
+/*                                                                      */
+/*      Free all cached CSV info.                                       */
+/************************************************************************/
+
+void GTIFDeaccessCSV()
+
+{
+    CSVDeaccess( NULL );
+}
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_normalize.h b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_normalize.h
new file mode 100644
index 0000000000..d792c0cb06
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_normalize.h
@@ -0,0 +1,238 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  libgeotiff
+ * Purpose:  Include file related to geo_normalize.c containing Code to
+ *           normalize PCS and other composite codes in a GeoTIFF file.
+ * Author:   Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: geo_normalize.h,v $
+ * Revision 1.12  2005/08/26 16:08:14  fwarmerdam
+ * Include void in empty argument list for prototype.
+ *
+ * Revision 1.11  2004/02/03 17:19:50  warmerda
+ * export GTIFAngleToDD() - used by GDAL mrsiddataset.cpp
+ *
+ * Revision 1.10  2003/01/15 04:39:16  warmerda
+ * Added GTIFDeaccessCSV
+ *
+ * Revision 1.9  2003/01/15 03:37:40  warmerda
+ * added GTIFFreeMemory()
+ *
+ * Revision 1.8  2002/11/28 22:27:42  warmerda
+ * preliminary upgrade to EPSG 6.2.2 tables
+ *
+ * Revision 1.7  1999/09/17 00:55:26  warmerda
+ * added GTIFGetUOMAngleInfo(), and UOMAngle in GTIFDefn
+ *
+ * Revision 1.6  1999/05/04 03:13:42  warmerda
+ * Added prototype
+ *
+ * Revision 1.5  1999/04/29 23:02:55  warmerda
+ * added docs, and MapSys related stuff
+ *
+ * Revision 1.4  1999/03/18 21:35:19  geotiff
+ * Added PROJ.4 related stuff
+ *
+ * Revision 1.3  1999/03/17 20:44:04  geotiff
+ * added CPL_DLL related support
+ *
+ * Revision 1.2  1999/03/10 18:24:06  geotiff
+ * corrected to use int'
+ *
+ */
+
+#ifndef GEO_NORMALIZE_H_INCLUDED
+#define GEO_NORMALIZE_H_INCLUDED
+
+#include <stdio.h>
+#include "geotiff.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file geo_normalize.h
+ *
+ * Include file for extended projection definition normalization api.
+ */
+    
+#define MAX_GTIF_PROJPARMS 	10
+
+/**
+ * Holds a definition of a coordinate system in normalized form.
+ */
+
+typedef struct {
+    /** From GTModelTypeGeoKey tag.  Can have the values ModelTypeGeographic
+        or ModelTypeProjected. */
+    short	Model;
+
+    /** From ProjectedCSTypeGeoKey tag.  For example PCS_NAD27_UTM_zone_3N.*/
+    short	PCS;
+
+    /** From GeographicTypeGeoKey tag.  For example GCS_WGS_84 or
+        GCS_Voirol_1875_Paris.  Includes datum and prime meridian value. */
+    short	GCS;	      
+
+    /** From ProjLinearUnitsGeoKey.  For example Linear_Meter. */
+    short	UOMLength;
+
+    /** One UOMLength = UOMLengthInMeters meters. */
+    double	UOMLengthInMeters;
+
+    /** The angular units of the GCS. */
+    short       UOMAngle;
+
+    /** One UOMAngle = UOMLengthInDegrees degrees. */
+    double      UOMAngleInDegrees;
+    
+    /** Datum from GeogGeodeticDatumGeoKey tag. For example Datum_WGS84 */
+    short	Datum;
+
+    /** Prime meridian from GeogPrimeMeridianGeoKey.  For example PM_Greenwich
+        or PM_Paris. */
+    short	PM;
+
+    /** Decimal degrees of longitude between this prime meridian and
+        Greenwich.  Prime meridians to the west of Greenwich are negative. */
+    double	PMLongToGreenwich;
+
+    /** Ellipsoid identifier from GeogELlipsoidGeoKey.  For example
+        Ellipse_Clarke_1866. */
+    short	Ellipsoid;
+
+    /** The length of the semi major ellipse axis in meters. */
+    double	SemiMajor;
+
+    /** The length of the semi minor ellipse axis in meters. */
+    double	SemiMinor;
+
+    /** Projection id from ProjectionGeoKey.  For example Proj_UTM_11S. */
+    short	ProjCode;
+
+    /** EPSG identifier for underlying projection method.  From the EPSG
+        TRF_METHOD table.  */
+    short	Projection;
+
+    /** GeoTIFF identifier for underlying projection method.  While some of
+      these values have corresponding vlaues in EPSG (Projection field),
+      others do not.  For example CT_TransverseMercator. */
+    short	CTProjection;   
+
+    /** Number of projection parameters in ProjParm and ProjParmId. */
+    int		nParms;
+
+    /** Projection parameter value.  The identify of this parameter
+        is established from the corresponding entry in ProjParmId.  The
+        value will be measured in meters, or decimal degrees if it is a
+        linear or angular measure. */
+    double	ProjParm[MAX_GTIF_PROJPARMS];
+
+    /** Projection parameter identifier.  For example ProjFalseEastingGeoKey.
+        The value will be 0 for unused table entries. */
+    int		ProjParmId[MAX_GTIF_PROJPARMS]; /* geokey identifier,
+                                                   eg. ProjFalseEastingGeoKey*/
+
+    /** Special zone map system code (MapSys_UTM_South, MapSys_UTM_North,
+        MapSys_State_Plane or KvUserDefined if none apply. */
+    int		MapSys;
+
+    /** UTM, or State Plane Zone number, zero if not known. */
+    int		Zone;
+
+} GTIFDefn;
+
+int CPL_DLL GTIFGetPCSInfo( int nPCSCode, char **ppszEPSGName,
+                            short *pnProjOp, 
+                            short *pnUOMLengthCode, short *pnGeogCS );
+int CPL_DLL GTIFGetProjTRFInfo( int nProjTRFCode,
+                                char ** ppszProjTRFName,
+                                short * pnProjMethod,
+                                double * padfProjParms );
+int CPL_DLL GTIFGetGCSInfo( int nGCSCode, char **ppszName,
+                            short *pnDatum, short *pnPM, short *pnUOMAngle );
+int CPL_DLL GTIFGetDatumInfo( int nDatumCode, char **ppszName,
+                              short * pnEllipsoid );
+int CPL_DLL GTIFGetEllipsoidInfo( int nEllipsoid, char ** ppszName,
+                                  double * pdfSemiMajor,
+                                  double * pdfSemiMinor );
+int CPL_DLL GTIFGetPMInfo( int nPM, char **ppszName,
+                           double * pdfLongToGreenwich );
+
+double CPL_DLL GTIFAngleStringToDD( const char *pszAngle, int nUOMAngle );
+int CPL_DLL GTIFGetUOMLengthInfo( int nUOMLengthCode,
+                                  char **ppszUOMName,
+                                  double * pdfInMeters );
+int CPL_DLL GTIFGetUOMAngleInfo( int nUOMAngleCode,
+                                 char **ppszUOMName,
+                                 double * pdfInDegrees );
+double CPL_DLL GTIFAngleToDD( double dfAngle, int nUOMAngle );
+    
+
+/* this should be used to free strings returned by GTIFGet... funcs */
+void CPL_DLL GTIFFreeMemory( char * );
+void CPL_DLL GTIFDeaccessCSV( void );
+
+int CPL_DLL GTIFGetDefn( GTIF *psGTIF, GTIFDefn * psDefn );
+void CPL_DLL GTIFPrintDefn( GTIFDefn *, FILE * );
+void CPL_DLL GTIFFreeDefn( GTIF * );
+
+void CPL_DLL SetCSVFilenameHook( const char *(*CSVFileOverride)(const char *) );
+
+const char CPL_DLL *GTIFDecToDMS( double, const char *, int );
+
+/*
+ * These are useful for recognising UTM and State Plane, with or without
+ * CSV files being found.
+ */
+
+#define MapSys_UTM_North	-9001
+#define MapSys_UTM_South	-9002
+#define MapSys_State_Plane_27	-9003
+#define MapSys_State_Plane_83	-9004
+
+int CPL_DLL   GTIFMapSysToPCS( int MapSys, int Datum, int nZone );
+int CPL_DLL   GTIFMapSysToProj( int MapSys, int nZone );
+int CPL_DLL   GTIFPCSToMapSys( int PCSCode, int * pDatum, int * pZone );
+int CPL_DLL   GTIFProjToMapSys( int ProjCode, int * pZone );
+
+/*
+ * These are only useful if using libgeotiff with libproj (PROJ.4+).
+ */
+char CPL_DLL *GTIFGetProj4Defn( GTIFDefn * );
+int  CPL_DLL  GTIFProj4ToLatLong( GTIFDefn *, int, double *, double * );
+int  CPL_DLL  GTIFProj4FromLatLong( GTIFDefn *, int, double *, double * );
+
+#if defined(HAVE_LIBPROJ) && defined(HAVE_PROJECTS_H)
+#  define HAVE_GTIFPROJ4
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+    
+#endif /* ndef GEO_NORMALIZE_H_INCLUDED */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_print.c b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_print.c
new file mode 100644
index 0000000000..4b969432e0
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_print.c
@@ -0,0 +1,517 @@
+/**********************************************************************
+ *
+ *  geo_print.c  -- Key-dumping routines for GEOTIFF files.
+ *
+ *    Written By: Niles D. Ritter.
+ *
+ *  copyright (c) 1995   Niles D. Ritter
+ *
+ *  Permission granted to use this software, so long as this copyright
+ *  notice accompanies any products derived therefrom.
+ *
+ *  Revision History;
+ *
+ *    20 June,  1995      Niles D. Ritter      New
+ *     7 July,  1995      NDR                  Fix indexing
+ *    27 July,  1995      NDR                  Added Import utils
+ *    28 July,  1995      NDR                  Made parser more strict.
+ *    29  Sep,  1995      NDR                  Fixed matrix printing.
+ *
+ * $Log: geo_print.c,v $
+ * Revision 1.9  2004/10/19 14:24:09  fwarmerdam
+ * dynamically allocate tag list so large lists work: Oliver Colin
+ *
+ * Revision 1.8  2004/04/27 21:31:31  warmerda
+ * avoid crash if gt_tif is NULL
+ *
+ * Revision 1.7  2003/10/21 19:19:53  warmerda
+ * fixed bug with large message texts sometimes causing a crash
+ *
+ * Revision 1.6  2003/09/23 18:27:30  warmerda
+ * fixed bug with long datum names: bug 399
+ *
+ * Revision 1.5  2003/07/08 17:31:30  warmerda
+ * cleanup various warnings
+ *
+ * Revision 1.4  2002/05/31 14:27:26  warmerda
+ * added escaping in metadata for string key values
+ *
+ * Revision 1.3  1999/05/04 03:14:35  warmerda
+ * avoid warnings
+ *
+ * Revision 1.2  1999/05/03 17:50:31  warmerda
+ * avoid warnings on IRIX
+ *
+ *
+ **********************************************************************/
+
+#include "geotiff.h"   /* public interface        */
+#include "geo_tiffp.h" /* external TIFF interface */
+#include "geo_keyp.h"  /* private interface       */
+#include "geokeys.h"
+
+#include <stdio.h>     /* for sprintf             */
+
+#define FMT_GEOTIFF "Geotiff_Information:"
+#define FMT_VERSION "Version: %hd"
+#define FMT_REV     "Key_Revision: %1hd.%hd"
+#define FMT_TAGS    "Tagged_Information:"
+#define FMT_TAGEND  "End_Of_Tags."
+#define FMT_KEYS    "Keyed_Information:"
+#define FMT_KEYEND  "End_Of_Keys."
+#define FMT_GEOEND  "End_Of_Geotiff."
+#define FMT_DOUBLE  "%-17.9g"
+#define FMT_SHORT   "%-11hd"
+
+static void DefaultPrint(char *string, void *aux);
+static void PrintKey(GeoKey *key, GTIFPrintMethod print,void *aux);
+static void PrintGeoTags(GTIF *gtif,GTIFReadMethod scan,void *aux);
+static void PrintTag(int tag, int nrows, double *data, int ncols, 
+					GTIFPrintMethod print,void *aux);
+static void DefaultRead(char *string, void *aux);
+static int  ReadKey(GTIF *gt, GTIFReadMethod scan, void *aux);
+static int  ReadTag(GTIF *gt,GTIFReadMethod scan,void *aux);
+
+/*
+ * Print off the directory info, using whatever method is specified
+ * (defaults to fprintf if null). The "aux" parameter is provided for user
+ * defined method for passing parameters or whatever.
+ *
+ * The output format is a "GeoTIFF meta-data" file, which may be
+ * used to import information with the GTIFFImport() routine.
+ */
+ 
+void GTIFPrint(GTIF *gtif, GTIFPrintMethod print,void *aux)
+{
+    int i;
+    int numkeys = gtif->gt_num_keys;
+    GeoKey *key = gtif->gt_keys;
+    char message[1024];
+	
+    if (!print) print = (GTIFPrintMethod) &DefaultPrint;
+    if (!aux) aux=stdout;	
+
+    sprintf(message,FMT_GEOTIFF "\n"); 
+    print(message,aux);
+    sprintf(message, "Version: %hd" ,gtif->gt_version);
+    sprintf(message, FMT_VERSION,gtif->gt_version);
+    print("   ",aux); print(message,aux); print("\n",aux);
+    sprintf(message, FMT_REV,gtif->gt_rev_major,
+            gtif->gt_rev_minor); 
+    print("   ",aux); print(message,aux); print("\n",aux);
+
+    sprintf(message,"   %s\n",FMT_TAGS); print(message,aux);
+    PrintGeoTags(gtif,print,aux);
+    sprintf(message,"      %s\n",FMT_TAGEND); print(message,aux);
+
+    sprintf(message,"   %s\n",FMT_KEYS); print(message,aux);
+    for (i=0; i<numkeys; i++)
+        PrintKey(++key,print,aux);
+    sprintf(message,"      %s\n",FMT_KEYEND); print(message,aux);
+
+    sprintf(message,"   %s\n",FMT_GEOEND); print(message,aux);
+}
+
+static void PrintGeoTags(GTIF *gt, GTIFPrintMethod print,void *aux)
+{
+	double *data;
+	int count;
+	tiff_t *tif=gt->gt_tif;
+
+        if( tif == NULL )
+            return;
+
+	if ((gt->gt_methods.get)(tif, GTIFF_TIEPOINTS, &count, &data ))
+		PrintTag(GTIFF_TIEPOINTS,count/3, data, 3, print, aux);
+	if ((gt->gt_methods.get)(tif, GTIFF_PIXELSCALE, &count, &data ))
+		PrintTag(GTIFF_PIXELSCALE,count/3, data, 3, print, aux);
+	if ((gt->gt_methods.get)(tif, GTIFF_TRANSMATRIX, &count, &data ))
+		PrintTag(GTIFF_TRANSMATRIX,count/4, data, 4, print, aux);
+}
+
+static void PrintTag(int tag, int nrows, double *dptr, int ncols, 
+					GTIFPrintMethod print,void *aux)
+{
+	int i,j;
+	double *data=dptr;
+        char message[1024];
+
+	print("      ",aux);
+	print(GTIFTagName(tag),aux);
+	sprintf(message," (%d,%d):\n",nrows,ncols);
+	print(message,aux);
+	for (i=0;i<nrows;i++)
+	{
+		print("         ",aux);
+		for (j=0;j<ncols;j++)
+		{
+			sprintf(message,FMT_DOUBLE,*data++);
+			print(message,aux);
+		}
+		print("\n",aux);
+	}
+	_GTIFFree(dptr); /* free up the allocated memory */
+}
+
+
+static void PrintKey(GeoKey *key, GTIFPrintMethod print, void *aux)
+{
+    char *data;
+    geokey_t keyid = (geokey_t) key->gk_key;
+    int count = key->gk_count;
+    int vals_now,i;
+    pinfo_t *sptr;
+    double *dptr;
+    char message[40];
+
+    print("      ",aux);
+    print(GTIFKeyName(keyid),aux);
+	
+    sprintf(message," (%s,%d): ",GTIFTypeName(key->gk_type),count);
+    print(message,aux);
+	
+    if (key->gk_type==TYPE_SHORT && count==1)
+        data = (char *)&key->gk_data;
+    else
+        data = key->gk_data;
+		
+    switch (key->gk_type)
+    {
+      case TYPE_ASCII: 
+      {
+          int  in_char, out_char;
+
+          print("\"",aux);
+
+          in_char = 0;
+          out_char = 0;
+          while( in_char < count-1 )
+          {
+              char ch = ((char *) data)[in_char++];
+
+              if( ch == '\n' )
+              {
+                  message[out_char++] = '\\';
+                  message[out_char++] = 'n';
+              }
+              else if( ch == '\\' )
+              {
+                  message[out_char++] = '\\';
+                  message[out_char++] = '\\';
+              }
+              else
+                  message[out_char++] = ch;
+
+              /* flush message if buffer full */
+              if( out_char >= sizeof(message)-3 )
+              {
+                  message[out_char] = '\0';
+                  print(message,aux);
+                  out_char = 0;
+              }
+          }
+
+          message[out_char]='\0';
+          print(message,aux);
+
+          print("\"\n",aux);
+      }
+      break;
+        
+      case TYPE_DOUBLE: 
+        for (dptr = (double *)data; count > 0; count-= vals_now)
+        {
+            vals_now = count > 3? 3: count;
+            for (i=0; i<vals_now; i++,dptr++)
+            {
+                sprintf(message,FMT_DOUBLE ,*dptr);
+                print(message,aux);
+            }
+            print("\n",aux);
+        }
+        break;
+        
+      case TYPE_SHORT: 
+        sptr = (pinfo_t *)data;
+        if (count==1)
+        {
+            print( GTIFValueName(keyid,*sptr), aux );
+            print( "\n", aux );
+        }
+        else
+            for (; count > 0; count-= vals_now)
+            {
+                vals_now = count > 3? 3: count;
+                for (i=0; i<vals_now; i++,sptr++)
+                {
+                    sprintf(message,FMT_SHORT,*sptr);
+                    print(message,aux);
+                }
+                print("\n",aux);
+            }
+        break;
+        
+      default: 
+        sprintf(message, "Unknown Type (%d)\n",key->gk_type);
+        print(message,aux);
+        break;
+    }
+}
+
+static void DefaultPrint(char *string, void *aux)
+{
+    /* Pretty boring */
+    fprintf((FILE *)aux,string);
+}
+
+
+/*
+ *  Importing metadata file
+ */
+
+/*
+ * Import the directory info, using whatever method is specified
+ * (defaults to fscanf if null). The "aux" parameter is provided for user
+ * defined method for passing file or whatever.
+ *
+ * The input format is a "GeoTIFF meta-data" file, which may be
+ * generated by the GTIFFPrint() routine.
+ */
+ 
+int GTIFImport(GTIF *gtif, GTIFReadMethod scan,void *aux)
+{
+    int status;
+    char message[1024];
+	
+    if (!scan) scan = (GTIFReadMethod) &DefaultRead;
+    if (!aux) aux=stdin;	
+	
+    scan(message,aux);
+    if (strncmp(message,FMT_GEOTIFF,8)) return 0; 
+    scan(message,aux);
+    if (!sscanf(message,FMT_VERSION,&gtif->gt_version)) return 0;
+    scan(message,aux);
+    if (sscanf(message,FMT_REV,&gtif->gt_rev_major,
+               &gtif->gt_rev_minor) !=2) return 0;
+
+    scan(message,aux);
+    if (strncmp(message,FMT_TAGS,8)) return 0;
+    while ((status=ReadTag(gtif,scan,aux))>0);
+    if (status < 0) return 0;
+
+    scan(message,aux);
+    if (strncmp(message,FMT_KEYS,8)) return 0;
+    while ((status=ReadKey(gtif,scan,aux))>0);
+	
+    return (status==0); /* success */
+}
+
+static int StringError(char *string)
+{
+    fprintf(stderr,"Parsing Error at \'%s\'\n",string);
+    return -1;
+}
+
+#define SKIPWHITE(vptr) \
+  while (*vptr && (*vptr==' '||*vptr=='\t')) vptr++
+#define FINDCHAR(vptr,c) \
+  while (*vptr && *vptr!=(c)) vptr++
+
+static int ReadTag(GTIF *gt,GTIFReadMethod scan,void *aux)
+{
+    int i,j,tag;
+    char *vptr;
+    char tagname[100];
+    double *data,*dptr;
+    int count,nrows,ncols,num;
+    char message[1024];
+
+    scan(message,aux);
+    if (!strncmp(message,FMT_TAGEND,8)) return 0;
+
+    num=sscanf(message,"%[^( ] (%d,%d):\n",tagname,&nrows,&ncols);
+    if (num!=3) return StringError(message);
+	
+    tag = GTIFTagCode(tagname);
+    if (tag < 0) return StringError(tagname);
+
+    count = nrows*ncols;
+
+    data = (double *) _GTIFcalloc(count * sizeof(double));
+    dptr = data;
+	
+    for (i=0;i<nrows;i++)
+    {
+        scan(message,aux);
+        vptr = message;
+        for (j=0;j<ncols;j++)
+        {
+            if (!sscanf(vptr,"%lg",dptr++))
+                return StringError(vptr);
+            FINDCHAR(vptr,' ');
+            SKIPWHITE(vptr);
+        }
+    }	
+    (gt->gt_methods.set)(gt->gt_tif, (pinfo_t) tag, count, data );	
+
+    _GTIFFree( data );
+
+    return 1;
+}
+
+
+static int ReadKey(GTIF *gt, GTIFReadMethod scan, void *aux)
+{
+    tagtype_t ktype;
+    int count,outcount;
+    int vals_now,i;
+    geokey_t key;
+    int icode;
+    pinfo_t code;
+    short  *sptr;
+    char name[1000];
+    char type[20];
+    double data[100];
+    double *dptr;
+    char *vptr;
+    int num;
+    char message[2048];
+
+    scan(message,aux); 
+    if (!strncmp(message,FMT_KEYEND,8)) return 0;
+
+    num=sscanf(message,"%[^( ] (%[^,],%d):\n",name,type,&count);
+    if (num!=3) return StringError(message);
+
+    vptr = message;
+    FINDCHAR(vptr,':'); 
+    if (!*vptr) return StringError(message);
+    vptr+=2;
+
+    if( GTIFKeyCode(name) < 0 )
+        return StringError(name);
+    else
+        key = (geokey_t) GTIFKeyCode(name);
+
+    if( GTIFTypeCode(type) < 0 )
+        return StringError(type);
+    else
+        ktype = (tagtype_t) GTIFTypeCode(type);
+
+    /* skip white space */
+    SKIPWHITE(vptr);
+    if (!*vptr) return StringError(message);
+			
+    switch (ktype)
+    {
+      case TYPE_ASCII: 
+      {
+          char *cdata;
+          int out_char = 0;
+
+          FINDCHAR(vptr,'"');
+          if (!*vptr) return StringError(message);
+
+          cdata = (char *) _GTIFcalloc( count+1 );
+
+          vptr++;
+          while( out_char < count-1 )
+          {
+              if( *vptr == '\0' )
+                  break;
+
+              else if( vptr[0] == '\\' && vptr[1] == 'n' )
+              {
+                  cdata[out_char++] = '\n';
+                  vptr += 2;
+              }
+              else if( vptr[0] == '\\' && vptr[1] == '\\' )
+              {
+                  cdata[out_char++] = '\\';
+                  vptr += 2;
+              }
+              else
+                  cdata[out_char++] = *(vptr++);
+          }
+
+          if( out_char < count-1 ) return StringError(message);
+          if( *vptr != '"' ) return StringError(message);
+
+          cdata[count-1] = '\0';
+          GTIFKeySet(gt,key,ktype,count,cdata);
+
+          _GTIFFree( cdata );
+      }
+      break;
+        
+      case TYPE_DOUBLE: 
+        outcount = count;
+        for (dptr = data; count > 0; count-= vals_now)
+        {
+            vals_now = count > 3? 3: count;
+            for (i=0; i<vals_now; i++,dptr++)
+            {
+                if (!sscanf(vptr,"%lg" ,dptr))
+                    StringError(vptr);
+                FINDCHAR(vptr,' ');
+                SKIPWHITE(vptr);
+            }
+            if (vals_now<count)
+            {
+                scan(message,aux);
+                vptr = message;
+            }
+        }
+        if (outcount==1)
+            GTIFKeySet(gt,key,ktype,outcount,data[0]);
+        else
+            GTIFKeySet(gt,key,ktype,outcount,data);
+        break;
+        
+      case TYPE_SHORT: 
+        if (count==1)
+        {
+            icode = GTIFValueCode(key,vptr);
+            if (icode < 0) return StringError(vptr);
+            code = (pinfo_t) icode;
+            GTIFKeySet(gt,key,ktype,count,code);
+        }
+        else  /* multi-valued short - no such thing yet */
+        {
+            sptr = (short *)data;
+            outcount = count;
+            for (; count > 0; count-= vals_now)
+            {
+                vals_now = count > 3? 3: count;
+                for (i=0; i<vals_now; i++,sptr++)
+                {
+                    int		work_int;
+
+                    /* note: FMT_SHORT (%11hd) not supported on IRIX */
+                    sscanf(message,"%11d",&work_int);
+                    *sptr = (short) work_int;
+                    scan(message,aux);
+                }
+                if (vals_now<count)
+                {
+                    scan(message,aux);
+                    vptr = message;
+                }
+            }
+            GTIFKeySet(gt,key,ktype,outcount,sptr);			
+        }
+        break;
+        
+      default: 
+        return -1;
+    }
+    return 1;
+}
+
+
+static void DefaultRead(char *string, void *aux)
+{
+	/* Pretty boring */
+	fscanf((FILE *)aux,"%[^\n]\n",string);
+}
+
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_set.c b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_set.c
new file mode 100644
index 0000000000..48831796a9
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_set.c
@@ -0,0 +1,262 @@
+/**********************************************************************
+ *
+ *  geo_set.c  -- Public routines for GEOTIFF GeoKey access.
+ *
+ *    Written By: Niles D. Ritter.
+ *
+ *  copyright (c) 1995   Niles D. Ritter
+ *
+ *  Permission granted to use this software, so long as this copyright
+ *  notice accompanies any products derived therefrom.
+ *
+ * $Log: geo_set.c,v $
+ * Revision 1.11  2004/04/27 21:32:33  warmerda
+ * reformat for clarity
+ *
+ * Revision 1.10  2003/07/08 17:31:30  warmerda
+ * cleanup various warnings
+ *
+ * Revision 1.9  2003/01/15 03:37:19  warmerda
+ * avoid warning
+ *
+ * Revision 1.8  2002/09/27 13:05:33  warmerda
+ * allow dynamic set/delete of ASCII tags. ASCIIPARAMS now kept split
+ *
+ * Revision 1.7  2001/05/02 16:48:22  warmerda
+ * fixed a couple bugs in delete code
+ *
+ * Revision 1.6  2001/05/02 13:54:34  warmerda
+ * updated geo_set.c to support deleting tags
+ *
+ * Revision 1.5  1999/05/04 03:09:33  warmerda
+ * avoid warnings
+ *
+ * Revision 1.4  1999/05/03 17:50:31  warmerda
+ * avoid warnings on IRIX
+ *
+ * Revision 1.3  1999/04/28 19:59:38  warmerda
+ * added some doxygen style documentation
+ *
+ * Revision 1.2  1999/03/11 17:39:38  geotiff
+ * Added fix for case where a key is being overwritten.
+ *
+ **********************************************************************/
+
+#include "geotiff.h"   /* public interface        */
+#include "geo_tiffp.h" /* external TIFF interface */
+#include "geo_keyp.h"  /* private interface       */
+
+#include <assert.h>
+
+/**
+This function writes a geokey_t value to a GeoTIFF file.
+
+@param gtif The geotiff information handle from GTIFNew().
+
+@param keyID The geokey_t name (such as ProjectedCSTypeGeoKey).
+This must come from the list of legal geokey_t values
+(an enumeration) listed below.
+
+@param val The <b>val</b> argument is a pointer to the
+variable into which the value should be read.  The type of the variable
+varies depending on the geokey_t given.  While there is no ready mapping
+of geokey_t values onto types, in general code values are of type <i>short</i>,
+citations are strings, and everything else is of type <i>double</i>.  Note
+that pointer's to <i>int</i> should never be passed to GTIFKeyGet() for
+integer values as they will be shorts, and the int's may not be properly
+initialized (and will be grossly wrong on MSB systems).
+
+@param index Indicates how far into the list of values
+for this geokey to offset. Should normally be zero.
+
+@param count Indicates how many values
+to read.  At this time all keys except for strings have only one value,
+so <b>index</b> should be zero, and <b>count</b> should be one.<p>
+
+The <b>key</b> indicates the key name to be written to the
+file and should from the geokey_t enumeration 
+(eg. <tt>ProjectedCSTypeGeoKey</tt>).  The full list of possible geokey_t
+values can be found in geokeys.inc, or in the online documentation for
+GTIFKeyGet().<p>
+
+The <b>type</b> should be one of TYPE_SHORT, TYPE_ASCII, or TYPE_DOUBLE and
+will indicate the type of value being passed at the end of the argument
+list (the key value).  The <b>count</b> should be one except for strings
+when it should be the length of the string (or zero to for this to be
+computed internally).  As a special case a <b>count</b> of -1 can be
+used to request an existing key be deleted, in which no value is passed.<p>
+
+The actual value is passed at the end of the argument list, and should be
+a short, a double, or a char * value.  Note that short and double values
+are passed as is, not as pointers.<p>
+
+Note that key values aren't actually flushed to the file until
+GTIFWriteKeys() is called.  Till then 
+the new values are just kept with the GTIF structure.<p>
+
+<b>Example:</b><p>
+
+<pre>
+    GTIFKeySet(gtif, GTRasterTypeGeoKey, TYPE_SHORT, 1, 
+               RasterPixelIsArea);
+    GTIFKeySet(gtif, GTCitationGeoKey, TYPE_ASCII, 0, 
+               "UTM 11 North / NAD27" );
+</pre>
+
+ */
+
+int GTIFKeySet(GTIF *gtif, geokey_t keyID, tagtype_t type, int count,...)
+{
+    va_list ap;
+    int index = gtif->gt_keyindex[ keyID ];
+    int newvalues = 0;
+    GeoKey *key;
+    char *data = NULL;
+    char *val = NULL;
+    pinfo_t sval;
+    double dval;
+
+    va_start(ap, count);
+    /* pass singleton keys by value */
+    if (count>1 && type!=TYPE_ASCII) 
+    {
+        val = va_arg(ap, char*);
+    }
+    else if( count == -1 )
+    {
+        /* delete the indicated tag */
+        va_end(ap);
+
+        if( index < 1 )
+            return 0;
+
+        if (gtif->gt_keys[index].gk_type == TYPE_ASCII)
+        {
+            _GTIFFree (gtif->gt_keys[index].gk_data);
+        }
+
+        while( index < gtif->gt_num_keys )
+        {
+            _GTIFmemcpy( gtif->gt_keys + index, 
+                         gtif->gt_keys + index + 1, 
+                         sizeof(GeoKey) );
+            gtif->gt_keyindex[gtif->gt_keys[index].gk_key] = index;
+            index++;
+        }
+
+        gtif->gt_num_keys--;
+        gtif->gt_nshorts -= sizeof(KeyEntry)/sizeof(pinfo_t);
+        gtif->gt_keyindex[keyID] = 0;
+        gtif->gt_flags |= FLAG_FILE_MODIFIED;
+
+        return 1;
+    }
+    else switch (type)
+    {
+      case TYPE_SHORT:  sval=(pinfo_t) va_arg(ap, int); val=(char *)&sval;     break;
+      case TYPE_DOUBLE: dval=va_arg(ap, dblparam_t); val=(char *)&dval;  break;
+      case TYPE_ASCII: 
+        val=va_arg(ap, char*);
+        count = strlen(val) + 1; /* force = string length */
+        break;
+      default:
+        assert( FALSE );
+        break;
+    }
+    va_end(ap);
+    
+    /* We assume here that there are no multi-valued SHORTS ! */
+    if (index)
+    {
+        /* Key already exists */
+        key = gtif->gt_keys+index;
+        if (type!=key->gk_type || count > key->gk_count)
+        {
+            /* need to reset data pointer */
+            key->gk_type = type;
+            key->gk_count = count;
+            key->gk_size = _gtiff_size[ type ];
+            newvalues = 1;
+        }
+    }
+    else
+    {
+        /* We need to create the key */
+        if (gtif->gt_num_keys == MAX_KEYS) return 0;
+        key = gtif->gt_keys + ++gtif->gt_num_keys;
+        index = gtif->gt_num_keys;
+        gtif->gt_keyindex[ keyID ] = index;
+        key->gk_key = keyID;
+        key->gk_type = type;
+        key->gk_count = count;
+        key->gk_size = _gtiff_size[ type ];
+        if (gtif->gt_keymin > keyID)  gtif->gt_keymin=keyID;
+        if (gtif->gt_keymax < keyID)  gtif->gt_keymax=keyID;
+        newvalues = 1;
+    }
+
+    if (newvalues)
+    {
+        switch (type)
+        {
+          case TYPE_SHORT:  
+            if (count > 1) return 0;
+            data = (char *)&key->gk_data; /* store value *in* data */
+            break;
+          case TYPE_DOUBLE:
+            key->gk_data = (char *)(gtif->gt_double + gtif->gt_ndoubles);
+            data = key->gk_data;
+            gtif->gt_ndoubles += count;
+            break;
+          case TYPE_ASCII:
+            break;
+          default:
+            va_end(ap);
+            return 0;
+        }
+        gtif->gt_nshorts += sizeof(KeyEntry)/sizeof(pinfo_t);
+    }
+
+    /* this fixes a bug where if a request is made to write a duplicate
+       key, we must initialize the data to a valid value.
+       Bryan Wells (bryan@athena.bangor.autometric.com) */
+        
+    else /* no new values, but still have something to write */
+    {
+        switch (type)
+        {
+          case TYPE_SHORT:  
+            if (count > 1) return 0;
+            data = (char *)&key->gk_data; /* store value *in* data */
+            break;
+          case TYPE_DOUBLE:
+            data = key->gk_data;
+            break;
+          case TYPE_ASCII:
+            break;
+          default:
+            return 0;
+        }
+    }
+        
+    switch (type)
+    {
+      case TYPE_ASCII:
+        /* throw away existing data and allocate room for new data */
+        if (key->gk_data != 0)
+        {
+            _GTIFFree(key->gk_data);
+        }
+        key->gk_data = (char *)_GTIFcalloc(count);
+        key->gk_count = count;
+        data = key->gk_data;
+        break;
+      default:
+        break;
+    }
+
+    _GTIFmemcpy(data, val, count*key->gk_size);
+    
+    gtif->gt_flags |= FLAG_FILE_MODIFIED;
+    return 1;
+}
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_tiffp.c b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_tiffp.c
new file mode 100644
index 0000000000..75de1dfe4f
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_tiffp.c
@@ -0,0 +1,140 @@
+/**********************************************************************
+ *
+ *  geo_tiffp.c  Private TIFF interface module for GEOTIFF
+ *
+ *    This module implements the interface between the GEOTIFF
+ *    tag parser and the TIFF i/o module. The current setup
+ *    relies on the "libtiff" code, but if you use your own
+ *    TIFF reader software, you may replace the module implementations
+ *    here with your own calls. No "libtiff" dependencies occur
+ *    anywhere else in this code.
+ *
+ **********************************************************************/
+ 
+#include "geotiff.h"    /* public GTIFF interface */
+
+#include "geo_tiffp.h"  /* Private TIFF interface */
+#include "geo_keyp.h"   /* Private GTIFF interface */
+
+/* tiff size array global */
+gsize_t _gtiff_size[] = { 0, 1, 2, 4, 8, 1, 4, 8, 1, 2, 4, 1 };
+
+static int        _GTIFGetField (tiff_t *tif, pinfo_t tag, int *count, void *value );
+static int        _GTIFSetField (tiff_t *tif, pinfo_t tag, int  count, void *value );
+static tagtype_t  _GTIFTagType  (tiff_t *tif, pinfo_t tag);
+
+/*
+ * Set up default TIFF handlers. 
+ */
+void _GTIFSetDefaultTIFF(TIFFMethod *method)
+{
+	if (!method) return;
+	
+	method->get = _GTIFGetField;
+	method->set = _GTIFSetField;
+	method->type = _GTIFTagType;
+}
+
+gdata_t _GTIFcalloc(gsize_t size)
+{
+    gdata_t data=(gdata_t)_TIFFmalloc((tsize_t)size);
+	if (data) _TIFFmemset((tdata_t)data,0,(tsize_t)size);
+	return data;
+}
+
+gdata_t _GTIFrealloc(gdata_t ptr, gsize_t size)
+{
+    return( _TIFFrealloc((tdata_t)ptr, (tsize_t) size) );
+}
+
+void _GTIFmemcpy(gdata_t out,gdata_t in,gsize_t size)
+{
+	_TIFFmemcpy((tdata_t)out,(tdata_t)in,(tsize_t)size);
+}
+
+void _GTIFFree(gdata_t data)
+{
+	if (data) _TIFFfree((tdata_t)data);
+}
+
+
+
+/* returns the value of TIFF tag <tag>, or if
+ * the value is an array, returns an allocated buffer
+ * containing the values. Allocate a copy of the actual
+ * buffer, sized up for updating.
+ */
+static int _GTIFGetField (tiff_t *tif, pinfo_t tag, int *count, void *val )
+{
+	int status;
+	unsigned short scount=0;
+	char *tmp;
+	char *value;
+	gsize_t size = _gtiff_size[_GTIFTagType (tif,tag)];
+	
+	if (_GTIFTagType(tif,  tag) == TYPE_ASCII)
+	{
+		status = TIFFGetField((TIFF *)tif,tag,&tmp);
+		if (!status) return status;
+		scount = (unsigned short) (strlen(tmp)+1);
+	}
+	else status = TIFFGetField((TIFF *)tif,tag,&scount,&tmp);
+	if (!status) return status;
+	
+	*count = scount;
+
+	value = (char *)_GTIFcalloc( (scount+MAX_VALUES)*size);
+	if (!value) return 0;
+	
+	_TIFFmemcpy( value, tmp,  size * scount);
+	
+	*(char **)val = value;
+	return status;
+}
+
+/* 
+ * Set a GeoTIFF TIFF field.
+ */
+static int _GTIFSetField (tiff_t *tif, pinfo_t tag, int count, void *value )
+{
+	int status;
+	unsigned short scount = (unsigned short) count;
+
+	/* libtiff ASCII uses null-delimiter */
+	if (_GTIFTagType(tif,  tag) == TYPE_ASCII)
+		status = TIFFSetField((TIFF *)tif,tag,value);
+	else 
+		status = TIFFSetField((TIFF *)tif,tag,scount,value);
+	return status;
+}
+
+
+/*
+ *  This routine is supposed to return the TagType of the <tag>
+ *  TIFF tag. Unfortunately, "libtiff" does not provide this
+ *  service by default, so we just have to "know" what type of tags
+ *  we've got, and how many. We only define the ones Geotiff
+ *  uses here, and others return UNKNOWN. The "tif" parameter
+ *  is provided for those TIFF implementations that provide
+ *  for tag-type queries.
+ */
+static tagtype_t  _GTIFTagType  (tiff_t *tif, pinfo_t tag)
+{
+	tagtype_t ttype;
+
+	(void) tif; /* dummy reference */
+	
+	switch (tag)
+	{
+		case GTIFF_ASCIIPARAMS:    ttype=TYPE_ASCII; break;
+		case GTIFF_PIXELSCALE:
+		case GTIFF_TRANSMATRIX:
+		case GTIFF_TIEPOINTS:
+		case GTIFF_DOUBLEPARAMS:   ttype=TYPE_DOUBLE; break;
+		case GTIFF_GEOKEYDIRECTORY: ttype=TYPE_SHORT; break;
+		default: ttype = TYPE_UNKNOWN;
+	}
+	
+	return ttype;
+}
+
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_tiffp.h b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_tiffp.h
new file mode 100644
index 0000000000..8b065b8dde
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_tiffp.h
@@ -0,0 +1,114 @@
+/**********************************************************************
+ *
+ *  geo_tiffp.h - Private interface for TIFF tag parsing.
+ *
+ *   Written by: Niles D. Ritter
+ *
+ *   This interface file encapsulates the interface to external TIFF
+ *   file-io routines and definitions. The current configuration
+ *   assumes that the "libtiff" module is used, but if you have your
+ *   own TIFF reader, you may replace the definitions with your own
+ *   here, and replace the implementations in geo_tiffp.c. No other
+ *   modules have any explicit dependence on external TIFF modules.
+ *
+ *  Revision History;
+ *
+ *    20 June, 1995      Niles D. Ritter         New
+ *    6 July,  1995      Niles D. Ritter         Fix prototypes
+ *
+ **********************************************************************/
+
+#ifndef __geo_tiffp_h_
+#define __geo_tiffp_h_
+
+/**********************************************************************
+ *
+ *                        Private includes
+ *
+ *   If you are not using libtiff and XTIFF, replace this include file
+ *    with the appropriate one for your own TIFF parsing routines.
+ *
+ *   Revision History
+ * 
+ *      19 September 1995   ndr    Demoted Intergraph trans matrix.
+ *
+ **********************************************************************/
+
+#include "geotiff.h"
+#include "xtiffio.h"
+#include "cpl_serv.h"
+
+/*
+ * dblparam_t is the type that a double precision
+ * floating point value will have on the parameter
+ * stack (when coerced by the compiler). You shouldn't
+ * have to change this.
+ */
+#ifdef applec
+typedef extended dblparam_t;
+#else
+typedef double dblparam_t;
+#endif
+
+
+/**********************************************************************
+ *
+ *                        Private defines
+ *
+ *   If you are not using "libtiff"/LIBXTIFF, replace these definitions
+ *   with the appropriate definitions to access the geo-tags
+ *
+ **********************************************************************/
+ 
+typedef unsigned short pinfo_t;    /* SHORT ProjectionInfo tag type */
+typedef TIFF    tiff_t;            /* TIFF file descriptor          */
+typedef tdata_t  gdata_t;          /* pointer to data */
+typedef tsize_t  gsize_t;          /* data allocation size */
+ 
+#define GTIFF_GEOKEYDIRECTORY   TIFFTAG_GEOKEYDIRECTORY /* from xtiffio.h */
+#define GTIFF_DOUBLEPARAMS      TIFFTAG_GEODOUBLEPARAMS
+#define GTIFF_ASCIIPARAMS       TIFFTAG_GEOASCIIPARAMS
+#define GTIFF_PIXELSCALE        TIFFTAG_GEOPIXELSCALE
+#define GTIFF_TRANSMATRIX       TIFFTAG_GEOTRANSMATRIX
+#define GTIFF_INTERGRAPH_MATRIX TIFFTAG_INTERGRAPH_MATRIX
+#define GTIFF_TIEPOINTS         TIFFTAG_GEOTIEPOINTS
+#define GTIFF_LOCAL          0
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * Method function pointer types
+ */
+typedef int        (*GTGetFunction) (tiff_t *tif, pinfo_t tag, int *count, void *value );
+typedef int        (*GTSetFunction) (tiff_t *tif, pinfo_t tag, int  count, void *value );
+typedef tagtype_t  (*GTTypeFunction) (tiff_t *tif, pinfo_t tag);
+typedef struct     _TIFFMethod {
+	GTGetFunction get;
+	GTSetFunction set;
+	GTTypeFunction type;
+} TIFFMethod;
+
+/**********************************************************************
+ *
+ *               Protected Function Declarations  
+ *
+ *   These routines are exposed implementations, and should not
+ *   be used by external GEOTIFF client programs.
+ *
+ **********************************************************************/
+
+extern gsize_t _gtiff_size[]; /* TIFF data sizes */
+extern void CPL_DLL _GTIFSetDefaultTIFF(TIFFMethod *method);
+extern gdata_t CPL_DLL _GTIFcalloc(gsize_t);
+extern gdata_t CPL_DLL _GTIFrealloc(gdata_t,gsize_t);
+extern void CPL_DLL _GTIFFree(gdata_t data);
+extern void CPL_DLL _GTIFmemcpy(gdata_t out,gdata_t in,gsize_t size);
+
+#if defined(__cplusplus)
+} 
+#endif
+
+
+#endif /* __geo_tiffp_h_ */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_trans.c b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_trans.c
new file mode 100644
index 0000000000..cd31d0b136
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_trans.c
@@ -0,0 +1,334 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  libgeotiff
+ * Purpose:  Code to abstract translation between pixel/line and PCS
+ *           coordinates.
+ * Author:   Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: geo_trans.c,v $
+ * Revision 1.10  2003/07/08 17:31:30  warmerda
+ * cleanup various warnings
+ *
+ * Revision 1.9  2001/11/28 14:20:30  warmerda
+ * fixed transform memory leak in GTIFPCSToImage
+ *
+ * Revision 1.8  2001/04/06 16:56:22  warmerda
+ * added support for PCSToImage with matrix
+ *
+ * Revision 1.7  2001/03/05 03:25:23  warmerda
+ * restructure cleanup, and apply to GTIFPCSToImage()
+ *
+ * Revision 1.6  2001/03/04 22:37:39  warmerda
+ * fixed memory leak for fields fetched with gt_methods.get - Alan Gray
+ *
+ * Revision 1.5  2000/08/22 03:32:46  warmerda
+ * removed GTIFTiepointTranslate code
+ *
+ * Revision 1.4  1999/09/17 01:19:51  warmerda
+ * Fixed bug in use of transform matrix.
+ *
+ * Revision 1.3  1999/09/16 21:25:40  warmerda
+ * Added tiepoint, and transformation matrix based translation.  Note
+ * that we don't try to invert the transformation matrix for
+ * GTIFPCSToImage().
+ *
+ * Revision 1.2  1999/09/07 20:00:40  warmerda
+ * Fixed count/tiepoint_count bug in GTIFPCSToImage().
+ *
+ * Revision 1.1  1999/05/04 03:07:57  warmerda
+ * New
+ *
+ */
+ 
+#include "geotiff.h"
+#include "geo_tiffp.h" /* external TIFF interface */
+#include "geo_keyp.h"  /* private interface       */
+#include "geokeys.h"
+
+/************************************************************************/
+/*                          inv_geotransform()                          */
+/*                                                                      */
+/*      Invert a 6 term geotransform style matrix.                      */
+/************************************************************************/
+
+static int inv_geotransform( double *gt_in, double *gt_out )
+
+{
+    double	det, inv_det;
+
+    /* we assume a 3rd row that is [0 0 1] */
+
+    /* Compute determinate */
+
+    det = gt_in[0] * gt_in[4] - gt_in[1] * gt_in[3];
+
+    if( fabs(det) < 0.000000000000001 )
+        return 0;
+
+    inv_det = 1.0 / det;
+
+    /* compute adjoint, and devide by determinate */
+
+    gt_out[0] =  gt_in[4] * inv_det;
+    gt_out[3] = -gt_in[3] * inv_det;
+
+    gt_out[1] = -gt_in[1] * inv_det;
+    gt_out[4] =  gt_in[0] * inv_det;
+
+    gt_out[2] = ( gt_in[1] * gt_in[5] - gt_in[2] * gt_in[4]) * inv_det;
+    gt_out[5] = (-gt_in[0] * gt_in[5] + gt_in[2] * gt_in[3]) * inv_det;
+
+    return 1;
+}
+
+/************************************************************************/
+/*                       GTIFTiepointTranslate()                        */
+/************************************************************************/
+
+int GTIFTiepointTranslate( int gcp_count, double * gcps_in, double * gcps_out,
+                           double x_in, double y_in,
+                           double *x_out, double *y_out )
+
+{
+    (void) gcp_count;
+    (void) gcps_in;
+    (void) gcps_out;
+    (void) x_in;
+    (void) y_in;
+    (void) x_out;
+    (void) y_out;
+    
+    /* I would appreciate a _brief_ block of code for doing second order
+       polynomial regression here! */
+    return FALSE;
+}
+
+
+/************************************************************************/
+/*                           GTIFImageToPCS()                           */
+/************************************************************************/
+
+/**
+ * Translate a pixel/line coordinate to projection coordinates.
+ *
+ * At this time this function does not support image to PCS translations for
+ * tiepoints-only definitions,  only pixelscale and transformation matrix
+ * formulations.
+ *
+ * @param gtif The handle from GTIFNew() indicating the target file.
+ * @param x A pointer to the double containing the pixel offset on input,
+ * and into which the easting/longitude will be put on completion.
+ * @param y A pointer to the double containing the line offset on input,
+ * and into which the northing/latitude will be put on completion.
+ *
+ * @return TRUE if the transformation succeeds, or FALSE if it fails.  It may
+ * fail if the file doesn't have properly setup transformation information,
+ * or it is in a form unsupported by this function.
+ */
+
+int GTIFImageToPCS( GTIF *gtif, double *x, double *y )
+
+{
+    int     res = FALSE;
+    int     tiepoint_count, count, transform_count;
+    tiff_t *tif=gtif->gt_tif;
+    double *tiepoints   = 0;
+    double *pixel_scale = 0;
+    double *transform   = 0;
+
+
+    if (!(gtif->gt_methods.get)(tif, GTIFF_TIEPOINTS,
+                              &tiepoint_count, &tiepoints ))
+        tiepoint_count = 0;
+
+    if (!(gtif->gt_methods.get)(tif, GTIFF_PIXELSCALE, &count, &pixel_scale ))
+        count = 0;
+
+    if (!(gtif->gt_methods.get)(tif, GTIFF_TRANSMATRIX,
+                                &transform_count, &transform ))
+        transform_count = 0;
+
+/* -------------------------------------------------------------------- */
+/*      If the pixelscale count is zero, but we have tiepoints use      */
+/*      the tiepoint based approach.                                    */
+/* -------------------------------------------------------------------- */
+    if( tiepoint_count > 6 && count == 0 ) 
+    {
+        res = GTIFTiepointTranslate( tiepoint_count / 6,
+                                     tiepoints, tiepoints + 3,
+                                     *x, *y, x, y );
+    }
+
+/* -------------------------------------------------------------------- */
+/*	If we have a transformation matrix, use it. 			*/
+/* -------------------------------------------------------------------- */
+    else if( transform_count == 16 ) 
+    {
+        double x_in = *x, y_in = *y;
+
+        *x = x_in * transform[0] + y_in * transform[1] + transform[3];
+        *y = x_in * transform[4] + y_in * transform[5] + transform[7];
+        
+        res = TRUE;
+    } 
+
+/* -------------------------------------------------------------------- */
+/*      For now we require one tie point, and a valid pixel scale.      */
+/* -------------------------------------------------------------------- */
+    else if( count < 3 || tiepoint_count < 6 ) 
+    {
+        res = FALSE;
+    } 
+
+    else 
+    {
+        *x = (*x - tiepoints[0]) * pixel_scale[0] + tiepoints[3];
+        *y = (*y - tiepoints[1]) * (-1 * pixel_scale[1]) + tiepoints[4];
+
+        res = TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    if(tiepoints)   
+        _GTIFFree(tiepoints);
+    if(pixel_scale)
+        _GTIFFree(pixel_scale);
+    if(transform)  
+        _GTIFFree(transform);
+
+    return res;
+}
+
+/************************************************************************/
+/*                           GTIFPCSToImage()                           */
+/************************************************************************/
+
+/**
+ * Translate a projection coordinate to pixel/line coordinates.
+ *
+ * At this time this function does not support PCS to image translations for
+ * tiepoints-only based definitions, only matrix and pixelscale/tiepoints 
+ * formulations are supposed.
+ *
+ * @param gtif The handle from GTIFNew() indicating the target file.
+ * @param x A pointer to the double containing the pixel offset on input,
+ * and into which the easting/longitude will be put on completion.
+ * @param y A pointer to the double containing the line offset on input,
+ * and into which the northing/latitude will be put on completion.
+ *
+ * @return TRUE if the transformation succeeds, or FALSE if it fails.  It may
+ * fail if the file doesn't have properly setup transformation information,
+ * or it is in a form unsupported by this function.
+ */
+
+int GTIFPCSToImage( GTIF *gtif, double *x, double *y )
+
+{
+    double 	*tiepoints = NULL;
+    int 	tiepoint_count, count, transform_count = 0;
+    double	*pixel_scale = NULL;
+    double 	*transform   = NULL;
+    tiff_t 	*tif=gtif->gt_tif;
+    int		result = FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Fetch tiepoints and pixel scale.                                */
+/* -------------------------------------------------------------------- */
+    if (!(gtif->gt_methods.get)(tif, GTIFF_TIEPOINTS,
+                              &tiepoint_count, &tiepoints ))
+        tiepoint_count = 0;
+
+    if (!(gtif->gt_methods.get)(tif, GTIFF_PIXELSCALE, &count, &pixel_scale ))
+        count = 0;
+
+    if (!(gtif->gt_methods.get)(tif, GTIFF_TRANSMATRIX,
+                                &transform_count, &transform ))
+        transform_count = 0;
+
+/* -------------------------------------------------------------------- */
+/*      If the pixelscale count is zero, but we have tiepoints use      */
+/*      the tiepoint based approach.                                    */
+/* -------------------------------------------------------------------- */
+    if( tiepoint_count > 6 && count == 0 )
+    {
+        result = GTIFTiepointTranslate( tiepoint_count / 6,
+                                        tiepoints + 3, tiepoints,
+                                        *x, *y, x, y );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Handle matrix - convert to "geotransform" format, invert and    */
+/*      apply.                                                          */
+/* -------------------------------------------------------------------- */
+    else if( transform_count == 16 )
+    {
+        double  x_in = *x, y_in = *y;
+        double	gt_in[6], gt_out[6];
+        
+        gt_in[0] = transform[0];
+        gt_in[1] = transform[1];
+        gt_in[2] = transform[3];
+        gt_in[3] = transform[4];
+        gt_in[4] = transform[5];
+        gt_in[5] = transform[7];
+
+        if( !inv_geotransform( gt_in, gt_out ) )
+            result = FALSE;
+        else
+        {
+            *x = x_in * gt_out[0] + y_in * gt_out[1] + gt_out[2];
+            *y = x_in * gt_out[3] + y_in * gt_out[4] + gt_out[5];
+            
+            result = TRUE;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      For now we require one tie point, and a valid pixel scale.      */
+/* -------------------------------------------------------------------- */
+    else if( count >= 3 && tiepoint_count >= 6 )
+    {
+        *x = (*x - tiepoints[3]) / pixel_scale[0] + tiepoints[0];
+        *y = (*y - tiepoints[4]) / (-1 * pixel_scale[1]) + tiepoints[1];
+
+        result = TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup.                                                        */
+/* -------------------------------------------------------------------- */
+    if(tiepoints)   
+        _GTIFFree(tiepoints);
+    if(pixel_scale)
+        _GTIFFree(pixel_scale);
+    if(transform)  
+        _GTIFFree(transform);
+
+    return result;
+}
+
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_write.c b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_write.c
new file mode 100644
index 0000000000..0e94d53dcb
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geo_write.c
@@ -0,0 +1,193 @@
+/**********************************************************************
+ *
+ *  geo_write.c  -- Public routines for GEOTIFF GeoKey access.
+ *
+ *    Written By: Niles D. Ritter.
+ *
+ *  copyright (c) 1995   Niles D. Ritter
+ *
+ *  Permission granted to use this software, so long as this copyright
+ *  notice accompanies any source code derived therefrom.
+ *
+ **********************************************************************/
+
+#include "geotiffio.h"   /* public interface        */
+#include "geo_tiffp.h" /* external TIFF interface */
+#include "geo_keyp.h"  /* private interface       */
+
+static int WriteKey(GTIF* gt, TempKeyData* tempData,
+                    KeyEntry* entptr, GeoKey* keyptr);
+static int SortKeys(GTIF* gt,int *sortkeys);
+
+
+/**
+This function flushes all the GeoTIFF keys that have been set with the 
+GTIFKeySet() function into the associated 
+TIFF file.
+
+@param gt The GeoTIFF handle returned by GTIFNew.
+
+GTIFWriteKeys() should be called before 
+GTIFFree() is used to deallocate a GeoTIFF access handle.
+ */
+
+int GTIFWriteKeys(GTIF *gt)
+{
+    int i;
+    GeoKey *keyptr;
+    KeyEntry *entptr;
+    KeyHeader *header;
+    TempKeyData tempData;
+    int sortkeys[MAX_KEYS];
+	
+    if (!(gt->gt_flags & FLAG_FILE_MODIFIED)) return 1;
+
+    if( gt->gt_tif == NULL )
+        return 0;
+	
+    tempData.tk_asciiParams = 0;
+    tempData.tk_asciiParamsLength = 0;
+    tempData.tk_asciiParamsOffset = 0;
+
+    /*  Sort the Keys into numerical order */
+    if (!SortKeys(gt,sortkeys))
+    {
+        /* XXX error: a key was not recognized */
+    }
+	
+    /* Set up header of ProjectionInfo tag */
+    header = (KeyHeader *)gt->gt_short;
+    header->hdr_num_keys = (pinfo_t) gt->gt_num_keys;
+    header->hdr_version  = GvCurrentVersion;
+    header->hdr_rev_major  = GvCurrentRevision;
+    header->hdr_rev_minor  = GvCurrentMinorRev;
+	
+    /* Sum up the ASCII tag lengths */
+    for (i = 0; i < gt->gt_num_keys; i++)
+    {
+        keyptr = gt->gt_keys + sortkeys[i];
+        if (keyptr->gk_type == TYPE_ASCII)
+        {
+            tempData.tk_asciiParamsLength += keyptr->gk_count;
+        }
+    }
+    if (tempData.tk_asciiParamsLength > 0)
+    {
+        tempData.tk_asciiParams =
+            (char *)_GTIFcalloc(tempData.tk_asciiParamsLength + 1);
+        tempData.tk_asciiParams[tempData.tk_asciiParamsLength] = '\0';
+    }
+
+    /* Set up the rest of SHORT array properly */
+    keyptr = gt->gt_keys;
+    entptr = (KeyEntry*)(gt->gt_short + 4);
+    for (i=0; i< gt->gt_num_keys; i++,entptr++)
+    {
+        if (!WriteKey(gt,&tempData,entptr,keyptr+sortkeys[i])) return 0;
+    }	
+	
+    /* Write out the Key Directory */
+    (gt->gt_methods.set)(gt->gt_tif, GTIFF_GEOKEYDIRECTORY, gt->gt_nshorts, gt->gt_short );	
+	
+    /* Write out the params directories */
+    if (gt->gt_ndoubles)
+        (gt->gt_methods.set)(gt->gt_tif, GTIFF_DOUBLEPARAMS, gt->gt_ndoubles, gt->gt_double );
+    if (tempData.tk_asciiParamsLength > 0)
+    {
+        /* just to be safe */
+        tempData.tk_asciiParams[tempData.tk_asciiParamsLength] = '\0';
+        (gt->gt_methods.set)(gt->gt_tif,
+                             GTIFF_ASCIIPARAMS, 0, tempData.tk_asciiParams);
+    }
+	
+    gt->gt_flags &= ~FLAG_FILE_MODIFIED;
+
+    if (tempData.tk_asciiParamsLength > 0)
+    {
+        _GTIFFree (tempData.tk_asciiParams);
+    }
+    return 1;
+}
+
+/**********************************************************************
+ *
+ *                        Private Routines
+ *
+ **********************************************************************/
+ 
+/*
+ * Given GeoKey, write out the KeyEntry entries, returning 0 if failure.
+ *  This is the exact complement of ReadKey().
+ */
+
+static int WriteKey(GTIF* gt, TempKeyData* tempData,
+                    KeyEntry* entptr, GeoKey* keyptr)
+{
+    int count;
+	
+    entptr->ent_key = (pinfo_t) keyptr->gk_key;
+    entptr->ent_count = (pinfo_t) keyptr->gk_count;
+    count = entptr->ent_count;
+	
+    if (count==1 && keyptr->gk_type==TYPE_SHORT)
+    {
+        entptr->ent_location = GTIFF_LOCAL;
+        entptr->ent_val_offset = *(pinfo_t*)&keyptr->gk_data;
+        return 1;
+    }
+		  
+    switch (keyptr->gk_type)
+    {
+      case TYPE_SHORT:
+        entptr->ent_location = GTIFF_GEOKEYDIRECTORY;
+        entptr->ent_val_offset = (pinfo_t)
+            ((pinfo_t*)keyptr->gk_data - gt->gt_short);
+        break;
+      case TYPE_DOUBLE:
+        entptr->ent_location = GTIFF_DOUBLEPARAMS;
+        entptr->ent_val_offset = (pinfo_t) 
+            ((double*)keyptr->gk_data - gt->gt_double);
+        break;
+      case TYPE_ASCII:
+        entptr->ent_location = GTIFF_ASCIIPARAMS;
+        entptr->ent_val_offset = (pinfo_t) tempData->tk_asciiParamsOffset;
+        _GTIFmemcpy (tempData->tk_asciiParams + tempData->tk_asciiParamsOffset
+                     , keyptr->gk_data, keyptr->gk_count);
+        tempData->tk_asciiParams[tempData->tk_asciiParamsOffset+keyptr->gk_count-1] = '|';
+        tempData->tk_asciiParamsOffset += keyptr->gk_count;
+        break;
+      default:
+        return 0; /* failure */
+    }
+	
+    return 1; /* success */
+}
+
+
+/* 
+ * Numerically sort the GeoKeys.
+ * We just do a linear search through
+ * the list and pull out the keys that were set.
+ */
+
+static int SortKeys(GTIF* gt,int *sortkeys)
+{
+    int loc;
+    int nkeys=0;
+    geokey_t key,kmin,kmax;
+    int *index = gt->gt_keyindex;
+	
+    kmin = (geokey_t) gt->gt_keymin;
+    kmax = (geokey_t) gt->gt_keymax;
+    for (key=kmin; key<=kmax; key++)
+    {
+        if ( (loc=index[key]) != 0 )
+        {
+            sortkeys[nkeys] = loc;
+            nkeys++;
+        }
+    }
+	
+    return nkeys==gt->gt_num_keys;
+}
+
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geokeys.h b/Utilities/GDAL/frmts/gtiff/libgeotiff/geokeys.h
new file mode 100644
index 0000000000..f3e47c14b3
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geokeys.h
@@ -0,0 +1,54 @@
+/**********************************************************************
+ *
+ *  geokeys.h - Public registry for valid GEOTIFF GeoKeys.
+ *
+ *   Written By: Niles D. Ritter
+ *
+ *  Revision History:
+ *
+ *     Rev.#  Author  M/D/Y Date     Key Changes/Additions
+ *     -----  ------  ----------  -------------------------------------
+ *       0      ndr    06/10/95      Inital Beta Release
+ *       1      ndr    09/18/95      Final 1.0 Release
+ *
+ **********************************************************************/
+
+#ifndef __geokeys_h_
+#define __geokeys_h_
+
+/* The GvCurrentRevision number should be incremented whenever a 
+ * new set of Keys are defined or modified in "geokeys.inc", and comments 
+ * added to the "Revision History" section above. If only code
+ * _values_ are augmented, the "GvCurrentMinorRev" number should
+ * be incremented instead (see "geovalues.h"). Whenever the 
+ * GvCurrentRevision is incremented, the GvCurrentMinorRev should
+ * be reset to zero.
+ *
+ *
+ * The Section Numbers below refer to the GeoTIFF Spec sections
+ * in which these values are documented.
+ *
+ */
+#define GvCurrentRevision  1  /* Final 1.0 Release */
+
+#ifdef ValuePair
+#  undef ValuePair
+#endif
+#define ValuePair(name,value)    name = value,
+
+typedef enum {
+   BaseGeoKey   =  1024,               /* First valid code */
+
+#  include "geokeys.inc"         /* geokey database */
+
+   ReservedEndGeoKey  =  32767,
+   
+   /* Key space available for Private or internal use */
+   PrivateBaseGeoKey = 32768,    /* Consistent with TIFF Private tags */
+   PrivateEndGeoKey  = 65535,    
+   
+   EndGeoKey = 65535             /* Largest Possible GeoKey ID */
+} geokey_t;
+
+
+#endif /* __geokeys_h_ */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geokeys.inc b/Utilities/GDAL/frmts/gtiff/libgeotiff/geokeys.inc
new file mode 100644
index 0000000000..286c23c2bd
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geokeys.inc
@@ -0,0 +1,76 @@
+/* GeoTIFF GeoKey Database */
+
+/* Note: Any changes/additions to this database require */
+/* a change in the revision value in geokeys.h          */
+
+/* C database for Geotiff include files.   */
+/* the macro ValuePair() must be defined   */
+/* by the enclosing include file           */
+
+/*  Revised 28 Sep 1995   NDR -- Added Rev. 1.0 aliases. */
+
+/* 6.2.1 GeoTIFF Configuration Keys */
+
+ValuePair(  GTModelTypeGeoKey,	1024) /* Section 6.3.1.1 Codes       */
+ValuePair(  GTRasterTypeGeoKey,	1025) /* Section 6.3.1.2 Codes       */
+ValuePair(  GTCitationGeoKey,	1026) /* documentation */
+
+/* 6.2.2 Geographic CS Parameter Keys */
+
+ValuePair(  GeographicTypeGeoKey,	2048) /* Section 6.3.2.1 Codes     */
+ValuePair(  GeogCitationGeoKey,	2049) /* documentation             */
+ValuePair(  GeogGeodeticDatumGeoKey,	2050) /* Section 6.3.2.2 Codes     */
+ValuePair(  GeogPrimeMeridianGeoKey,	2051) /* Section 6.3.2.4 codes     */
+ValuePair(  GeogLinearUnitsGeoKey,	2052) /* Section 6.3.1.3 Codes     */
+ValuePair(  GeogLinearUnitSizeGeoKey,	2053) /* meters                    */
+ValuePair(  GeogAngularUnitsGeoKey,	2054) /* Section 6.3.1.4 Codes     */
+ValuePair(  GeogAngularUnitSizeGeoKey,	2055) /* radians                   */
+ValuePair(  GeogEllipsoidGeoKey,	2056) /* Section 6.3.2.3 Codes     */
+ValuePair(  GeogSemiMajorAxisGeoKey,	2057) /* GeogLinearUnits           */
+ValuePair(  GeogSemiMinorAxisGeoKey,	2058) /* GeogLinearUnits           */
+ValuePair(  GeogInvFlatteningGeoKey,	2059) /* ratio                     */
+ValuePair(  GeogAzimuthUnitsGeoKey,	2060) /* Section 6.3.1.4 Codes     */
+ValuePair(  GeogPrimeMeridianLongGeoKey,	2061) /* GeoAngularUnit            */
+
+/* 6.2.3 Projected CS Parameter Keys */
+/*    Several keys have been renamed,*/
+/*    and the deprecated names aliased for backward compatibility */
+
+ValuePair(  ProjectedCSTypeGeoKey,	3072)     /* Section 6.3.3.1 codes   */
+ValuePair(  PCSCitationGeoKey,	3073)     /* documentation           */
+ValuePair(  ProjectionGeoKey,	3074)     /* Section 6.3.3.2 codes   */
+ValuePair(  ProjCoordTransGeoKey,	3075)     /* Section 6.3.3.3 codes   */
+ValuePair(  ProjLinearUnitsGeoKey,	3076)     /* Section 6.3.1.3 codes   */
+ValuePair(  ProjLinearUnitSizeGeoKey,	3077)     /* meters                  */
+ValuePair(  ProjStdParallel1GeoKey,	3078)     /* GeogAngularUnit */
+ValuePair(  ProjStdParallelGeoKey,ProjStdParallel1GeoKey) /* ** alias **   */
+ValuePair(  ProjStdParallel2GeoKey,	3079)     /* GeogAngularUnit */
+ValuePair(  ProjNatOriginLongGeoKey,	3080)     /* GeogAngularUnit */
+ValuePair(  ProjOriginLongGeoKey,ProjNatOriginLongGeoKey) /* ** alias **     */
+ValuePair(  ProjNatOriginLatGeoKey,	3081)     /* GeogAngularUnit */
+ValuePair(  ProjOriginLatGeoKey,ProjNatOriginLatGeoKey)   /* ** alias **     */
+ValuePair(  ProjFalseEastingGeoKey,	3082)     /* ProjLinearUnits */
+ValuePair(  ProjFalseNorthingGeoKey,	3083)     /* ProjLinearUnits */
+ValuePair(  ProjFalseOriginLongGeoKey,	3084)     /* GeogAngularUnit */
+ValuePair(  ProjFalseOriginLatGeoKey,	3085)     /* GeogAngularUnit */
+ValuePair(  ProjFalseOriginEastingGeoKey,	3086)     /* ProjLinearUnits */
+ValuePair(  ProjFalseOriginNorthingGeoKey,	3087)     /* ProjLinearUnits */
+ValuePair(  ProjCenterLongGeoKey,	3088)     /* GeogAngularUnit */
+ValuePair(  ProjCenterLatGeoKey,	3089)     /* GeogAngularUnit */
+ValuePair(  ProjCenterEastingGeoKey,	3090)     /* ProjLinearUnits */
+ValuePair(  ProjCenterNorthingGeoKey,	3091)     /* ProjLinearUnits */
+ValuePair(  ProjScaleAtNatOriginGeoKey,	3092)     /* ratio   */
+ValuePair(  ProjScaleAtOriginGeoKey,ProjScaleAtNatOriginGeoKey)  /* ** alias **   */
+ValuePair(  ProjScaleAtCenterGeoKey,	3093)     /* ratio   */
+ValuePair(  ProjAzimuthAngleGeoKey,	3094)     /* GeogAzimuthUnit */
+ValuePair(  ProjStraightVertPoleLongGeoKey, 3095) /* GeogAngularUnit */
+ValuePair(  ProjRectifiedGridAngleGeoKey, 3096)   /* GeogAngularUnit */
+
+/* 6.2.4 Vertical CS Keys */
+   
+ValuePair(  VerticalCSTypeGeoKey,	4096)  /* Section 6.3.4.1 codes   */
+ValuePair(  VerticalCitationGeoKey,	4097)  /* documentation */
+ValuePair(  VerticalDatumGeoKey,	4098)  /* Section 6.3.4.2 codes   */
+ValuePair(  VerticalUnitsGeoKey,	4099)  /* Section 6.3.1 (.x) codes   */
+
+/* End of Data base */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geonames.h b/Utilities/GDAL/frmts/gtiff/libgeotiff/geonames.h
new file mode 100644
index 0000000000..ccedc7df32
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geonames.h
@@ -0,0 +1,146 @@
+/*
+ * geonames.h
+ *
+ *  This encapsulates all of the value-naming mechanism of 
+ *  libgeotiff. 
+ *
+ *  Written By: Niles Ritter
+ *
+ *  Revision History:
+ *
+ *      Author     Date     Key Changes/Additions
+ *      ------  ----------  -------------------------------------
+ *      ndr    10 Jun 95     Inital Beta Release
+ *      ndr    28 Jul 95     Added ModelType aliases, Kv aliases.
+ */
+
+#ifndef __geonames_h
+#define __geonames_h
+
+struct _KeyInfo {
+	int ki_key;
+	char *ki_name;
+};
+typedef struct _KeyInfo KeyInfo;
+
+/* If memory is a premium, then omitting the 
+ * long name lists may save some space; simply 
+ * #define OMIT_GEOTIFF_NAMES in the compile statement
+ * to remove all key->string translation.
+ */
+#ifdef ValuePair
+#  undef ValuePair
+#endif
+
+#ifndef OMIT_GEOTIFF_NAMES
+#define ValuePair(token,value)  {token,#token},
+#else
+#define ValuePair(token,value)
+#endif
+
+#define END_LIST { -1, (char *)0}
+
+/************************************************************
+ *         6.2.x GeoTIFF Keys
+ ************************************************************/
+
+static KeyInfo _keyInfo[] =  {
+#   include "geokeys.inc"   /* geokey database */
+    END_LIST
+};
+
+#define COMMON_VALUES \
+   {KvUndefined, "Undefined"}, \
+   {KvUserDefined,"User-Defined"}, \
+   ValuePair(KvUndefined,KvUndefined) \
+   ValuePair(KvUserDefined,KvUserDefined) 
+
+static KeyInfo _csdefaultValue[] = {
+   COMMON_VALUES
+   END_LIST  
+};
+
+/************************************************************
+ *         6.3.x GeoTIFF Key Values
+ ************************************************************/
+
+static KeyInfo _modeltypeValue[] = {
+   COMMON_VALUES
+    ValuePair(ModelTypeProjected,1)
+    ValuePair(ModelTypeGeographic,2)
+    ValuePair(ModelTypeGeocentric,3)
+    ValuePair(ModelProjected,1)     /* aliases */
+    ValuePair(ModelGeographic,2)    /* aliases */
+    ValuePair(ModelGeocentric,3)    /* aliases */
+   END_LIST  
+};
+
+static KeyInfo _rastertypeValue[] = {
+   COMMON_VALUES
+    ValuePair(RasterPixelIsArea,1)
+    ValuePair(RasterPixelIsPoint,2)
+   END_LIST  
+};
+
+static KeyInfo _geounitsValue[] = {
+   COMMON_VALUES
+#  include "epsg_units.inc"
+   END_LIST  
+};
+
+static KeyInfo _geographicValue[] = {
+   COMMON_VALUES
+#  include "epsg_gcs.inc"
+   END_LIST  
+};
+
+static KeyInfo _geodeticdatumValue[] = {
+   COMMON_VALUES
+#  include "epsg_datum.inc"
+   END_LIST  
+};
+
+static KeyInfo _ellipsoidValue[] = {
+   COMMON_VALUES
+#  include "epsg_ellipse.inc"
+   END_LIST  
+};
+
+static KeyInfo _primemeridianValue[] = {
+   COMMON_VALUES
+#  include "epsg_pm.inc"
+   END_LIST  
+};
+
+static KeyInfo _pcstypeValue[] = {
+   COMMON_VALUES
+#  include "epsg_pcs.inc"
+   END_LIST  
+};
+
+static KeyInfo _projectionValue[] = {
+   COMMON_VALUES
+#  include "epsg_proj.inc"
+   END_LIST  
+};
+
+static KeyInfo _coordtransValue[] = {
+   COMMON_VALUES
+#  include "geo_ctrans.inc"
+   END_LIST  
+};
+
+static KeyInfo _vertcstypeValue[] = {
+   COMMON_VALUES
+#  include "epsg_vertcs.inc"
+   END_LIST  
+};
+
+static KeyInfo _vdatumValue[] = {
+   COMMON_VALUES
+    ValuePair(VDatumBase,1)
+   END_LIST  
+};
+
+#endif /* __geonames_h */
+
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geotiff.h b/Utilities/GDAL/frmts/gtiff/libgeotiff/geotiff.h
new file mode 100644
index 0000000000..054998ef6c
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geotiff.h
@@ -0,0 +1,117 @@
+/**********************************************************************
+ *
+ *  geotiff.h - Public interface for Geotiff tag parsing.
+ *
+ *
+ *   Written By: Niles D. Ritter
+ *
+ **********************************************************************/
+
+#ifndef __geotiff_h_
+#define __geotiff_h_
+
+/**
+ * \file geotiff.h
+ *
+ * Primary libgeotiff include file.
+ *
+ * This is the defacto registry for valid GEOTIFF GeoKeys
+ * and their associated symbolic values. This is also the only file
+ * of the GeoTIFF library which needs to be included in client source
+ * code.
+ */
+
+/* This Version code should only change if a drastic
+ * alteration is made to the GeoTIFF key structure. Readers
+ * encountering a larger value should give up gracefully.
+ */
+#define GvCurrentVersion   1
+
+#define LIBGEOTIFF_VERSION 1230
+
+#include "geo_config.h"
+#include "geokeys.h"
+
+/**********************************************************************
+ * Do we want to build as a DLL on windows?
+ **********************************************************************/
+#if !defined(CPL_DLL)
+#  if defined(_WIN32) && defined(BUILD_AS_DLL)
+#    define CPL_DLL     __declspec(dllexport)
+#  else
+#    define CPL_DLL
+#  endif
+#endif
+
+/**********************************************************************
+ *
+ *                 Public Structures & Definitions
+ *
+ **********************************************************************/
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct gtiff GTIF;   /* struct gtiff is private */
+typedef unsigned short tifftag_t;
+typedef unsigned short geocode_t;
+typedef int (*GTIFPrintMethod)(char *string, void *aux);
+typedef int (*GTIFReadMethod)(char *string, void *aux);
+
+typedef enum {
+   TYPE_BYTE=1,
+   TYPE_SHORT=2,
+   TYPE_LONG=3,
+   TYPE_RATIONAL=4,
+   TYPE_ASCII=5,
+   TYPE_FLOAT=6,
+   TYPE_DOUBLE=7,
+   TYPE_SBYTE=8,
+   TYPE_SSHORT=9,
+   TYPE_SLONG=10,
+   TYPE_UNKNOWN=11
+} tagtype_t;
+
+
+/**********************************************************************
+ *
+ *                 Public Function Declarations
+ *
+ **********************************************************************/
+
+/* TIFF-level interface */
+GTIF CPL_DLL *GTIFNew(void *tif);
+void CPL_DLL  GTIFFree(GTIF *gtif);
+int  CPL_DLL  GTIFWriteKeys(GTIF *gtif);
+void CPL_DLL  GTIFDirectoryInfo(GTIF *gtif, int *versions, int *keycount);
+
+/* GeoKey Access */
+int  CPL_DLL  GTIFKeyInfo(GTIF *gtif, geokey_t key, int *size, tagtype_t* type);
+int  CPL_DLL  GTIFKeyGet(GTIF *gtif, geokey_t key, void *val, int index,
+                         int count);
+int  CPL_DLL  GTIFKeySet(GTIF *gtif, geokey_t keyID, tagtype_t type,
+                         int count,...);
+
+/* Metadata Import-Export utilities */
+void  CPL_DLL  GTIFPrint(GTIF *gtif, GTIFPrintMethod print, void *aux);
+int   CPL_DLL  GTIFImport(GTIF *gtif, GTIFReadMethod scan, void *aux);
+char  CPL_DLL *GTIFKeyName(geokey_t key);
+char  CPL_DLL *GTIFValueName(geokey_t key,int value);
+char  CPL_DLL *GTIFTypeName(tagtype_t type);
+char  CPL_DLL *GTIFTagName(int tag);
+int   CPL_DLL  GTIFKeyCode(char * key);
+int   CPL_DLL  GTIFValueCode(geokey_t key,char *value);
+int   CPL_DLL  GTIFTypeCode(char *type);
+int   CPL_DLL  GTIFTagCode(char *tag);
+
+/* Translation between image/PCS space */
+
+int CPL_DLL    GTIFImageToPCS( GTIF *gtif, double *x, double *y );
+int CPL_DLL    GTIFPCSToImage( GTIF *gtif, double *x, double *y );
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __geotiff_h_ */
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geotiff_proj4.c b/Utilities/GDAL/frmts/gtiff/libgeotiff/geotiff_proj4.c
new file mode 100644
index 0000000000..6456ac5a0a
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geotiff_proj4.c
@@ -0,0 +1,716 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  libgeotiff
+ * Purpose:  Code to convert a normalized GeoTIFF definition into a PROJ.4
+ *           (OGDI) compatible projection string.
+ * Author:   Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: geotiff_proj4.c,v $
+ * Revision 1.22  2005/03/04 04:32:37  fwarmerdam
+ * added cylindricalequalarea support
+ *
+ * Revision 1.21  2003/08/21 18:42:39  warmerda
+ * fixed support for ModelTypeGeographic as per email from Young Su, Cha
+ *
+ * Revision 1.20  2003/07/08 17:31:30  warmerda
+ * cleanup various warnings
+ *
+ * Revision 1.19  2002/11/29 20:57:09  warmerda
+ * added LCC1SP mapping
+ *
+ * Revision 1.18  2002/07/09 14:47:53  warmerda
+ * fixed translation of polar stereographic
+ *
+ * Revision 1.17  2001/11/23 19:53:56  warmerda
+ * free PROJ.4 definitions after use
+ *
+ * Revision 1.16  2000/12/05 19:21:45  warmerda
+ * added cassini support
+ *
+ * Revision 1.15  2000/12/05 17:44:41  warmerda
+ * Use +R_A for Miller and VanDerGrinten
+ *
+ * Revision 1.14  2000/10/13 18:06:51  warmerda
+ * added econic support for PROJ.4 translation
+ *
+ * Revision 1.13  2000/09/15 19:30:48  warmerda
+ * *** empty log message ***
+ *
+ * Revision 1.12  2000/09/15 18:21:07  warmerda
+ * Fixed order of parameters for LCC 2SP.  When parameters
+ * were read from EPSG CSV files the standard parallels and origin
+ * were mixed up.  This affects alot of state plane zones!
+ *
+ * Revision 1.11  2000/06/06 17:39:45  warmerda
+ * Modify to work with projUV version of library.
+ *
+ * Revision 1.10  1999/07/06 15:05:51  warmerda
+ * Fixed up LCC_1SP notes.
+ *
+ * Revision 1.9  1999/05/04 16:24:49  warmerda
+ * Fixed projection string formating with zones.
+ *
+ * Revision 1.8  1999/05/04 12:27:01  geotiff
+ * only emit proj unsupported warning if DEBUG defined
+ *
+ * Revision 1.7  1999/05/04 03:14:59  warmerda
+ * fixed use of foot instead of ft for units
+ *
+ * Revision 1.6  1999/05/03 17:50:31  warmerda
+ * avoid warnings on IRIX
+ *
+ * Revision 1.5  1999/04/29 23:02:24  warmerda
+ * added mapsys utm test.
+ *
+ * Revision 1.4  1999/03/18 21:35:42  geotiff
+ * Added reprojection functions
+ *
+ * Revision 1.3  1999/03/10 18:11:17  geotiff
+ * Removed comment about this not being the master ... now it is.
+ *
+ * Revision 1.2  1999/03/10 18:10:27  geotiff
+ * Avoid use of cpl_serv.h and CPLStrdup().
+ *
+ * Revision 1.1  1999/03/10 15:20:43  geotiff
+ * New
+ *
+ */
+
+#include "cpl_serv.h"
+#include "geotiff.h"
+#include "geo_normalize.h"
+#include "geovalues.h"
+
+/************************************************************************/
+/*                          GTIFGetProj4Defn()                          */
+/************************************************************************/
+
+char * GTIFGetProj4Defn( GTIFDefn * psDefn )
+
+{
+    char	szProjection[512];
+    char	szUnits[24];
+    double      dfFalseEasting, dfFalseNorthing;
+
+    szProjection[0] = '\0';
+    
+/* ==================================================================== */
+/*      Translate the units of measure.                                 */
+/*                                                                      */
+/*      Note that even with a +units, or +to_meter in effect, it is     */
+/*      still assumed that all the projection parameters are in         */
+/*      meters.                                                         */
+/* ==================================================================== */
+    if( psDefn->UOMLength == Linear_Meter )
+    {
+        strcpy( szUnits, "+units=m " ); 
+    }
+    else if( psDefn->UOMLength == Linear_Foot )
+    {
+        strcpy( szUnits, "+units=ft " );
+    }
+    else if( psDefn->UOMLength == Linear_Foot_US_Survey )
+    {
+        strcpy( szUnits, "+units=us-ft " );
+    }
+    else if( psDefn->UOMLength == Linear_Foot_Indian )
+    {
+        strcpy( szUnits, "+units=ind-ft " );
+    }
+    else if( psDefn->UOMLength == Linear_Link )
+    {
+        strcpy( szUnits, "+units=link " );
+    }
+    else if( psDefn->UOMLength == Linear_Yard_Indian)
+    {
+        strcpy( szUnits, "+units=ind-yd " );
+    }
+    else if( psDefn->UOMLength == Linear_Fathom )
+    {
+        strcpy( szUnits, "+units=fath " );
+    }
+    else if( psDefn->UOMLength == Linear_Mile_International_Nautical )
+    {
+        strcpy( szUnits, "+units=kmi " );
+    }
+    else
+    {
+        sprintf( szUnits, "+to_meter=%.10f", psDefn->UOMLengthInMeters );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      false easting and northing are in meters and that is what       */
+/*      PROJ.4 wants regardless of the linear units.                    */
+/* -------------------------------------------------------------------- */
+    dfFalseEasting = psDefn->ProjParm[5];
+    dfFalseNorthing = psDefn->ProjParm[6];
+    
+/* ==================================================================== */
+/*      Handle general projection methods.                              */
+/* ==================================================================== */
+ 
+/* -------------------------------------------------------------------- */
+/*      Geographic.                                                     */
+/* -------------------------------------------------------------------- */
+    if(psDefn->Model==ModelTypeGeographic)
+    {
+        sprintf(szProjection+strlen(szProjection),"+proj=latlong ");
+        
+    }
+ 
+/* -------------------------------------------------------------------- */
+/*      UTM - special case override on transverse mercator so things    */
+/*      will be more meaningful to the user.                            */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->MapSys == MapSys_UTM_North )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=utm +zone=%d ",
+                 psDefn->Zone );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Transverse Mercator                                             */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_TransverseMercator )
+    {
+        sprintf( szProjection+strlen(szProjection),
+           "+proj=tmerc +lat_0=%.9f +lon_0=%.9f +k=%f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 psDefn->ProjParm[4],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Mercator							*/
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_Mercator )
+    {
+        sprintf( szProjection+strlen(szProjection),
+           "+proj=merc +lat_ts=%.9f +lon_0=%.9f +k=%f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 psDefn->ProjParm[4],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cassini/Soldner                                                 */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_CassiniSoldner )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=cass +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Oblique Stereographic - Should this really map onto             */
+/*      Stereographic?                                                  */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_ObliqueStereographic )
+    {
+        sprintf( szProjection+strlen(szProjection),
+           "+proj=stere +lat_0=%.9f +lon_0=%.9f +k=%f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 psDefn->ProjParm[4],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Stereographic                                                   */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_Stereographic )
+    {
+        sprintf( szProjection+strlen(szProjection),
+           "+proj=stere +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Polar Stereographic                                             */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_PolarStereographic )
+    {
+        if( psDefn->ProjParm[0] > 0.0 )
+            sprintf( szProjection+strlen(szProjection),
+                     "+proj=stere +lat_0=90 +lat_ts=%.9f +lon_0=%.9f "
+                     "+k=%.9f +x_0=%.3f +y_0=%.3f ",
+                     psDefn->ProjParm[0],
+                     psDefn->ProjParm[1],
+                     psDefn->ProjParm[4],
+                     dfFalseEasting,
+                     dfFalseNorthing );
+        else
+            sprintf( szProjection+strlen(szProjection),
+                     "+proj=stere +lat_0=-90 +lat_ts=%.9f +lon_0=%.9f "
+                     "+k=%.9f +x_0=%.3f +y_0=%.3f ",
+                     psDefn->ProjParm[0],
+                     psDefn->ProjParm[1],
+                     psDefn->ProjParm[4],
+                     dfFalseEasting,
+                     dfFalseNorthing );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Equirectangular                                                 */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_Equirectangular )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=eqc +lat_ts=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Gnomonic                                                        */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_Gnomonic )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=gnom +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Orthographic                                                    */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_Orthographic )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=ortho +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Lambert Azimuthal Equal Area                                    */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_LambertAzimEqualArea )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=laea +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Azimuthal Equidistant                                           */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_AzimuthalEquidistant )
+    {
+        sprintf( szProjection+strlen(szProjection),
+           "+proj=aeqd +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Miller Cylindrical                                              */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_MillerCylindrical )
+    {
+        sprintf( szProjection+strlen(szProjection),
+           "+proj=mill +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f +R_A ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Polyconic                                                       */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_Polyconic )
+    {
+        sprintf( szProjection+strlen(szProjection),
+           "+proj=poly +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      AlbersEqualArea                                                 */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_AlbersEqualArea )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=aea +lat_1=%.9f +lat_2=%.9f +lat_0=%.9f +lon_0=%.9f"
+                 " +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 psDefn->ProjParm[2],
+                 psDefn->ProjParm[3],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      EquidistantConic                                                */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_EquidistantConic )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=eqdc +lat_1=%.9f +lat_2=%.9f +lat_0=%.9f +lon_0=%.9f"
+                 " +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 psDefn->ProjParm[2],
+                 psDefn->ProjParm[3],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Robinson                                                        */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_Robinson )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=robin +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[1],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      VanDerGrinten                                                   */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_VanDerGrinten )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=vandg +lon_0=%.9f +x_0=%.3f +y_0=%.3f +R_A ",
+                 psDefn->ProjParm[1],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Sinusoidal                                                      */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_Sinusoidal )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=sinu +lon_0=%.9f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[1],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      LambertConfConic_2SP                                            */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_LambertConfConic_2SP )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=lcc +lat_0=%.9f +lon_0=%.9f +lat_1=%.9f +lat_2=%.9f "
+                 " +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 psDefn->ProjParm[2],
+                 psDefn->ProjParm[3],
+                 dfFalseEasting,
+                 dfFalseNorthing );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      LambertConfConic_1SP                                            */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_LambertConfConic_1SP )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=lcc +lat_0=%.9f +lat_1=%.9f +lon_0=%.9f"
+                 " +k_0=%.9f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 psDefn->ProjParm[4],
+                 psDefn->ProjParm[5],
+                 psDefn->ProjParm[6] );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      CT_CylindricalEqualArea                                         */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_CylindricalEqualArea )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=cea +lat_ts=%.9f +lon_0=%.9f "
+                 " +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 psDefn->ProjParm[5],
+                 psDefn->ProjParm[6] );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      NewZealandMapGrid                                               */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_NewZealandMapGrid )
+    {
+        /* this appears to be an unsupported formulation with PROJ.4 */
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Transverse Mercator - south oriented.                           */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_TransvMercator_SouthOriented )
+    {
+        /* this appears to be an unsupported formulation with PROJ.4 */
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      ObliqueMercator (Hotine)                                        */
+/* -------------------------------------------------------------------- */
+    else if( psDefn->CTProjection == CT_ObliqueMercator )
+    {
+        /* not clear how ProjParm[3] - angle from rectified to skewed grid -
+           should be applied ... see the +not_rot flag for PROJ.4.
+           Just ignoring for now. */
+
+        sprintf( szProjection+strlen(szProjection),
+                 "+proj=omerc +lat_0=%.9f +lonc=%.9f +alpha=%.9f"
+                 " +k=%.9f +x_0=%.3f +y_0=%.3f ",
+                 psDefn->ProjParm[0],
+                 psDefn->ProjParm[1],
+                 psDefn->ProjParm[2],
+                 psDefn->ProjParm[4],
+                 psDefn->ProjParm[5],
+                 psDefn->ProjParm[6] );
+    }
+
+/* ==================================================================== */
+/*      Handle ellipsoid information.                                   */
+/* ==================================================================== */
+    if( psDefn->Ellipsoid == Ellipse_WGS_84 )
+        strcat( szProjection, "+ellps=WGS84 " );
+    else if( psDefn->Ellipsoid == Ellipse_Clarke_1866 )
+        strcat( szProjection, "+ellps=clrk66 " );
+    else if( psDefn->Ellipsoid == Ellipse_Clarke_1880 )
+        strcat( szProjection, "+ellps=clrk80 " );
+    else if( psDefn->Ellipsoid == Ellipse_GRS_1980 )
+        strcat( szProjection, "+ellps=GRS80 " );
+    else
+    {
+        if( psDefn->SemiMajor != 0.0 && psDefn->SemiMinor != 0.0 )
+        {
+            sprintf( szProjection+strlen(szProjection),
+                     "+a=%.3f +b=%.3f ",
+                     psDefn->SemiMajor,
+                     psDefn->SemiMinor );
+        }
+    }
+
+    strcat( szProjection, szUnits );
+
+    return( strdup( szProjection ) );
+}
+
+#if !defined(HAVE_LIBPROJ) || !defined(HAVE_PROJECTS_H)
+
+int GTIFProj4ToLatLong( GTIFDefn * psDefn, int nPoints,
+                        double *padfX, double *padfY )
+{
+    (void) psDefn;
+    (void) nPoints;
+    (void) padfX;
+    (void) padfY;
+#ifdef DEBUG    
+    fprintf( stderr,
+             "GTIFProj4ToLatLong() - PROJ.4 support not compiled in.\n" );
+#endif    
+    return FALSE;
+}
+
+int GTIFProj4FromLatLong( GTIFDefn * psDefn, int nPoints,
+                          double *padfX, double *padfY )
+{
+    (void) psDefn;
+    (void) nPoints;
+    (void) padfX;
+    (void) padfY;
+#ifdef DEBUG    
+    fprintf( stderr,
+             "GTIFProj4FromLatLong() - PROJ.4 support not compiled in.\n" );
+#endif    
+    return FALSE;
+}
+#else
+
+#include "projects.h"
+
+#ifdef USE_PROJUV
+#  define UV projUV
+#endif
+
+/************************************************************************/
+/*                        GTIFProj4FromLatLong()                        */
+/*                                                                      */
+/*      Convert lat/long values to projected coordinate for a           */
+/*      particular definition.                                          */
+/************************************************************************/
+
+int GTIFProj4FromLatLong( GTIFDefn * psDefn, int nPoints,
+                          double *padfX, double *padfY )
+
+{
+    char	*pszProjection, **papszArgs;
+    PJ		*psPJ;
+    int		i;
+    
+/* -------------------------------------------------------------------- */
+/*      Get a projection definition.                                    */
+/* -------------------------------------------------------------------- */
+    pszProjection = GTIFGetProj4Defn( psDefn );
+
+    if( pszProjection == NULL )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Parse into tokens for pj_init(), and initialize the projection. */
+/* -------------------------------------------------------------------- */
+    
+    papszArgs = CSLTokenizeStringComplex( pszProjection, " +", TRUE, FALSE );
+    free( pszProjection );
+
+    psPJ = pj_init( CSLCount(papszArgs), papszArgs );
+
+    CSLDestroy( papszArgs );
+
+    if( psPJ == NULL )
+    {
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Process each of the points.                                     */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < nPoints; i++ )
+    {
+        UV	sUV;
+
+        sUV.u = padfX[i] * DEG_TO_RAD;
+        sUV.v = padfY[i] * DEG_TO_RAD;
+
+        sUV = pj_fwd( sUV, psPJ );
+
+        padfX[i] = sUV.u;
+        padfY[i] = sUV.v;
+    }
+
+    pj_free( psPJ );
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                         GTIFProj4ToLatLong()                         */
+/*                                                                      */
+/*      Convert projection coordinates to lat/long for a particular     */
+/*      definition.                                                     */
+/************************************************************************/
+
+int GTIFProj4ToLatLong( GTIFDefn * psDefn, int nPoints,
+                        double *padfX, double *padfY )
+
+{
+    char	*pszProjection, **papszArgs;
+    PJ		*psPJ;
+    int		i;
+    
+/* -------------------------------------------------------------------- */
+/*      Get a projection definition.                                    */
+/* -------------------------------------------------------------------- */
+    pszProjection = GTIFGetProj4Defn( psDefn );
+
+    if( pszProjection == NULL )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Parse into tokens for pj_init(), and initialize the projection. */
+/* -------------------------------------------------------------------- */
+    
+    papszArgs = CSLTokenizeStringComplex( pszProjection, " +", TRUE, FALSE );
+    free( pszProjection );
+
+    psPJ = pj_init( CSLCount(papszArgs), papszArgs );
+
+    CSLDestroy( papszArgs );
+
+    if( psPJ == NULL )
+    {
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Process each of the points.                                     */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < nPoints; i++ )
+    {
+        UV	sUV;
+
+        sUV.u = padfX[i];
+        sUV.v = padfY[i];
+
+        sUV = pj_inv( sUV, psPJ );
+
+        padfX[i] = sUV.u * RAD_TO_DEG;
+        padfY[i] = sUV.v * RAD_TO_DEG;
+    }
+
+    pj_free( psPJ );
+
+    return TRUE;
+}
+
+
+#endif /* has projects.h and -lproj */
+
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geotiffio.h b/Utilities/GDAL/frmts/gtiff/libgeotiff/geotiffio.h
new file mode 100644
index 0000000000..72442b4440
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geotiffio.h
@@ -0,0 +1,16 @@
+/* 
+ * geotiffio.h
+ * 
+ * Standard include file for geotiff, including all
+ *  key and code definitions.
+ */
+
+
+#ifndef __geotiffio_h
+#define __geotiffio_h
+
+#include "geotiff.h"  /* public key interface */
+#include "geovalues.h" /* key code definitions */
+
+#endif /* __geotiffio_h */
+
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/geovalues.h b/Utilities/GDAL/frmts/gtiff/libgeotiff/geovalues.h
new file mode 100644
index 0000000000..625d33a31a
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/geovalues.h
@@ -0,0 +1,120 @@
+/**********************************************************************
+ *
+ *  geovalues.h - Public registry for valid GEOTIFF  key-values.
+ *
+ *   Written By: Niles D. Ritter
+ *
+ *  Revision History:
+ *
+ *     Rev.#  Author     Date     Key Changes/Additions
+ *     -----  ------  ----------  -------------------------------------
+ *      0.1     ndr    10 Jun 95     Inital Beta Release
+ *      0.2     ndr    12 Jul 95     New EPSG Tables installed.
+ *      0.2.1   ndr    28 Jul 95     Added ModelType aliases to Model's
+ *      1.0     ndr    18 Sep 95     Promoted to Revision 1.0
+ *
+ **********************************************************************/
+
+#ifndef __geovalues_h_
+#define __geovalues_h_
+
+/* If code values are added or modified, the "GvCurrentMinorRev" 
+ * number should be incremented here. If new Keys are added, then the
+ * GvCurrentRevision number should be incremented instead, and the
+ * GvCurrentMinorRev should be reset to zero (see "geokeys.h").
+ *
+ * In addition, any changes here should be reflected in "geo_names.c"
+ *
+ */
+ 
+#define GvCurrentMinorRev  0  /* First Major Rev EPSG Code Release  */
+
+
+/*
+ * Universal key values -- defined for consistency
+ */
+#define KvUndefined         0
+#define KvUserDefined   32767
+
+#ifdef ValuePair
+#  undef ValuePair
+#endif
+#define ValuePair(name,value)    name = value,
+
+/*
+ * The section numbers refer to the GeoTIFF Specification section
+ * in which the code values are documented.
+ */
+ 
+/************************************************************
+ *         6.3.1 GeoTIFF General Codes
+ ************************************************************/
+
+/* 6.3.1.1 Model Type Codes */
+typedef enum {
+	ModelTypeProjected  = 1,  /* Projection Coordinate System */
+	ModelTypeGeographic = 2,  /* Geographic latitude-longitude System */
+	ModelTypeGeocentric = 3,   /* Geocentric (X,Y,Z) Coordinate System */
+	ModelProjected  = ModelTypeProjected,   /* alias */
+	ModelGeographic = ModelTypeGeographic,  /* alias */
+	ModelGeocentric = ModelTypeGeocentric   /* alias */
+} modeltype_t;
+
+/* 6.3.1.2 Raster Type Codes */
+typedef enum {
+	RasterPixelIsArea   = 1,  /* Standard pixel-fills-grid-cell */
+	RasterPixelIsPoint  = 2   /* Pixel-at-grid-vertex */
+} rastertype_t;
+
+typedef enum {
+#  include "epsg_gcs.inc"
+  geographic_end
+} geographic_t;
+
+typedef enum {
+#  include "epsg_datum.inc"
+   geodeticdatum_end
+} geodeticdatum_t;
+
+typedef enum {
+#  include "epsg_units.inc"
+   Unit_End
+} geounits_t;
+
+typedef enum {
+#  include "epsg_ellipse.inc"
+    ellipsoid_end
+} ellipsoid_t;
+
+typedef enum {
+#  include "epsg_pm.inc"
+   primemeridian_end
+} primemeridian_t;
+
+typedef enum {
+#  include "epsg_pcs.inc"
+   pcstype_end
+} pcstype_t;
+
+typedef enum {
+#  include "epsg_proj.inc"
+   projection_end
+} projection_t;
+
+typedef enum {
+#  include "geo_ctrans.inc"
+   coordtrans_end
+} coordtrans_t;
+
+typedef enum {
+#  include "epsg_vertcs.inc"
+   vertcs_end
+} vertcstype_t;
+
+
+typedef enum {
+	VDatumBase = 1
+} vdatum_t;
+
+#endif /* __geovalues_h_ */
+
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/makefile.vc b/Utilities/GDAL/frmts/gtiff/libgeotiff/makefile.vc
new file mode 100644
index 0000000000..91d218fca2
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/makefile.vc
@@ -0,0 +1,27 @@
+
+EXTRAFLAGS =	-I..\libtiff
+
+OBJ	= \
+	xtiff.obj \
+	geo_free.obj \
+	geo_get.obj \
+	geo_names.obj \
+	geo_new.obj \
+	geo_print.obj \
+	geo_set.obj \
+	geo_tiffp.obj \
+	geo_write.obj \
+	geo_normalize.obj \
+	geo_extra.obj \
+	geotiff_proj4.obj
+
+GDAL_ROOT	=	..\..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\..\o
+
+clean:
+	del *.obj
+
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/xtiff.c b/Utilities/GDAL/frmts/gtiff/libgeotiff/xtiff.c
new file mode 100644
index 0000000000..f0be436d84
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/xtiff.c
@@ -0,0 +1,199 @@
+/*
+ * xtiff.c
+ *
+ * Extended TIFF Directory GEO Tag Support.
+ *
+ *  You may use this file as a template to add your own
+ *  extended tags to the library. Only the parts of the code
+ *  marked with "XXX" require modification.
+ *
+ *  Author: Niles D. Ritter
+ *
+ *  Revisions:
+ *    18 Sep 1995   -- Deprecated Integraph Matrix tag with new one.
+ *                     Backward compatible support provided.  --NDR.
+ */
+ 
+#include "xtiffio.h"
+#include <stdio.h>
+#include "cpl_serv.h"
+
+/*  Tiff info structure.
+ *
+ *     Entry format:
+ *        { TAGNUMBER, ReadCount, WriteCount, DataType, FIELDNUM, 
+ *          OkToChange, PassDirCountOnSet, AsciiName }
+ *
+ *     For ReadCount, WriteCount, -1 = unknown.
+ */
+
+static const TIFFFieldInfo xtiffFieldInfo[] = {
+  
+  /* XXX Insert Your tags here */
+    { TIFFTAG_GEOPIXELSCALE,	-1,-1, TIFF_DOUBLE,	FIELD_CUSTOM,
+      TRUE,	TRUE,	"GeoPixelScale" },
+    { TIFFTAG_INTERGRAPH_MATRIX,-1,-1, TIFF_DOUBLE,	FIELD_CUSTOM,
+      TRUE,	TRUE,	"Intergraph TransformationMatrix" },
+    { TIFFTAG_GEOTRANSMATRIX,	-1,-1, TIFF_DOUBLE,	FIELD_CUSTOM,
+      TRUE,	TRUE,	"GeoTransformationMatrix" },
+    { TIFFTAG_GEOTIEPOINTS,	-1,-1, TIFF_DOUBLE,	FIELD_CUSTOM,
+      TRUE,	TRUE,	"GeoTiePoints" },
+    { TIFFTAG_GEOKEYDIRECTORY,-1,-1, TIFF_SHORT,	FIELD_CUSTOM,
+      TRUE,	TRUE,	"GeoKeyDirectory" },
+    { TIFFTAG_GEODOUBLEPARAMS,	-1,-1, TIFF_DOUBLE,	FIELD_CUSTOM,
+      TRUE,	TRUE,	"GeoDoubleParams" },
+    { TIFFTAG_GEOASCIIPARAMS,	-1,-1, TIFF_ASCII,	FIELD_CUSTOM,
+      TRUE,	FALSE,	"GeoASCIIParams" },
+#ifdef JPL_TAG_SUPPORT
+    { TIFFTAG_JPL_CARTO_IFD,	 1, 1, TIFF_LONG,	FIELD_CUSTOM,
+      TRUE,	TRUE,	"JPL Carto IFD offset" },  /** Don't use this! **/
+#endif
+};
+
+#define	N(a)	(sizeof (a) / sizeof (a[0]))
+static void _XTIFFLocalDefaultDirectory(TIFF *tif)
+{
+    /* Install the extended Tag field info */
+    TIFFMergeFieldInfo(tif, xtiffFieldInfo, N(xtiffFieldInfo));
+}
+
+
+/**********************************************************************
+ *    Nothing below this line should need to be changed.
+ **********************************************************************/
+
+static TIFFExtendProc _ParentExtender;
+
+/*
+ *  This is the callback procedure, and is
+ *  called by the DefaultDirectory method
+ *  every time a new TIFF directory is opened.
+ */
+
+static void
+_XTIFFDefaultDirectory(TIFF *tif)
+{
+    /* set up our own defaults */
+    _XTIFFLocalDefaultDirectory(tif);
+
+    /* Since an XTIFF client module may have overridden
+     * the default directory method, we call it now to
+     * allow it to set up the rest of its own methods.
+     */
+
+    if (_ParentExtender) 
+        (*_ParentExtender)(tif);
+}
+
+/*
+ *  XTIFF Initializer -- sets up the callback
+ *   procedure for the TIFF module.
+ */
+
+static
+void _XTIFFInitialize(void)
+{
+    static int first_time=1;
+	
+    if (! first_time) return; /* Been there. Done that. */
+    first_time = 0;
+	
+    /* Grab the inherited method and install */
+    _ParentExtender = TIFFSetTagExtender(_XTIFFDefaultDirectory);
+}
+
+
+/**
+ * GeoTIFF compatible TIFF file open function.
+ *
+ * @param name The filename of a TIFF file to open.
+ * @param mode The open mode ("r", "w" or "a").
+ *
+ * @return a TIFF * for the file, or NULL if the open failed.
+ *
+This function is used to open GeoTIFF files instead of TIFFOpen() from
+libtiff.  Internally it calls TIFFOpen(), but sets up some extra hooks
+so that GeoTIFF tags can be extracted from the file.  If XTIFFOpen() isn't
+used, GTIFNew() won't work properly.  Files opened
+with XTIFFOpen() should be closed with XTIFFClose().
+
+The name of the file to be opened should be passed as <b>name</b>, and an
+opening mode ("r", "w" or "a") acceptable to TIFFOpen() should be passed as the
+<b>mode</b>.<p>
+
+If XTIFFOpen() fails it will return NULL.  Otherwise, normal TIFFOpen()
+error reporting steps will have already taken place.<p>
+ */
+
+TIFF*
+XTIFFOpen(const char* name, const char* mode)
+{
+    TIFF *tif;
+
+    /* Set up the callback */
+    _XTIFFInitialize();	
+	
+    /* Open the file; the callback will set everything up
+     */
+    tif = TIFFOpen(name, mode);
+    if (!tif) return tif;
+	
+    return tif;
+}
+
+TIFF*
+XTIFFFdOpen(int fd, const char* name, const char* mode)
+{
+    TIFF *tif;
+
+    /* Set up the callback */
+    _XTIFFInitialize();	
+
+    /* Open the file; the callback will set everything up
+     */
+    tif = TIFFFdOpen(fd, name, mode);
+    if (!tif) return tif;
+	
+    return tif;
+}
+
+TIFF*
+XTIFFClientOpen(const char* name, const char* mode, thandle_t thehandle,
+	    TIFFReadWriteProc RWProc, TIFFReadWriteProc RWProc2,
+	    TIFFSeekProc SProc, TIFFCloseProc CProc,
+	    TIFFSizeProc SzProc,
+	    TIFFMapFileProc MFProvc, TIFFUnmapFileProc UMFProc )
+{
+    TIFF *tif;
+    
+    /* Set up the callback */
+    _XTIFFInitialize();	
+    
+    /* Open the file; the callback will set everything up
+     */
+    tif = TIFFClientOpen(name, mode, thehandle,
+                         RWProc, RWProc2,
+                         SProc, CProc,
+                         SzProc,
+                         MFProvc, UMFProc);
+    
+    if (!tif) return tif;
+    
+    return tif;
+}
+
+/**
+ * Close a file opened with XTIFFOpen().
+ *
+ * @param tif The file handle returned by XTIFFOpen().
+ * 
+ * If a GTIF structure was created with GTIFNew()
+ * for this file, it should be freed with GTIFFree()
+ * <i>before</i> calling XTIFFClose().
+*/
+
+void
+XTIFFClose(TIFF *tif)
+{
+    TIFFClose(tif);
+}
diff --git a/Utilities/GDAL/frmts/gtiff/libgeotiff/xtiffio.h b/Utilities/GDAL/frmts/gtiff/libgeotiff/xtiffio.h
new file mode 100644
index 0000000000..31975a2c49
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libgeotiff/xtiffio.h
@@ -0,0 +1,83 @@
+/*
+ *  xtiffio.h -- Public interface to Extended GEO TIFF tags
+ *
+ *    written by: Niles D. Ritter
+ */
+
+#ifndef __xtiffio_h
+#define __xtiffio_h
+
+#include "tiffio.h"
+#include "geo_config.h"
+
+/**
+ * \file xtiffio.h
+ *
+ * Definitions relating GeoTIFF functions from geotiff.h to the TIFF
+ * library (usually libtiff).
+ */
+
+/* 
+ *  Define public Tag names and values here 
+ */
+
+/* tags 33550 is a private tag registered to SoftDesk, Inc */
+#define TIFFTAG_GEOPIXELSCALE       33550
+/* tags 33920-33921 are private tags registered to Intergraph, Inc */
+#define TIFFTAG_INTERGRAPH_MATRIX    33920   /* $use TIFFTAG_GEOTRANSMATRIX ! */
+#define TIFFTAG_GEOTIEPOINTS         33922
+/* tags 34263-34264 are private tags registered to NASA-JPL Carto Group */
+#ifdef JPL_TAG_SUPPORT
+#define TIFFTAG_JPL_CARTO_IFD        34263    /* $use GeoProjectionInfo ! */
+#endif
+#define TIFFTAG_GEOTRANSMATRIX       34264    /* New Matrix Tag replaces 33920 */
+/* tags 34735-3438 are private tags registered to SPOT Image, Inc */
+#define TIFFTAG_GEOKEYDIRECTORY      34735
+#define TIFFTAG_GEODOUBLEPARAMS      34736
+#define TIFFTAG_GEOASCIIPARAMS       34737
+
+/* 
+ *  Define Printing method flags. These
+ *  flags may be passed in to TIFFPrintDirectory() to
+ *  indicate that those particular field values should
+ *  be printed out in full, rather than just an indicator
+ *  of whether they are present or not.
+ */
+#define	TIFFPRINT_GEOKEYDIRECTORY	0x80000000
+#define	TIFFPRINT_GEOKEYPARAMS		0x40000000
+
+/**********************************************************************
+ *    Nothing below this line should need to be changed by the user.
+ **********************************************************************/
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/**********************************************************************
+ * Do we want to build as a DLL on windows?
+ **********************************************************************/
+#if !defined(CPL_DLL)
+#  if defined(_WIN32) && defined(BUILD_AS_DLL)
+#    define CPL_DLL     __declspec(dllexport)
+#  else
+#    define CPL_DLL
+#  endif
+#endif
+
+extern TIFF CPL_DLL * XTIFFOpen(const char* name, const char* mode);
+extern TIFF CPL_DLL * XTIFFFdOpen(int fd, const char* name, const char* mode);
+extern void CPL_DLL XTIFFClose(TIFF *tif);
+
+extern TIFF CPL_DLL * XTIFFClientOpen(const char* name, const char* mode, 
+                                      thandle_t thehandle,
+                                      TIFFReadWriteProc, TIFFReadWriteProc,
+                                      TIFFSeekProc, TIFFCloseProc,
+                                      TIFFSizeProc,
+                                      TIFFMapFileProc, TIFFUnmapFileProc);
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __xtiffio_h */
+
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/GNUmakefile b/Utilities/GDAL/frmts/gtiff/libtiff/GNUmakefile
new file mode 100644
index 0000000000..4b94ad9174
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/GNUmakefile
@@ -0,0 +1,79 @@
+
+include ../../../GDALmake.opt
+
+OBJ	= \
+	tif_aux.o \
+	tif_close.o \
+	tif_codec.o \
+	tif_color.o \
+	tif_compress.o \
+	tif_dir.o \
+	tif_dirinfo.o \
+	tif_dirread.o \
+	tif_dirwrite.o \
+	tif_dumpmode.o \
+	tif_error.o \
+	tif_extension.o \
+	tif_fax3.o \
+	tif_fax3sm.o \
+	tif_getimage.o \
+	tif_jpeg.o \
+	tif_flush.o \
+	tif_luv.o \
+	tif_lzw.o \
+	tif_next.o \
+	tif_open.o \
+	tif_packbits.o \
+	tif_pixarlog.o \
+	tif_predict.o \
+	tif_print.o \
+	tif_read.o \
+	tif_swab.o \
+	tif_strip.o \
+	tif_thunder.o \
+	tif_tile.o \
+	tif_vsi.o \
+	tif_version.o \
+	tif_warning.o \
+	tif_write.o \
+	tif_zip.o
+
+O_OBJ	=	$(foreach file,$(OBJ),../../o/$(file))
+
+ALL_C_FLAGS =	$(CFLAGS) $(CPPFLAGS) \
+		-DZIP_SUPPORT -DPIXARLOG_SUPPORT -I../../zlib
+
+ifneq ($(JPEG_SETTING),no)
+ALL_C_FLAGS 	:=	$(ALL_C_FLAGS) -DJPEG_SUPPORT
+endif
+
+ifeq ($(JPEG_SETTING),internal)
+ALL_C_FLAGS 	:=	$(ALL_C_FLAGS) -I../../jpeg/libjpeg
+endif
+
+default:	$(OBJ)
+
+clean:
+	rm -f $(O_OBJ) *.o *.a
+
+import:
+	@if test ! -d ~/libtiff ; then \
+	  echo reimport requires libtiff checked out ~/libtiff ; \
+	  exit 1; \
+	fi
+
+	mv tif_config.h tif_config_safe.h
+	mv tiffconf.h tiffconf_safe.h
+	copymatch.sh ~/libtiff/libtiff *.c *.h
+	mv tif_config_safe.h tif_config.h
+	mv tiffconf_safe.h tiffconf.h
+
+	@echo
+	@echo 'Now do something like:'
+	@echo '% cvs commit -m "updated to libtiff 3.6.0"'
+	@echo
+
+install-obj:	$(O_OBJ)
+
+../../o/%.o:	%.c
+	$(CC) -c -I../../port $(ALL_C_FLAGS) $< -o $@
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/makefile.vc b/Utilities/GDAL/frmts/gtiff/libtiff/makefile.vc
new file mode 100644
index 0000000000..53a378f613
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/makefile.vc
@@ -0,0 +1,60 @@
+
+OBJ	= \
+	tif_aux.obj \
+	tif_close.obj \
+	tif_codec.obj \
+	tif_color.obj \
+	tif_compress.obj \
+	tif_dir.obj \
+	tif_dirinfo.obj \
+	tif_dirread.obj \
+	tif_dirwrite.obj \
+	tif_dumpmode.obj \
+	tif_error.obj \
+	tif_extension.obj \
+	tif_fax3.obj \
+	tif_fax3sm.obj \
+	tif_getimage.obj \
+	tif_jpeg.obj \
+	tif_flush.obj \
+	tif_luv.obj \
+	tif_lzw.obj \
+	tif_next.obj \
+	tif_open.obj \
+	tif_packbits.obj \
+	tif_pixarlog.obj \
+	tif_predict.obj \
+	tif_print.obj \
+	tif_read.obj \
+	tif_swab.obj \
+	tif_strip.obj \
+	tif_thunder.obj \
+	tif_tile.obj \
+	tif_vsi.obj \
+	tif_version.obj \
+	tif_warning.obj \
+	tif_write.obj \
+	tif_zip.obj
+
+GDAL_ROOT	=	..\..\..
+
+EXTRAFLAGS = 	-I..\..\zlib -DZIP_SUPPORT -DPIXARLOG_SUPPORT $(JPEG_FLAGS)
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+!IFDEF JPEG_SUPPORTED
+!IFDEF JPEG_EXTERNAL_LIB
+JPEG_FLAGS =	-I$(JPEGDIR) -DJPEG_SUPPORT 
+!ELSE
+JPEG_FLAGS =	-I..\..\jpeg\libjpeg -DJPEG_SUPPORT 
+!ENDIF
+!ENDIF
+
+
+
+default:	$(OBJ)
+	copy *.obj ..\..\o
+
+clean:
+	del *.obj
+
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/t4.h b/Utilities/GDAL/frmts/gtiff/libtiff/t4.h
new file mode 100644
index 0000000000..fa4f5f75d0
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/t4.h
@@ -0,0 +1,285 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _T4_
+#define	_T4_
+/*
+ * CCITT T.4 1D Huffman runlength codes and
+ * related definitions.  Given the small sizes
+ * of these tables it does not seem
+ * worthwhile to make code & length 8 bits.
+ */
+typedef struct tableentry {
+    unsigned short length;	/* bit length of g3 code */
+    unsigned short code;	/* g3 code */
+    short	runlen;		/* run length in bits */
+} tableentry;
+
+#define	EOL	0x001	/* EOL code value - 0000 0000 0000 1 */
+
+/* status values returned instead of a run length */
+#define	G3CODE_EOL	-1	/* NB: ACT_EOL - ACT_WRUNT */
+#define	G3CODE_INVALID	-2	/* NB: ACT_INVALID - ACT_WRUNT */
+#define	G3CODE_EOF	-3	/* end of input data */
+#define	G3CODE_INCOMP	-4	/* incomplete run code */
+
+/*
+ * Note that these tables are ordered such that the
+ * index into the table is known to be either the
+ * run length, or (run length / 64) + a fixed offset.
+ *
+ * NB: The G3CODE_INVALID entries are only used
+ *     during state generation (see mkg3states.c).
+ */
+#ifdef G3CODES
+const tableentry TIFFFaxWhiteCodes[] = {
+    { 8, 0x35, 0 },	/* 0011 0101 */
+    { 6, 0x7, 1 },	/* 0001 11 */
+    { 4, 0x7, 2 },	/* 0111 */
+    { 4, 0x8, 3 },	/* 1000 */
+    { 4, 0xB, 4 },	/* 1011 */
+    { 4, 0xC, 5 },	/* 1100 */
+    { 4, 0xE, 6 },	/* 1110 */
+    { 4, 0xF, 7 },	/* 1111 */
+    { 5, 0x13, 8 },	/* 1001 1 */
+    { 5, 0x14, 9 },	/* 1010 0 */
+    { 5, 0x7, 10 },	/* 0011 1 */
+    { 5, 0x8, 11 },	/* 0100 0 */
+    { 6, 0x8, 12 },	/* 0010 00 */
+    { 6, 0x3, 13 },	/* 0000 11 */
+    { 6, 0x34, 14 },	/* 1101 00 */
+    { 6, 0x35, 15 },	/* 1101 01 */
+    { 6, 0x2A, 16 },	/* 1010 10 */
+    { 6, 0x2B, 17 },	/* 1010 11 */
+    { 7, 0x27, 18 },	/* 0100 111 */
+    { 7, 0xC, 19 },	/* 0001 100 */
+    { 7, 0x8, 20 },	/* 0001 000 */
+    { 7, 0x17, 21 },	/* 0010 111 */
+    { 7, 0x3, 22 },	/* 0000 011 */
+    { 7, 0x4, 23 },	/* 0000 100 */
+    { 7, 0x28, 24 },	/* 0101 000 */
+    { 7, 0x2B, 25 },	/* 0101 011 */
+    { 7, 0x13, 26 },	/* 0010 011 */
+    { 7, 0x24, 27 },	/* 0100 100 */
+    { 7, 0x18, 28 },	/* 0011 000 */
+    { 8, 0x2, 29 },	/* 0000 0010 */
+    { 8, 0x3, 30 },	/* 0000 0011 */
+    { 8, 0x1A, 31 },	/* 0001 1010 */
+    { 8, 0x1B, 32 },	/* 0001 1011 */
+    { 8, 0x12, 33 },	/* 0001 0010 */
+    { 8, 0x13, 34 },	/* 0001 0011 */
+    { 8, 0x14, 35 },	/* 0001 0100 */
+    { 8, 0x15, 36 },	/* 0001 0101 */
+    { 8, 0x16, 37 },	/* 0001 0110 */
+    { 8, 0x17, 38 },	/* 0001 0111 */
+    { 8, 0x28, 39 },	/* 0010 1000 */
+    { 8, 0x29, 40 },	/* 0010 1001 */
+    { 8, 0x2A, 41 },	/* 0010 1010 */
+    { 8, 0x2B, 42 },	/* 0010 1011 */
+    { 8, 0x2C, 43 },	/* 0010 1100 */
+    { 8, 0x2D, 44 },	/* 0010 1101 */
+    { 8, 0x4, 45 },	/* 0000 0100 */
+    { 8, 0x5, 46 },	/* 0000 0101 */
+    { 8, 0xA, 47 },	/* 0000 1010 */
+    { 8, 0xB, 48 },	/* 0000 1011 */
+    { 8, 0x52, 49 },	/* 0101 0010 */
+    { 8, 0x53, 50 },	/* 0101 0011 */
+    { 8, 0x54, 51 },	/* 0101 0100 */
+    { 8, 0x55, 52 },	/* 0101 0101 */
+    { 8, 0x24, 53 },	/* 0010 0100 */
+    { 8, 0x25, 54 },	/* 0010 0101 */
+    { 8, 0x58, 55 },	/* 0101 1000 */
+    { 8, 0x59, 56 },	/* 0101 1001 */
+    { 8, 0x5A, 57 },	/* 0101 1010 */
+    { 8, 0x5B, 58 },	/* 0101 1011 */
+    { 8, 0x4A, 59 },	/* 0100 1010 */
+    { 8, 0x4B, 60 },	/* 0100 1011 */
+    { 8, 0x32, 61 },	/* 0011 0010 */
+    { 8, 0x33, 62 },	/* 0011 0011 */
+    { 8, 0x34, 63 },	/* 0011 0100 */
+    { 5, 0x1B, 64 },	/* 1101 1 */
+    { 5, 0x12, 128 },	/* 1001 0 */
+    { 6, 0x17, 192 },	/* 0101 11 */
+    { 7, 0x37, 256 },	/* 0110 111 */
+    { 8, 0x36, 320 },	/* 0011 0110 */
+    { 8, 0x37, 384 },	/* 0011 0111 */
+    { 8, 0x64, 448 },	/* 0110 0100 */
+    { 8, 0x65, 512 },	/* 0110 0101 */
+    { 8, 0x68, 576 },	/* 0110 1000 */
+    { 8, 0x67, 640 },	/* 0110 0111 */
+    { 9, 0xCC, 704 },	/* 0110 0110 0 */
+    { 9, 0xCD, 768 },	/* 0110 0110 1 */
+    { 9, 0xD2, 832 },	/* 0110 1001 0 */
+    { 9, 0xD3, 896 },	/* 0110 1001 1 */
+    { 9, 0xD4, 960 },	/* 0110 1010 0 */
+    { 9, 0xD5, 1024 },	/* 0110 1010 1 */
+    { 9, 0xD6, 1088 },	/* 0110 1011 0 */
+    { 9, 0xD7, 1152 },	/* 0110 1011 1 */
+    { 9, 0xD8, 1216 },	/* 0110 1100 0 */
+    { 9, 0xD9, 1280 },	/* 0110 1100 1 */
+    { 9, 0xDA, 1344 },	/* 0110 1101 0 */
+    { 9, 0xDB, 1408 },	/* 0110 1101 1 */
+    { 9, 0x98, 1472 },	/* 0100 1100 0 */
+    { 9, 0x99, 1536 },	/* 0100 1100 1 */
+    { 9, 0x9A, 1600 },	/* 0100 1101 0 */
+    { 6, 0x18, 1664 },	/* 0110 00 */
+    { 9, 0x9B, 1728 },	/* 0100 1101 1 */
+    { 11, 0x8, 1792 },	/* 0000 0001 000 */
+    { 11, 0xC, 1856 },	/* 0000 0001 100 */
+    { 11, 0xD, 1920 },	/* 0000 0001 101 */
+    { 12, 0x12, 1984 },	/* 0000 0001 0010 */
+    { 12, 0x13, 2048 },	/* 0000 0001 0011 */
+    { 12, 0x14, 2112 },	/* 0000 0001 0100 */
+    { 12, 0x15, 2176 },	/* 0000 0001 0101 */
+    { 12, 0x16, 2240 },	/* 0000 0001 0110 */
+    { 12, 0x17, 2304 },	/* 0000 0001 0111 */
+    { 12, 0x1C, 2368 },	/* 0000 0001 1100 */
+    { 12, 0x1D, 2432 },	/* 0000 0001 1101 */
+    { 12, 0x1E, 2496 },	/* 0000 0001 1110 */
+    { 12, 0x1F, 2560 },	/* 0000 0001 1111 */
+    { 12, 0x1, G3CODE_EOL },	/* 0000 0000 0001 */
+    { 9, 0x1, G3CODE_INVALID },	/* 0000 0000 1 */
+    { 10, 0x1, G3CODE_INVALID },	/* 0000 0000 01 */
+    { 11, 0x1, G3CODE_INVALID },	/* 0000 0000 001 */
+    { 12, 0x0, G3CODE_INVALID },	/* 0000 0000 0000 */
+};
+
+const tableentry TIFFFaxBlackCodes[] = {
+    { 10, 0x37, 0 },	/* 0000 1101 11 */
+    { 3, 0x2, 1 },	/* 010 */
+    { 2, 0x3, 2 },	/* 11 */
+    { 2, 0x2, 3 },	/* 10 */
+    { 3, 0x3, 4 },	/* 011 */
+    { 4, 0x3, 5 },	/* 0011 */
+    { 4, 0x2, 6 },	/* 0010 */
+    { 5, 0x3, 7 },	/* 0001 1 */
+    { 6, 0x5, 8 },	/* 0001 01 */
+    { 6, 0x4, 9 },	/* 0001 00 */
+    { 7, 0x4, 10 },	/* 0000 100 */
+    { 7, 0x5, 11 },	/* 0000 101 */
+    { 7, 0x7, 12 },	/* 0000 111 */
+    { 8, 0x4, 13 },	/* 0000 0100 */
+    { 8, 0x7, 14 },	/* 0000 0111 */
+    { 9, 0x18, 15 },	/* 0000 1100 0 */
+    { 10, 0x17, 16 },	/* 0000 0101 11 */
+    { 10, 0x18, 17 },	/* 0000 0110 00 */
+    { 10, 0x8, 18 },	/* 0000 0010 00 */
+    { 11, 0x67, 19 },	/* 0000 1100 111 */
+    { 11, 0x68, 20 },	/* 0000 1101 000 */
+    { 11, 0x6C, 21 },	/* 0000 1101 100 */
+    { 11, 0x37, 22 },	/* 0000 0110 111 */
+    { 11, 0x28, 23 },	/* 0000 0101 000 */
+    { 11, 0x17, 24 },	/* 0000 0010 111 */
+    { 11, 0x18, 25 },	/* 0000 0011 000 */
+    { 12, 0xCA, 26 },	/* 0000 1100 1010 */
+    { 12, 0xCB, 27 },	/* 0000 1100 1011 */
+    { 12, 0xCC, 28 },	/* 0000 1100 1100 */
+    { 12, 0xCD, 29 },	/* 0000 1100 1101 */
+    { 12, 0x68, 30 },	/* 0000 0110 1000 */
+    { 12, 0x69, 31 },	/* 0000 0110 1001 */
+    { 12, 0x6A, 32 },	/* 0000 0110 1010 */
+    { 12, 0x6B, 33 },	/* 0000 0110 1011 */
+    { 12, 0xD2, 34 },	/* 0000 1101 0010 */
+    { 12, 0xD3, 35 },	/* 0000 1101 0011 */
+    { 12, 0xD4, 36 },	/* 0000 1101 0100 */
+    { 12, 0xD5, 37 },	/* 0000 1101 0101 */
+    { 12, 0xD6, 38 },	/* 0000 1101 0110 */
+    { 12, 0xD7, 39 },	/* 0000 1101 0111 */
+    { 12, 0x6C, 40 },	/* 0000 0110 1100 */
+    { 12, 0x6D, 41 },	/* 0000 0110 1101 */
+    { 12, 0xDA, 42 },	/* 0000 1101 1010 */
+    { 12, 0xDB, 43 },	/* 0000 1101 1011 */
+    { 12, 0x54, 44 },	/* 0000 0101 0100 */
+    { 12, 0x55, 45 },	/* 0000 0101 0101 */
+    { 12, 0x56, 46 },	/* 0000 0101 0110 */
+    { 12, 0x57, 47 },	/* 0000 0101 0111 */
+    { 12, 0x64, 48 },	/* 0000 0110 0100 */
+    { 12, 0x65, 49 },	/* 0000 0110 0101 */
+    { 12, 0x52, 50 },	/* 0000 0101 0010 */
+    { 12, 0x53, 51 },	/* 0000 0101 0011 */
+    { 12, 0x24, 52 },	/* 0000 0010 0100 */
+    { 12, 0x37, 53 },	/* 0000 0011 0111 */
+    { 12, 0x38, 54 },	/* 0000 0011 1000 */
+    { 12, 0x27, 55 },	/* 0000 0010 0111 */
+    { 12, 0x28, 56 },	/* 0000 0010 1000 */
+    { 12, 0x58, 57 },	/* 0000 0101 1000 */
+    { 12, 0x59, 58 },	/* 0000 0101 1001 */
+    { 12, 0x2B, 59 },	/* 0000 0010 1011 */
+    { 12, 0x2C, 60 },	/* 0000 0010 1100 */
+    { 12, 0x5A, 61 },	/* 0000 0101 1010 */
+    { 12, 0x66, 62 },	/* 0000 0110 0110 */
+    { 12, 0x67, 63 },	/* 0000 0110 0111 */
+    { 10, 0xF, 64 },	/* 0000 0011 11 */
+    { 12, 0xC8, 128 },	/* 0000 1100 1000 */
+    { 12, 0xC9, 192 },	/* 0000 1100 1001 */
+    { 12, 0x5B, 256 },	/* 0000 0101 1011 */
+    { 12, 0x33, 320 },	/* 0000 0011 0011 */
+    { 12, 0x34, 384 },	/* 0000 0011 0100 */
+    { 12, 0x35, 448 },	/* 0000 0011 0101 */
+    { 13, 0x6C, 512 },	/* 0000 0011 0110 0 */
+    { 13, 0x6D, 576 },	/* 0000 0011 0110 1 */
+    { 13, 0x4A, 640 },	/* 0000 0010 0101 0 */
+    { 13, 0x4B, 704 },	/* 0000 0010 0101 1 */
+    { 13, 0x4C, 768 },	/* 0000 0010 0110 0 */
+    { 13, 0x4D, 832 },	/* 0000 0010 0110 1 */
+    { 13, 0x72, 896 },	/* 0000 0011 1001 0 */
+    { 13, 0x73, 960 },	/* 0000 0011 1001 1 */
+    { 13, 0x74, 1024 },	/* 0000 0011 1010 0 */
+    { 13, 0x75, 1088 },	/* 0000 0011 1010 1 */
+    { 13, 0x76, 1152 },	/* 0000 0011 1011 0 */
+    { 13, 0x77, 1216 },	/* 0000 0011 1011 1 */
+    { 13, 0x52, 1280 },	/* 0000 0010 1001 0 */
+    { 13, 0x53, 1344 },	/* 0000 0010 1001 1 */
+    { 13, 0x54, 1408 },	/* 0000 0010 1010 0 */
+    { 13, 0x55, 1472 },	/* 0000 0010 1010 1 */
+    { 13, 0x5A, 1536 },	/* 0000 0010 1101 0 */
+    { 13, 0x5B, 1600 },	/* 0000 0010 1101 1 */
+    { 13, 0x64, 1664 },	/* 0000 0011 0010 0 */
+    { 13, 0x65, 1728 },	/* 0000 0011 0010 1 */
+    { 11, 0x8, 1792 },	/* 0000 0001 000 */
+    { 11, 0xC, 1856 },	/* 0000 0001 100 */
+    { 11, 0xD, 1920 },	/* 0000 0001 101 */
+    { 12, 0x12, 1984 },	/* 0000 0001 0010 */
+    { 12, 0x13, 2048 },	/* 0000 0001 0011 */
+    { 12, 0x14, 2112 },	/* 0000 0001 0100 */
+    { 12, 0x15, 2176 },	/* 0000 0001 0101 */
+    { 12, 0x16, 2240 },	/* 0000 0001 0110 */
+    { 12, 0x17, 2304 },	/* 0000 0001 0111 */
+    { 12, 0x1C, 2368 },	/* 0000 0001 1100 */
+    { 12, 0x1D, 2432 },	/* 0000 0001 1101 */
+    { 12, 0x1E, 2496 },	/* 0000 0001 1110 */
+    { 12, 0x1F, 2560 },	/* 0000 0001 1111 */
+    { 12, 0x1, G3CODE_EOL },	/* 0000 0000 0001 */
+    { 9, 0x1, G3CODE_INVALID },	/* 0000 0000 1 */
+    { 10, 0x1, G3CODE_INVALID },	/* 0000 0000 01 */
+    { 11, 0x1, G3CODE_INVALID },	/* 0000 0000 001 */
+    { 12, 0x0, G3CODE_INVALID },	/* 0000 0000 0000 */
+};
+#else
+extern	const tableentry TIFFFaxWhiteCodes[];
+extern	const tableentry TIFFFaxBlackCodes[];
+#endif
+#endif /* _T4_ */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_aux.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_aux.c
new file mode 100644
index 0000000000..c61f47162c
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_aux.c
@@ -0,0 +1,267 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1991-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ *
+ * Auxiliary Support Routines.
+ */
+#include "tiffiop.h"
+#include "tif_predict.h"
+#include <math.h>
+
+tdata_t
+_TIFFCheckMalloc(TIFF* tif, size_t nmemb, size_t elem_size, const char* what)
+{
+	tdata_t cp = NULL;
+	tsize_t	bytes = nmemb * elem_size;
+
+	/*
+	 * XXX: Check for integer overflow.
+	 */
+	if (nmemb && elem_size && bytes / elem_size == nmemb)
+		cp = _TIFFmalloc(bytes);
+
+	if (cp == NULL)
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "No space %s", what);
+
+	return (cp);
+}
+
+static int
+TIFFDefaultTransferFunction(TIFFDirectory* td)
+{
+	uint16 **tf = td->td_transferfunction;
+	tsize_t i, n, nbytes;
+
+	tf[0] = tf[1] = tf[2] = 0;
+	if (td->td_bitspersample >= sizeof(tsize_t) * 8 - 2)
+		return 0;
+
+	n = 1<<td->td_bitspersample;
+	nbytes = n * sizeof (uint16);
+	if (!(tf[0] = (uint16 *)_TIFFmalloc(nbytes)))
+		return 0;
+	tf[0][0] = 0;
+	for (i = 1; i < n; i++) {
+		double t = (double)i/((double) n-1.);
+		tf[0][i] = (uint16)floor(65535.*pow(t, 2.2) + .5);
+	}
+
+	if (td->td_samplesperpixel - td->td_extrasamples > 1) {
+		if (!(tf[1] = (uint16 *)_TIFFmalloc(nbytes)))
+			goto bad;
+		_TIFFmemcpy(tf[1], tf[0], nbytes);
+		if (!(tf[2] = (uint16 *)_TIFFmalloc(nbytes)))
+			goto bad;
+		_TIFFmemcpy(tf[2], tf[0], nbytes);
+	}
+	return 1;
+
+bad:
+	if (tf[0])
+		_TIFFfree(tf[0]);
+	if (tf[1])
+		_TIFFfree(tf[1]);
+	if (tf[2])
+		_TIFFfree(tf[2]);
+	tf[0] = tf[1] = tf[2] = 0;
+	return 0;
+}
+
+/*
+ * Like TIFFGetField, but return any default
+ * value if the tag is not present in the directory.
+ *
+ * NB:	We use the value in the directory, rather than
+ *	explcit values so that defaults exist only one
+ *	place in the library -- in TIFFDefaultDirectory.
+ */
+int
+TIFFVGetFieldDefaulted(TIFF* tif, ttag_t tag, va_list ap)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+
+	if (TIFFVGetField(tif, tag, ap))
+		return (1);
+	switch (tag) {
+	case TIFFTAG_SUBFILETYPE:
+		*va_arg(ap, uint32 *) = td->td_subfiletype;
+		return (1);
+	case TIFFTAG_BITSPERSAMPLE:
+		*va_arg(ap, uint16 *) = td->td_bitspersample;
+		return (1);
+	case TIFFTAG_THRESHHOLDING:
+		*va_arg(ap, uint16 *) = td->td_threshholding;
+		return (1);
+	case TIFFTAG_FILLORDER:
+		*va_arg(ap, uint16 *) = td->td_fillorder;
+		return (1);
+	case TIFFTAG_ORIENTATION:
+		*va_arg(ap, uint16 *) = td->td_orientation;
+		return (1);
+	case TIFFTAG_SAMPLESPERPIXEL:
+		*va_arg(ap, uint16 *) = td->td_samplesperpixel;
+		return (1);
+	case TIFFTAG_ROWSPERSTRIP:
+		*va_arg(ap, uint32 *) = td->td_rowsperstrip;
+		return (1);
+	case TIFFTAG_MINSAMPLEVALUE:
+		*va_arg(ap, uint16 *) = td->td_minsamplevalue;
+		return (1);
+	case TIFFTAG_MAXSAMPLEVALUE:
+		*va_arg(ap, uint16 *) = td->td_maxsamplevalue;
+		return (1);
+	case TIFFTAG_PLANARCONFIG:
+		*va_arg(ap, uint16 *) = td->td_planarconfig;
+		return (1);
+	case TIFFTAG_RESOLUTIONUNIT:
+		*va_arg(ap, uint16 *) = td->td_resolutionunit;
+		return (1);
+	case TIFFTAG_PREDICTOR:
+                {
+			TIFFPredictorState* sp = (TIFFPredictorState*) tif->tif_data;
+			*va_arg(ap, uint16*) = (uint16) sp->predictor;
+			return 1;
+                }
+	case TIFFTAG_DOTRANGE:
+		*va_arg(ap, uint16 *) = 0;
+		*va_arg(ap, uint16 *) = (1<<td->td_bitspersample)-1;
+		return (1);
+	case TIFFTAG_INKSET:
+		*va_arg(ap, uint16 *) = INKSET_CMYK;
+		return 1;
+	case TIFFTAG_NUMBEROFINKS:
+		*va_arg(ap, uint16 *) = 4;
+		return (1);
+	case TIFFTAG_EXTRASAMPLES:
+		*va_arg(ap, uint16 *) = td->td_extrasamples;
+		*va_arg(ap, uint16 **) = td->td_sampleinfo;
+		return (1);
+	case TIFFTAG_MATTEING:
+		*va_arg(ap, uint16 *) =
+		    (td->td_extrasamples == 1 &&
+		     td->td_sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA);
+		return (1);
+	case TIFFTAG_TILEDEPTH:
+		*va_arg(ap, uint32 *) = td->td_tiledepth;
+		return (1);
+	case TIFFTAG_DATATYPE:
+		*va_arg(ap, uint16 *) = td->td_sampleformat-1;
+		return (1);
+	case TIFFTAG_SAMPLEFORMAT:
+		*va_arg(ap, uint16 *) = td->td_sampleformat;
+                return(1);
+	case TIFFTAG_IMAGEDEPTH:
+		*va_arg(ap, uint32 *) = td->td_imagedepth;
+		return (1);
+	case TIFFTAG_YCBCRCOEFFICIENTS:
+		{
+			/* defaults are from CCIR Recommendation 601-1 */
+			static float ycbcrcoeffs[] = { 0.299f, 0.587f, 0.114f };
+			*va_arg(ap, float **) = ycbcrcoeffs;
+			return 1;
+		}
+	case TIFFTAG_YCBCRSUBSAMPLING:
+		*va_arg(ap, uint16 *) = td->td_ycbcrsubsampling[0];
+		*va_arg(ap, uint16 *) = td->td_ycbcrsubsampling[1];
+		return (1);
+	case TIFFTAG_YCBCRPOSITIONING:
+		*va_arg(ap, uint16 *) = td->td_ycbcrpositioning;
+		return (1);
+	case TIFFTAG_WHITEPOINT:
+		{
+			static float whitepoint[2];
+
+			/* TIFF 6.0 specification tells that it is no default
+			   value for the WhitePoint, but AdobePhotoshop TIFF
+			   Technical Note tells that it should be CIE D50. */
+			whitepoint[0] =	D50_X0 / (D50_X0 + D50_Y0 + D50_Z0);
+			whitepoint[1] =	D50_Y0 / (D50_X0 + D50_Y0 + D50_Z0);
+			*va_arg(ap, float **) = whitepoint;
+			return 1;
+		}
+	case TIFFTAG_TRANSFERFUNCTION:
+		if (!td->td_transferfunction[0] &&
+		    !TIFFDefaultTransferFunction(td)) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "No space for \"TransferFunction\" tag");
+			return (0);
+		}
+		*va_arg(ap, uint16 **) = td->td_transferfunction[0];
+		if (td->td_samplesperpixel - td->td_extrasamples > 1) {
+			*va_arg(ap, uint16 **) = td->td_transferfunction[1];
+			*va_arg(ap, uint16 **) = td->td_transferfunction[2];
+		}
+		return (1);
+	case TIFFTAG_REFERENCEBLACKWHITE:
+		{
+			int i;
+			static float ycbcr_refblackwhite[] = 
+			{ 0.0F, 255.0F, 128.0F, 255.0F, 128.0F, 255.0F };
+			static float rgb_refblackwhite[6];
+
+			for (i = 0; i < 3; i++) {
+				rgb_refblackwhite[2 * i + 0] = 0.0F;
+				rgb_refblackwhite[2 * i + 1] =
+					(float)((1L<<td->td_bitspersample)-1L);
+			}
+			
+			if (td->td_photometric == PHOTOMETRIC_YCBCR) {
+				/*
+				 * YCbCr (Class Y) images must have the
+				 * ReferenceBlackWhite tag set. Fix the
+				 * broken images, which lacks that tag.
+				 */
+				*va_arg(ap, float **) = ycbcr_refblackwhite;
+			} else {
+				/*
+				 * Assume RGB (Class R)
+				 */
+				*va_arg(ap, float **) = rgb_refblackwhite;
+			}
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Like TIFFGetField, but return any default
+ * value if the tag is not present in the directory.
+ */
+int
+TIFFGetFieldDefaulted(TIFF* tif, ttag_t tag, ...)
+{
+	int ok;
+	va_list ap;
+
+	va_start(ap, tag);
+	ok =  TIFFVGetFieldDefaulted(tif, tag, ap);
+	va_end(ap);
+	return (ok);
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_close.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_close.c
new file mode 100644
index 0000000000..e1bc5569ac
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_close.c
@@ -0,0 +1,119 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ */
+#include "tiffiop.h"
+
+/************************************************************************/
+/*                            TIFFCleanup()                             */
+/************************************************************************/
+
+/**
+ * Auxiliary function to free the TIFF structure. Given structure will be
+ * completetly freed, so you should save opened file handle and pointer
+ * to the close procedure in external variables before calling
+ * _TIFFCleanup(), if you will need these ones to close the file.
+ * 
+ * @param tif A TIFF pointer.
+ */
+
+void
+TIFFCleanup(TIFF* tif)
+{
+	if (tif->tif_mode != O_RDONLY)
+	    /*
+	     * Flush buffered data and directory (if dirty).
+	     */
+	    TIFFFlush(tif);
+	(*tif->tif_cleanup)(tif);
+	TIFFFreeDirectory(tif);
+
+	if (tif->tif_dirlist)
+		_TIFFfree(tif->tif_dirlist);
+
+	/* Clean up client info links */
+	while( tif->tif_clientinfo )
+	{
+		TIFFClientInfoLink *link = tif->tif_clientinfo;
+
+		tif->tif_clientinfo = link->next;
+		_TIFFfree( link->name );
+		_TIFFfree( link );
+	}
+
+	if (tif->tif_rawdata && (tif->tif_flags&TIFF_MYBUFFER))
+		_TIFFfree(tif->tif_rawdata);
+	if (isMapped(tif))
+		TIFFUnmapFileContents(tif, tif->tif_base, tif->tif_size);
+
+	/* Clean up custom fields */
+	if (tif->tif_nfields > 0)
+	{
+		size_t  i;
+
+	    for (i = 0; i < tif->tif_nfields; i++) 
+	    {
+		TIFFFieldInfo *fld = tif->tif_fieldinfo[i];
+		if (fld->field_bit == FIELD_CUSTOM && 
+		    strncmp("Tag ", fld->field_name, 4) == 0) 
+		{
+		    _TIFFfree(fld->field_name);
+		    _TIFFfree(fld);
+		}
+	    }   
+	  
+	    _TIFFfree(tif->tif_fieldinfo);
+	}
+
+	_TIFFfree(tif);
+}
+
+/************************************************************************/
+/*                            TIFFClose()                               */
+/************************************************************************/
+
+/**
+ * Close a previously opened TIFF file.
+ *
+ * TIFFClose closes a file that was previously opened with TIFFOpen().
+ * Any buffered data are flushed to the file, including the contents of
+ * the current directory (if modified); and all resources are reclaimed.
+ * 
+ * @param tif A TIFF pointer.
+ */
+
+void
+TIFFClose(TIFF* tif)
+{
+	TIFFCloseProc closeproc = tif->tif_closeproc;
+	thandle_t fd = tif->tif_clientdata;
+
+	TIFFCleanup(tif);
+	(void) (*closeproc)(fd);
+}
+
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_codec.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_codec.c
new file mode 100644
index 0000000000..94863d15e3
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_codec.c
@@ -0,0 +1,150 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library
+ *
+ * Builtin Compression Scheme Configuration Support.
+ */
+#include "tiffiop.h"
+
+static	int NotConfigured(TIFF*, int);
+
+#ifndef	LZW_SUPPORT
+#define	TIFFInitLZW		NotConfigured
+#endif
+#ifndef	PACKBITS_SUPPORT
+#define	TIFFInitPackBits	NotConfigured
+#endif
+#ifndef	THUNDER_SUPPORT
+#define	TIFFInitThunderScan	NotConfigured
+#endif
+#ifndef	NEXT_SUPPORT
+#define	TIFFInitNeXT		NotConfigured
+#endif
+#ifndef	JPEG_SUPPORT
+#define	TIFFInitJPEG		NotConfigured
+#endif
+#ifndef	OJPEG_SUPPORT
+#define	TIFFInitOJPEG		NotConfigured
+#endif
+#ifndef	CCITT_SUPPORT
+#define	TIFFInitCCITTRLE	NotConfigured
+#define	TIFFInitCCITTRLEW	NotConfigured
+#define	TIFFInitCCITTFax3	NotConfigured
+#define	TIFFInitCCITTFax4	NotConfigured
+#endif
+#ifndef JBIG_SUPPORT
+#define	TIFFInitJBIG		NotConfigured
+#endif
+#ifndef	ZIP_SUPPORT
+#define	TIFFInitZIP		NotConfigured
+#endif
+#ifndef	PIXARLOG_SUPPORT
+#define	TIFFInitPixarLog	NotConfigured
+#endif
+#ifndef LOGLUV_SUPPORT
+#define TIFFInitSGILog		NotConfigured
+#endif
+
+/*
+ * Compression schemes statically built into the library.
+ */
+#ifdef VMS
+const TIFFCodec _TIFFBuiltinCODECS[] = {
+#else
+TIFFCodec _TIFFBuiltinCODECS[] = {
+#endif
+    { "None",		COMPRESSION_NONE,	TIFFInitDumpMode },
+    { "LZW",		COMPRESSION_LZW,	TIFFInitLZW },
+    { "PackBits",	COMPRESSION_PACKBITS,	TIFFInitPackBits },
+    { "ThunderScan",	COMPRESSION_THUNDERSCAN,TIFFInitThunderScan },
+    { "NeXT",		COMPRESSION_NEXT,	TIFFInitNeXT },
+    { "JPEG",		COMPRESSION_JPEG,	TIFFInitJPEG },
+    { "Old-style JPEG",	COMPRESSION_OJPEG,	TIFFInitOJPEG },
+    { "CCITT RLE",	COMPRESSION_CCITTRLE,	TIFFInitCCITTRLE },
+    { "CCITT RLE/W",	COMPRESSION_CCITTRLEW,	TIFFInitCCITTRLEW },
+    { "CCITT Group 3",	COMPRESSION_CCITTFAX3,	TIFFInitCCITTFax3 },
+    { "CCITT Group 4",	COMPRESSION_CCITTFAX4,	TIFFInitCCITTFax4 },
+    { "ISO JBIG",	COMPRESSION_JBIG,	TIFFInitJBIG },
+    { "Deflate",	COMPRESSION_DEFLATE,	TIFFInitZIP },
+    { "AdobeDeflate",   COMPRESSION_ADOBE_DEFLATE , TIFFInitZIP }, 
+    { "PixarLog",	COMPRESSION_PIXARLOG,	TIFFInitPixarLog },
+    { "SGILog",		COMPRESSION_SGILOG,	TIFFInitSGILog },
+    { "SGILog24",	COMPRESSION_SGILOG24,	TIFFInitSGILog },
+    { NULL,             0,                      NULL }
+};
+
+static int
+_notConfigured(TIFF* tif)
+{
+	const TIFFCodec* c = TIFFFindCODEC(tif->tif_dir.td_compression);
+
+	TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+	    "%s compression support is not configured", c->name);
+	return (0);
+}
+
+static int
+NotConfigured(TIFF* tif, int scheme)
+{
+    (void) scheme;
+    
+    tif->tif_decodestatus = FALSE;
+    tif->tif_setupdecode = _notConfigured;
+    tif->tif_encodestatus = FALSE;
+    tif->tif_setupencode = _notConfigured;
+    return (1);
+}
+
+/************************************************************************/
+/*                       TIFFIsCODECConfigured()                        */
+/************************************************************************/
+
+/**
+ * Check whether we have working codec for the specific coding scheme.
+ * 
+ * @return returns 1 if the codec is configured and working. Otherwise
+ * 0 will be returned.
+ */
+
+int
+TIFFIsCODECConfigured(uint16 scheme)
+{
+	const TIFFCodec* codec = TIFFFindCODEC(scheme);
+
+	if(codec == NULL) {
+            return 0;
+        }
+        if(codec->init == NULL) {
+            return 0;
+        }
+	if(codec->init != NotConfigured){
+            return 1;
+        }
+	return 0;
+}
+
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_color.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_color.c
new file mode 100644
index 0000000000..72ebb5d3e8
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_color.c
@@ -0,0 +1,275 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * CIE L*a*b* to CIE XYZ and CIE XYZ to RGB conversion routines are taken
+ * from the VIPS library (http://www.vips.ecs.soton.ac.uk) with
+ * the permission of John Cupitt, the VIPS author.
+ */
+
+/*
+ * TIFF Library.
+ *
+ * Color space conversion routines.
+ */
+
+#include "tiffiop.h"
+#include <math.h>
+
+/*
+ * Convert color value from the CIE L*a*b* 1976 space to CIE XYZ.
+ */
+void
+TIFFCIELabToXYZ(TIFFCIELabToRGB *cielab, uint32 l, int32 a, int32 b,
+		float *X, float *Y, float *Z)
+{
+	float L = (float)l * 100.0F / 255.0F;
+	float cby, tmp;
+
+	if( L < 8.856F ) {
+		*Y = (L * cielab->Y0) / 903.292F;
+		cby = 7.787F * (*Y / cielab->Y0) + 16.0F / 116.0F;
+	} else {
+		cby = (L + 16.0F) / 116.0F;
+		*Y = cielab->Y0 * cby * cby * cby;
+	}
+
+	tmp = (float)a / 500.0F + cby;
+	if( tmp < 0.2069F )
+		*X = cielab->X0 * (tmp - 0.13793F) / 7.787F;
+	else    
+		*X = cielab->X0 * tmp * tmp * tmp;
+
+	tmp = cby - (float)b / 200.0F;
+	if( tmp < 0.2069F )
+		*Z = cielab->Z0 * (tmp - 0.13793F) / 7.787F;
+	else    
+		*Z = cielab->Z0 * tmp * tmp * tmp;
+}
+
+#define RINT(R) ((uint32)((R)>0?((R)+0.5):((R)-0.5)))
+/*
+ * Convert color value from the XYZ space to RGB.
+ */
+void
+TIFFXYZToRGB(TIFFCIELabToRGB *cielab, float X, float Y, float Z,
+	     uint32 *r, uint32 *g, uint32 *b)
+{
+	int i;
+	float Yr, Yg, Yb;
+	float *matrix = &cielab->display.d_mat[0][0];
+
+	/* Multiply through the matrix to get luminosity values. */
+	Yr =  matrix[0] * X + matrix[1] * Y + matrix[2] * Z;
+	Yg =  matrix[3] * X + matrix[4] * Y + matrix[5] * Z;
+	Yb =  matrix[6] * X + matrix[7] * Y + matrix[8] * Z;
+
+	/* Clip input */
+	Yr = TIFFmax(Yr, cielab->display.d_Y0R);
+	Yg = TIFFmax(Yg, cielab->display.d_Y0G);
+	Yb = TIFFmax(Yb, cielab->display.d_Y0B);
+
+	/* Avoid overflow in case of wrong input values */
+	Yr = TIFFmin(Yr, cielab->display.d_YCR);
+	Yg = TIFFmin(Yg, cielab->display.d_YCG);
+	Yb = TIFFmin(Yb, cielab->display.d_YCB);
+
+	/* Turn luminosity to colour value. */
+	i = (int)((Yr - cielab->display.d_Y0R) / cielab->rstep);
+	i = TIFFmin(cielab->range, i);
+	*r = RINT(cielab->Yr2r[i]);
+
+	i = (int)((Yg - cielab->display.d_Y0G) / cielab->gstep);
+	i = TIFFmin(cielab->range, i);
+	*g = RINT(cielab->Yg2g[i]);
+
+	i = (int)((Yb - cielab->display.d_Y0B) / cielab->bstep);
+	i = TIFFmin(cielab->range, i);
+	*b = RINT(cielab->Yb2b[i]);
+
+	/* Clip output. */
+	*r = TIFFmin(*r, cielab->display.d_Vrwr);
+	*g = TIFFmin(*g, cielab->display.d_Vrwg);
+	*b = TIFFmin(*b, cielab->display.d_Vrwb);
+}
+#undef RINT
+
+/* 
+ * Allocate conversion state structures and make look_up tables for
+ * the Yr,Yb,Yg <=> r,g,b conversions.
+ */
+int
+TIFFCIELabToRGBInit(TIFFCIELabToRGB* cielab,
+		    TIFFDisplay *display, float *refWhite)
+{
+	int i;
+	double gamma;
+
+	cielab->range = CIELABTORGB_TABLE_RANGE;
+
+	_TIFFmemcpy(&cielab->display, display, sizeof(TIFFDisplay));
+
+	/* Red */
+	gamma = 1.0 / cielab->display.d_gammaR ;
+	cielab->rstep =
+		(cielab->display.d_YCR - cielab->display.d_Y0R)	/ cielab->range;
+	for(i = 0; i <= cielab->range; i++) {
+		cielab->Yr2r[i] = cielab->display.d_Vrwr
+		    * ((float)pow((double)i / cielab->range, gamma));
+	}
+
+	/* Green */
+	gamma = 1.0 / cielab->display.d_gammaG ;
+	cielab->gstep =
+	    (cielab->display.d_YCR - cielab->display.d_Y0R) / cielab->range;
+	for(i = 0; i <= cielab->range; i++) {
+		cielab->Yg2g[i] = cielab->display.d_Vrwg
+		    * ((float)pow((double)i / cielab->range, gamma));
+	}
+
+	/* Blue */
+	gamma = 1.0 / cielab->display.d_gammaB ;
+	cielab->bstep =
+	    (cielab->display.d_YCR - cielab->display.d_Y0R) / cielab->range;
+	for(i = 0; i <= cielab->range; i++) {
+		cielab->Yb2b[i] = cielab->display.d_Vrwb
+		    * ((float)pow((double)i / cielab->range, gamma));
+	}
+
+	/* Init reference white point */
+	cielab->X0 = refWhite[0];
+	cielab->Y0 = refWhite[1];
+	cielab->Z0 = refWhite[2];
+
+	return 0;
+}
+
+/* 
+ * Convert color value from the YCbCr space to CIE XYZ.
+ * The colorspace conversion algorithm comes from the IJG v5a code;
+ * see below for more information on how it works.
+ */
+#define	SHIFT			16
+#define	FIX(x)			((int32)((x) * (1L<<SHIFT) + 0.5))
+#define	ONE_HALF		((int32)(1<<(SHIFT-1)))
+#define	Code2V(c, RB, RW, CR)	((((c)-(int32)(RB))*(float)(CR))/(float)(((RW)-(RB)) ? ((RW)-(RB)) : 1))
+#define	CLAMP(f,min,max)	((f)<(min)?(min):(f)>(max)?(max):(f))
+#define HICLAMP(f,max)		((f)>(max)?(max):(f))
+
+void
+TIFFYCbCrtoRGB(TIFFYCbCrToRGB *ycbcr, uint32 Y, int32 Cb, int32 Cr,
+	       uint32 *r, uint32 *g, uint32 *b)
+{
+	/* XXX: Only 8-bit YCbCr input supported for now */
+	Y = HICLAMP(Y, 255), Cb = CLAMP(Cb, 0, 255), Cr = CLAMP(Cr, 0, 255);
+
+	*r = ycbcr->clamptab[ycbcr->Y_tab[Y] + ycbcr->Cr_r_tab[Cr]];
+	*g = ycbcr->clamptab[ycbcr->Y_tab[Y]
+	    + (int)((ycbcr->Cb_g_tab[Cb] + ycbcr->Cr_g_tab[Cr]) >> SHIFT)];
+	*b = ycbcr->clamptab[ycbcr->Y_tab[Y] + ycbcr->Cb_b_tab[Cb]];
+}
+
+/*
+ * Initialize the YCbCr->RGB conversion tables.  The conversion
+ * is done according to the 6.0 spec:
+ *
+ *    R = Y + Cr*(2 - 2*LumaRed)
+ *    B = Y + Cb*(2 - 2*LumaBlue)
+ *    G =   Y
+ *        - LumaBlue*Cb*(2-2*LumaBlue)/LumaGreen
+ *        - LumaRed*Cr*(2-2*LumaRed)/LumaGreen
+ *
+ * To avoid floating point arithmetic the fractional constants that
+ * come out of the equations are represented as fixed point values
+ * in the range 0...2^16.  We also eliminate multiplications by
+ * pre-calculating possible values indexed by Cb and Cr (this code
+ * assumes conversion is being done for 8-bit samples).
+ */
+int
+TIFFYCbCrToRGBInit(TIFFYCbCrToRGB* ycbcr, float *luma, float *refBlackWhite)
+{
+    TIFFRGBValue* clamptab;
+    int i;
+    
+#define LumaRed	    luma[0]
+#define LumaGreen   luma[1]
+#define LumaBlue    luma[2]
+
+    clamptab = (TIFFRGBValue*)(
+	(tidata_t) ycbcr+TIFFroundup(sizeof (TIFFYCbCrToRGB), sizeof (long)));
+    _TIFFmemset(clamptab, 0, 256);		/* v < 0 => 0 */
+    ycbcr->clamptab = (clamptab += 256);
+    for (i = 0; i < 256; i++)
+	clamptab[i] = (TIFFRGBValue) i;
+    _TIFFmemset(clamptab+256, 255, 2*256);	/* v > 255 => 255 */
+    ycbcr->Cr_r_tab = (int*) (clamptab + 3*256);
+    ycbcr->Cb_b_tab = ycbcr->Cr_r_tab + 256;
+    ycbcr->Cr_g_tab = (int32*) (ycbcr->Cb_b_tab + 256);
+    ycbcr->Cb_g_tab = ycbcr->Cr_g_tab + 256;
+    ycbcr->Y_tab = ycbcr->Cb_g_tab + 256;
+
+    { float f1 = 2-2*LumaRed;		int32 D1 = FIX(f1);
+      float f2 = LumaRed*f1/LumaGreen;	int32 D2 = -FIX(f2);
+      float f3 = 2-2*LumaBlue;		int32 D3 = FIX(f3);
+      float f4 = LumaBlue*f3/LumaGreen;	int32 D4 = -FIX(f4);
+      int x;
+
+#undef LumaBlue
+#undef LumaGreen
+#undef LumaRed
+      
+      /*
+       * i is the actual input pixel value in the range 0..255
+       * Cb and Cr values are in the range -128..127 (actually
+       * they are in a range defined by the ReferenceBlackWhite
+       * tag) so there is some range shifting to do here when
+       * constructing tables indexed by the raw pixel data.
+       */
+      for (i = 0, x = -128; i < 256; i++, x++) {
+	    int32 Cr = (int32)Code2V(x, refBlackWhite[4] - 128.0F,
+			    refBlackWhite[5] - 128.0F, 127);
+	    int32 Cb = (int32)Code2V(x, refBlackWhite[2] - 128.0F,
+			    refBlackWhite[3] - 128.0F, 127);
+
+	    ycbcr->Cr_r_tab[i] = (int32)((D1*Cr + ONE_HALF)>>SHIFT);
+	    ycbcr->Cb_b_tab[i] = (int32)((D3*Cb + ONE_HALF)>>SHIFT);
+	    ycbcr->Cr_g_tab[i] = D2*Cr;
+	    ycbcr->Cb_g_tab[i] = D4*Cb + ONE_HALF;
+	    ycbcr->Y_tab[i] =
+		    (int32)Code2V(x + 128, refBlackWhite[0], refBlackWhite[1], 255);
+      }
+    }
+
+    return 0;
+}
+#undef	HICLAMP
+#undef	CLAMP
+#undef	Code2V
+#undef	SHIFT
+#undef	ONE_HALF
+#undef	FIX
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_compress.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_compress.c
new file mode 100644
index 0000000000..55241e3644
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_compress.c
@@ -0,0 +1,286 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library
+ *
+ * Compression Scheme Configuration Support.
+ */
+#include "tiffiop.h"
+
+static int
+TIFFNoEncode(TIFF* tif, const char* method)
+{
+	const TIFFCodec* c = TIFFFindCODEC(tif->tif_dir.td_compression);
+
+	if (c) { 
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "%s %s encoding is not implemented",
+                          c->name, method);
+	} else { 
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			  "Compression scheme %u %s encoding is not implemented",
+		    tif->tif_dir.td_compression, method);
+	}
+	return (-1);
+}
+
+int
+_TIFFNoRowEncode(TIFF* tif, tidata_t pp, tsize_t cc, tsample_t s)
+{
+	(void) pp; (void) cc; (void) s;
+	return (TIFFNoEncode(tif, "scanline"));
+}
+
+int
+_TIFFNoStripEncode(TIFF* tif, tidata_t pp, tsize_t cc, tsample_t s)
+{
+	(void) pp; (void) cc; (void) s;
+	return (TIFFNoEncode(tif, "strip"));
+}
+
+int
+_TIFFNoTileEncode(TIFF* tif, tidata_t pp, tsize_t cc, tsample_t s)
+{
+	(void) pp; (void) cc; (void) s;
+	return (TIFFNoEncode(tif, "tile"));
+}
+
+static int
+TIFFNoDecode(TIFF* tif, const char* method)
+{
+	const TIFFCodec* c = TIFFFindCODEC(tif->tif_dir.td_compression);
+
+	if (c)
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "%s %s decoding is not implemented",
+		    c->name, method);
+	else
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		    "Compression scheme %u %s decoding is not implemented",
+		    tif->tif_dir.td_compression, method);
+	return (-1);
+}
+
+int
+_TIFFNoRowDecode(TIFF* tif, tidata_t pp, tsize_t cc, tsample_t s)
+{
+	(void) pp; (void) cc; (void) s;
+	return (TIFFNoDecode(tif, "scanline"));
+}
+
+int
+_TIFFNoStripDecode(TIFF* tif, tidata_t pp, tsize_t cc, tsample_t s)
+{
+	(void) pp; (void) cc; (void) s;
+	return (TIFFNoDecode(tif, "strip"));
+}
+
+int
+_TIFFNoTileDecode(TIFF* tif, tidata_t pp, tsize_t cc, tsample_t s)
+{
+	(void) pp; (void) cc; (void) s;
+	return (TIFFNoDecode(tif, "tile"));
+}
+
+int
+_TIFFNoSeek(TIFF* tif, uint32 off)
+{
+	(void) off;
+	TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+	    "Compression algorithm does not support random access");
+	return (0);
+}
+
+int
+_TIFFNoPreCode(TIFF* tif, tsample_t s)
+{
+	(void) tif; (void) s;
+	return (1);
+}
+
+static int _TIFFtrue(TIFF* tif) { (void) tif; return (1); }
+static void _TIFFvoid(TIFF* tif) { (void) tif; }
+
+void
+_TIFFSetDefaultCompressionState(TIFF* tif)
+{
+	tif->tif_decodestatus = TRUE;
+	tif->tif_setupdecode = _TIFFtrue;
+	tif->tif_predecode = _TIFFNoPreCode;
+	tif->tif_decoderow = _TIFFNoRowDecode;
+	tif->tif_decodestrip = _TIFFNoStripDecode;
+	tif->tif_decodetile = _TIFFNoTileDecode;
+	tif->tif_encodestatus = TRUE;
+	tif->tif_setupencode = _TIFFtrue;
+	tif->tif_preencode = _TIFFNoPreCode;
+	tif->tif_postencode = _TIFFtrue;
+	tif->tif_encoderow = _TIFFNoRowEncode;
+	tif->tif_encodestrip = _TIFFNoStripEncode;
+	tif->tif_encodetile = _TIFFNoTileEncode;
+	tif->tif_close = _TIFFvoid;
+	tif->tif_seek = _TIFFNoSeek;
+	tif->tif_cleanup = _TIFFvoid;
+	tif->tif_defstripsize = _TIFFDefaultStripSize;
+	tif->tif_deftilesize = _TIFFDefaultTileSize;
+	tif->tif_flags &= ~(TIFF_NOBITREV|TIFF_NOREADRAW);
+}
+
+int
+TIFFSetCompressionScheme(TIFF* tif, int scheme)
+{
+	const TIFFCodec *c = TIFFFindCODEC((uint16) scheme);
+
+	_TIFFSetDefaultCompressionState(tif);
+	/*
+	 * Don't treat an unknown compression scheme as an error.
+	 * This permits applications to open files with data that
+	 * the library does not have builtin support for, but which
+	 * may still be meaningful.
+	 */
+	return (c ? (*c->init)(tif, scheme) : 1);
+}
+
+/*
+ * Other compression schemes may be registered.  Registered
+ * schemes can also override the builtin versions provided
+ * by this library.
+ */
+typedef struct _codec {
+	struct _codec*	next;
+	TIFFCodec*	info;
+} codec_t;
+static	codec_t* registeredCODECS = NULL;
+
+const TIFFCodec*
+TIFFFindCODEC(uint16 scheme)
+{
+	const TIFFCodec* c;
+	codec_t* cd;
+
+	for (cd = registeredCODECS; cd; cd = cd->next)
+		if (cd->info->scheme == scheme)
+			return ((const TIFFCodec*) cd->info);
+	for (c = _TIFFBuiltinCODECS; c->name; c++)
+		if (c->scheme == scheme)
+			return (c);
+	return ((const TIFFCodec*) 0);
+}
+
+TIFFCodec*
+TIFFRegisterCODEC(uint16 scheme, const char* name, TIFFInitMethod init)
+{
+	codec_t* cd = (codec_t*)
+	    _TIFFmalloc(sizeof (codec_t) + sizeof (TIFFCodec) + strlen(name)+1);
+
+	if (cd != NULL) {
+		cd->info = (TIFFCodec*) ((tidata_t) cd + sizeof (codec_t));
+		cd->info->name = (char*)
+		    ((tidata_t) cd->info + sizeof (TIFFCodec));
+		strcpy(cd->info->name, name);
+		cd->info->scheme = scheme;
+		cd->info->init = init;
+		cd->next = registeredCODECS;
+		registeredCODECS = cd;
+	} else {
+		TIFFErrorExt(0, "TIFFRegisterCODEC",
+		    "No space to register compression scheme %s", name);
+		return NULL;
+	}
+	return (cd->info);
+}
+
+void
+TIFFUnRegisterCODEC(TIFFCodec* c)
+{
+	codec_t* cd;
+	codec_t** pcd;
+
+	for (pcd = &registeredCODECS; (cd = *pcd); pcd = &cd->next)
+		if (cd->info == c) {
+			*pcd = cd->next;
+			_TIFFfree(cd);
+			return;
+		}
+	TIFFErrorExt(0, "TIFFUnRegisterCODEC",
+	    "Cannot remove compression scheme %s; not registered", c->name);
+}
+
+/************************************************************************/
+/*                       TIFFGetConfisuredCODECs()                      */
+/************************************************************************/
+
+/**
+ * Get list of configured codecs, both built-in and registered by user.
+ * Caller is responsible to free this structure.
+ * 
+ * @return returns array of TIFFCodec records (the last record should be NULL)
+ * or NULL if function failed.
+ */
+
+TIFFCodec*
+TIFFGetConfiguredCODECs()
+{
+	int		i = 1;
+        codec_t		*cd;
+        const TIFFCodec	*c;
+	TIFFCodec	*codecs = NULL, *new_codecs;
+
+        for (cd = registeredCODECS; cd; cd = cd->next) {
+                new_codecs = (TIFFCodec *)
+			_TIFFrealloc(codecs, i * sizeof(TIFFCodec));
+		if (!new_codecs) {
+			_TIFFfree (codecs);
+			return NULL;
+		}
+		codecs = new_codecs;
+		_TIFFmemcpy(codecs + i - 1, cd, sizeof(TIFFCodec));
+		i++;
+	}
+        for (c = _TIFFBuiltinCODECS; c->name; c++) {
+                if (TIFFIsCODECConfigured(c->scheme)) {
+                        new_codecs = (TIFFCodec *)
+				_TIFFrealloc(codecs, i * sizeof(TIFFCodec));
+			if (!new_codecs) {
+				_TIFFfree (codecs);
+				return NULL;
+			}
+			codecs = new_codecs;
+			_TIFFmemcpy(codecs + i - 1, (const tdata_t)c, sizeof(TIFFCodec));
+			i++;
+		}
+	}
+
+	new_codecs = (TIFFCodec *) _TIFFrealloc(codecs, i * sizeof(TIFFCodec));
+	if (!new_codecs) {
+		_TIFFfree (codecs);
+		return NULL;
+	}
+	codecs = new_codecs;
+	_TIFFmemset(codecs + i - 1, 0, sizeof(TIFFCodec));
+
+        return codecs;
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_config.h b/Utilities/GDAL/frmts/gtiff/libtiff/tif_config.h
new file mode 100644
index 0000000000..b4e82b31e4
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_config.h
@@ -0,0 +1,41 @@
+/* Get the common system configuration switches from the main file. */
+#include "cpl_config.h"
+
+/* Libtiff specific switches. */
+
+/* Support CCITT Group 3 & 4 algorithms */
+#define CCITT_SUPPORT 1
+
+/* Support LogLuv high dynamic range encoding */
+#define LOGLUV_SUPPORT 1
+
+/* Support LZW algorithm */
+#define LZW_SUPPORT 1
+
+/* Support NeXT 2-bit RLE algorithm */
+#define NEXT_SUPPORT 1
+
+/* Support Macintosh PackBits algorithm */
+#define PACKBITS_SUPPORT 1
+
+/* Support ThunderScan 4-bit RLE algorithm */
+#define THUNDER_SUPPORT 1
+
+/* Pick up YCbCr subsampling info from the JPEG data stream to support files
+   lacking the tag (default enabled). */
+//#define CHECK_JPEG_YCBCR_SUBSAMPLING 1
+
+/* Treat extra sample as alpha (default enabled). The RGBA interface will
+   treat a fourth sample with no EXTRASAMPLE_ value as being ASSOCALPHA. Many
+   packages produce RGBA files but don't mark the alpha properly. */
+#define DEFAULT_EXTRASAMPLE_AS_ALPHA 1
+
+/* Support strip chopping (whether or not to convert single-strip uncompressed
+   images to mutiple strips of ~8Kb to reduce memory usage) */
+#define STRIPCHOP_DEFAULT TIFF_STRIPCHOP
+
+/* Enable SubIFD tag (330) support */
+#define SUBIFD_SUPPORT 1
+
+
+
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_dir.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_dir.c
new file mode 100644
index 0000000000..160c7b85be
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_dir.c
@@ -0,0 +1,1350 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ *
+ * Directory Tag Get & Set Routines.
+ * (and also some miscellaneous stuff)
+ */
+#include "tiffiop.h"
+
+/*
+ * These are used in the backwards compatibility code...
+ */
+#define DATATYPE_VOID		0       /* !untyped data */
+#define DATATYPE_INT		1       /* !signed integer data */
+#define DATATYPE_UINT		2       /* !unsigned integer data */
+#define DATATYPE_IEEEFP		3       /* !IEEE floating point data */
+
+static void
+setByteArray(void** vpp, void* vp, size_t nmemb, size_t elem_size)
+{
+	if (*vpp)
+		_TIFFfree(*vpp), *vpp = 0;
+	if (vp) {
+		tsize_t	bytes = nmemb * elem_size;
+		if (elem_size && bytes / elem_size == nmemb)
+			*vpp = (void*) _TIFFmalloc(bytes);
+		if (*vpp)
+			_TIFFmemcpy(*vpp, vp, bytes);
+	}
+}
+void _TIFFsetByteArray(void** vpp, void* vp, uint32 n)
+    { setByteArray(vpp, vp, n, 1); }
+void _TIFFsetString(char** cpp, char* cp)
+    { setByteArray((void**) cpp, (void*) cp, strlen(cp)+1, 1); }
+void _TIFFsetNString(char** cpp, char* cp, uint32 n)
+    { setByteArray((void**) cpp, (void*) cp, n, 1); }
+void _TIFFsetShortArray(uint16** wpp, uint16* wp, uint32 n)
+    { setByteArray((void**) wpp, (void*) wp, n, sizeof (uint16)); }
+void _TIFFsetLongArray(uint32** lpp, uint32* lp, uint32 n)
+    { setByteArray((void**) lpp, (void*) lp, n, sizeof (uint32)); }
+void _TIFFsetFloatArray(float** fpp, float* fp, uint32 n)
+    { setByteArray((void**) fpp, (void*) fp, n, sizeof (float)); }
+void _TIFFsetDoubleArray(double** dpp, double* dp, uint32 n)
+    { setByteArray((void**) dpp, (void*) dp, n, sizeof (double)); }
+
+/*
+ * Install extra samples information.
+ */
+static int
+setExtraSamples(TIFFDirectory* td, va_list ap, uint32* v)
+{
+	uint16* va;
+	uint32 i;
+
+	*v = va_arg(ap, uint32);
+	if ((uint16) *v > td->td_samplesperpixel)
+		return (0);
+	va = va_arg(ap, uint16*);
+	if (*v > 0 && va == NULL)		/* typically missing param */
+		return (0);
+	for (i = 0; i < *v; i++)
+		if (va[i] > EXTRASAMPLE_UNASSALPHA)
+			return (0);
+	td->td_extrasamples = (uint16) *v;
+	_TIFFsetShortArray(&td->td_sampleinfo, va, td->td_extrasamples);
+	return (1);
+}
+
+static uint32
+checkInkNamesString(TIFF* tif, uint32 slen, const char* s)
+{
+	TIFFDirectory* td = &tif->tif_dir;
+	uint16 i = td->td_samplesperpixel;
+
+	if (slen > 0) {
+		const char* ep = s+slen;
+		const char* cp = s;
+		for (; i > 0; i--) {
+			for (; *cp != '\0'; cp++)
+				if (cp >= ep)
+					goto bad;
+			cp++;				/* skip \0 */
+		}
+		return (cp-s);
+	}
+bad:
+	TIFFErrorExt(tif->tif_clientdata, "TIFFSetField",
+	    "%s: Invalid InkNames value; expecting %d names, found %d",
+	    tif->tif_name,
+	    td->td_samplesperpixel,
+	    td->td_samplesperpixel-i);
+	return (0);
+}
+
+static int
+_TIFFVSetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+	static const char module[] = "_TIFFVSetField";
+	
+	TIFFDirectory* td = &tif->tif_dir;
+	int status = 1;
+	uint32 v32, i, v;
+	char* s;
+
+	switch (tag) {
+	case TIFFTAG_SUBFILETYPE:
+		td->td_subfiletype = va_arg(ap, uint32);
+		break;
+	case TIFFTAG_IMAGEWIDTH:
+		td->td_imagewidth = va_arg(ap, uint32);
+		break;
+	case TIFFTAG_IMAGELENGTH:
+		td->td_imagelength = va_arg(ap, uint32);
+		break;
+	case TIFFTAG_BITSPERSAMPLE:
+		td->td_bitspersample = (uint16) va_arg(ap, int);
+		/*
+		 * If the data require post-decoding processing to byte-swap
+		 * samples, set it up here.  Note that since tags are required
+		 * to be ordered, compression code can override this behaviour
+		 * in the setup method if it wants to roll the post decoding
+		 * work in with its normal work.
+		 */
+		if (tif->tif_flags & TIFF_SWAB) {
+			if (td->td_bitspersample == 16)
+				tif->tif_postdecode = _TIFFSwab16BitData;
+			else if (td->td_bitspersample == 24)
+				tif->tif_postdecode = _TIFFSwab24BitData;
+			else if (td->td_bitspersample == 32)
+				tif->tif_postdecode = _TIFFSwab32BitData;
+			else if (td->td_bitspersample == 64)
+				tif->tif_postdecode = _TIFFSwab64BitData;
+			else if (td->td_bitspersample == 128) /* two 64's */
+				tif->tif_postdecode = _TIFFSwab64BitData;
+		}
+		break;
+	case TIFFTAG_COMPRESSION:
+		v = va_arg(ap, uint32) & 0xffff;
+		/*
+		 * If we're changing the compression scheme, the notify the
+		 * previous module so that it can cleanup any state it's
+		 * setup.
+		 */
+		if (TIFFFieldSet(tif, FIELD_COMPRESSION)) {
+			if (td->td_compression == v)
+				break;
+			(*tif->tif_cleanup)(tif);
+			tif->tif_flags &= ~TIFF_CODERSETUP;
+		}
+		/*
+		 * Setup new compression routine state.
+		 */
+		if( (status = TIFFSetCompressionScheme(tif, v)) != 0 )
+                    td->td_compression = (uint16) v;
+                else
+                    status = 0;
+		break;
+	case TIFFTAG_PHOTOMETRIC:
+		td->td_photometric = (uint16) va_arg(ap, int);
+		break;
+	case TIFFTAG_THRESHHOLDING:
+		td->td_threshholding = (uint16) va_arg(ap, int);
+		break;
+	case TIFFTAG_FILLORDER:
+		v = va_arg(ap, uint32);
+		if (v != FILLORDER_LSB2MSB && v != FILLORDER_MSB2LSB)
+			goto badvalue;
+		td->td_fillorder = (uint16) v;
+		break;
+		break;
+	case TIFFTAG_ORIENTATION:
+		v = va_arg(ap, uint32);
+		if (v < ORIENTATION_TOPLEFT || ORIENTATION_LEFTBOT < v) {
+			TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
+			    "Bad value %lu for \"%s\" tag ignored",
+			    v, _TIFFFieldWithTag(tif, tag)->field_name);
+		} else
+			td->td_orientation = (uint16) v;
+		break;
+	case TIFFTAG_SAMPLESPERPIXEL:
+		/* XXX should cross check -- e.g. if pallette, then 1 */
+		v = va_arg(ap, uint32);
+		if (v == 0)
+			goto badvalue;
+		td->td_samplesperpixel = (uint16) v;
+		break;
+	case TIFFTAG_ROWSPERSTRIP:
+		v32 = va_arg(ap, uint32);
+		if (v32 == 0)
+			goto badvalue32;
+		td->td_rowsperstrip = v32;
+		if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) {
+			td->td_tilelength = v32;
+			td->td_tilewidth = td->td_imagewidth;
+		}
+		break;
+	case TIFFTAG_MINSAMPLEVALUE:
+		td->td_minsamplevalue = (uint16) va_arg(ap, int);
+		break;
+	case TIFFTAG_MAXSAMPLEVALUE:
+		td->td_maxsamplevalue = (uint16) va_arg(ap, int);
+		break;
+	case TIFFTAG_SMINSAMPLEVALUE:
+		td->td_sminsamplevalue = va_arg(ap, double);
+		break;
+	case TIFFTAG_SMAXSAMPLEVALUE:
+		td->td_smaxsamplevalue = va_arg(ap, double);
+		break;
+	case TIFFTAG_XRESOLUTION:
+		td->td_xresolution = (float) va_arg(ap, double);
+		break;
+	case TIFFTAG_YRESOLUTION:
+		td->td_yresolution = (float) va_arg(ap, double);
+		break;
+	case TIFFTAG_PLANARCONFIG:
+		v = va_arg(ap, uint32);
+		if (v != PLANARCONFIG_CONTIG && v != PLANARCONFIG_SEPARATE)
+			goto badvalue;
+		td->td_planarconfig = (uint16) v;
+		break;
+	case TIFFTAG_XPOSITION:
+		td->td_xposition = (float) va_arg(ap, double);
+		break;
+	case TIFFTAG_YPOSITION:
+		td->td_yposition = (float) va_arg(ap, double);
+		break;
+	case TIFFTAG_RESOLUTIONUNIT:
+		v = va_arg(ap, uint32);
+		if (v < RESUNIT_NONE || RESUNIT_CENTIMETER < v)
+			goto badvalue;
+		td->td_resolutionunit = (uint16) v;
+		break;
+	case TIFFTAG_PAGENUMBER:
+		td->td_pagenumber[0] = (uint16) va_arg(ap, int);
+		td->td_pagenumber[1] = (uint16) va_arg(ap, int);
+		break;
+	case TIFFTAG_HALFTONEHINTS:
+		td->td_halftonehints[0] = (uint16) va_arg(ap, int);
+		td->td_halftonehints[1] = (uint16) va_arg(ap, int);
+		break;
+	case TIFFTAG_COLORMAP:
+		v32 = (uint32)(1L<<td->td_bitspersample);
+		_TIFFsetShortArray(&td->td_colormap[0], va_arg(ap, uint16*), v32);
+		_TIFFsetShortArray(&td->td_colormap[1], va_arg(ap, uint16*), v32);
+		_TIFFsetShortArray(&td->td_colormap[2], va_arg(ap, uint16*), v32);
+		break;
+	case TIFFTAG_EXTRASAMPLES:
+		if (!setExtraSamples(td, ap, &v))
+			goto badvalue;
+		break;
+	case TIFFTAG_MATTEING:
+		td->td_extrasamples = (uint16) (va_arg(ap, int) != 0);
+		if (td->td_extrasamples) {
+			uint16 sv = EXTRASAMPLE_ASSOCALPHA;
+			_TIFFsetShortArray(&td->td_sampleinfo, &sv, 1);
+		}
+		break;
+	case TIFFTAG_TILEWIDTH:
+		v32 = va_arg(ap, uint32);
+		if (v32 % 16) {
+			if (tif->tif_mode != O_RDONLY)
+				goto badvalue32;
+			TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
+				"Nonstandard tile width %d, convert file", v32);
+		}
+		td->td_tilewidth = v32;
+		tif->tif_flags |= TIFF_ISTILED;
+		break;
+	case TIFFTAG_TILELENGTH:
+		v32 = va_arg(ap, uint32);
+		if (v32 % 16) {
+			if (tif->tif_mode != O_RDONLY)
+				goto badvalue32;
+			TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
+			    "Nonstandard tile length %d, convert file", v32);
+		}
+		td->td_tilelength = v32;
+		tif->tif_flags |= TIFF_ISTILED;
+		break;
+	case TIFFTAG_TILEDEPTH:
+		v32 = va_arg(ap, uint32);
+		if (v32 == 0)
+			goto badvalue32;
+		td->td_tiledepth = v32;
+		break;
+	case TIFFTAG_DATATYPE:
+		v = va_arg(ap, uint32);
+		switch (v) {
+		case DATATYPE_VOID:	v = SAMPLEFORMAT_VOID;	break;
+		case DATATYPE_INT:	v = SAMPLEFORMAT_INT;	break;
+		case DATATYPE_UINT:	v = SAMPLEFORMAT_UINT;	break;
+		case DATATYPE_IEEEFP:	v = SAMPLEFORMAT_IEEEFP;break;
+		default:		goto badvalue;
+		}
+		td->td_sampleformat = (uint16) v;
+		break;
+	case TIFFTAG_SAMPLEFORMAT:
+		v = va_arg(ap, uint32);
+		if (v < SAMPLEFORMAT_UINT || SAMPLEFORMAT_COMPLEXIEEEFP < v)
+			goto badvalue;
+		td->td_sampleformat = (uint16) v;
+
+                /*  Try to fix up the SWAB function for complex data. */
+                if( td->td_sampleformat == SAMPLEFORMAT_COMPLEXINT 
+                    && td->td_bitspersample == 32
+                    && tif->tif_postdecode == _TIFFSwab32BitData )
+                    tif->tif_postdecode = _TIFFSwab16BitData;
+                else if( (td->td_sampleformat == SAMPLEFORMAT_COMPLEXINT 
+                          || td->td_sampleformat == SAMPLEFORMAT_COMPLEXIEEEFP)
+                         && td->td_bitspersample == 64
+                         && tif->tif_postdecode == _TIFFSwab64BitData )
+                    tif->tif_postdecode = _TIFFSwab32BitData;
+		break;
+	case TIFFTAG_IMAGEDEPTH:
+		td->td_imagedepth = va_arg(ap, uint32);
+		break;
+	case TIFFTAG_SUBIFD:
+		if ((tif->tif_flags & TIFF_INSUBIFD) == 0) {
+			td->td_nsubifd = (uint16) va_arg(ap, int);
+			_TIFFsetLongArray(&td->td_subifd, va_arg(ap, uint32*),
+			    (long) td->td_nsubifd);
+		} else {
+			TIFFErrorExt(tif->tif_clientdata, module, "%s: Sorry, cannot nest SubIFDs",
+				  tif->tif_name);
+			status = 0;
+		}
+		break;
+	case TIFFTAG_YCBCRPOSITIONING:
+		td->td_ycbcrpositioning = (uint16) va_arg(ap, int);
+		break;
+	case TIFFTAG_YCBCRSUBSAMPLING:
+		td->td_ycbcrsubsampling[0] = (uint16) va_arg(ap, int);
+		td->td_ycbcrsubsampling[1] = (uint16) va_arg(ap, int);
+		break;
+	case TIFFTAG_TRANSFERFUNCTION:
+		v = (td->td_samplesperpixel - td->td_extrasamples) > 1 ? 3 : 1;
+		for (i = 0; i < v; i++)
+			_TIFFsetShortArray(&td->td_transferfunction[i],
+			    va_arg(ap, uint16*), 1L<<td->td_bitspersample);
+		break;
+	case TIFFTAG_INKNAMES:
+		v = va_arg(ap, uint32);
+		s = va_arg(ap, char*);
+		v = checkInkNamesString(tif, v, s);
+                status = v > 0;
+		if( v > 0 ) {
+			_TIFFsetNString(&td->td_inknames, s, v);
+			td->td_inknameslen = v;
+		}
+		break;
+        default: {
+            const TIFFFieldInfo* fip = _TIFFFindFieldInfo(tif, tag, TIFF_ANY);
+            TIFFTagValue *tv;
+            int tv_size, iCustom;
+
+            /*
+	     * This can happen if multiple images are open with different
+	     * codecs which have private tags.  The global tag information
+	     * table may then have tags that are valid for one file but not
+	     * the other. If the client tries to set a tag that is not valid
+	     * for the image's codec then we'll arrive here.  This
+	     * happens, for example, when tiffcp is used to convert between
+	     * compression schemes and codec-specific tags are blindly copied.
+             */
+            if(fip == NULL || fip->field_bit != FIELD_CUSTOM) {
+		TIFFErrorExt(tif->tif_clientdata, module,
+		    "%s: Invalid %stag \"%s\" (not supported by codec)",
+		    tif->tif_name, isPseudoTag(tag) ? "pseudo-" : "",
+		    _TIFFFieldWithTag(tif, tag)->field_name);
+		status = 0;
+		break;
+            }
+
+            /*
+             * Find the existing entry for this custom value.
+             */
+            tv = NULL;
+            for(iCustom = 0; iCustom < td->td_customValueCount; iCustom++) {
+                if(td->td_customValues[iCustom].info == fip) {
+                    tv = td->td_customValues + iCustom;
+                    if(tv->value != NULL)
+                    {
+                        _TIFFfree(tv->value);
+                        tv->value = NULL;
+                    }
+                    break;
+                }
+            }
+
+            /*
+             * Grow the custom list if the entry was not found.
+             */
+            if(tv == NULL) {
+		TIFFTagValue	*new_customValues;
+		
+		td->td_customValueCount++;
+		new_customValues = (TIFFTagValue *)
+			_TIFFrealloc(td->td_customValues,
+				     sizeof(TIFFTagValue) * td->td_customValueCount);
+		if (!new_customValues) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+		"%s: Failed to allocate space for list of custom values",
+				  tif->tif_name);
+			status = 0;
+			goto end;
+		}
+
+		td->td_customValues = new_customValues;
+
+                tv = td->td_customValues + (td->td_customValueCount-1);
+                tv->info = fip;
+                tv->value = NULL;
+                tv->count = 0;
+            }
+
+            /*
+             * Set custom value ... save a copy of the custom tag value.
+             */
+	    tv_size = _TIFFDataSize(fip->field_type);
+	    if (tv_size == 0) {
+		    status = 0;
+		    TIFFErrorExt(tif->tif_clientdata, module,
+				 "%s: Bad field type %d for \"%s\"",
+				 tif->tif_name, fip->field_type,
+				 fip->field_name);
+		    goto end;
+	    }
+           
+            if(fip->field_passcount) {
+		    if (fip->field_writecount == TIFF_VARIABLE2)
+			tv->count = (uint32) va_arg(ap, uint32);
+		    else
+			tv->count = (int) va_arg(ap, int);
+	    } else if (fip->field_writecount == TIFF_VARIABLE
+		       || fip->field_writecount == TIFF_VARIABLE2)
+		tv->count = 1;
+	    else if (fip->field_writecount == TIFF_SPP)
+		tv->count = td->td_samplesperpixel;
+	    else
+                tv->count = fip->field_writecount;
+            
+    
+	    if (fip->field_type == TIFF_ASCII)
+		    _TIFFsetString((char **)&tv->value, va_arg(ap, char *));
+	    else {
+                tv->value = _TIFFmalloc(tv_size * tv->count);
+		if (!tv->value) {
+		    status = 0;
+		    goto end;
+		}
+
+		if ((fip->field_passcount
+		    || fip->field_writecount == TIFF_VARIABLE
+		    || fip->field_writecount == TIFF_VARIABLE2
+		    || fip->field_writecount == TIFF_SPP
+		    || tv->count > 1)
+		    && fip->field_tag != TIFFTAG_PAGENUMBER
+		    && fip->field_tag != TIFFTAG_HALFTONEHINTS
+		    && fip->field_tag != TIFFTAG_YCBCRSUBSAMPLING
+		    && fip->field_tag != TIFFTAG_DOTRANGE) {
+                    _TIFFmemcpy(tv->value, va_arg(ap, void *),
+				tv->count * tv_size);
+		} else {
+		    /*
+		     * XXX: The following loop required to handle
+		     * TIFFTAG_PAGENUMBER, TIFFTAG_HALFTONEHINTS,
+		     * TIFFTAG_YCBCRSUBSAMPLING and TIFFTAG_DOTRANGE tags.
+		     * These tags are actually arrays and should be passed as
+		     * array pointers to TIFFSetField() function, but actually
+		     * passed as a list of separate values. This behaviour
+		     * must be changed in the future!
+		     */
+		    int i;
+		    char *val = (char *)tv->value;
+
+		    for (i = 0; i < tv->count; i++, val += tv_size) {
+			    switch (fip->field_type) {
+				case TIFF_BYTE:
+				case TIFF_UNDEFINED:
+				    {
+					uint8 v = (uint8)va_arg(ap, int);
+					_TIFFmemcpy(val, &v, tv_size);
+				    }
+				    break;
+				case TIFF_SBYTE:
+				    {
+					int8 v = (int8)va_arg(ap, int);
+					_TIFFmemcpy(val, &v, tv_size);
+				    }
+				    break;
+				case TIFF_SHORT:
+				    {
+					uint16 v = (uint16)va_arg(ap, int);
+					_TIFFmemcpy(val, &v, tv_size);
+				    }
+				    break;
+				case TIFF_SSHORT:
+				    {
+					int16 v = (int16)va_arg(ap, int);
+					_TIFFmemcpy(val, &v, tv_size);
+				    }
+				    break;
+				case TIFF_LONG:
+				case TIFF_IFD:
+				    {
+					uint32 v = va_arg(ap, uint32);
+					_TIFFmemcpy(val, &v, tv_size);
+				    }
+				    break;
+				case TIFF_SLONG:
+				    {
+					int32 v = va_arg(ap, int32);
+					_TIFFmemcpy(val, &v, tv_size);
+				    }
+				    break;
+				case TIFF_RATIONAL:
+				case TIFF_SRATIONAL:
+				case TIFF_FLOAT:
+				    {
+					float v = (float)va_arg(ap, double);
+					_TIFFmemcpy(val, &v, tv_size);
+				    }
+				    break;
+				case TIFF_DOUBLE:
+				    {
+					double v = va_arg(ap, double);
+					_TIFFmemcpy(val, &v, tv_size);
+				    }
+				    break;
+				default:
+				    _TIFFmemset(val, 0, tv_size);
+				    status = 0;
+				    break;
+			    }
+		    }
+		}
+	    }
+          }
+	}
+	if (status) {
+		TIFFSetFieldBit(tif, _TIFFFieldWithTag(tif, tag)->field_bit);
+		tif->tif_flags |= TIFF_DIRTYDIRECT;
+	}
+
+end:
+	va_end(ap);
+	return (status);
+badvalue:
+	TIFFErrorExt(tif->tif_clientdata, module, "%s: Bad value %d for \"%s\"",
+		  tif->tif_name, v, _TIFFFieldWithTag(tif, tag)->field_name);
+	va_end(ap);
+	return (0);
+badvalue32:
+	TIFFErrorExt(tif->tif_clientdata, module, "%s: Bad value %ld for \"%s\"",
+		   tif->tif_name, v32, _TIFFFieldWithTag(tif, tag)->field_name);
+	va_end(ap);
+	return (0);
+}
+
+/*
+ * Return 1/0 according to whether or not
+ * it is permissible to set the tag's value.
+ * Note that we allow ImageLength to be changed
+ * so that we can append and extend to images.
+ * Any other tag may not be altered once writing
+ * has commenced, unless its value has no effect
+ * on the format of the data that is written.
+ */
+static int
+OkToChangeTag(TIFF* tif, ttag_t tag)
+{
+	const TIFFFieldInfo* fip = _TIFFFindFieldInfo(tif, tag, TIFF_ANY);
+	if (!fip) {			/* unknown tag */
+		TIFFErrorExt(tif->tif_clientdata, "TIFFSetField", "%s: Unknown %stag %u",
+		    tif->tif_name, isPseudoTag(tag) ? "pseudo-" : "", tag);
+		return (0);
+	}
+	if (tag != TIFFTAG_IMAGELENGTH && (tif->tif_flags & TIFF_BEENWRITING) &&
+	    !fip->field_oktochange) {
+		/*
+		 * Consult info table to see if tag can be changed
+		 * after we've started writing.  We only allow changes
+		 * to those tags that don't/shouldn't affect the
+		 * compression and/or format of the data.
+		 */
+		TIFFErrorExt(tif->tif_clientdata, "TIFFSetField",
+		    "%s: Cannot modify tag \"%s\" while writing",
+		    tif->tif_name, fip->field_name);
+		return (0);
+	}
+	return (1);
+}
+
+/*
+ * Record the value of a field in the
+ * internal directory structure.  The
+ * field will be written to the file
+ * when/if the directory structure is
+ * updated.
+ */
+int
+TIFFSetField(TIFF* tif, ttag_t tag, ...)
+{
+	va_list ap;
+	int status;
+
+	va_start(ap, tag);
+	status = TIFFVSetField(tif, tag, ap);
+	va_end(ap);
+	return (status);
+}
+
+/*
+ * Like TIFFSetField, but taking a varargs
+ * parameter list.  This routine is useful
+ * for building higher-level interfaces on
+ * top of the library.
+ */
+int
+TIFFVSetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+	return OkToChangeTag(tif, tag) ?
+	    (*tif->tif_tagmethods.vsetfield)(tif, tag, ap) : 0;
+}
+
+static int
+_TIFFVGetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+    TIFFDirectory* td = &tif->tif_dir;
+    int            ret_val = 1;
+
+    switch (tag) {
+	case TIFFTAG_SUBFILETYPE:
+            *va_arg(ap, uint32*) = td->td_subfiletype;
+            break;
+	case TIFFTAG_IMAGEWIDTH:
+            *va_arg(ap, uint32*) = td->td_imagewidth;
+            break;
+	case TIFFTAG_IMAGELENGTH:
+            *va_arg(ap, uint32*) = td->td_imagelength;
+            break;
+	case TIFFTAG_BITSPERSAMPLE:
+            *va_arg(ap, uint16*) = td->td_bitspersample;
+            break;
+	case TIFFTAG_COMPRESSION:
+            *va_arg(ap, uint16*) = td->td_compression;
+            break;
+	case TIFFTAG_PHOTOMETRIC:
+            *va_arg(ap, uint16*) = td->td_photometric;
+            break;
+	case TIFFTAG_THRESHHOLDING:
+            *va_arg(ap, uint16*) = td->td_threshholding;
+            break;
+	case TIFFTAG_FILLORDER:
+            *va_arg(ap, uint16*) = td->td_fillorder;
+            break;
+	case TIFFTAG_ORIENTATION:
+            *va_arg(ap, uint16*) = td->td_orientation;
+            break;
+	case TIFFTAG_SAMPLESPERPIXEL:
+            *va_arg(ap, uint16*) = td->td_samplesperpixel;
+            break;
+	case TIFFTAG_ROWSPERSTRIP:
+            *va_arg(ap, uint32*) = td->td_rowsperstrip;
+            break;
+	case TIFFTAG_MINSAMPLEVALUE:
+            *va_arg(ap, uint16*) = td->td_minsamplevalue;
+            break;
+	case TIFFTAG_MAXSAMPLEVALUE:
+            *va_arg(ap, uint16*) = td->td_maxsamplevalue;
+            break;
+	case TIFFTAG_SMINSAMPLEVALUE:
+            *va_arg(ap, double*) = td->td_sminsamplevalue;
+            break;
+	case TIFFTAG_SMAXSAMPLEVALUE:
+            *va_arg(ap, double*) = td->td_smaxsamplevalue;
+            break;
+	case TIFFTAG_XRESOLUTION:
+            *va_arg(ap, float*) = td->td_xresolution;
+            break;
+	case TIFFTAG_YRESOLUTION:
+            *va_arg(ap, float*) = td->td_yresolution;
+            break;
+	case TIFFTAG_PLANARCONFIG:
+            *va_arg(ap, uint16*) = td->td_planarconfig;
+            break;
+	case TIFFTAG_XPOSITION:
+            *va_arg(ap, float*) = td->td_xposition;
+            break;
+	case TIFFTAG_YPOSITION:
+            *va_arg(ap, float*) = td->td_yposition;
+            break;
+	case TIFFTAG_RESOLUTIONUNIT:
+            *va_arg(ap, uint16*) = td->td_resolutionunit;
+            break;
+	case TIFFTAG_PAGENUMBER:
+            *va_arg(ap, uint16*) = td->td_pagenumber[0];
+            *va_arg(ap, uint16*) = td->td_pagenumber[1];
+            break;
+	case TIFFTAG_HALFTONEHINTS:
+            *va_arg(ap, uint16*) = td->td_halftonehints[0];
+            *va_arg(ap, uint16*) = td->td_halftonehints[1];
+            break;
+	case TIFFTAG_COLORMAP:
+            *va_arg(ap, uint16**) = td->td_colormap[0];
+            *va_arg(ap, uint16**) = td->td_colormap[1];
+            *va_arg(ap, uint16**) = td->td_colormap[2];
+            break;
+	case TIFFTAG_STRIPOFFSETS:
+	case TIFFTAG_TILEOFFSETS:
+            *va_arg(ap, uint32**) = td->td_stripoffset;
+            break;
+	case TIFFTAG_STRIPBYTECOUNTS:
+	case TIFFTAG_TILEBYTECOUNTS:
+            *va_arg(ap, uint32**) = td->td_stripbytecount;
+            break;
+	case TIFFTAG_MATTEING:
+            *va_arg(ap, uint16*) =
+                (td->td_extrasamples == 1 &&
+                 td->td_sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA);
+            break;
+	case TIFFTAG_EXTRASAMPLES:
+            *va_arg(ap, uint16*) = td->td_extrasamples;
+            *va_arg(ap, uint16**) = td->td_sampleinfo;
+            break;
+	case TIFFTAG_TILEWIDTH:
+            *va_arg(ap, uint32*) = td->td_tilewidth;
+            break;
+	case TIFFTAG_TILELENGTH:
+            *va_arg(ap, uint32*) = td->td_tilelength;
+            break;
+	case TIFFTAG_TILEDEPTH:
+            *va_arg(ap, uint32*) = td->td_tiledepth;
+            break;
+	case TIFFTAG_DATATYPE:
+            switch (td->td_sampleformat) {
+		case SAMPLEFORMAT_UINT:
+                    *va_arg(ap, uint16*) = DATATYPE_UINT;
+                    break;
+		case SAMPLEFORMAT_INT:
+                    *va_arg(ap, uint16*) = DATATYPE_INT;
+                    break;
+		case SAMPLEFORMAT_IEEEFP:
+                    *va_arg(ap, uint16*) = DATATYPE_IEEEFP;
+                    break;
+		case SAMPLEFORMAT_VOID:
+                    *va_arg(ap, uint16*) = DATATYPE_VOID;
+                    break;
+            }
+            break;
+	case TIFFTAG_SAMPLEFORMAT:
+            *va_arg(ap, uint16*) = td->td_sampleformat;
+            break;
+	case TIFFTAG_IMAGEDEPTH:
+            *va_arg(ap, uint32*) = td->td_imagedepth;
+            break;
+	case TIFFTAG_SUBIFD:
+            *va_arg(ap, uint16*) = td->td_nsubifd;
+            *va_arg(ap, uint32**) = td->td_subifd;
+            break;
+	case TIFFTAG_YCBCRPOSITIONING:
+            *va_arg(ap, uint16*) = td->td_ycbcrpositioning;
+            break;
+	case TIFFTAG_YCBCRSUBSAMPLING:
+            *va_arg(ap, uint16*) = td->td_ycbcrsubsampling[0];
+            *va_arg(ap, uint16*) = td->td_ycbcrsubsampling[1];
+            break;
+	case TIFFTAG_TRANSFERFUNCTION:
+            *va_arg(ap, uint16**) = td->td_transferfunction[0];
+            if (td->td_samplesperpixel - td->td_extrasamples > 1) {
+                *va_arg(ap, uint16**) = td->td_transferfunction[1];
+                *va_arg(ap, uint16**) = td->td_transferfunction[2];
+            }
+            break;
+	case TIFFTAG_INKNAMES:
+            *va_arg(ap, char**) = td->td_inknames;
+            break;
+        default:
+        {
+            const TIFFFieldInfo* fip = _TIFFFindFieldInfo(tif, tag, TIFF_ANY);
+            int           i;
+            
+            /*
+             * This can happen if multiple images are open with
+             * different codecs which have private tags.  The
+             * global tag information table may then have tags
+             * that are valid for one file but not the other. 
+             * If the client tries to get a tag that is not valid
+             * for the image's codec then we'll arrive here.
+             */
+            if( fip == NULL || fip->field_bit != FIELD_CUSTOM )
+            {
+				TIFFErrorExt(tif->tif_clientdata, "_TIFFVGetField",
+                          "%s: Invalid %stag \"%s\" (not supported by codec)",
+                          tif->tif_name, isPseudoTag(tag) ? "pseudo-" : "",
+                          _TIFFFieldWithTag(tif, tag)->field_name);
+                ret_val = 0;
+                break;
+            }
+
+            /*
+	     * Do we have a custom value?
+	     */
+            ret_val = 0;
+            for (i = 0; i < td->td_customValueCount; i++) {
+		TIFFTagValue *tv = td->td_customValues + i;
+
+		if (tv->info->field_tag != tag)
+			continue;
+                
+		if (fip->field_passcount) {
+			if (fip->field_readcount == TIFF_VARIABLE2) 
+				*va_arg(ap, uint32*) = (uint32)tv->count;
+			else	/* Assume TIFF_VARIABLE */
+				*va_arg(ap, uint16*) = (uint16)tv->count;
+			*va_arg(ap, void **) = tv->value;
+			ret_val = 1;
+                } else {
+			if ((fip->field_type == TIFF_ASCII
+			    || fip->field_readcount == TIFF_VARIABLE
+			    || fip->field_readcount == TIFF_VARIABLE2
+			    || fip->field_readcount == TIFF_SPP
+			    || tv->count > 1)
+			    && fip->field_tag != TIFFTAG_PAGENUMBER
+			    && fip->field_tag != TIFFTAG_HALFTONEHINTS
+			    && fip->field_tag != TIFFTAG_YCBCRSUBSAMPLING
+			    && fip->field_tag != TIFFTAG_DOTRANGE) {
+				*va_arg(ap, void **) = tv->value;
+				ret_val = 1;
+			} else {
+			    int j;
+			    char *val = (char *)tv->value;
+
+			    for (j = 0; j < tv->count;
+				 j++, val += _TIFFDataSize(tv->info->field_type)) {
+				switch (fip->field_type) {
+					case TIFF_BYTE:
+					case TIFF_UNDEFINED:
+						*va_arg(ap, uint8*) =
+							*(uint8 *)val;
+						ret_val = 1;
+						break;
+					case TIFF_SBYTE:
+						*va_arg(ap, int8*) =
+							*(int8 *)val;
+						ret_val = 1;
+						break;
+					case TIFF_SHORT:
+						*va_arg(ap, uint16*) =
+							*(uint16 *)val;
+						ret_val = 1;
+						break;
+					case TIFF_SSHORT:
+						*va_arg(ap, int16*) =
+							*(int16 *)val;
+						ret_val = 1;
+						break;
+					case TIFF_LONG:
+					case TIFF_IFD:
+						*va_arg(ap, uint32*) =
+							*(uint32 *)val;
+						ret_val = 1;
+						break;
+					case TIFF_SLONG:
+						*va_arg(ap, int32*) =
+							*(int32 *)val;
+						ret_val = 1;
+						break;
+					case TIFF_RATIONAL:
+					case TIFF_SRATIONAL:
+					case TIFF_FLOAT:
+						*va_arg(ap, float*) =
+							*(float *)val;
+						ret_val = 1;
+						break;
+					case TIFF_DOUBLE:
+						*va_arg(ap, double*) =
+							*(double *)val;
+						ret_val = 1;
+						break;
+					default:
+						ret_val = 0;
+						break;
+				}
+			    }
+			}
+                }
+		break;
+            }
+        }
+    }
+    return(ret_val);
+}
+
+/*
+ * Return the value of a field in the
+ * internal directory structure.
+ */
+int
+TIFFGetField(TIFF* tif, ttag_t tag, ...)
+{
+	int status;
+	va_list ap;
+
+	va_start(ap, tag);
+	status = TIFFVGetField(tif, tag, ap);
+	va_end(ap);
+	return (status);
+}
+
+/*
+ * Like TIFFGetField, but taking a varargs
+ * parameter list.  This routine is useful
+ * for building higher-level interfaces on
+ * top of the library.
+ */
+int
+TIFFVGetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+	const TIFFFieldInfo* fip = _TIFFFindFieldInfo(tif, tag, TIFF_ANY);
+	return (fip && (isPseudoTag(tag) || TIFFFieldSet(tif, fip->field_bit)) ?
+	    (*tif->tif_tagmethods.vgetfield)(tif, tag, ap) : 0);
+}
+
+#define	CleanupField(member) {		\
+    if (td->member) {			\
+	_TIFFfree(td->member);		\
+	td->member = 0;			\
+    }					\
+}
+
+/*
+ * Release storage associated with a directory.
+ */
+void
+TIFFFreeDirectory(TIFF* tif)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	int            i;
+
+	_TIFFmemset(td->td_fieldsset, 0, FIELD_SETLONGS);
+	CleanupField(td_colormap[0]);
+	CleanupField(td_colormap[1]);
+	CleanupField(td_colormap[2]);
+	CleanupField(td_sampleinfo);
+	CleanupField(td_subifd);
+	CleanupField(td_inknames);
+	CleanupField(td_transferfunction[0]);
+	CleanupField(td_transferfunction[1]);
+	CleanupField(td_transferfunction[2]);
+	CleanupField(td_stripoffset);
+	CleanupField(td_stripbytecount);
+	TIFFClrFieldBit(tif, FIELD_YCBCRSUBSAMPLING);
+	TIFFClrFieldBit(tif, FIELD_YCBCRPOSITIONING);
+
+	/* Cleanup custom tag values */
+	for( i = 0; i < td->td_customValueCount; i++ ) {
+		if (td->td_customValues[i].value)
+			_TIFFfree(td->td_customValues[i].value);
+	}
+
+	td->td_customValueCount = 0;
+	CleanupField(td_customValues);
+}
+#undef CleanupField
+
+/*
+ * Client Tag extension support (from Niles Ritter).
+ */
+static TIFFExtendProc _TIFFextender = (TIFFExtendProc) NULL;
+
+TIFFExtendProc
+TIFFSetTagExtender(TIFFExtendProc extender)
+{
+	TIFFExtendProc prev = _TIFFextender;
+	_TIFFextender = extender;
+	return (prev);
+}
+
+/*
+ * Setup for a new directory.  Should we automatically call
+ * TIFFWriteDirectory() if the current one is dirty?
+ *
+ * The newly created directory will not exist on the file till
+ * TIFFWriteDirectory(), TIFFFlush() or TIFFClose() is called.
+ */
+int
+TIFFCreateDirectory(TIFF* tif)
+{
+    TIFFDefaultDirectory(tif);
+    tif->tif_diroff = 0;
+    tif->tif_nextdiroff = 0;
+    tif->tif_curoff = 0;
+    tif->tif_row = (uint32) -1;
+    tif->tif_curstrip = (tstrip_t) -1;
+
+    return 0;
+}
+
+/*
+ * Setup a default directory structure.
+ */
+int
+TIFFDefaultDirectory(TIFF* tif)
+{
+	register TIFFDirectory* td = &tif->tif_dir;
+
+	size_t tiffFieldInfoCount;
+	const TIFFFieldInfo *tiffFieldInfo =
+	    _TIFFGetFieldInfo(&tiffFieldInfoCount);
+	_TIFFSetupFieldInfo(tif, tiffFieldInfo, tiffFieldInfoCount);
+
+	_TIFFmemset(td, 0, sizeof (*td));
+	td->td_fillorder = FILLORDER_MSB2LSB;
+	td->td_bitspersample = 1;
+	td->td_threshholding = THRESHHOLD_BILEVEL;
+	td->td_orientation = ORIENTATION_TOPLEFT;
+	td->td_samplesperpixel = 1;
+	td->td_rowsperstrip = (uint32) -1;
+	td->td_tilewidth = 0;
+	td->td_tilelength = 0;
+	td->td_tiledepth = 1;
+	td->td_stripbytecountsorted = 1; /* Our own arrays always sorted. */
+	td->td_resolutionunit = RESUNIT_INCH;
+	td->td_sampleformat = SAMPLEFORMAT_UINT;
+	td->td_imagedepth = 1;
+	td->td_ycbcrsubsampling[0] = 2;
+	td->td_ycbcrsubsampling[1] = 2;
+	td->td_ycbcrpositioning = YCBCRPOSITION_CENTERED;
+	tif->tif_postdecode = _TIFFNoPostDecode;
+	tif->tif_foundfield = NULL;
+	tif->tif_tagmethods.vsetfield = _TIFFVSetField;
+	tif->tif_tagmethods.vgetfield = _TIFFVGetField;
+	tif->tif_tagmethods.printdir = NULL;
+	/*
+	 *  Give client code a chance to install their own
+	 *  tag extensions & methods, prior to compression overloads.
+	 */
+	if (_TIFFextender)
+		(*_TIFFextender)(tif);
+	(void) TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
+	/*
+	 * NB: The directory is marked dirty as a result of setting
+	 * up the default compression scheme.  However, this really
+	 * isn't correct -- we want TIFF_DIRTYDIRECT to be set only
+	 * if the user does something.  We could just do the setup
+	 * by hand, but it seems better to use the normal mechanism
+	 * (i.e. TIFFSetField).
+	 */
+	tif->tif_flags &= ~TIFF_DIRTYDIRECT;
+
+	/*
+	 * As per http://bugzilla.remotesensing.org/show_bug.cgi?id=19
+	 * we clear the ISTILED flag when setting up a new directory.
+	 * Should we also be clearing stuff like INSUBIFD?
+	 */
+	tif->tif_flags &= ~TIFF_ISTILED;
+
+	return (1);
+}
+
+static int
+TIFFAdvanceDirectory(TIFF* tif, uint32* nextdir, toff_t* off)
+{
+	static const char module[] = "TIFFAdvanceDirectory";
+	uint16 dircount;
+	if (isMapped(tif))
+	{
+		toff_t poff=*nextdir;
+		if (poff+sizeof(uint16) > tif->tif_size)
+		{
+			TIFFErrorExt(tif->tif_clientdata, module, "%s: Error fetching directory count",
+			    tif->tif_name);
+			return (0);
+		}
+		_TIFFmemcpy(&dircount, tif->tif_base+poff, sizeof (uint16));
+		if (tif->tif_flags & TIFF_SWAB)
+			TIFFSwabShort(&dircount);
+		poff+=sizeof (uint16)+dircount*sizeof (TIFFDirEntry);
+		if (off != NULL)
+			*off = poff;
+		if (((toff_t) (poff+sizeof (uint32))) > tif->tif_size)
+		{
+			TIFFErrorExt(tif->tif_clientdata, module, "%s: Error fetching directory link",
+			    tif->tif_name);
+			return (0);
+		}
+		_TIFFmemcpy(nextdir, tif->tif_base+poff, sizeof (uint32));
+		if (tif->tif_flags & TIFF_SWAB)
+			TIFFSwabLong(nextdir);
+		return (1);
+	}
+	else
+	{
+		if (!SeekOK(tif, *nextdir) ||
+		    !ReadOK(tif, &dircount, sizeof (uint16))) {
+			TIFFErrorExt(tif->tif_clientdata, module, "%s: Error fetching directory count",
+			    tif->tif_name);
+			return (0);
+		}
+		if (tif->tif_flags & TIFF_SWAB)
+			TIFFSwabShort(&dircount);
+		if (off != NULL)
+			*off = TIFFSeekFile(tif,
+			    dircount*sizeof (TIFFDirEntry), SEEK_CUR);
+		else
+			(void) TIFFSeekFile(tif,
+			    dircount*sizeof (TIFFDirEntry), SEEK_CUR);
+		if (!ReadOK(tif, nextdir, sizeof (uint32))) {
+			TIFFErrorExt(tif->tif_clientdata, module, "%s: Error fetching directory link",
+			    tif->tif_name);
+			return (0);
+		}
+		if (tif->tif_flags & TIFF_SWAB)
+			TIFFSwabLong(nextdir);
+		return (1);
+	}
+}
+
+/*
+ * Count the number of directories in a file.
+ */
+tdir_t
+TIFFNumberOfDirectories(TIFF* tif)
+{
+    toff_t nextdir = tif->tif_header.tiff_diroff;
+    tdir_t n = 0;
+    
+    while (nextdir != 0 && TIFFAdvanceDirectory(tif, &nextdir, NULL))
+        n++;
+    return (n);
+}
+
+/*
+ * Set the n-th directory as the current directory.
+ * NB: Directories are numbered starting at 0.
+ */
+int
+TIFFSetDirectory(TIFF* tif, tdir_t dirn)
+{
+	toff_t nextdir;
+	tdir_t n;
+
+	nextdir = tif->tif_header.tiff_diroff;
+	for (n = dirn; n > 0 && nextdir != 0; n--)
+		if (!TIFFAdvanceDirectory(tif, &nextdir, NULL))
+			return (0);
+	tif->tif_nextdiroff = nextdir;
+	/*
+	 * Set curdir to the actual directory index.  The
+	 * -1 is because TIFFReadDirectory will increment
+	 * tif_curdir after successfully reading the directory.
+	 */
+	tif->tif_curdir = (dirn - n) - 1;
+	/*
+	 * Reset tif_dirnumber counter and start new list of seen directories.
+	 * We need this to prevent IFD loops.
+	 */
+	tif->tif_dirnumber = 0;
+	return (TIFFReadDirectory(tif));
+}
+
+/*
+ * Set the current directory to be the directory
+ * located at the specified file offset.  This interface
+ * is used mainly to access directories linked with
+ * the SubIFD tag (e.g. thumbnail images).
+ */
+int
+TIFFSetSubDirectory(TIFF* tif, uint32 diroff)
+{
+	tif->tif_nextdiroff = diroff;
+	/*
+	 * Reset tif_dirnumber counter and start new list of seen directories.
+	 * We need this to prevent IFD loops.
+	 */
+	tif->tif_dirnumber = 0;
+	return (TIFFReadDirectory(tif));
+}
+
+/*
+ * Return file offset of the current directory.
+ */
+uint32
+TIFFCurrentDirOffset(TIFF* tif)
+{
+	return (tif->tif_diroff);
+}
+
+/*
+ * Return an indication of whether or not we are
+ * at the last directory in the file.
+ */
+int
+TIFFLastDirectory(TIFF* tif)
+{
+	return (tif->tif_nextdiroff == 0);
+}
+
+/*
+ * Unlink the specified directory from the directory chain.
+ */
+int
+TIFFUnlinkDirectory(TIFF* tif, tdir_t dirn)
+{
+	static const char module[] = "TIFFUnlinkDirectory";
+	toff_t nextdir;
+	toff_t off;
+	tdir_t n;
+
+	if (tif->tif_mode == O_RDONLY) {
+		TIFFErrorExt(tif->tif_clientdata, module,
+                             "Can not unlink directory in read-only file");
+		return (0);
+	}
+	/*
+	 * Go to the directory before the one we want
+	 * to unlink and nab the offset of the link
+	 * field we'll need to patch.
+	 */
+	nextdir = tif->tif_header.tiff_diroff;
+	off = sizeof (uint16) + sizeof (uint16);
+	for (n = dirn-1; n > 0; n--) {
+		if (nextdir == 0) {
+			TIFFErrorExt(tif->tif_clientdata, module, "Directory %d does not exist", dirn);
+			return (0);
+		}
+		if (!TIFFAdvanceDirectory(tif, &nextdir, &off))
+			return (0);
+	}
+	/*
+	 * Advance to the directory to be unlinked and fetch
+	 * the offset of the directory that follows.
+	 */
+	if (!TIFFAdvanceDirectory(tif, &nextdir, NULL))
+		return (0);
+	/*
+	 * Go back and patch the link field of the preceding
+	 * directory to point to the offset of the directory
+	 * that follows.
+	 */
+	(void) TIFFSeekFile(tif, off, SEEK_SET);
+	if (tif->tif_flags & TIFF_SWAB)
+		TIFFSwabLong(&nextdir);
+	if (!WriteOK(tif, &nextdir, sizeof (uint32))) {
+		TIFFErrorExt(tif->tif_clientdata, module, "Error writing directory link");
+		return (0);
+	}
+	/*
+	 * Leave directory state setup safely.  We don't have
+	 * facilities for doing inserting and removing directories,
+	 * so it's safest to just invalidate everything.  This
+	 * means that the caller can only append to the directory
+	 * chain.
+	 */
+	(*tif->tif_cleanup)(tif);
+	if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata) {
+		_TIFFfree(tif->tif_rawdata);
+		tif->tif_rawdata = NULL;
+		tif->tif_rawcc = 0;
+	}
+	tif->tif_flags &= ~(TIFF_BEENWRITING|TIFF_BUFFERSETUP|TIFF_POSTENCODE);
+	TIFFFreeDirectory(tif);
+	TIFFDefaultDirectory(tif);
+	tif->tif_diroff = 0;			/* force link on next write */
+	tif->tif_nextdiroff = 0;		/* next write must be at end */
+	tif->tif_curoff = 0;
+	tif->tif_row = (uint32) -1;
+	tif->tif_curstrip = (tstrip_t) -1;
+	return (1);
+}
+
+/*			[BFC]
+ *
+ * Author: Bruce Cameron <cameron@petris.com>
+ *
+ * Set a table of tags that are to be replaced during directory process by the
+ * 'IGNORE' state - or return TRUE/FALSE for the requested tag such that
+ * 'ReadDirectory' can use the stored information.
+ *
+ * FIXME: this is never used properly. Should be removed in the future.
+ */
+int
+TIFFReassignTagToIgnore (enum TIFFIgnoreSense task, int TIFFtagID)
+{
+    static int TIFFignoretags [FIELD_LAST];
+    static int tagcount = 0 ;
+    int		i;					/* Loop index */
+    int		j;					/* Loop index */
+
+    switch (task)
+    {
+      case TIS_STORE:
+        if ( tagcount < (FIELD_LAST - 1) )
+        {
+            for ( j = 0 ; j < tagcount ; ++j )
+            {					/* Do not add duplicate tag */
+                if ( TIFFignoretags [j] == TIFFtagID )
+                    return (TRUE) ;
+            }
+            TIFFignoretags [tagcount++] = TIFFtagID ;
+            return (TRUE) ;
+        }
+        break ;
+        
+      case TIS_EXTRACT:
+        for ( i = 0 ; i < tagcount ; ++i )
+        {
+            if ( TIFFignoretags [i] == TIFFtagID )
+                return (TRUE) ;
+        }
+        break;
+        
+      case TIS_EMPTY:
+        tagcount = 0 ;			/* Clear the list */
+        return (TRUE) ;
+        
+      default:
+        break;
+    }
+    
+    return (FALSE);
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_dir.h b/Utilities/GDAL/frmts/gtiff/libtiff/tif_dir.h
new file mode 100644
index 0000000000..eb1d43c11c
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_dir.h
@@ -0,0 +1,202 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _TIFFDIR_
+#define	_TIFFDIR_
+/*
+ * ``Library-private'' Directory-related Definitions.
+ */
+
+/*
+ * Internal format of a TIFF directory entry.
+ */
+typedef	struct {
+#define	FIELD_SETLONGS	4
+	/* bit vector of fields that are set */
+	unsigned long	td_fieldsset[FIELD_SETLONGS];
+
+	uint32  td_imagewidth, td_imagelength, td_imagedepth;
+	uint32  td_tilewidth, td_tilelength, td_tiledepth;
+	uint32  td_subfiletype;
+	uint16  td_bitspersample;
+	uint16  td_sampleformat;
+	uint16  td_compression;
+	uint16  td_photometric;
+	uint16  td_threshholding;
+	uint16  td_fillorder;
+	uint16  td_orientation;
+	uint16  td_samplesperpixel;
+	uint32  td_rowsperstrip;
+	uint16  td_minsamplevalue, td_maxsamplevalue;
+	double  td_sminsamplevalue, td_smaxsamplevalue;
+	float   td_xresolution, td_yresolution;
+	uint16  td_resolutionunit;
+	uint16  td_planarconfig;
+	float   td_xposition, td_yposition;
+	uint16  td_pagenumber[2];
+	uint16* td_colormap[3];
+	uint16  td_halftonehints[2];
+	uint16  td_extrasamples;
+	uint16* td_sampleinfo;
+	/* even though the name is misleading, td_stripsperimage is the number
+	 * of striles (=strips or tiles) per plane, and td_nstrips the total
+	 * number of striles */
+	tstrile_t td_stripsperimage;
+	tstrile_t td_nstrips;            /* size of offset & bytecount arrays */
+	toff_t* td_stripoffset;
+	toff_t* td_stripbytecount;
+	int     td_stripbytecountsorted; /* is the bytecount array sorted ascending? */
+	uint16  td_nsubifd;
+	uint32* td_subifd;
+	/* YCbCr parameters */
+	uint16  td_ycbcrsubsampling[2];
+	uint16  td_ycbcrpositioning;
+	/* Colorimetry parameters */
+	uint16* td_transferfunction[3];
+	/* CMYK parameters */
+	int     td_inknameslen;
+	char*   td_inknames;
+
+	int     td_customValueCount;
+        TIFFTagValue *td_customValues;
+} TIFFDirectory;
+
+/*
+ * Field flags used to indicate fields that have
+ * been set in a directory, and to reference fields
+ * when manipulating a directory.
+ */
+
+/*
+ * FIELD_IGNORE is used to signify tags that are to
+ * be processed but otherwise ignored.  This permits
+ * antiquated tags to be quietly read and discarded.
+ * Note that a bit *is* allocated for ignored tags;
+ * this is understood by the directory reading logic
+ * which uses this fact to avoid special-case handling
+ */ 
+#define	FIELD_IGNORE			0
+
+/* multi-item fields */
+#define	FIELD_IMAGEDIMENSIONS		1
+#define FIELD_TILEDIMENSIONS		2
+#define	FIELD_RESOLUTION		3
+#define	FIELD_POSITION			4
+
+/* single-item fields */
+#define	FIELD_SUBFILETYPE		5
+#define	FIELD_BITSPERSAMPLE		6
+#define	FIELD_COMPRESSION		7
+#define	FIELD_PHOTOMETRIC		8
+#define	FIELD_THRESHHOLDING		9
+#define	FIELD_FILLORDER			10
+#define	FIELD_ORIENTATION		15
+#define	FIELD_SAMPLESPERPIXEL		16
+#define	FIELD_ROWSPERSTRIP		17
+#define	FIELD_MINSAMPLEVALUE		18
+#define	FIELD_MAXSAMPLEVALUE		19
+#define	FIELD_PLANARCONFIG		20
+#define	FIELD_RESOLUTIONUNIT		22
+#define	FIELD_PAGENUMBER		23
+#define	FIELD_STRIPBYTECOUNTS		24
+#define	FIELD_STRIPOFFSETS		25
+#define	FIELD_COLORMAP			26
+#define	FIELD_EXTRASAMPLES		31
+#define FIELD_SAMPLEFORMAT		32
+#define	FIELD_SMINSAMPLEVALUE		33
+#define	FIELD_SMAXSAMPLEVALUE		34
+#define FIELD_IMAGEDEPTH		35
+#define FIELD_TILEDEPTH			36
+#define	FIELD_HALFTONEHINTS		37
+#define FIELD_YCBCRSUBSAMPLING		39
+#define FIELD_YCBCRPOSITIONING		40
+#define	FIELD_TRANSFERFUNCTION		44
+#define	FIELD_INKNAMES			46
+#define	FIELD_SUBIFD			49
+/*      FIELD_CUSTOM (see tiffio.h)     65 */
+/* end of support for well-known tags; codec-private tags follow */
+#define	FIELD_CODEC			66	/* base of codec-private tags */
+
+
+/*
+ * Pseudo-tags don't normally need field bits since they
+ * are not written to an output file (by definition).
+ * The library also has express logic to always query a
+ * codec for a pseudo-tag so allocating a field bit for
+ * one is a waste.   If codec wants to promote the notion
+ * of a pseudo-tag being ``set'' or ``unset'' then it can
+ * do using internal state flags without polluting the
+ * field bit space defined for real tags.
+ */
+#define	FIELD_PSEUDO			0
+
+#define	FIELD_LAST			(32*FIELD_SETLONGS-1)
+
+#define	TIFFExtractData(tif, type, v) \
+    ((uint32) ((tif)->tif_header.tiff_magic == TIFF_BIGENDIAN ? \
+        ((v) >> (tif)->tif_typeshift[type]) & (tif)->tif_typemask[type] : \
+	(v) & (tif)->tif_typemask[type]))
+#define	TIFFInsertData(tif, type, v) \
+    ((uint32) ((tif)->tif_header.tiff_magic == TIFF_BIGENDIAN ? \
+        ((v) & (tif)->tif_typemask[type]) << (tif)->tif_typeshift[type] : \
+	(v) & (tif)->tif_typemask[type]))
+
+
+#define BITn(n)				(((unsigned long)1L)<<((n)&0x1f)) 
+#define BITFIELDn(tif, n)		((tif)->tif_dir.td_fieldsset[(n)/32]) 
+#define TIFFFieldSet(tif, field)	(BITFIELDn(tif, field) & BITn(field)) 
+#define TIFFSetFieldBit(tif, field)	(BITFIELDn(tif, field) |= BITn(field))
+#define TIFFClrFieldBit(tif, field)	(BITFIELDn(tif, field) &= ~BITn(field))
+
+#define	FieldSet(fields, f)		(fields[(f)/32] & BITn(f))
+#define	ResetFieldBit(fields, f)	(fields[(f)/32] &= ~BITn(f))
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+extern	const TIFFFieldInfo *_TIFFGetFieldInfo(size_t *);
+extern	const TIFFFieldInfo *_TIFFGetExifFieldInfo(size_t *);
+extern	void _TIFFSetupFieldInfo(TIFF*, const TIFFFieldInfo[], size_t);
+extern	void _TIFFPrintFieldInfo(TIFF*, FILE*);
+extern	TIFFDataType _TIFFSampleToTagType(TIFF*);
+extern  const TIFFFieldInfo* _TIFFFindOrRegisterFieldInfo( TIFF *tif,
+							   ttag_t tag,
+							   TIFFDataType dt );
+extern  TIFFFieldInfo* _TIFFCreateAnonFieldInfo( TIFF *tif, ttag_t tag,
+                                                 TIFFDataType dt );
+
+#define _TIFFMergeFieldInfo	    TIFFMergeFieldInfo
+#define _TIFFFindFieldInfo	    TIFFFindFieldInfo
+#define _TIFFFindFieldInfoByName    TIFFFindFieldInfoByName
+#define _TIFFFieldWithTag	    TIFFFieldWithTag
+#define _TIFFFieldWithName	    TIFFFieldWithName
+
+#if defined(__cplusplus)
+}
+#endif
+#endif /* _TIFFDIR_ */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_dirinfo.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_dirinfo.c
new file mode 100644
index 0000000000..60fd39eafd
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_dirinfo.c
@@ -0,0 +1,846 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ *
+ * Core Directory Tag Support.
+ */
+#include "tiffiop.h"
+#include <stdlib.h>
+
+/*
+ * NB: NB: THIS ARRAY IS ASSUMED TO BE SORTED BY TAG.
+ *       If a tag can have both LONG and SHORT types then the LONG must be
+ *       placed before the SHORT for writing to work properly.
+ *
+ * NOTE: The second field (field_readcount) and third field (field_writecount)
+ *       sometimes use the values TIFF_VARIABLE (-1), TIFF_VARIABLE2 (-3)
+ *       and TIFFTAG_SPP (-2). The macros should be used but would throw off 
+ *       the formatting of the code, so please interprete the -1, -2 and -3 
+ *       values accordingly.
+ */
+static const TIFFFieldInfo
+tiffFieldInfo[] = {
+    { TIFFTAG_SUBFILETYPE,	 1, 1,	TIFF_LONG,	FIELD_SUBFILETYPE,
+      1,	0,	"SubfileType" },
+/* XXX SHORT for compatibility w/ old versions of the library */
+    { TIFFTAG_SUBFILETYPE,	 1, 1,	TIFF_SHORT,	FIELD_SUBFILETYPE,
+      1,	0,	"SubfileType" },
+    { TIFFTAG_OSUBFILETYPE,	 1, 1,	TIFF_SHORT,	FIELD_SUBFILETYPE,
+      1,	0,	"OldSubfileType" },
+    { TIFFTAG_IMAGEWIDTH,	 1, 1,	TIFF_LONG,	FIELD_IMAGEDIMENSIONS,
+      0,	0,	"ImageWidth" },
+    { TIFFTAG_IMAGEWIDTH,	 1, 1,	TIFF_SHORT,	FIELD_IMAGEDIMENSIONS,
+      0,	0,	"ImageWidth" },
+    { TIFFTAG_IMAGELENGTH,	 1, 1,	TIFF_LONG,	FIELD_IMAGEDIMENSIONS,
+      1,	0,	"ImageLength" },
+    { TIFFTAG_IMAGELENGTH,	 1, 1,	TIFF_SHORT,	FIELD_IMAGEDIMENSIONS,
+      1,	0,	"ImageLength" },
+    { TIFFTAG_BITSPERSAMPLE,	-1,-1,	TIFF_SHORT,	FIELD_BITSPERSAMPLE,
+      0,	0,	"BitsPerSample" },
+/* XXX LONG for compatibility with some broken TIFF writers */
+    { TIFFTAG_BITSPERSAMPLE,	-1,-1,	TIFF_LONG,	FIELD_BITSPERSAMPLE,
+      0,	0,	"BitsPerSample" },
+    { TIFFTAG_COMPRESSION,	-1, 1,	TIFF_SHORT,	FIELD_COMPRESSION,
+      0,	0,	"Compression" },
+/* XXX LONG for compatibility with some broken TIFF writers */
+    { TIFFTAG_COMPRESSION,	-1, 1,	TIFF_LONG,	FIELD_COMPRESSION,
+      0,	0,	"Compression" },
+    { TIFFTAG_PHOTOMETRIC,	 1, 1,	TIFF_SHORT,	FIELD_PHOTOMETRIC,
+      0,	0,	"PhotometricInterpretation" },
+/* XXX LONG for compatibility with some broken TIFF writers */
+    { TIFFTAG_PHOTOMETRIC,	 1, 1,	TIFF_LONG,	FIELD_PHOTOMETRIC,
+      0,	0,	"PhotometricInterpretation" },
+    { TIFFTAG_THRESHHOLDING,	 1, 1,	TIFF_SHORT,	FIELD_THRESHHOLDING,
+      1,	0,	"Threshholding" },
+    { TIFFTAG_CELLWIDTH,	 1, 1,	TIFF_SHORT,	FIELD_IGNORE,
+      1,	0,	"CellWidth" },
+    { TIFFTAG_CELLLENGTH,	 1, 1,	TIFF_SHORT,	FIELD_IGNORE,
+      1,	0,	"CellLength" },
+    { TIFFTAG_FILLORDER,	 1, 1,	TIFF_SHORT,	FIELD_FILLORDER,
+      0,	0,	"FillOrder" },
+    { TIFFTAG_DOCUMENTNAME,	-1,-1,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"DocumentName" },
+    { TIFFTAG_IMAGEDESCRIPTION,	-1,-1,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"ImageDescription" },
+    { TIFFTAG_MAKE,		-1,-1,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"Make" },
+    { TIFFTAG_MODEL,		-1,-1,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"Model" },
+    { TIFFTAG_STRIPOFFSETS,	-1,-1,	TIFF_LONG,	FIELD_STRIPOFFSETS,
+      0,	0,	"StripOffsets" },
+    { TIFFTAG_STRIPOFFSETS,	-1,-1,	TIFF_SHORT,	FIELD_STRIPOFFSETS,
+      0,	0,	"StripOffsets" },
+    { TIFFTAG_ORIENTATION,	 1, 1,	TIFF_SHORT,	FIELD_ORIENTATION,
+      0,	0,	"Orientation" },
+    { TIFFTAG_SAMPLESPERPIXEL,	 1, 1,	TIFF_SHORT,	FIELD_SAMPLESPERPIXEL,
+      0,	0,	"SamplesPerPixel" },
+    { TIFFTAG_ROWSPERSTRIP,	 1, 1,	TIFF_LONG,	FIELD_ROWSPERSTRIP,
+      0,	0,	"RowsPerStrip" },
+    { TIFFTAG_ROWSPERSTRIP,	 1, 1,	TIFF_SHORT,	FIELD_ROWSPERSTRIP,
+      0,	0,	"RowsPerStrip" },
+    { TIFFTAG_STRIPBYTECOUNTS,	-1,-1,	TIFF_LONG,	FIELD_STRIPBYTECOUNTS,
+      0,	0,	"StripByteCounts" },
+    { TIFFTAG_STRIPBYTECOUNTS,	-1,-1,	TIFF_SHORT,	FIELD_STRIPBYTECOUNTS,
+      0,	0,	"StripByteCounts" },
+    { TIFFTAG_MINSAMPLEVALUE,	-2,-1,	TIFF_SHORT,	FIELD_MINSAMPLEVALUE,
+      1,	0,	"MinSampleValue" },
+    { TIFFTAG_MAXSAMPLEVALUE,	-2,-1,	TIFF_SHORT,	FIELD_MAXSAMPLEVALUE,
+      1,	0,	"MaxSampleValue" },
+    { TIFFTAG_XRESOLUTION,	 1, 1,	TIFF_RATIONAL,	FIELD_RESOLUTION,
+      1,	0,	"XResolution" },
+    { TIFFTAG_YRESOLUTION,	 1, 1,	TIFF_RATIONAL,	FIELD_RESOLUTION,
+      1,	0,	"YResolution" },
+    { TIFFTAG_PLANARCONFIG,	 1, 1,	TIFF_SHORT,	FIELD_PLANARCONFIG,
+      0,	0,	"PlanarConfiguration" },
+    { TIFFTAG_PAGENAME,		-1,-1,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"PageName" },
+    { TIFFTAG_XPOSITION,	 1, 1,	TIFF_RATIONAL,	FIELD_POSITION,
+      1,	0,	"XPosition" },
+    { TIFFTAG_YPOSITION,	 1, 1,	TIFF_RATIONAL,	FIELD_POSITION,
+      1,	0,	"YPosition" },
+    { TIFFTAG_FREEOFFSETS,	-1,-1,	TIFF_LONG,	FIELD_IGNORE,
+      0,	0,	"FreeOffsets" },
+    { TIFFTAG_FREEBYTECOUNTS,	-1,-1,	TIFF_LONG,	FIELD_IGNORE,
+      0,	0,	"FreeByteCounts" },
+    { TIFFTAG_GRAYRESPONSEUNIT,	 1, 1,	TIFF_SHORT,	FIELD_IGNORE,
+      1,	0,	"GrayResponseUnit" },
+    { TIFFTAG_GRAYRESPONSECURVE,-1,-1,	TIFF_SHORT,	FIELD_IGNORE,
+      1,	0,	"GrayResponseCurve" },
+    { TIFFTAG_RESOLUTIONUNIT,	 1, 1,	TIFF_SHORT,	FIELD_RESOLUTIONUNIT,
+      1,	0,	"ResolutionUnit" },
+    { TIFFTAG_PAGENUMBER,	 2, 2,	TIFF_SHORT,	FIELD_PAGENUMBER,
+      1,	0,	"PageNumber" },
+    { TIFFTAG_COLORRESPONSEUNIT, 1, 1,	TIFF_SHORT,	FIELD_IGNORE,
+      1,	0,	"ColorResponseUnit" },
+    { TIFFTAG_TRANSFERFUNCTION,	-1,-1,	TIFF_SHORT,	FIELD_TRANSFERFUNCTION,
+      1,	0,	"TransferFunction" },
+    { TIFFTAG_SOFTWARE,		-1,-1,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"Software" },
+    { TIFFTAG_DATETIME,		20,20,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"DateTime" },
+    { TIFFTAG_ARTIST,		-1,-1,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"Artist" },
+    { TIFFTAG_HOSTCOMPUTER,	-1,-1,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"HostComputer" },
+    { TIFFTAG_WHITEPOINT,	 2, 2,	TIFF_RATIONAL,	FIELD_CUSTOM,
+      1,	0,	"WhitePoint" },
+    { TIFFTAG_PRIMARYCHROMATICITIES,6,6,TIFF_RATIONAL,	FIELD_CUSTOM,
+      1,	0,	"PrimaryChromaticities" },
+    { TIFFTAG_COLORMAP,		-1,-1,	TIFF_SHORT,	FIELD_COLORMAP,
+      1,	0,	"ColorMap" },
+    { TIFFTAG_HALFTONEHINTS,	 2, 2,	TIFF_SHORT,	FIELD_HALFTONEHINTS,
+      1,	0,	"HalftoneHints" },
+    { TIFFTAG_TILEWIDTH,	 1, 1,	TIFF_LONG,	FIELD_TILEDIMENSIONS,
+      0,	0,	"TileWidth" },
+    { TIFFTAG_TILEWIDTH,	 1, 1,	TIFF_SHORT,	FIELD_TILEDIMENSIONS,
+      0,	0,	"TileWidth" },
+    { TIFFTAG_TILELENGTH,	 1, 1,	TIFF_LONG,	FIELD_TILEDIMENSIONS,
+      0,	0,	"TileLength" },
+    { TIFFTAG_TILELENGTH,	 1, 1,	TIFF_SHORT,	FIELD_TILEDIMENSIONS,
+      0,	0,	"TileLength" },
+    { TIFFTAG_TILEOFFSETS,	-1, 1,	TIFF_LONG,	FIELD_STRIPOFFSETS,
+      0,	0,	"TileOffsets" },
+    { TIFFTAG_TILEBYTECOUNTS,	-1, 1,	TIFF_LONG,	FIELD_STRIPBYTECOUNTS,
+      0,	0,	"TileByteCounts" },
+    { TIFFTAG_TILEBYTECOUNTS,	-1, 1,	TIFF_SHORT,	FIELD_STRIPBYTECOUNTS,
+      0,	0,	"TileByteCounts" },
+    { TIFFTAG_SUBIFD,		-1,-1,	TIFF_IFD,	FIELD_SUBIFD,
+      1,	1,	"SubIFD" },
+    { TIFFTAG_SUBIFD,		-1,-1,	TIFF_LONG,	FIELD_SUBIFD,
+      1,	1,	"SubIFD" },
+    { TIFFTAG_INKSET,		 1, 1,	TIFF_SHORT,	FIELD_CUSTOM,
+      0,	0,	"InkSet" },
+    { TIFFTAG_INKNAMES,		-1,-1,	TIFF_ASCII,	FIELD_INKNAMES,
+      1,	1,	"InkNames" },
+    { TIFFTAG_NUMBEROFINKS,	 1, 1,	TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"NumberOfInks" },
+    { TIFFTAG_DOTRANGE,		 2, 2,	TIFF_SHORT,	FIELD_CUSTOM,
+      0,	0,	"DotRange" },
+    { TIFFTAG_DOTRANGE,		 2, 2,	TIFF_BYTE,	FIELD_CUSTOM,
+      0,	0,	"DotRange" },
+    { TIFFTAG_TARGETPRINTER,	-1,-1,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"TargetPrinter" },
+    { TIFFTAG_EXTRASAMPLES,	-1,-1,	TIFF_SHORT,	FIELD_EXTRASAMPLES,
+      0,	1,	"ExtraSamples" },
+/* XXX for bogus Adobe Photoshop v2.5 files */
+    { TIFFTAG_EXTRASAMPLES,	-1,-1,	TIFF_BYTE,	FIELD_EXTRASAMPLES,
+      0,	1,	"ExtraSamples" },
+    { TIFFTAG_SAMPLEFORMAT,	-1,-1,	TIFF_SHORT,	FIELD_SAMPLEFORMAT,
+      0,	0,	"SampleFormat" },
+    { TIFFTAG_SMINSAMPLEVALUE,	-2,-1,	TIFF_ANY,	FIELD_SMINSAMPLEVALUE,
+      1,	0,	"SMinSampleValue" },
+    { TIFFTAG_SMAXSAMPLEVALUE,	-2,-1,	TIFF_ANY,	FIELD_SMAXSAMPLEVALUE,
+      1,	0,	"SMaxSampleValue" },
+    { TIFFTAG_CLIPPATH,		-1, -3, TIFF_BYTE,	FIELD_CUSTOM,
+      0,	1,	"ClipPath" },
+    { TIFFTAG_XCLIPPATHUNITS,	 1, 1,	TIFF_SLONG,	FIELD_CUSTOM,
+      0,	0,	"XClipPathUnits" },
+    { TIFFTAG_XCLIPPATHUNITS,	 1, 1,	TIFF_SSHORT,	FIELD_CUSTOM,
+      0,	0,	"XClipPathUnits" },
+    { TIFFTAG_XCLIPPATHUNITS,	 1, 1,	TIFF_SBYTE,	FIELD_CUSTOM,
+      0,	0,	"XClipPathUnits" },
+    { TIFFTAG_YCLIPPATHUNITS,	 1, 1,	TIFF_SLONG,	FIELD_CUSTOM,
+      0,	0,	"YClipPathUnits" },
+    { TIFFTAG_YCLIPPATHUNITS,	 1, 1,	TIFF_SSHORT,	FIELD_CUSTOM,
+      0,	0,	"YClipPathUnits" },
+    { TIFFTAG_YCLIPPATHUNITS,	 1, 1,	TIFF_SBYTE,	FIELD_CUSTOM,
+      0,	0,	"YClipPathUnits" },
+    { TIFFTAG_YCBCRCOEFFICIENTS, 3, 3,	TIFF_RATIONAL,	FIELD_CUSTOM,
+      0,	0,	"YCbCrCoefficients" },
+    { TIFFTAG_YCBCRSUBSAMPLING,	 2, 2,	TIFF_SHORT,	FIELD_YCBCRSUBSAMPLING,
+      0,	0,	"YCbCrSubsampling" },
+    { TIFFTAG_YCBCRPOSITIONING,	 1, 1,	TIFF_SHORT,	FIELD_YCBCRPOSITIONING,
+      0,	0,	"YCbCrPositioning" },
+    { TIFFTAG_REFERENCEBLACKWHITE, 6, 6, TIFF_RATIONAL,	FIELD_CUSTOM,
+      1,	0,	"ReferenceBlackWhite" },
+/* XXX temporarily accept LONG for backwards compatibility */
+    { TIFFTAG_REFERENCEBLACKWHITE, 6, 6, TIFF_LONG,	FIELD_CUSTOM,
+      1,	0,	"ReferenceBlackWhite" },
+    { TIFFTAG_XMLPACKET,	-3,-3,	TIFF_BYTE,	FIELD_CUSTOM,
+      0,	1,	"XMLPacket" },
+/* begin SGI tags */
+    { TIFFTAG_MATTEING,		 1, 1,	TIFF_SHORT,	FIELD_EXTRASAMPLES,
+      0,	0,	"Matteing" },
+    { TIFFTAG_DATATYPE,		-2,-1,	TIFF_SHORT,	FIELD_SAMPLEFORMAT,
+      0,	0,	"DataType" },
+    { TIFFTAG_IMAGEDEPTH,	 1, 1,	TIFF_LONG,	FIELD_IMAGEDEPTH,
+      0,	0,	"ImageDepth" },
+    { TIFFTAG_IMAGEDEPTH,	 1, 1,	TIFF_SHORT,	FIELD_IMAGEDEPTH,
+      0,	0,	"ImageDepth" },
+    { TIFFTAG_TILEDEPTH,	 1, 1,	TIFF_LONG,	FIELD_TILEDEPTH,
+      0,	0,	"TileDepth" },
+    { TIFFTAG_TILEDEPTH,	 1, 1,	TIFF_SHORT,	FIELD_TILEDEPTH,
+      0,	0,	"TileDepth" },
+/* end SGI tags */
+/* begin Pixar tags */
+    { TIFFTAG_PIXAR_IMAGEFULLWIDTH,  1, 1, TIFF_LONG,	FIELD_CUSTOM,
+      1,	0,	"ImageFullWidth" },
+    { TIFFTAG_PIXAR_IMAGEFULLLENGTH, 1, 1, TIFF_LONG,	FIELD_CUSTOM,
+      1,	0,	"ImageFullLength" },
+    { TIFFTAG_PIXAR_TEXTUREFORMAT,  -1, -1, TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"TextureFormat" },
+    { TIFFTAG_PIXAR_WRAPMODES,	    -1, -1, TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"TextureWrapModes" },
+    { TIFFTAG_PIXAR_FOVCOT,	     1, 1, TIFF_FLOAT,	FIELD_CUSTOM,
+      1,	0,	"FieldOfViewCotangent" },
+    { TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN,	16,16,	TIFF_FLOAT,
+      FIELD_CUSTOM,	1,	0,	"MatrixWorldToScreen" },
+    { TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA,	16,16,	TIFF_FLOAT,
+       FIELD_CUSTOM,	1,	0,	"MatrixWorldToCamera" },
+    { TIFFTAG_COPYRIGHT,	-1, -1,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"Copyright" },
+/* end Pixar tags */
+    { TIFFTAG_RICHTIFFIPTC, -3, -3,	TIFF_LONG,	FIELD_CUSTOM, 
+      0,    1,   "RichTIFFIPTC" },
+    { TIFFTAG_PHOTOSHOP,    -3, -3,	TIFF_BYTE,	FIELD_CUSTOM, 
+      0,    1,   "Photoshop" },
+    { TIFFTAG_EXIFIFD,		1, 1,	TIFF_LONG,	FIELD_CUSTOM,
+      0,	0,	"EXIFIFDOffset" },
+    { TIFFTAG_ICCPROFILE,	-3, -3,	TIFF_UNDEFINED,	FIELD_CUSTOM,
+      0,	1,	"ICC Profile" },
+    { TIFFTAG_GPSIFD,		1, 1,	TIFF_LONG,	FIELD_CUSTOM,
+      0,	0,	"GPSIFDOffset" },
+    { TIFFTAG_STONITS,		 1, 1,	TIFF_DOUBLE,	FIELD_CUSTOM,
+      0,	0,	"StoNits" },
+    { TIFFTAG_INTEROPERABILITYIFD, 1, 1, TIFF_LONG,	FIELD_CUSTOM,
+      0,	0,	"InteroperabilityIFDOffset" },
+/* begin DNG tags */
+    { TIFFTAG_DNGVERSION,	4, 4,	TIFF_BYTE,	FIELD_CUSTOM, 
+      0,	0,	"DNGVersion" },
+    { TIFFTAG_DNGBACKWARDVERSION, 4, 4,	TIFF_BYTE,	FIELD_CUSTOM, 
+      0,	0,	"DNGBackwardVersion" },
+    { TIFFTAG_UNIQUECAMERAMODEL,    -1, -1, TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"UniqueCameraModel" },
+    { TIFFTAG_LOCALIZEDCAMERAMODEL, -1, -1, TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"LocalizedCameraModel" },
+    { TIFFTAG_LOCALIZEDCAMERAMODEL, -1, -1, TIFF_BYTE,	FIELD_CUSTOM,
+      1,	1,	"LocalizedCameraModel" },
+    { TIFFTAG_CFAPLANECOLOR,	-1, -1,	TIFF_BYTE,	FIELD_CUSTOM, 
+      0,	1,	"CFAPlaneColor" },
+    { TIFFTAG_CFALAYOUT,	1, 1,	TIFF_SHORT,	FIELD_CUSTOM, 
+      0,	0,	"CFALayout" },
+    { TIFFTAG_LINEARIZATIONTABLE, -1, -1, TIFF_SHORT,	FIELD_CUSTOM, 
+      0,	1,	"LinearizationTable" },
+    { TIFFTAG_BLACKLEVELREPEATDIM, 2, 2, TIFF_SHORT,	FIELD_CUSTOM, 
+      0,	0,	"BlackLevelRepeatDim" },
+    { TIFFTAG_BLACKLEVEL,	-1, -1,	TIFF_LONG,	FIELD_CUSTOM, 
+      0,	1,	"BlackLevel" },
+    { TIFFTAG_BLACKLEVEL,	-1, -1,	TIFF_SHORT,	FIELD_CUSTOM, 
+      0,	1,	"BlackLevel" },
+    { TIFFTAG_BLACKLEVEL,	-1, -1,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	1,	"BlackLevel" },
+    { TIFFTAG_BLACKLEVELDELTAH,	-1, -1,	TIFF_SRATIONAL,	FIELD_CUSTOM, 
+      0,	1,	"BlackLevelDeltaH" },
+    { TIFFTAG_BLACKLEVELDELTAV,	-1, -1,	TIFF_SRATIONAL,	FIELD_CUSTOM, 
+      0,	1,	"BlackLevelDeltaV" },
+    { TIFFTAG_WHITELEVEL,	-2, -2,	TIFF_LONG,	FIELD_CUSTOM, 
+      0,	0,	"WhiteLevel" },
+    { TIFFTAG_WHITELEVEL,	-2, -2,	TIFF_SHORT,	FIELD_CUSTOM, 
+      0,	0,	"WhiteLevel" },
+    { TIFFTAG_DEFAULTSCALE,	2, 2,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	0,	"DefaultScale" },
+    { TIFFTAG_BESTQUALITYSCALE,	1, 1,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	0,	"BestQualityScale" },
+    { TIFFTAG_DEFAULTCROPORIGIN,	2, 2,	TIFF_LONG,	FIELD_CUSTOM, 
+      0,	0,	"DefaultCropOrigin" },
+    { TIFFTAG_DEFAULTCROPORIGIN,	2, 2,	TIFF_SHORT,	FIELD_CUSTOM, 
+      0,	0,	"DefaultCropOrigin" },
+    { TIFFTAG_DEFAULTCROPORIGIN,	2, 2,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	0,	"DefaultCropOrigin" },
+    { TIFFTAG_DEFAULTCROPSIZE,	2, 2,	TIFF_LONG,	FIELD_CUSTOM, 
+      0,	0,	"DefaultCropSize" },
+    { TIFFTAG_DEFAULTCROPSIZE,	2, 2,	TIFF_SHORT,	FIELD_CUSTOM, 
+      0,	0,	"DefaultCropSize" },
+    { TIFFTAG_DEFAULTCROPSIZE,	2, 2,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	0,	"DefaultCropSize" },
+    { TIFFTAG_COLORMATRIX1,	-1, -1,	TIFF_SRATIONAL,	FIELD_CUSTOM, 
+      0,	1,	"ColorMatrix1" },
+    { TIFFTAG_COLORMATRIX2,	-1, -1,	TIFF_SRATIONAL,	FIELD_CUSTOM, 
+      0,	1,	"ColorMatrix2" },
+    { TIFFTAG_CAMERACALIBRATION1,	-1, -1,	TIFF_SRATIONAL,	FIELD_CUSTOM, 
+      0,	1,	"CameraCalibration1" },
+    { TIFFTAG_CAMERACALIBRATION2,	-1, -1,	TIFF_SRATIONAL,	FIELD_CUSTOM, 
+      0,	1,	"CameraCalibration2" },
+    { TIFFTAG_REDUCTIONMATRIX1,	-1, -1,	TIFF_SRATIONAL,	FIELD_CUSTOM, 
+      0,	1,	"ReductionMatrix1" },
+    { TIFFTAG_REDUCTIONMATRIX2,	-1, -1,	TIFF_SRATIONAL,	FIELD_CUSTOM, 
+      0,	1,	"ReductionMatrix2" },
+    { TIFFTAG_ANALOGBALANCE,	-1, -1,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	1,	"AnalogBalance" },
+    { TIFFTAG_ASSHOTNEUTRAL,	-1, -1,	TIFF_SHORT,	FIELD_CUSTOM, 
+      0,	1,	"AsShotNeutral" },
+    { TIFFTAG_ASSHOTNEUTRAL,	-1, -1,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	1,	"AsShotNeutral" },
+    { TIFFTAG_ASSHOTWHITEXY,	2, 2,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	0,	"AsShotWhiteXY" },
+    { TIFFTAG_BASELINEEXPOSURE,	1, 1,	TIFF_SRATIONAL,	FIELD_CUSTOM, 
+      0,	0,	"BaselineExposure" },
+    { TIFFTAG_BASELINENOISE,	1, 1,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	0,	"BaselineNoise" },
+    { TIFFTAG_BASELINESHARPNESS,	1, 1,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	0,	"BaselineSharpness" },
+    { TIFFTAG_BAYERGREENSPLIT,	1, 1,	TIFF_LONG,	FIELD_CUSTOM, 
+      0,	0,	"BayerGreenSplit" },
+    { TIFFTAG_LINEARRESPONSELIMIT,	1, 1,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	0,	"LinearResponseLimit" },
+    { TIFFTAG_CAMERASERIALNUMBER,    -1, -1, TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"CameraSerialNumber" },
+    { TIFFTAG_LENSINFO,	4, 4,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	0,	"LensInfo" },
+    { TIFFTAG_CHROMABLURRADIUS,	1, 1,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	0,	"ChromaBlurRadius" },
+    { TIFFTAG_ANTIALIASSTRENGTH,	1, 1,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	0,	"AntiAliasStrength" },
+    { TIFFTAG_SHADOWSCALE,	1, 1,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      0,	0,	"ShadowScale" },
+    { TIFFTAG_DNGPRIVATEDATA,    -1, -1, TIFF_BYTE,	FIELD_CUSTOM,
+      0,	1,	"DNGPrivateData" },
+    { TIFFTAG_MAKERNOTESAFETY,	1, 1,	TIFF_SHORT,	FIELD_CUSTOM, 
+      0,	0,	"MakerNoteSafety" },
+    { TIFFTAG_CALIBRATIONILLUMINANT1,	1, 1,	TIFF_SHORT,	FIELD_CUSTOM, 
+      0,	0,	"CalibrationIlluminant1" },
+    { TIFFTAG_CALIBRATIONILLUMINANT2,	1, 1,	TIFF_SHORT,	FIELD_CUSTOM, 
+      0,	0,	"CalibrationIlluminant2" },
+    { TIFFTAG_RAWDATAUNIQUEID,	16, 16,	TIFF_BYTE,	FIELD_CUSTOM, 
+      0,	0,	"RawDataUniqueID" },
+    { TIFFTAG_ORIGINALRAWFILENAME,    -1, -1, TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"OriginalRawFileName" },
+    { TIFFTAG_ORIGINALRAWFILENAME,    -1, -1, TIFF_BYTE,	FIELD_CUSTOM,
+      1,	1,	"OriginalRawFileName" },
+    { TIFFTAG_ORIGINALRAWFILEDATA,    -1, -1, TIFF_UNDEFINED,	FIELD_CUSTOM,
+      0,	1,	"OriginalRawFileData" },
+    { TIFFTAG_ACTIVEAREA,	4, 4,	TIFF_LONG,	FIELD_CUSTOM, 
+      0,	0,	"ActiveArea" },
+    { TIFFTAG_ACTIVEAREA,	4, 4,	TIFF_SHORT,	FIELD_CUSTOM, 
+      0,	0,	"ActiveArea" },
+    { TIFFTAG_MASKEDAREAS,	-1, -1,	TIFF_LONG,	FIELD_CUSTOM, 
+      0,	1,	"MaskedAreas" },
+    { TIFFTAG_ASSHOTICCPROFILE,    -1, -1, TIFF_UNDEFINED,	FIELD_CUSTOM,
+      0,	1,	"AsShotICCProfile" },
+    { TIFFTAG_ASSHOTPREPROFILEMATRIX,	-1, -1,	TIFF_SRATIONAL,	FIELD_CUSTOM, 
+      0,	1,	"AsShotPreProfileMatrix" },
+    { TIFFTAG_CURRENTICCPROFILE,    -1, -1, TIFF_UNDEFINED,	FIELD_CUSTOM,
+      0,	1,	"CurrentICCProfile" },
+    { TIFFTAG_CURRENTPREPROFILEMATRIX,	-1, -1,	TIFF_SRATIONAL,	FIELD_CUSTOM, 
+      0,	1,	"CurrentPreProfileMatrix" },
+/* end DNG tags */
+};
+
+static const TIFFFieldInfo
+exifFieldInfo[] = {
+    { EXIFTAG_EXPOSURETIME,	1, 1,		TIFF_RATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"ExposureTime" },
+    { EXIFTAG_FNUMBER,		1, 1,		TIFF_RATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"FNumber" },
+    { EXIFTAG_EXPOSUREPROGRAM,	1, 1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"ExposureProgram" },
+    { EXIFTAG_SPECTRALSENSITIVITY,    -1, -1,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"SpectralSensitivity" },
+    { EXIFTAG_ISOSPEEDRATINGS,  -1, -1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	1,	"ISOSpeedRatings" },
+    { EXIFTAG_OECF,	-1, -1,			TIFF_UNDEFINED,	FIELD_CUSTOM,
+      1,	1,	"OptoelectricConversionFactor" },
+    { EXIFTAG_EXIFVERSION,	4, 4,		TIFF_UNDEFINED,	FIELD_CUSTOM,
+      1,	0,	"ExifVersion" },
+    { EXIFTAG_DATETIMEORIGINAL,	20, 20,		TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"DateTimeOriginal" },
+    { EXIFTAG_DATETIMEDIGITIZED, 20, 20,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"DateTimeDigitized" },
+    { EXIFTAG_COMPONENTSCONFIGURATION,	 4, 4,	TIFF_UNDEFINED,	FIELD_CUSTOM,
+      1,	0,	"ComponentsConfiguration" },
+    { EXIFTAG_COMPRESSEDBITSPERPIXEL,	 1, 1,	TIFF_RATIONAL,	FIELD_CUSTOM,
+      1,	0,	"CompressedBitsPerPixel" },
+    { EXIFTAG_SHUTTERSPEEDVALUE,	1, 1,	TIFF_SRATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"ShutterSpeedValue" },
+    { EXIFTAG_APERTUREVALUE,	1, 1,		TIFF_RATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"ApertureValue" },
+    { EXIFTAG_BRIGHTNESSVALUE,	1, 1,		TIFF_SRATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"BrightnessValue" },
+    { EXIFTAG_EXPOSUREBIASVALUE,	1, 1,	TIFF_SRATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"ExposureBiasValue" },
+    { EXIFTAG_MAXAPERTUREVALUE,	1, 1,		TIFF_RATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"MaxApertureValue" },
+    { EXIFTAG_SUBJECTDISTANCE,	1, 1,		TIFF_RATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"SubjectDistance" },
+    { EXIFTAG_METERINGMODE,	1, 1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"MeteringMode" },
+    { EXIFTAG_LIGHTSOURCE,	1, 1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"LightSource" },
+    { EXIFTAG_FLASH,	1, 1,			TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"Flash" },
+    { EXIFTAG_FOCALLENGTH,	1, 1,		TIFF_RATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"FocalLength" },
+    { EXIFTAG_SUBJECTAREA,	-1, -1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	1,	"SubjectArea" },
+    { EXIFTAG_MAKERNOTE,	-1, -1,		TIFF_UNDEFINED,	FIELD_CUSTOM,
+      1,	1,	"MakerNote" },
+    { EXIFTAG_USERCOMMENT,	-1, -1,		TIFF_UNDEFINED,	FIELD_CUSTOM,
+      1,	1,	"UserComment" },
+    { EXIFTAG_SUBSECTIME,    -1, -1,		TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"SubSecTime" },
+    { EXIFTAG_SUBSECTIMEORIGINAL, -1, -1,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"SubSecTimeOriginal" },
+    { EXIFTAG_SUBSECTIMEDIGITIZED,-1, -1,	TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"SubSecTimeDigitized" },
+    { EXIFTAG_FLASHPIXVERSION,	4, 4,		TIFF_UNDEFINED,	FIELD_CUSTOM,
+      1,	0,	"FlashpixVersion" },
+    { EXIFTAG_PIXELXDIMENSION,	1, 1,		TIFF_LONG,	FIELD_CUSTOM,
+      1,	0,	"PixelXDimension" },
+    { EXIFTAG_PIXELXDIMENSION,	1, 1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"PixelXDimension" },
+    { EXIFTAG_PIXELYDIMENSION,	1, 1,		TIFF_LONG,	FIELD_CUSTOM,
+      1,	0,	"PixelYDimension" },
+    { EXIFTAG_PIXELYDIMENSION,	1, 1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"PixelYDimension" },
+    { EXIFTAG_RELATEDSOUNDFILE,	13, 13,		TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"RelatedSoundFile" },
+    { EXIFTAG_FLASHENERGY,	1, 1,		TIFF_RATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"FlashEnergy" },
+    { EXIFTAG_SPATIALFREQUENCYRESPONSE,	-1, -1,	TIFF_UNDEFINED,	FIELD_CUSTOM,
+      1,	1,	"SpatialFrequencyResponse" },
+    { EXIFTAG_FOCALPLANEXRESOLUTION,	1, 1,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"FocalPlaneXResolution" },
+    { EXIFTAG_FOCALPLANEYRESOLUTION,	1, 1,	TIFF_RATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"FocalPlaneYResolution" },
+    { EXIFTAG_FOCALPLANERESOLUTIONUNIT,	1, 1,	TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"FocalPlaneResolutionUnit" },
+    { EXIFTAG_SUBJECTLOCATION,	2, 2,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"SubjectLocation" },
+    { EXIFTAG_EXPOSUREINDEX,	1, 1,		TIFF_RATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"ExposureIndex" },
+    { EXIFTAG_SENSINGMETHOD,	1, 1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"SensingMethod" },
+    { EXIFTAG_FILESOURCE,	1, 1,		TIFF_UNDEFINED,	FIELD_CUSTOM,
+      1,	0,	"FileSource" },
+    { EXIFTAG_SCENETYPE,	1, 1,		TIFF_UNDEFINED,	FIELD_CUSTOM,
+      1,	0,	"SceneType" },
+    { EXIFTAG_CFAPATTERN,	-1, -1,		TIFF_UNDEFINED,	FIELD_CUSTOM,
+      1,	1,	"CFAPattern" },
+    { EXIFTAG_CUSTOMRENDERED,	1, 1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"CustomRendered" },
+    { EXIFTAG_EXPOSUREMODE,	1, 1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"ExposureMode" },
+    { EXIFTAG_WHITEBALANCE,	1, 1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"WhiteBalance" },
+    { EXIFTAG_DIGITALZOOMRATIO,	1, 1,		TIFF_RATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"DigitalZoomRatio" },
+    { EXIFTAG_FOCALLENGTHIN35MMFILM, 1, 1,	TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"FocalLengthIn35mmFilm" },
+    { EXIFTAG_SCENECAPTURETYPE,	1, 1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"SceneCaptureType" },
+    { EXIFTAG_GAINCONTROL,	1, 1,		TIFF_RATIONAL,	FIELD_CUSTOM, 
+      1,	0,	"GainControl" },
+    { EXIFTAG_CONTRAST,		1, 1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"Contrast" },
+    { EXIFTAG_SATURATION,	1, 1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"Saturation" },
+    { EXIFTAG_SHARPNESS,	1, 1,		TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"Sharpness" },
+    { EXIFTAG_DEVICESETTINGDESCRIPTION,	-1, -1,	TIFF_UNDEFINED,	FIELD_CUSTOM,
+      1,	1,	"DeviceSettingDescription" },
+    { EXIFTAG_SUBJECTDISTANCERANGE, 1, 1,	TIFF_SHORT,	FIELD_CUSTOM,
+      1,	0,	"SubjectDistanceRange" },
+    { EXIFTAG_IMAGEUNIQUEID,	33, 33,		TIFF_ASCII,	FIELD_CUSTOM,
+      1,	0,	"ImageUniqueID" }
+};
+
+const TIFFFieldInfo *
+_TIFFGetFieldInfo(size_t *size)
+{
+	*size = TIFFArrayCount(tiffFieldInfo);
+	return tiffFieldInfo;
+}
+
+const TIFFFieldInfo *
+_TIFFGetExifFieldInfo(size_t *size)
+{
+	*size = TIFFArrayCount(exifFieldInfo);
+	return exifFieldInfo;
+}
+
+void
+_TIFFSetupFieldInfo(TIFF* tif, const TIFFFieldInfo info[], size_t n)
+{
+	if (tif->tif_fieldinfo) {
+		size_t  i;
+
+		for (i = 0; i < tif->tif_nfields; i++) 
+		{
+			TIFFFieldInfo *fld = tif->tif_fieldinfo[i];
+			if (fld->field_bit == FIELD_CUSTOM && 
+				strncmp("Tag ", fld->field_name, 4) == 0) {
+					_TIFFfree(fld->field_name);
+					_TIFFfree(fld);
+				}
+		}   
+      
+		_TIFFfree(tif->tif_fieldinfo);
+		tif->tif_nfields = 0;
+	}
+	_TIFFMergeFieldInfo(tif, info, n);
+}
+
+static int
+tagCompare(const void* a, const void* b)
+{
+	const TIFFFieldInfo* ta = *(const TIFFFieldInfo**) a;
+	const TIFFFieldInfo* tb = *(const TIFFFieldInfo**) b;
+	/* NB: be careful of return values for 16-bit platforms */
+	if (ta->field_tag != tb->field_tag)
+		return (ta->field_tag < tb->field_tag ? -1 : 1);
+	else
+		return ((int)tb->field_type - (int)ta->field_type);
+}
+
+static int
+tagNameCompare(const void* a, const void* b)
+{
+	const TIFFFieldInfo* ta = *(const TIFFFieldInfo**) a;
+	const TIFFFieldInfo* tb = *(const TIFFFieldInfo**) b;
+
+        return strcmp(ta->field_name, tb->field_name);
+}
+
+void
+_TIFFMergeFieldInfo(TIFF* tif, const TIFFFieldInfo info[], int n)
+{
+	TIFFFieldInfo** tp;
+	int i;
+
+        tif->tif_foundfield = NULL;
+
+	if (tif->tif_nfields > 0) {
+		tif->tif_fieldinfo = (TIFFFieldInfo**)
+		    _TIFFrealloc(tif->tif_fieldinfo,
+			(tif->tif_nfields+n) * sizeof (TIFFFieldInfo*));
+	} else {
+		tif->tif_fieldinfo = (TIFFFieldInfo**)
+		    _TIFFmalloc(n * sizeof (TIFFFieldInfo*));
+	}
+	assert(tif->tif_fieldinfo != NULL);
+	tp = tif->tif_fieldinfo + tif->tif_nfields;
+	for (i = 0; i < n; i++)
+		*tp++ = (TIFFFieldInfo*) (info + i);	/* XXX */
+
+        /* Sort the field info by tag number */
+        qsort(tif->tif_fieldinfo, tif->tif_nfields += n,
+	      sizeof (TIFFFieldInfo*), tagCompare);
+}
+
+void
+_TIFFPrintFieldInfo(TIFF* tif, FILE* fd)
+{
+	size_t i;
+
+	fprintf(fd, "%s: \n", tif->tif_name);
+	for (i = 0; i < tif->tif_nfields; i++) {
+		const TIFFFieldInfo* fip = tif->tif_fieldinfo[i];
+		fprintf(fd, "field[%2d] %5lu, %2d, %2d, %d, %2d, %5s, %5s, %s\n"
+			, (int)i
+			, (unsigned long) fip->field_tag
+			, fip->field_readcount, fip->field_writecount
+			, fip->field_type
+			, fip->field_bit
+			, fip->field_oktochange ? "TRUE" : "FALSE"
+			, fip->field_passcount ? "TRUE" : "FALSE"
+			, fip->field_name
+		);
+	}
+}
+
+/*
+ * Return size of TIFFDataType in bytes
+ */
+int
+TIFFDataWidth(TIFFDataType type)
+{
+	switch(type)
+	{
+	case 0:  /* nothing */
+	case 1:  /* TIFF_BYTE */
+	case 2:  /* TIFF_ASCII */
+	case 6:  /* TIFF_SBYTE */
+	case 7:  /* TIFF_UNDEFINED */
+		return 1;
+	case 3:  /* TIFF_SHORT */
+	case 8:  /* TIFF_SSHORT */
+		return 2;
+	case 4:  /* TIFF_LONG */
+	case 9:  /* TIFF_SLONG */
+	case 11: /* TIFF_FLOAT */
+        case 13: /* TIFF_IFD */
+		return 4;
+	case 5:  /* TIFF_RATIONAL */
+	case 10: /* TIFF_SRATIONAL */
+	case 12: /* TIFF_DOUBLE */
+		return 8;
+	default:
+		return 0; /* will return 0 for unknown types */
+	}
+}
+
+/*
+ * Return size of TIFFDataType in bytes.
+ *
+ * XXX: We need a separate function to determine the space needed
+ * to store the value. For TIFF_RATIONAL values TIFFDataWidth() returns 8,
+ * but we use 4-byte float to represent rationals.
+ */
+int
+_TIFFDataSize(TIFFDataType type)
+{
+	switch (type) {
+		case TIFF_BYTE:
+		case TIFF_SBYTE:
+		case TIFF_ASCII:
+		case TIFF_UNDEFINED:
+		    return 1;
+		case TIFF_SHORT:
+		case TIFF_SSHORT:
+		    return 2;
+		case TIFF_LONG:
+		case TIFF_SLONG:
+		case TIFF_FLOAT:
+		case TIFF_IFD:
+		case TIFF_RATIONAL:
+		case TIFF_SRATIONAL:
+		    return 4;
+		case TIFF_DOUBLE:
+		    return 8;
+		default:
+		    return 0;
+	}
+}
+
+/*
+ * Return nearest TIFFDataType to the sample type of an image.
+ */
+TIFFDataType
+_TIFFSampleToTagType(TIFF* tif)
+{
+	uint32 bps = TIFFhowmany8(tif->tif_dir.td_bitspersample);
+
+	switch (tif->tif_dir.td_sampleformat) {
+	case SAMPLEFORMAT_IEEEFP:
+		return (bps == 4 ? TIFF_FLOAT : TIFF_DOUBLE);
+	case SAMPLEFORMAT_INT:
+		return (bps <= 1 ? TIFF_SBYTE :
+		    bps <= 2 ? TIFF_SSHORT : TIFF_SLONG);
+	case SAMPLEFORMAT_UINT:
+		return (bps <= 1 ? TIFF_BYTE :
+		    bps <= 2 ? TIFF_SHORT : TIFF_LONG);
+	case SAMPLEFORMAT_VOID:
+		return (TIFF_UNDEFINED);
+	}
+	/*NOTREACHED*/
+	return (TIFF_UNDEFINED);
+}
+
+const TIFFFieldInfo*
+_TIFFFindFieldInfo(TIFF* tif, ttag_t tag, TIFFDataType dt)
+{
+	int i, n;
+
+	if (tif->tif_foundfield && tif->tif_foundfield->field_tag == tag &&
+	    (dt == TIFF_ANY || dt == tif->tif_foundfield->field_type))
+		return (tif->tif_foundfield);
+	/* NB: use sorted search (e.g. binary search) */
+	if(dt != TIFF_ANY) {
+            TIFFFieldInfo key = {0, 0, 0, TIFF_NOTYPE, 0, 0, 0, 0};
+	    TIFFFieldInfo* pkey = &key;
+	    const TIFFFieldInfo **ret;
+
+	    key.field_tag = tag;
+            key.field_type = dt;
+
+	    ret = (const TIFFFieldInfo **) bsearch(&pkey,
+						   tif->tif_fieldinfo, 
+						   tif->tif_nfields,
+						   sizeof(TIFFFieldInfo *), 
+						   tagCompare);
+	    return (ret) ? (*ret) : NULL;
+        } else for (i = 0, n = tif->tif_nfields; i < n; i++) {
+		const TIFFFieldInfo* fip = tif->tif_fieldinfo[i];
+		if (fip->field_tag == tag &&
+		    (dt == TIFF_ANY || fip->field_type == dt))
+			return (tif->tif_foundfield = fip);
+	}
+	return ((const TIFFFieldInfo *)0);
+}
+
+const TIFFFieldInfo*
+_TIFFFindFieldInfoByName(TIFF* tif, const char *field_name, TIFFDataType dt)
+{
+	int i, n;
+
+	if (tif->tif_foundfield
+	    && streq(tif->tif_foundfield->field_name, field_name)
+	    && (dt == TIFF_ANY || dt == tif->tif_foundfield->field_type))
+		return (tif->tif_foundfield);
+	/* NB: use sorted search (e.g. binary search) */
+	if(dt != TIFF_ANY) {
+            TIFFFieldInfo key = {0, 0, 0, TIFF_NOTYPE, 0, 0, 0, 0};
+	    TIFFFieldInfo* pkey = &key;
+	    const TIFFFieldInfo **ret;
+
+            key.field_name = (char *)field_name;
+            key.field_type = dt;
+
+            ret = (const TIFFFieldInfo **) lfind(&pkey,
+						 tif->tif_fieldinfo, 
+						 &tif->tif_nfields,
+						 sizeof(TIFFFieldInfo *),
+						 tagNameCompare);
+	    return (ret) ? (*ret) : NULL;
+        } else
+		for (i = 0, n = tif->tif_nfields; i < n; i++) {
+			const TIFFFieldInfo* fip = tif->tif_fieldinfo[i];
+			if (streq(fip->field_name, field_name) &&
+			    (dt == TIFF_ANY || fip->field_type == dt))
+				return (tif->tif_foundfield = fip);
+		}
+	return ((const TIFFFieldInfo *)0);
+}
+
+const TIFFFieldInfo*
+_TIFFFieldWithTag(TIFF* tif, ttag_t tag)
+{
+	const TIFFFieldInfo* fip = _TIFFFindFieldInfo(tif, tag, TIFF_ANY);
+	if (!fip) {
+		TIFFErrorExt(tif->tif_clientdata, "TIFFFieldWithTag",
+			  "Internal error, unknown tag 0x%x",
+                          (unsigned int) tag);
+		assert(fip != NULL);
+		/*NOTREACHED*/
+	}
+	return (fip);
+}
+
+const TIFFFieldInfo*
+_TIFFFieldWithName(TIFF* tif, const char *field_name)
+{
+	const TIFFFieldInfo* fip =
+		_TIFFFindFieldInfoByName(tif, field_name, TIFF_ANY);
+	if (!fip) {
+		TIFFErrorExt(tif->tif_clientdata, "TIFFFieldWithName",
+			  "Internal error, unknown tag %s", field_name);
+		assert(fip != NULL);
+		/*NOTREACHED*/
+	}
+	return (fip);
+}
+
+const TIFFFieldInfo*
+_TIFFFindOrRegisterFieldInfo( TIFF *tif, ttag_t tag, TIFFDataType dt )
+
+{
+    const TIFFFieldInfo *fld;
+
+    fld = _TIFFFindFieldInfo( tif, tag, dt );
+    if( fld == NULL )
+    {
+        fld = _TIFFCreateAnonFieldInfo( tif, tag, dt );
+        _TIFFMergeFieldInfo( tif, fld, 1 );
+    }
+
+    return fld;
+}
+
+TIFFFieldInfo*
+_TIFFCreateAnonFieldInfo(TIFF *tif, ttag_t tag, TIFFDataType field_type)
+{
+	TIFFFieldInfo *fld;
+	(void) tif;
+
+	fld = (TIFFFieldInfo *) _TIFFmalloc(sizeof (TIFFFieldInfo));
+	if (fld == NULL)
+	    return NULL;
+	_TIFFmemset( fld, 0, sizeof(TIFFFieldInfo) );
+
+	fld->field_tag = tag;
+	fld->field_readcount = TIFF_VARIABLE;
+	fld->field_writecount = TIFF_VARIABLE;
+	fld->field_type = field_type;
+	fld->field_bit = FIELD_CUSTOM;
+	fld->field_oktochange = TRUE;
+	fld->field_passcount = TRUE;
+	fld->field_name = (char *) _TIFFmalloc(32);
+	if (fld->field_name == NULL) {
+	    _TIFFfree(fld);
+	    return NULL;
+	}
+
+	/* note that this name is a special sign to TIFFClose() and
+	 * _TIFFSetupFieldInfo() to free the field
+	 */
+	sprintf(fld->field_name, "Tag %d", (int) tag);
+
+	return fld;    
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_dirread.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_dirread.c
new file mode 100644
index 0000000000..bc97e3de22
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_dirread.c
@@ -0,0 +1,1898 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ *
+ * Directory Read Support Routines.
+ */
+#include "tiffiop.h"
+
+#define	IGNORE	0		/* tag placeholder used below */
+
+#ifdef HAVE_IEEEFP
+# define	TIFFCvtIEEEFloatToNative(tif, n, fp)
+# define	TIFFCvtIEEEDoubleToNative(tif, n, dp)
+#else
+extern	void TIFFCvtIEEEFloatToNative(TIFF*, uint32, float*);
+extern	void TIFFCvtIEEEDoubleToNative(TIFF*, uint32, double*);
+#endif
+
+static  TIFFDirEntry* TIFFReadDirectoryFind(TIFFDirEntry* dir, uint16 dircount, uint16 tagid);
+static	int EstimateStripByteCounts(TIFF*, TIFFDirEntry*, uint16);
+static	void MissingRequired(TIFF*, const char*);
+static	int CheckDirCount(TIFF*, TIFFDirEntry*, uint32);
+static	tsize_t TIFFFetchData(TIFF*, TIFFDirEntry*, char*);
+static	tsize_t TIFFFetchString(TIFF*, TIFFDirEntry*, char*);
+static	float TIFFFetchRational(TIFF*, TIFFDirEntry*);
+static	int TIFFFetchNormalTag(TIFF*, TIFFDirEntry*);
+static	int TIFFFetchPerSampleShorts(TIFF*, TIFFDirEntry*, uint16*);
+static	int TIFFFetchPerSampleLongs(TIFF*, TIFFDirEntry*, uint32*);
+static	int TIFFFetchPerSampleAnys(TIFF*, TIFFDirEntry*, double*);
+static	int TIFFFetchShortArray(TIFF*, TIFFDirEntry*, uint16*);
+static	int TIFFFetchStripThing(TIFF*, TIFFDirEntry*, long, uint32**);
+static	int TIFFFetchRefBlackWhite(TIFF*, TIFFDirEntry*);
+static	float TIFFFetchFloat(TIFF*, TIFFDirEntry*);
+static	int TIFFFetchFloatArray(TIFF*, TIFFDirEntry*, float*);
+static	int TIFFFetchDoubleArray(TIFF*, TIFFDirEntry*, double*);
+static	int TIFFFetchAnyArray(TIFF*, TIFFDirEntry*, double*);
+static	int TIFFFetchShortPair(TIFF*, TIFFDirEntry*);
+static	void ChopUpSingleUncompressedStrip(TIFF*);
+
+/*
+ * Read the next TIFF directory from a file
+ * and convert it to the internal format.
+ * We read directories sequentially.
+ */
+int
+TIFFReadDirectory(TIFF* tif)
+{
+	static const char module[] = "TIFFReadDirectory";
+
+	int n;
+	TIFFDirectory* td;
+	TIFFDirEntry *dp, *dir = NULL;
+	uint16 iv;
+	uint32 v;
+	const TIFFFieldInfo* fip;
+	size_t fix;
+	uint16 dircount;
+	toff_t nextdiroff;
+	int diroutoforderwarning = 0;
+	toff_t* new_dirlist;
+
+	tif->tif_diroff = tif->tif_nextdiroff;
+	if (tif->tif_diroff == 0)		/* no more directories */
+		return (0);
+
+	/*
+	 * XXX: Trick to prevent IFD looping. The one can create TIFF file
+	 * with looped directory pointers. We will maintain a list of already
+	 * seen directories and check every IFD offset against this list.
+	 */
+	for (n = 0; n < tif->tif_dirnumber; n++) {
+		if (tif->tif_dirlist[n] == tif->tif_diroff)
+			return (0);
+	}
+	tif->tif_dirnumber++;
+	new_dirlist = (toff_t *)_TIFFrealloc(tif->tif_dirlist,
+					tif->tif_dirnumber * sizeof(toff_t));
+	if (!new_dirlist) {
+		TIFFErrorExt(tif->tif_clientdata, module,
+			  "%s: Failed to allocate space for IFD list",
+			  tif->tif_name);
+		return (0);
+	}
+	tif->tif_dirlist = new_dirlist;
+	tif->tif_dirlist[tif->tif_dirnumber - 1] = tif->tif_diroff;
+
+	/*
+	 * Cleanup any previous compression state.
+	 */
+	(*tif->tif_cleanup)(tif);
+	tif->tif_curdir++;
+	nextdiroff = 0;
+	if (!isMapped(tif)) {
+		if (!SeekOK(tif, tif->tif_diroff)) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+			    "%s: Seek error accessing TIFF directory",
+			    tif->tif_name);
+			return (0);
+		}
+		if (!ReadOK(tif, &dircount, sizeof (uint16))) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+			    "%s: Can not read TIFF directory count",
+			    tif->tif_name);
+			return (0);
+		}
+		if (tif->tif_flags & TIFF_SWAB)
+			TIFFSwabShort(&dircount);
+		dir = (TIFFDirEntry *)_TIFFCheckMalloc(tif,
+						  dircount,
+						  sizeof (TIFFDirEntry),
+						  "to read TIFF directory");
+		if (dir == NULL)
+			return (0);
+		if (!ReadOK(tif, dir, dircount*sizeof (TIFFDirEntry))) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+			    "%.100s: Can not read TIFF directory",
+			    tif->tif_name);
+			goto bad;
+		}
+		/*
+		 * Read offset to next directory for sequential scans.
+		 */
+		(void) ReadOK(tif, &nextdiroff, sizeof (uint32));
+	} else {
+		toff_t off = tif->tif_diroff;
+
+		if (off + sizeof (uint16) > tif->tif_size) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+			    "%s: Can not read TIFF directory count",
+							tif->tif_name);
+			return (0);
+		} else
+			_TIFFmemcpy(&dircount, tif->tif_base + off, sizeof (uint16));
+		off += sizeof (uint16);
+		if (tif->tif_flags & TIFF_SWAB)
+			TIFFSwabShort(&dircount);
+		dir = (TIFFDirEntry *)_TIFFCheckMalloc(tif,
+		    dircount, sizeof (TIFFDirEntry), "to read TIFF directory");
+		if (dir == NULL)
+			return (0);
+		if (off + dircount*sizeof (TIFFDirEntry) > tif->tif_size) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+			    "%s: Can not read TIFF directory",
+			    tif->tif_name);
+			goto bad;
+		} else {
+			_TIFFmemcpy(dir, tif->tif_base + off,
+			    dircount*sizeof (TIFFDirEntry));
+		}
+		off += dircount* sizeof (TIFFDirEntry);
+		if (off + sizeof (uint32) <= tif->tif_size)
+			_TIFFmemcpy(&nextdiroff, tif->tif_base+off, sizeof (uint32));
+	}
+	if (tif->tif_flags & TIFF_SWAB)
+		TIFFSwabLong(&nextdiroff);
+	tif->tif_nextdiroff = nextdiroff;
+
+	tif->tif_flags &= ~TIFF_BEENWRITING;	/* reset before new dir */
+	/*
+	 * Setup default value and then make a pass over
+	 * the fields to check type and tag information,
+	 * and to extract info required to size data
+	 * structures.  A second pass is made afterwards
+	 * to read in everthing not taken in the first pass.
+	 */
+	td = &tif->tif_dir;
+	/* free any old stuff and reinit */
+	TIFFFreeDirectory(tif);
+	TIFFDefaultDirectory(tif);
+	/*
+	 * Electronic Arts writes gray-scale TIFF files
+	 * without a PlanarConfiguration directory entry.
+	 * Thus we setup a default value here, even though
+	 * the TIFF spec says there is no default value.
+	 */
+	TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+
+	/*
+	 * Sigh, we must make a separate pass through the
+	 * directory for the following reason:
+	 *
+	 * We must process the Compression tag in the first pass
+	 * in order to merge in codec-private tag definitions (otherwise
+	 * we may get complaints about unknown tags).  However, the
+	 * Compression tag may be dependent on the SamplesPerPixel
+	 * tag value because older TIFF specs permited Compression
+	 * to be written as a SamplesPerPixel-count tag entry.
+	 * Thus if we don't first figure out the correct SamplesPerPixel
+	 * tag value then we may end up ignoring the Compression tag
+	 * value because it has an incorrect count value (if the
+	 * true value of SamplesPerPixel is not 1).
+	 *
+	 * It sure would have been nice if Aldus had really thought
+	 * this stuff through carefully.
+	 */
+	for (dp = dir, n = dircount; n > 0; n--, dp++) {
+		if (tif->tif_flags & TIFF_SWAB) {
+			TIFFSwabArrayOfShort(&dp->tdir_tag, 2);
+			TIFFSwabArrayOfLong(&dp->tdir_count, 2);
+		}
+		if (dp->tdir_tag == TIFFTAG_SAMPLESPERPIXEL) {
+			if (!TIFFFetchNormalTag(tif, dp))
+				goto bad;
+			dp->tdir_tag = IGNORE;
+		}
+	}
+	/*
+	 * First real pass over the directory.
+	 */
+	fix = 0;
+	for (dp = dir, n = dircount; n > 0; n--, dp++) {
+
+		if (fix >= tif->tif_nfields || dp->tdir_tag == IGNORE)
+			continue;
+
+		/*
+		 * Silicon Beach (at least) writes unordered
+		 * directory tags (violating the spec).  Handle
+		 * it here, but be obnoxious (maybe they'll fix it?).
+		 */
+		if (dp->tdir_tag < tif->tif_fieldinfo[fix]->field_tag) {
+			if (!diroutoforderwarning) {
+				TIFFWarningExt(tif->tif_clientdata, module,
+"%s: invalid TIFF directory; tags are not sorted in ascending order",
+					    tif->tif_name);
+				diroutoforderwarning = 1;
+			}
+			fix = 0;			/* O(n^2) */
+		}
+		while (fix < tif->tif_nfields &&
+		    tif->tif_fieldinfo[fix]->field_tag < dp->tdir_tag)
+			fix++;
+		if (fix >= tif->tif_nfields ||
+		    tif->tif_fieldinfo[fix]->field_tag != dp->tdir_tag) {
+
+					TIFFWarningExt(tif->tif_clientdata,
+						       module,
+                        "%s: unknown field with tag %d (0x%x) encountered",
+						       tif->tif_name,
+						       dp->tdir_tag,
+						       dp->tdir_tag,
+						       dp->tdir_type);
+
+                    TIFFMergeFieldInfo(tif,
+                                       _TIFFCreateAnonFieldInfo(tif,
+						dp->tdir_tag,
+						(TIFFDataType) dp->tdir_type),
+				       1 );
+                    fix = 0;
+                    while (fix < tif->tif_nfields &&
+                           tif->tif_fieldinfo[fix]->field_tag < dp->tdir_tag)
+			fix++;
+		}
+		/*
+		 * Null out old tags that we ignore.
+		 */
+		if (tif->tif_fieldinfo[fix]->field_bit == FIELD_IGNORE) {
+	ignore:
+			dp->tdir_tag = IGNORE;
+			continue;
+		}
+		/*
+		 * Check data type.
+		 */
+		fip = tif->tif_fieldinfo[fix];
+		while (dp->tdir_type != (unsigned short) fip->field_type
+		    && fix < tif->tif_nfields) {
+			if (fip->field_type == TIFF_ANY)	/* wildcard */
+				break;
+			fip = tif->tif_fieldinfo[++fix];
+			if (fix >= tif->tif_nfields ||
+			    fip->field_tag != dp->tdir_tag) {
+				TIFFWarningExt(tif->tif_clientdata, module,
+			"%s: wrong data type %d for \"%s\"; tag ignored",
+					    tif->tif_name, dp->tdir_type,
+					    tif->tif_fieldinfo[fix-1]->field_name);
+				goto ignore;
+			}
+		}
+		/*
+		 * Check count if known in advance.
+		 */
+		if (fip->field_readcount != TIFF_VARIABLE
+		    && fip->field_readcount != TIFF_VARIABLE2) {
+			uint32 expected = (fip->field_readcount == TIFF_SPP) ?
+			    (uint32) td->td_samplesperpixel :
+			    (uint32) fip->field_readcount;
+			if (!CheckDirCount(tif, dp, expected))
+				goto ignore;
+		}
+
+		switch (dp->tdir_tag) {
+		case TIFFTAG_COMPRESSION:
+			/*
+			 * The 5.0 spec says the Compression tag has
+			 * one value, while earlier specs say it has
+			 * one value per sample.  Because of this, we
+			 * accept the tag if one value is supplied.
+			 */
+			if (dp->tdir_count == 1) {
+				v = TIFFExtractData(tif,
+				    dp->tdir_type, dp->tdir_offset);
+				if (!TIFFSetField(tif, dp->tdir_tag, (uint16)v))
+					goto bad;
+				break;
+			/* XXX: workaround for broken TIFFs */
+			} else if (dp->tdir_type == TIFF_LONG) {
+				if (!TIFFFetchPerSampleLongs(tif, dp, &v) ||
+				    !TIFFSetField(tif, dp->tdir_tag, (uint16)v))
+					goto bad;
+			} else {
+				if (!TIFFFetchPerSampleShorts(tif, dp, &iv)
+				    || !TIFFSetField(tif, dp->tdir_tag, iv))
+					goto bad;
+			}
+			dp->tdir_tag = IGNORE;
+			break;
+		case TIFFTAG_STRIPOFFSETS:
+		case TIFFTAG_STRIPBYTECOUNTS:
+		case TIFFTAG_TILEOFFSETS:
+		case TIFFTAG_TILEBYTECOUNTS:
+			TIFFSetFieldBit(tif, fip->field_bit);
+			break;
+		case TIFFTAG_IMAGEWIDTH:
+		case TIFFTAG_IMAGELENGTH:
+		case TIFFTAG_IMAGEDEPTH:
+		case TIFFTAG_TILELENGTH:
+		case TIFFTAG_TILEWIDTH:
+		case TIFFTAG_TILEDEPTH:
+		case TIFFTAG_PLANARCONFIG:
+		case TIFFTAG_ROWSPERSTRIP:
+		case TIFFTAG_EXTRASAMPLES:
+			if (!TIFFFetchNormalTag(tif, dp))
+				goto bad;
+			dp->tdir_tag = IGNORE;
+			break;
+		}
+	}
+
+	/*
+	 * Joris: OJPEG hack:
+	 * If a) compression is OJPEG, b) planarconfig tag says it's separate,
+	 * c) strip offsets/bytecounts tag are both present and d) both contain exactly
+	 * one value, then we consistently find that the buggy implementation of the
+	 * buggy compression scheme matches contig planarconfig best. So we 'fix-up' the tag
+	 * here
+	 */
+	if ((td->td_compression==COMPRESSION_OJPEG) &&
+	    (td->td_planarconfig==PLANARCONFIG_SEPARATE))
+	{
+		dp=TIFFReadDirectoryFind(dir,dircount,TIFFTAG_STRIPOFFSETS);
+		if ((dp!=0) && (dp->tdir_count==1))
+		{
+			dp=TIFFReadDirectoryFind(dir,dircount,TIFFTAG_STRIPBYTECOUNTS);
+			if ((dp!=0) && (dp->tdir_count==1))
+			{
+				td->td_planarconfig=PLANARCONFIG_CONTIG;
+				TIFFWarningExt(tif->tif_clientdata,"TIFFReadDirectory","Planarconfig tag value assumed incorrect, assuming data is contig instead of chunky");
+			}
+		}
+	}
+
+	/*
+	 * Allocate directory structure and setup defaults.
+	 */
+	if (!TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS)) {
+		MissingRequired(tif, "ImageLength");
+		goto bad;
+	}
+	/* 
+	 * Setup appropriate structures (by strip or by tile)
+	 */
+	if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) {
+		td->td_nstrips = TIFFNumberOfStrips(tif);
+		td->td_tilewidth = td->td_imagewidth;
+		td->td_tilelength = td->td_rowsperstrip;
+		td->td_tiledepth = td->td_imagedepth;
+		tif->tif_flags &= ~TIFF_ISTILED;
+	} else {
+		td->td_nstrips = TIFFNumberOfTiles(tif);
+		tif->tif_flags |= TIFF_ISTILED;
+	}
+	if (!td->td_nstrips) {
+		TIFFErrorExt(tif->tif_clientdata, module,
+			     "%s: cannot handle zero number of %s",
+			     tif->tif_name, isTiled(tif) ? "tiles" : "strips");
+		goto bad;
+	}
+	td->td_stripsperimage = td->td_nstrips;
+	if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+		td->td_stripsperimage /= td->td_samplesperpixel;
+	if (!TIFFFieldSet(tif, FIELD_STRIPOFFSETS)) {
+		if ((td->td_compression==COMPRESSION_OJPEG) &&
+		    (isTiled(tif)==0) &&
+		    (td->td_nstrips==1)) {
+			/*
+			 * Joris: OJPEG hack:
+			 * If a) compression is OJPEG, b) it's not a tiled TIFF,
+			 * and c) the number of strips is 1, then we tolerate the
+			 * absence of stripoffsets tag, because, presumably, all
+			 * required data is in the JpegInterchangeFormat stream
+			 */
+			TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS);
+		} else {
+			MissingRequired(tif,
+			    isTiled(tif) ? "TileOffsets" : "StripOffsets");
+			goto bad;
+		}
+	}
+
+	/*
+	 * Second pass: extract other information.
+	 */
+	for (dp = dir, n = dircount; n > 0; n--, dp++) {
+		if (dp->tdir_tag == IGNORE)
+			continue;
+		switch (dp->tdir_tag) {
+		case TIFFTAG_MINSAMPLEVALUE:
+		case TIFFTAG_MAXSAMPLEVALUE:
+		case TIFFTAG_BITSPERSAMPLE:
+		case TIFFTAG_DATATYPE:
+		case TIFFTAG_SAMPLEFORMAT:
+			/*
+			 * The 5.0 spec says the Compression tag has
+			 * one value, while earlier specs say it has
+			 * one value per sample.  Because of this, we
+			 * accept the tag if one value is supplied.
+			 *
+			 * The MinSampleValue, MaxSampleValue, BitsPerSample
+			 * DataType and SampleFormat tags are supposed to be
+			 * written as one value/sample, but some vendors
+			 * incorrectly write one value only -- so we accept
+			 * that as well (yech). Other vendors write correct
+			 * value for NumberOfSamples, but incorrect one for
+			 * BitsPerSample and friends, and we will read this
+			 * too.
+			 */
+			if (dp->tdir_count == 1) {
+				v = TIFFExtractData(tif,
+				    dp->tdir_type, dp->tdir_offset);
+				if (!TIFFSetField(tif, dp->tdir_tag, (uint16)v))
+					goto bad;
+			/* XXX: workaround for broken TIFFs */
+			} else if (dp->tdir_tag == TIFFTAG_BITSPERSAMPLE
+				   && dp->tdir_type == TIFF_LONG) {
+				if (!TIFFFetchPerSampleLongs(tif, dp, &v) ||
+				    !TIFFSetField(tif, dp->tdir_tag, (uint16)v))
+					goto bad;
+			} else {
+				if (!TIFFFetchPerSampleShorts(tif, dp, &iv) ||
+				    !TIFFSetField(tif, dp->tdir_tag, iv))
+					goto bad;
+			}
+			break;
+		case TIFFTAG_SMINSAMPLEVALUE:
+		case TIFFTAG_SMAXSAMPLEVALUE:
+			{
+				double dv = 0.0;
+				if (!TIFFFetchPerSampleAnys(tif, dp, &dv) ||
+				    !TIFFSetField(tif, dp->tdir_tag, dv))
+					goto bad;
+			}
+			break;
+		case TIFFTAG_STRIPOFFSETS:
+		case TIFFTAG_TILEOFFSETS:
+			if (!TIFFFetchStripThing(tif, dp,
+			    td->td_nstrips, &td->td_stripoffset))
+				goto bad;
+			break;
+		case TIFFTAG_STRIPBYTECOUNTS:
+		case TIFFTAG_TILEBYTECOUNTS:
+			if (!TIFFFetchStripThing(tif, dp,
+			    td->td_nstrips, &td->td_stripbytecount))
+				goto bad;
+			break;
+		case TIFFTAG_COLORMAP:
+		case TIFFTAG_TRANSFERFUNCTION:
+			{
+				char* cp;
+				/*
+				 * TransferFunction can have either 1x or 3x
+				 * data values; Colormap can have only 3x
+				 * items.
+				 */
+				v = 1L<<td->td_bitspersample;
+				if (dp->tdir_tag == TIFFTAG_COLORMAP ||
+				    dp->tdir_count != v) {
+					if (!CheckDirCount(tif, dp, 3 * v))
+						break;
+				}
+				v *= sizeof(uint16);
+				cp = (char *)_TIFFCheckMalloc(tif,
+							      dp->tdir_count,
+							      sizeof (uint16),
+					"to read \"TransferFunction\" tag");
+				if (cp != NULL) {
+					if (TIFFFetchData(tif, dp, cp)) {
+						/*
+						 * This deals with there being
+						 * only one array to apply to
+						 * all samples.
+						 */
+						uint32 c = 1L << td->td_bitspersample;
+						if (dp->tdir_count == c)
+							v = 0L;
+						TIFFSetField(tif, dp->tdir_tag,
+						    cp, cp+v, cp+2*v);
+					}
+					_TIFFfree(cp);
+				}
+				break;
+			}
+		case TIFFTAG_PAGENUMBER:
+		case TIFFTAG_HALFTONEHINTS:
+		case TIFFTAG_YCBCRSUBSAMPLING:
+		case TIFFTAG_DOTRANGE:
+			(void) TIFFFetchShortPair(tif, dp);
+			break;
+		case TIFFTAG_REFERENCEBLACKWHITE:
+			(void) TIFFFetchRefBlackWhite(tif, dp);
+			break;
+/* BEGIN REV 4.0 COMPATIBILITY */
+		case TIFFTAG_OSUBFILETYPE:
+			v = 0L;
+			switch (TIFFExtractData(tif, dp->tdir_type,
+			    dp->tdir_offset)) {
+			case OFILETYPE_REDUCEDIMAGE:
+				v = FILETYPE_REDUCEDIMAGE;
+				break;
+			case OFILETYPE_PAGE:
+				v = FILETYPE_PAGE;
+				break;
+			}
+			if (v)
+				TIFFSetField(tif, TIFFTAG_SUBFILETYPE, v);
+			break;
+/* END REV 4.0 COMPATIBILITY */
+		default:
+			(void) TIFFFetchNormalTag(tif, dp);
+			break;
+		}
+	}
+	/*
+	 * Joris: OJPEG hack:
+	 * - If a) compression is OJPEG, and b) photometric tag is missing,
+	 * then we consistently find that photometric should be YCbCr
+	 * - If a) compression is OJPEG, and b) photometric tag says it's RGB,
+	 * then we consistently find that the buggy implementation of the
+	 * buggy compression scheme matches photometric YCbCr instead.
+	 * - If a) compression is OJPEG, and b) bitspersample tag is missing,
+	 * then we consistently find bitspersample should be 8.
+	 * - If a) compression is OJPEG, b) samplesperpixel tag is missing,
+	 * and c) photometric is RGB or YCbCr, then we consistently find
+	 * samplesperpixel should be 3
+	 * - If a) compression is OJPEG, b) samplesperpixel tag is missing,
+	 * and c) photometric is MINISWHITE or MINISBLACK, then we consistently
+	 * find samplesperpixel should be 3
+	 */
+	if (td->td_compression==COMPRESSION_OJPEG)
+	{
+		if (!TIFFFieldSet(tif,FIELD_PHOTOMETRIC))
+		{
+			TIFFWarningExt(tif->tif_clientdata,"TIFFReadDirectory","Photometric tag is missing, assuming data is YCbCr");
+			if (!TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,PHOTOMETRIC_YCBCR))
+				goto bad;
+		}
+		else if (td->td_photometric==PHOTOMETRIC_RGB)
+		{
+			td->td_photometric=PHOTOMETRIC_YCBCR;
+			TIFFWarningExt(tif->tif_clientdata,"TIFFReadDirectory","Photometric tag value assumed incorrect, assuming data is YCbCr instead of RGB");
+		}
+		if (!TIFFFieldSet(tif,FIELD_BITSPERSAMPLE))
+		{
+			TIFFWarningExt(tif->tif_clientdata,"TIFFReadDirectory","BitsPerSample tag is missing, assuming 8 bits per sample");
+			if (!TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,8))
+				goto bad;
+		}
+		if (!TIFFFieldSet(tif,FIELD_SAMPLESPERPIXEL))
+		{
+			if ((td->td_photometric==PHOTOMETRIC_RGB) || (td->td_photometric==PHOTOMETRIC_YCBCR))
+			{
+				TIFFWarningExt(tif->tif_clientdata,"TIFFReadDirectory","SamplesPerPixel tag is missing, assuming correct SamplesPerPixel value is 3");
+				if (!TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,3))
+					goto bad;
+			}
+			else if ((td->td_photometric==PHOTOMETRIC_MINISWHITE) || (td->td_photometric==PHOTOMETRIC_MINISBLACK))
+			{
+				TIFFWarningExt(tif->tif_clientdata,"TIFFReadDirectory","SamplesPerPixel tag is missing, assuming correct SamplesPerPixel value is 1");
+				if (!TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,1))
+					goto bad;
+			}
+		}
+	}
+	/*
+	 * Verify Palette image has a Colormap.
+	 */
+	if (td->td_photometric == PHOTOMETRIC_PALETTE &&
+	    !TIFFFieldSet(tif, FIELD_COLORMAP)) {
+		MissingRequired(tif, "Colormap");
+		goto bad;
+	}
+	/*
+	 * Joris: OJPEG hack:
+	 * We do no further messing with strip/tile offsets/bytecounts in OJPEG
+	 * TIFFs
+	 */
+	if (td->td_compression!=COMPRESSION_OJPEG)
+	{
+		/*
+		 * Attempt to deal with a missing StripByteCounts tag.
+		 */
+		if (!TIFFFieldSet(tif, FIELD_STRIPBYTECOUNTS)) {
+			/*
+			 * Some manufacturers violate the spec by not giving
+			 * the size of the strips.  In this case, assume there
+			 * is one uncompressed strip of data.
+			 */
+			if ((td->td_planarconfig == PLANARCONFIG_CONTIG &&
+			    td->td_nstrips > 1) ||
+			    (td->td_planarconfig == PLANARCONFIG_SEPARATE &&
+			     td->td_nstrips != td->td_samplesperpixel)) {
+			    MissingRequired(tif, "StripByteCounts");
+			    goto bad;
+			}
+			TIFFWarningExt(tif->tif_clientdata, module,
+				"%s: TIFF directory is missing required "
+				"\"%s\" field, calculating from imagelength",
+				tif->tif_name,
+				_TIFFFieldWithTag(tif,TIFFTAG_STRIPBYTECOUNTS)->field_name);
+			if (EstimateStripByteCounts(tif, dir, dircount) < 0)
+			    goto bad;
+		/*
+		 * Assume we have wrong StripByteCount value (in case of single strip) in
+		 * following cases:
+		 *   - it is equal to zero along with StripOffset;
+		 *   - it is larger than file itself (in case of uncompressed image);
+		 *   - it is smaller than the size of the bytes per row multiplied on the
+		 *     number of rows.  The last case should not be checked in the case of
+		 *     writing new image, because we may do not know the exact strip size
+		 *     until the whole image will be written and directory dumped out.
+		 */
+		#define	BYTECOUNTLOOKSBAD \
+		    ( (td->td_stripbytecount[0] == 0 && td->td_stripoffset[0] != 0) || \
+		      (td->td_compression == COMPRESSION_NONE && \
+		       td->td_stripbytecount[0] > TIFFGetFileSize(tif) - td->td_stripoffset[0]) || \
+		      (tif->tif_mode == O_RDONLY && \
+		       td->td_compression == COMPRESSION_NONE && \
+		       td->td_stripbytecount[0] < TIFFScanlineSize(tif) * td->td_imagelength) )
+
+		} else if (td->td_nstrips == 1
+			   && td->td_stripoffset[0] != 0
+			   && BYTECOUNTLOOKSBAD) {
+			/*
+			 * XXX: Plexus (and others) sometimes give a value of zero for
+			 * a tag when they don't know what the correct value is!  Try
+			 * and handle the simple case of estimating the size of a one
+			 * strip image.
+			 */
+			TIFFWarningExt(tif->tif_clientdata, module,
+		"%s: Bogus \"%s\" field, ignoring and calculating from imagelength",
+				    tif->tif_name,
+				    _TIFFFieldWithTag(tif,TIFFTAG_STRIPBYTECOUNTS)->field_name);
+			if(EstimateStripByteCounts(tif, dir, dircount) < 0)
+			    goto bad;
+		} else if (td->td_planarconfig == PLANARCONFIG_CONTIG
+			   && td->td_nstrips > 2
+			   && td->td_compression == COMPRESSION_NONE
+			   && td->td_stripbytecount[0] != td->td_stripbytecount[1]) {
+			/*
+			 * XXX: Some vendors fill StripByteCount array with absolutely
+			 * wrong values (it can be equal to StripOffset array, for
+			 * example). Catch this case here.
+			 */
+			TIFFWarningExt(tif->tif_clientdata, module,
+		"%s: Wrong \"%s\" field, ignoring and calculating from imagelength",
+				    tif->tif_name,
+				    _TIFFFieldWithTag(tif,TIFFTAG_STRIPBYTECOUNTS)->field_name);
+			if (EstimateStripByteCounts(tif, dir, dircount) < 0)
+			    goto bad;
+		}
+	}
+	if (dir) {
+		_TIFFfree((char *)dir);
+		dir = NULL;
+	}
+	if (!TIFFFieldSet(tif, FIELD_MAXSAMPLEVALUE))
+		td->td_maxsamplevalue = (uint16)((1L<<td->td_bitspersample)-1);
+	/*
+	 * Setup default compression scheme.
+	 */
+
+	/*
+	 * XXX: We can optimize checking for the strip bounds using the sorted
+	 * bytecounts array. See also comments for TIFFAppendToStrip()
+	 * function in tif_write.c.
+	 */
+	if (td->td_nstrips > 1) {
+		tstrip_t strip;
+
+		td->td_stripbytecountsorted = 1;
+		for (strip = 1; strip < td->td_nstrips; strip++) {
+			if (td->td_stripoffset[strip - 1] >
+			    td->td_stripoffset[strip]) {
+				td->td_stripbytecountsorted = 0;
+				break;
+			}
+		}
+	}
+
+	if (!TIFFFieldSet(tif, FIELD_COMPRESSION))
+		TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
+	/*
+	 * Some manufacturers make life difficult by writing
+	 * large amounts of uncompressed data as a single strip.
+	 * This is contrary to the recommendations of the spec.
+	 * The following makes an attempt at breaking such images
+	 * into strips closer to the recommended 8k bytes.  A
+	 * side effect, however, is that the RowsPerStrip tag
+	 * value may be changed.
+	 */
+	if (td->td_nstrips == 1 && td->td_compression == COMPRESSION_NONE &&
+	    (tif->tif_flags & (TIFF_STRIPCHOP|TIFF_ISTILED)) == TIFF_STRIPCHOP)
+		ChopUpSingleUncompressedStrip(tif);
+
+	/*
+	 * Reinitialize i/o since we are starting on a new directory.
+	 */
+	tif->tif_row = (uint32) -1;
+	tif->tif_curstrip = (tstrip_t) -1;
+	tif->tif_col = (uint32) -1;
+	tif->tif_curtile = (ttile_t) -1;
+	tif->tif_tilesize = (tsize_t) -1;
+
+	tif->tif_scanlinesize = TIFFScanlineSize(tif);
+	if (!tif->tif_scanlinesize) {
+		TIFFErrorExt(tif->tif_clientdata, module, "%s: cannot handle zero scanline size",
+			  tif->tif_name);
+		return (0);
+	}
+
+	if (isTiled(tif)) {
+		tif->tif_tilesize = TIFFTileSize(tif);
+		if (!tif->tif_tilesize) {
+			TIFFErrorExt(tif->tif_clientdata, module, "%s: cannot handle zero tile size",
+				  tif->tif_name);
+			return (0);
+		}
+	} else {
+		if (!TIFFStripSize(tif)) {
+			TIFFErrorExt(tif->tif_clientdata, module, "%s: cannot handle zero strip size",
+				  tif->tif_name);
+			return (0);
+		}
+	}
+	return (1);
+bad:
+	if (dir)
+		_TIFFfree(dir);
+	return (0);
+}
+
+static TIFFDirEntry*
+TIFFReadDirectoryFind(TIFFDirEntry* dir, uint16 dircount, uint16 tagid)
+{
+	TIFFDirEntry* m;
+	uint16 n;
+	for (m=dir, n=0; n<dircount; m++, n++)
+	{
+		if (m->tdir_tag==tagid)
+			return(m);
+	}
+	return(0);
+}
+
+/*
+ * Read custom directory from the arbitarry offset.
+ * The code is very similar to TIFFReadDirectory().
+ */
+int
+TIFFReadCustomDirectory(TIFF* tif, toff_t diroff,
+			const TIFFFieldInfo info[], size_t n)
+{
+	static const char module[] = "TIFFReadCustomDirectory";
+
+	TIFFDirectory* td = &tif->tif_dir;
+	TIFFDirEntry *dp, *dir = NULL;
+	const TIFFFieldInfo* fip;
+	size_t fix;
+	uint16 i, dircount;
+
+	_TIFFSetupFieldInfo(tif, info, n);
+
+	tif->tif_diroff = diroff;
+
+	if (!isMapped(tif)) {
+		if (!SeekOK(tif, diroff)) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+			    "%s: Seek error accessing TIFF directory",
+                            tif->tif_name);
+			return (0);
+		}
+		if (!ReadOK(tif, &dircount, sizeof (uint16))) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+			    "%s: Can not read TIFF directory count",
+                            tif->tif_name);
+			return (0);
+		}
+		if (tif->tif_flags & TIFF_SWAB)
+			TIFFSwabShort(&dircount);
+		dir = (TIFFDirEntry *)_TIFFCheckMalloc(tif, dircount,
+						       sizeof (TIFFDirEntry),
+					"to read TIFF custom directory");
+		if (dir == NULL)
+			return (0);
+		if (!ReadOK(tif, dir, dircount * sizeof (TIFFDirEntry))) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+                                  "%.100s: Can not read TIFF directory",
+                                  tif->tif_name);
+			goto bad;
+		}
+	} else {
+		toff_t off = diroff;
+
+		if (off + sizeof (uint16) > tif->tif_size) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+			    "%s: Can not read TIFF directory count",
+                            tif->tif_name);
+			return (0);
+		} else
+			_TIFFmemcpy(&dircount, tif->tif_base + off, sizeof (uint16));
+		off += sizeof (uint16);
+		if (tif->tif_flags & TIFF_SWAB)
+			TIFFSwabShort(&dircount);
+		dir = (TIFFDirEntry *)_TIFFCheckMalloc(tif, dircount,
+						       sizeof (TIFFDirEntry),
+					"to read TIFF custom directory");
+		if (dir == NULL)
+			return (0);
+		if (off + dircount * sizeof (TIFFDirEntry) > tif->tif_size) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+                                  "%s: Can not read TIFF directory",
+                                  tif->tif_name);
+			goto bad;
+		} else {
+			_TIFFmemcpy(dir, tif->tif_base + off,
+				    dircount * sizeof (TIFFDirEntry));
+		}
+	}
+
+	TIFFFreeDirectory(tif);
+
+	fix = 0;
+	for (dp = dir, i = dircount; i > 0; i--, dp++) {
+		if (tif->tif_flags & TIFF_SWAB) {
+			TIFFSwabArrayOfShort(&dp->tdir_tag, 2);
+			TIFFSwabArrayOfLong(&dp->tdir_count, 2);
+		}
+
+		if (fix >= tif->tif_nfields || dp->tdir_tag == IGNORE)
+			continue;
+
+		while (fix < tif->tif_nfields &&
+		       tif->tif_fieldinfo[fix]->field_tag < dp->tdir_tag)
+			fix++;
+
+		if (fix >= tif->tif_nfields ||
+		    tif->tif_fieldinfo[fix]->field_tag != dp->tdir_tag) {
+
+			TIFFWarningExt(tif->tif_clientdata, module,
+                        "%s: unknown field with tag %d (0x%x) encountered",
+				    tif->tif_name, dp->tdir_tag, dp->tdir_tag,
+				    dp->tdir_type);
+
+			TIFFMergeFieldInfo(tif,
+					   _TIFFCreateAnonFieldInfo(tif,
+						dp->tdir_tag,
+						(TIFFDataType)dp->tdir_type),
+					   1);
+
+			fix = 0;
+			while (fix < tif->tif_nfields &&
+			       tif->tif_fieldinfo[fix]->field_tag < dp->tdir_tag)
+				fix++;
+		}
+		/*
+		 * Null out old tags that we ignore.
+		 */
+		if (tif->tif_fieldinfo[fix]->field_bit == FIELD_IGNORE) {
+	ignore:
+			dp->tdir_tag = IGNORE;
+			continue;
+		}
+		/*
+		 * Check data type.
+		 */
+		fip = tif->tif_fieldinfo[fix];
+		while (dp->tdir_type != (unsigned short) fip->field_type
+                       && fix < tif->tif_nfields) {
+			if (fip->field_type == TIFF_ANY)	/* wildcard */
+				break;
+                        fip = tif->tif_fieldinfo[++fix];
+			if (fix >= tif->tif_nfields ||
+			    fip->field_tag != dp->tdir_tag) {
+				TIFFWarningExt(tif->tif_clientdata, module,
+			"%s: wrong data type %d for \"%s\"; tag ignored",
+					    tif->tif_name, dp->tdir_type,
+					    tif->tif_fieldinfo[fix-1]->field_name);
+				goto ignore;
+			}
+		}
+		/*
+		 * Check count if known in advance.
+		 */
+		if (fip->field_readcount != TIFF_VARIABLE
+		    && fip->field_readcount != TIFF_VARIABLE2) {
+			uint32 expected = (fip->field_readcount == TIFF_SPP) ?
+			    (uint32) td->td_samplesperpixel :
+			    (uint32) fip->field_readcount;
+			if (!CheckDirCount(tif, dp, expected))
+				goto ignore;
+		}
+
+		(void) TIFFFetchNormalTag(tif, dp);
+	}
+	
+	if (dir)
+		_TIFFfree(dir);
+	return 1;
+
+bad:
+	if (dir)
+		_TIFFfree(dir);
+	return 0;
+}
+
+/*
+ * EXIF is important special case of custom IFD, so we have a special
+ * function to read it.
+ */
+int
+TIFFReadEXIFDirectory(TIFF* tif, toff_t diroff)
+{
+	size_t exifFieldInfoCount;
+	const TIFFFieldInfo *exifFieldInfo =
+		_TIFFGetExifFieldInfo(&exifFieldInfoCount);
+	return TIFFReadCustomDirectory(tif, diroff, exifFieldInfo,
+				       exifFieldInfoCount);
+}
+
+static int
+EstimateStripByteCounts(TIFF* tif, TIFFDirEntry* dir, uint16 dircount)
+{
+	static const char module[] = "EstimateStripByteCounts";
+
+	register TIFFDirEntry *dp;
+	register TIFFDirectory *td = &tif->tif_dir;
+	uint16 i;
+
+	if (td->td_stripbytecount)
+		_TIFFfree(td->td_stripbytecount);
+	td->td_stripbytecount = (uint32*)
+	    _TIFFCheckMalloc(tif, td->td_nstrips, sizeof (uint32),
+		"for \"StripByteCounts\" array");
+	if (td->td_compression != COMPRESSION_NONE) {
+		uint32 space = (uint32)(sizeof (TIFFHeader)
+		    + sizeof (uint16)
+		    + (dircount * sizeof (TIFFDirEntry))
+		    + sizeof (uint32));
+		toff_t filesize = TIFFGetFileSize(tif);
+		uint16 n;
+
+		/* calculate amount of space used by indirect values */
+		for (dp = dir, n = dircount; n > 0; n--, dp++)
+		{
+			uint32 cc = TIFFDataWidth((TIFFDataType) dp->tdir_type);
+			if (cc == 0) {
+				TIFFErrorExt(tif->tif_clientdata, module,
+			"%s: Cannot determine size of unknown tag type %d",
+					  tif->tif_name, dp->tdir_type);
+				return -1;
+			}
+			cc = cc * dp->tdir_count;
+			if (cc > sizeof (uint32))
+				space += cc;
+		}
+		space = filesize - space;
+		if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+			space /= td->td_samplesperpixel;
+		for (i = 0; i < td->td_nstrips; i++)
+			td->td_stripbytecount[i] = space;
+		/*
+		 * This gross hack handles the case were the offset to
+		 * the last strip is past the place where we think the strip
+		 * should begin.  Since a strip of data must be contiguous,
+		 * it's safe to assume that we've overestimated the amount
+		 * of data in the strip and trim this number back accordingly.
+		 */ 
+		i--;
+		if (((toff_t)(td->td_stripoffset[i]+td->td_stripbytecount[i]))
+                                                               > filesize)
+			td->td_stripbytecount[i] =
+			    filesize - td->td_stripoffset[i];
+	} else {
+		uint32 rowbytes = TIFFScanlineSize(tif);
+		uint32 rowsperstrip = td->td_imagelength/td->td_stripsperimage;
+		for (i = 0; i < td->td_nstrips; i++)
+			td->td_stripbytecount[i] = rowbytes*rowsperstrip;
+	}
+	TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS);
+	if (!TIFFFieldSet(tif, FIELD_ROWSPERSTRIP))
+		td->td_rowsperstrip = td->td_imagelength;
+	return 1;
+}
+
+static void
+MissingRequired(TIFF* tif, const char* tagname)
+{
+	static const char module[] = "MissingRequired";
+
+	TIFFErrorExt(tif->tif_clientdata, module,
+		  "%s: TIFF directory is missing required \"%s\" field",
+		  tif->tif_name, tagname);
+}
+
+/*
+ * Check the count field of a directory
+ * entry against a known value.  The caller
+ * is expected to skip/ignore the tag if
+ * there is a mismatch.
+ */
+static int
+CheckDirCount(TIFF* tif, TIFFDirEntry* dir, uint32 count)
+{
+	if (count > dir->tdir_count) {
+		TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
+	"incorrect count for field \"%s\" (%lu, expecting %lu); tag ignored",
+		    _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name,
+		    dir->tdir_count, count);
+		return (0);
+	} else if (count < dir->tdir_count) {
+		TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
+	"incorrect count for field \"%s\" (%lu, expecting %lu); tag trimmed",
+		    _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name,
+		    dir->tdir_count, count);
+		return (1);
+	}
+	return (1);
+}
+
+/*
+ * Fetch a contiguous directory item.
+ */
+static tsize_t
+TIFFFetchData(TIFF* tif, TIFFDirEntry* dir, char* cp)
+{
+	int w = TIFFDataWidth((TIFFDataType) dir->tdir_type);
+	tsize_t cc = dir->tdir_count * w;
+
+	/* Check for overflow. */
+	if (!dir->tdir_count || !w || cc / w != (tsize_t)dir->tdir_count)
+		goto bad;
+
+	if (!isMapped(tif)) {
+		if (!SeekOK(tif, dir->tdir_offset))
+			goto bad;
+		if (!ReadOK(tif, cp, cc))
+			goto bad;
+	} else {
+		/* Check for overflow. */
+		if ((tsize_t)dir->tdir_offset + cc < (tsize_t)dir->tdir_offset
+		    || (tsize_t)dir->tdir_offset + cc < cc
+		    || (tsize_t)dir->tdir_offset + cc > (tsize_t)tif->tif_size)
+			goto bad;
+		_TIFFmemcpy(cp, tif->tif_base + dir->tdir_offset, cc);
+	}
+	if (tif->tif_flags & TIFF_SWAB) {
+		switch (dir->tdir_type) {
+		case TIFF_SHORT:
+		case TIFF_SSHORT:
+			TIFFSwabArrayOfShort((uint16*) cp, dir->tdir_count);
+			break;
+		case TIFF_LONG:
+		case TIFF_SLONG:
+		case TIFF_FLOAT:
+			TIFFSwabArrayOfLong((uint32*) cp, dir->tdir_count);
+			break;
+		case TIFF_RATIONAL:
+		case TIFF_SRATIONAL:
+			TIFFSwabArrayOfLong((uint32*) cp, 2*dir->tdir_count);
+			break;
+		case TIFF_DOUBLE:
+			TIFFSwabArrayOfDouble((double*) cp, dir->tdir_count);
+			break;
+		}
+	}
+	return (cc);
+bad:
+	TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		     "Error fetching data for field \"%s\"",
+		     _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name);
+	return (tsize_t) 0;
+}
+
+/*
+ * Fetch an ASCII item from the file.
+ */
+static tsize_t
+TIFFFetchString(TIFF* tif, TIFFDirEntry* dir, char* cp)
+{
+	if (dir->tdir_count <= 4) {
+		uint32 l = dir->tdir_offset;
+		if (tif->tif_flags & TIFF_SWAB)
+			TIFFSwabLong(&l);
+		_TIFFmemcpy(cp, &l, dir->tdir_count);
+		return (1);
+	}
+	return (TIFFFetchData(tif, dir, cp));
+}
+
+/*
+ * Convert numerator+denominator to float.
+ */
+static int
+cvtRational(TIFF* tif, TIFFDirEntry* dir, uint32 num, uint32 denom, float* rv)
+{
+	if (denom == 0) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		    "%s: Rational with zero denominator (num = %lu)",
+		    _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name, num);
+		return (0);
+	} else {
+		if (dir->tdir_type == TIFF_RATIONAL)
+			*rv = ((float)num / (float)denom);
+		else
+			*rv = ((float)(int32)num / (float)(int32)denom);
+		return (1);
+	}
+}
+
+/*
+ * Fetch a rational item from the file
+ * at offset off and return the value
+ * as a floating point number.
+ */
+static float
+TIFFFetchRational(TIFF* tif, TIFFDirEntry* dir)
+{
+	uint32 l[2];
+	float v;
+
+	return (!TIFFFetchData(tif, dir, (char *)l) ||
+	    !cvtRational(tif, dir, l[0], l[1], &v) ? 1.0f : v);
+}
+
+/*
+ * Fetch a single floating point value
+ * from the offset field and return it
+ * as a native float.
+ */
+static float
+TIFFFetchFloat(TIFF* tif, TIFFDirEntry* dir)
+{
+	float v;
+	int32 l = TIFFExtractData(tif, dir->tdir_type, dir->tdir_offset);
+        _TIFFmemcpy(&v, &l, sizeof(float));
+	TIFFCvtIEEEFloatToNative(tif, 1, &v);
+	return (v);
+}
+
+/*
+ * Fetch an array of BYTE or SBYTE values.
+ */
+static int
+TIFFFetchByteArray(TIFF* tif, TIFFDirEntry* dir, uint8* v)
+{
+    if (dir->tdir_count <= 4) {
+        /*
+         * Extract data from offset field.
+         */
+        if (tif->tif_header.tiff_magic == TIFF_BIGENDIAN) {
+	    if (dir->tdir_type == TIFF_SBYTE)
+                switch (dir->tdir_count) {
+                    case 4: v[3] = dir->tdir_offset & 0xff;
+                    case 3: v[2] = (dir->tdir_offset >> 8) & 0xff;
+                    case 2: v[1] = (dir->tdir_offset >> 16) & 0xff;
+		    case 1: v[0] = dir->tdir_offset >> 24;
+                }
+	    else
+                switch (dir->tdir_count) {
+                    case 4: v[3] = dir->tdir_offset & 0xff;
+                    case 3: v[2] = (dir->tdir_offset >> 8) & 0xff;
+                    case 2: v[1] = (dir->tdir_offset >> 16) & 0xff;
+		    case 1: v[0] = dir->tdir_offset >> 24;
+                }
+	} else {
+	    if (dir->tdir_type == TIFF_SBYTE)
+                switch (dir->tdir_count) {
+                    case 4: v[3] = dir->tdir_offset >> 24;
+                    case 3: v[2] = (dir->tdir_offset >> 16) & 0xff;
+                    case 2: v[1] = (dir->tdir_offset >> 8) & 0xff;
+                    case 1: v[0] = dir->tdir_offset & 0xff;
+		}
+	    else
+                switch (dir->tdir_count) {
+                    case 4: v[3] = dir->tdir_offset >> 24;
+                    case 3: v[2] = (dir->tdir_offset >> 16) & 0xff;
+                    case 2: v[1] = (dir->tdir_offset >> 8) & 0xff;
+                    case 1: v[0] = dir->tdir_offset & 0xff;
+		}
+	}
+        return (1);
+    } else
+        return (TIFFFetchData(tif, dir, (char*) v) != 0);	/* XXX */
+}
+
+/*
+ * Fetch an array of SHORT or SSHORT values.
+ */
+static int
+TIFFFetchShortArray(TIFF* tif, TIFFDirEntry* dir, uint16* v)
+{
+	if (dir->tdir_count <= 2) {
+		if (tif->tif_header.tiff_magic == TIFF_BIGENDIAN) {
+			switch (dir->tdir_count) {
+			case 2: v[1] = (uint16) (dir->tdir_offset & 0xffff);
+			case 1: v[0] = (uint16) (dir->tdir_offset >> 16);
+			}
+		} else {
+			switch (dir->tdir_count) {
+			case 2: v[1] = (uint16) (dir->tdir_offset >> 16);
+			case 1: v[0] = (uint16) (dir->tdir_offset & 0xffff);
+			}
+		}
+		return (1);
+	} else
+		return (TIFFFetchData(tif, dir, (char *)v) != 0);
+}
+
+/*
+ * Fetch a pair of SHORT or BYTE values. Some tags may have either BYTE
+ * or SHORT type and this function works with both ones.
+ */
+static int
+TIFFFetchShortPair(TIFF* tif, TIFFDirEntry* dir)
+{
+	switch (dir->tdir_type) {
+		case TIFF_BYTE:
+		case TIFF_SBYTE:
+			{
+			uint8 v[4];
+			return TIFFFetchByteArray(tif, dir, v)
+				&& TIFFSetField(tif, dir->tdir_tag, v[0], v[1]);
+			}
+		case TIFF_SHORT:
+		case TIFF_SSHORT:
+			{
+			uint16 v[2];
+			return TIFFFetchShortArray(tif, dir, v)
+				&& TIFFSetField(tif, dir->tdir_tag, v[0], v[1]);
+			}
+		default:
+			return 0;
+	}
+}
+
+/*
+ * Fetch an array of LONG or SLONG values.
+ */
+static int
+TIFFFetchLongArray(TIFF* tif, TIFFDirEntry* dir, uint32* v)
+{
+	if (dir->tdir_count == 1) {
+		v[0] = dir->tdir_offset;
+		return (1);
+	} else
+		return (TIFFFetchData(tif, dir, (char*) v) != 0);
+}
+
+/*
+ * Fetch an array of RATIONAL or SRATIONAL values.
+ */
+static int
+TIFFFetchRationalArray(TIFF* tif, TIFFDirEntry* dir, float* v)
+{
+	int ok = 0;
+	uint32* l;
+
+	l = (uint32*)_TIFFCheckMalloc(tif,
+	    dir->tdir_count, TIFFDataWidth((TIFFDataType) dir->tdir_type),
+	    "to fetch array of rationals");
+	if (l) {
+		if (TIFFFetchData(tif, dir, (char *)l)) {
+			uint32 i;
+			for (i = 0; i < dir->tdir_count; i++) {
+				ok = cvtRational(tif, dir,
+				    l[2*i+0], l[2*i+1], &v[i]);
+				if (!ok)
+					break;
+			}
+		}
+		_TIFFfree((char *)l);
+	}
+	return (ok);
+}
+
+/*
+ * Fetch an array of FLOAT values.
+ */
+static int
+TIFFFetchFloatArray(TIFF* tif, TIFFDirEntry* dir, float* v)
+{
+
+	if (dir->tdir_count == 1) {
+		v[0] = *(float*) &dir->tdir_offset;
+		TIFFCvtIEEEFloatToNative(tif, dir->tdir_count, v);
+		return (1);
+	} else	if (TIFFFetchData(tif, dir, (char*) v)) {
+		TIFFCvtIEEEFloatToNative(tif, dir->tdir_count, v);
+		return (1);
+	} else
+		return (0);
+}
+
+/*
+ * Fetch an array of DOUBLE values.
+ */
+static int
+TIFFFetchDoubleArray(TIFF* tif, TIFFDirEntry* dir, double* v)
+{
+	if (TIFFFetchData(tif, dir, (char*) v)) {
+		TIFFCvtIEEEDoubleToNative(tif, dir->tdir_count, v);
+		return (1);
+	} else
+		return (0);
+}
+
+/*
+ * Fetch an array of ANY values.  The actual values are
+ * returned as doubles which should be able hold all the
+ * types.  Yes, there really should be an tany_t to avoid
+ * this potential non-portability ...  Note in particular
+ * that we assume that the double return value vector is
+ * large enough to read in any fundamental type.  We use
+ * that vector as a buffer to read in the base type vector
+ * and then convert it in place to double (from end
+ * to front of course).
+ */
+static int
+TIFFFetchAnyArray(TIFF* tif, TIFFDirEntry* dir, double* v)
+{
+	int i;
+
+	switch (dir->tdir_type) {
+	case TIFF_BYTE:
+	case TIFF_SBYTE:
+		if (!TIFFFetchByteArray(tif, dir, (uint8*) v))
+			return (0);
+		if (dir->tdir_type == TIFF_BYTE) {
+			uint8* vp = (uint8*) v;
+			for (i = dir->tdir_count-1; i >= 0; i--)
+				v[i] = vp[i];
+		} else {
+			int8* vp = (int8*) v;
+			for (i = dir->tdir_count-1; i >= 0; i--)
+				v[i] = vp[i];
+		}
+		break;
+	case TIFF_SHORT:
+	case TIFF_SSHORT:
+		if (!TIFFFetchShortArray(tif, dir, (uint16*) v))
+			return (0);
+		if (dir->tdir_type == TIFF_SHORT) {
+			uint16* vp = (uint16*) v;
+			for (i = dir->tdir_count-1; i >= 0; i--)
+				v[i] = vp[i];
+		} else {
+			int16* vp = (int16*) v;
+			for (i = dir->tdir_count-1; i >= 0; i--)
+				v[i] = vp[i];
+		}
+		break;
+	case TIFF_LONG:
+	case TIFF_SLONG:
+		if (!TIFFFetchLongArray(tif, dir, (uint32*) v))
+			return (0);
+		if (dir->tdir_type == TIFF_LONG) {
+			uint32* vp = (uint32*) v;
+			for (i = dir->tdir_count-1; i >= 0; i--)
+				v[i] = vp[i];
+		} else {
+			int32* vp = (int32*) v;
+			for (i = dir->tdir_count-1; i >= 0; i--)
+				v[i] = vp[i];
+		}
+		break;
+	case TIFF_RATIONAL:
+	case TIFF_SRATIONAL:
+		if (!TIFFFetchRationalArray(tif, dir, (float*) v))
+			return (0);
+		{ float* vp = (float*) v;
+		  for (i = dir->tdir_count-1; i >= 0; i--)
+			v[i] = vp[i];
+		}
+		break;
+	case TIFF_FLOAT:
+		if (!TIFFFetchFloatArray(tif, dir, (float*) v))
+			return (0);
+		{ float* vp = (float*) v;
+		  for (i = dir->tdir_count-1; i >= 0; i--)
+			v[i] = vp[i];
+		}
+		break;
+	case TIFF_DOUBLE:
+		return (TIFFFetchDoubleArray(tif, dir, (double*) v));
+	default:
+		/* TIFF_NOTYPE */
+		/* TIFF_ASCII */
+		/* TIFF_UNDEFINED */
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			     "cannot read TIFF_ANY type %d for field \"%s\"",
+			     dir->tdir_type,
+			     _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name);
+		return (0);
+	}
+	return (1);
+}
+
+/*
+ * Fetch a tag that is not handled by special case code.
+ */
+static int
+TIFFFetchNormalTag(TIFF* tif, TIFFDirEntry* dp)
+{
+	static const char mesg[] = "to fetch tag value";
+	int ok = 0;
+	const TIFFFieldInfo* fip = _TIFFFieldWithTag(tif, dp->tdir_tag);
+
+	if (dp->tdir_count > 1) {		/* array of values */
+		char* cp = NULL;
+
+		switch (dp->tdir_type) {
+		case TIFF_BYTE:
+		case TIFF_SBYTE:
+			cp = (char *)_TIFFCheckMalloc(tif,
+			    dp->tdir_count, sizeof (uint8), mesg);
+			ok = cp && TIFFFetchByteArray(tif, dp, (uint8*) cp);
+			break;
+		case TIFF_SHORT:
+		case TIFF_SSHORT:
+			cp = (char *)_TIFFCheckMalloc(tif,
+			    dp->tdir_count, sizeof (uint16), mesg);
+			ok = cp && TIFFFetchShortArray(tif, dp, (uint16*) cp);
+			break;
+		case TIFF_LONG:
+		case TIFF_SLONG:
+			cp = (char *)_TIFFCheckMalloc(tif,
+			    dp->tdir_count, sizeof (uint32), mesg);
+			ok = cp && TIFFFetchLongArray(tif, dp, (uint32*) cp);
+			break;
+		case TIFF_RATIONAL:
+		case TIFF_SRATIONAL:
+			cp = (char *)_TIFFCheckMalloc(tif,
+			    dp->tdir_count, sizeof (float), mesg);
+			ok = cp && TIFFFetchRationalArray(tif, dp, (float*) cp);
+			break;
+		case TIFF_FLOAT:
+			cp = (char *)_TIFFCheckMalloc(tif,
+			    dp->tdir_count, sizeof (float), mesg);
+			ok = cp && TIFFFetchFloatArray(tif, dp, (float*) cp);
+			break;
+		case TIFF_DOUBLE:
+			cp = (char *)_TIFFCheckMalloc(tif,
+			    dp->tdir_count, sizeof (double), mesg);
+			ok = cp && TIFFFetchDoubleArray(tif, dp, (double*) cp);
+			break;
+		case TIFF_ASCII:
+		case TIFF_UNDEFINED:		/* bit of a cheat... */
+			/*
+			 * Some vendors write strings w/o the trailing
+			 * NULL byte, so always append one just in case.
+			 */
+			cp = (char *)_TIFFCheckMalloc(tif, dp->tdir_count + 1,
+						      1, mesg);
+			if( (ok = (cp && TIFFFetchString(tif, dp, cp))) != 0 )
+				cp[dp->tdir_count] = '\0';	/* XXX */
+			break;
+		}
+		if (ok) {
+			ok = (fip->field_passcount ?
+			    TIFFSetField(tif, dp->tdir_tag, dp->tdir_count, cp)
+			  : TIFFSetField(tif, dp->tdir_tag, cp));
+		}
+		if (cp != NULL)
+			_TIFFfree(cp);
+	} else if (CheckDirCount(tif, dp, 1)) {	/* singleton value */
+		switch (dp->tdir_type) {
+		case TIFF_BYTE:
+		case TIFF_SBYTE:
+		case TIFF_SHORT:
+		case TIFF_SSHORT:
+			/*
+			 * If the tag is also acceptable as a LONG or SLONG
+			 * then TIFFSetField will expect an uint32 parameter
+			 * passed to it (through varargs).  Thus, for machines
+			 * where sizeof (int) != sizeof (uint32) we must do
+			 * a careful check here.  It's hard to say if this
+			 * is worth optimizing.
+			 *
+			 * NB: We use TIFFFieldWithTag here knowing that
+			 *     it returns us the first entry in the table
+			 *     for the tag and that that entry is for the
+			 *     widest potential data type the tag may have.
+			 */
+			{ TIFFDataType type = fip->field_type;
+			  if (type != TIFF_LONG && type != TIFF_SLONG) {
+				uint16 v = (uint16)
+			   TIFFExtractData(tif, dp->tdir_type, dp->tdir_offset);
+				ok = (fip->field_passcount ?
+				    TIFFSetField(tif, dp->tdir_tag, 1, &v)
+				  : TIFFSetField(tif, dp->tdir_tag, v));
+				break;
+			  }
+			}
+			/* fall thru... */
+		case TIFF_LONG:
+		case TIFF_SLONG:
+			{ uint32 v32 =
+		    TIFFExtractData(tif, dp->tdir_type, dp->tdir_offset);
+			  ok = (fip->field_passcount ? 
+			      TIFFSetField(tif, dp->tdir_tag, 1, &v32)
+			    : TIFFSetField(tif, dp->tdir_tag, v32));
+			}
+			break;
+		case TIFF_RATIONAL:
+		case TIFF_SRATIONAL:
+		case TIFF_FLOAT:
+			{ float v = (dp->tdir_type == TIFF_FLOAT ? 
+			      TIFFFetchFloat(tif, dp)
+			    : TIFFFetchRational(tif, dp));
+			  ok = (fip->field_passcount ?
+			      TIFFSetField(tif, dp->tdir_tag, 1, &v)
+			    : TIFFSetField(tif, dp->tdir_tag, v));
+			}
+			break;
+		case TIFF_DOUBLE:
+			{ double v;
+			  ok = (TIFFFetchDoubleArray(tif, dp, &v) &&
+			    (fip->field_passcount ?
+			      TIFFSetField(tif, dp->tdir_tag, 1, &v)
+			    : TIFFSetField(tif, dp->tdir_tag, v))
+			  );
+			}
+			break;
+		case TIFF_ASCII:
+		case TIFF_UNDEFINED:		/* bit of a cheat... */
+			{ char c[2];
+			  if( (ok = (TIFFFetchString(tif, dp, c) != 0)) != 0 ) {
+				c[1] = '\0';		/* XXX paranoid */
+				ok = (fip->field_passcount ?
+					TIFFSetField(tif, dp->tdir_tag, 1, c)
+				      : TIFFSetField(tif, dp->tdir_tag, c));
+			  }
+			}
+			break;
+		}
+	}
+	return (ok);
+}
+
+#define	NITEMS(x)	(sizeof (x) / sizeof (x[0]))
+/*
+ * Fetch samples/pixel short values for 
+ * the specified tag and verify that
+ * all values are the same.
+ */
+static int
+TIFFFetchPerSampleShorts(TIFF* tif, TIFFDirEntry* dir, uint16* pl)
+{
+    uint16 samples = tif->tif_dir.td_samplesperpixel;
+    int status = 0;
+
+    if (CheckDirCount(tif, dir, (uint32) samples)) {
+        uint16 buf[10];
+        uint16* v = buf;
+
+        if (dir->tdir_count > NITEMS(buf))
+            v = (uint16*) _TIFFCheckMalloc(tif, dir->tdir_count, sizeof(uint16),
+                                      "to fetch per-sample values");
+        if (v && TIFFFetchShortArray(tif, dir, v)) {
+            uint16 i;
+            int check_count = dir->tdir_count;
+            if( samples < check_count )
+                check_count = samples;
+
+            for (i = 1; i < check_count; i++)
+                if (v[i] != v[0]) {
+					TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+                              "Cannot handle different per-sample values for field \"%s\"",
+                              _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name);
+                    goto bad;
+                }
+            *pl = v[0];
+            status = 1;
+        }
+      bad:
+        if (v && v != buf)
+            _TIFFfree(v);
+    }
+    return (status);
+}
+
+/*
+ * Fetch samples/pixel long values for 
+ * the specified tag and verify that
+ * all values are the same.
+ */
+static int
+TIFFFetchPerSampleLongs(TIFF* tif, TIFFDirEntry* dir, uint32* pl)
+{
+    uint16 samples = tif->tif_dir.td_samplesperpixel;
+    int status = 0;
+
+    if (CheckDirCount(tif, dir, (uint32) samples)) {
+        uint32 buf[10];
+        uint32* v = buf;
+
+        if (dir->tdir_count > NITEMS(buf))
+            v = (uint32*) _TIFFCheckMalloc(tif, dir->tdir_count, sizeof(uint32),
+                                      "to fetch per-sample values");
+        if (v && TIFFFetchLongArray(tif, dir, v)) {
+            uint16 i;
+            int check_count = dir->tdir_count;
+
+            if( samples < check_count )
+                check_count = samples;
+            for (i = 1; i < check_count; i++)
+                if (v[i] != v[0]) {
+					TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+                              "Cannot handle different per-sample values for field \"%s\"",
+                              _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name);
+                    goto bad;
+                }
+            *pl = v[0];
+            status = 1;
+        }
+      bad:
+        if (v && v != buf)
+            _TIFFfree(v);
+    }
+    return (status);
+}
+
+/*
+ * Fetch samples/pixel ANY values for the specified tag and verify that all
+ * values are the same.
+ */
+static int
+TIFFFetchPerSampleAnys(TIFF* tif, TIFFDirEntry* dir, double* pl)
+{
+    uint16 samples = tif->tif_dir.td_samplesperpixel;
+    int status = 0;
+
+    if (CheckDirCount(tif, dir, (uint32) samples)) {
+        double buf[10];
+        double* v = buf;
+
+        if (dir->tdir_count > NITEMS(buf))
+            v = (double*) _TIFFCheckMalloc(tif, dir->tdir_count, sizeof (double),
+                                      "to fetch per-sample values");
+        if (v && TIFFFetchAnyArray(tif, dir, v)) {
+            uint16 i;
+            int check_count = dir->tdir_count;
+            if( samples < check_count )
+                check_count = samples;
+
+            for (i = 1; i < check_count; i++)
+                if (v[i] != v[0]) {
+                    TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+                              "Cannot handle different per-sample values for field \"%s\"",
+                              _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name);
+                    goto bad;
+                }
+            *pl = v[0];
+            status = 1;
+        }
+      bad:
+        if (v && v != buf)
+            _TIFFfree(v);
+    }
+    return (status);
+}
+#undef NITEMS
+
+/*
+ * Fetch a set of offsets or lengths.
+ * While this routine says "strips", in fact it's also used for tiles.
+ */
+static int
+TIFFFetchStripThing(TIFF* tif, TIFFDirEntry* dir, long nstrips, uint32** lpp)
+{
+	register uint32* lp;
+	int status;
+
+        CheckDirCount(tif, dir, (uint32) nstrips);
+
+	/*
+	 * Allocate space for strip information.
+	 */
+	if (*lpp == NULL &&
+	    (*lpp = (uint32 *)_TIFFCheckMalloc(tif,
+	      nstrips, sizeof (uint32), "for strip array")) == NULL)
+		return (0);
+	lp = *lpp;
+        _TIFFmemset( lp, 0, sizeof(uint32) * nstrips );
+
+	if (dir->tdir_type == (int)TIFF_SHORT) {
+		/*
+		 * Handle uint16->uint32 expansion.
+		 */
+		uint16* dp = (uint16*) _TIFFCheckMalloc(tif,
+		    dir->tdir_count, sizeof (uint16), "to fetch strip tag");
+		if (dp == NULL)
+			return (0);
+		if( (status = TIFFFetchShortArray(tif, dir, dp)) != 0 ) {
+                    int i;
+                    
+                    for( i = 0; i < nstrips && i < (int) dir->tdir_count; i++ )
+                    {
+                        lp[i] = dp[i];
+                    }
+		}
+		_TIFFfree((char*) dp);
+
+        } else if( nstrips != (int) dir->tdir_count ) {
+            /* Special case to correct length */
+
+            uint32* dp = (uint32*) _TIFFCheckMalloc(tif,
+		    dir->tdir_count, sizeof (uint32), "to fetch strip tag");
+            if (dp == NULL)
+                return (0);
+
+            status = TIFFFetchLongArray(tif, dir, dp);
+            if( status != 0 ) {
+                int i;
+
+                for( i = 0; i < nstrips && i < (int) dir->tdir_count; i++ )
+                {
+                    lp[i] = dp[i];
+                }
+            }
+
+            _TIFFfree( (char *) dp );
+	} else
+            status = TIFFFetchLongArray(tif, dir, lp);
+        
+	return (status);
+}
+
+/*
+ * Fetch and set the RefBlackWhite tag.
+ */
+static int
+TIFFFetchRefBlackWhite(TIFF* tif, TIFFDirEntry* dir)
+{
+	static const char mesg[] = "for \"ReferenceBlackWhite\" array";
+	char* cp;
+	int ok;
+
+	if (dir->tdir_type == TIFF_RATIONAL)
+		return (TIFFFetchNormalTag(tif, dir));
+	/*
+	 * Handle LONG's for backward compatibility.
+	 */
+	cp = (char *)_TIFFCheckMalloc(tif, dir->tdir_count,
+				      sizeof (uint32), mesg);
+	if( (ok = (cp && TIFFFetchLongArray(tif, dir, (uint32*) cp))) != 0) {
+		float* fp = (float*)
+		    _TIFFCheckMalloc(tif, dir->tdir_count, sizeof (float), mesg);
+		if( (ok = (fp != NULL)) != 0 ) {
+			uint32 i;
+			for (i = 0; i < dir->tdir_count; i++)
+				fp[i] = (float)((uint32*) cp)[i];
+			ok = TIFFSetField(tif, dir->tdir_tag, fp);
+			_TIFFfree((char*) fp);
+		}
+	}
+	if (cp)
+		_TIFFfree(cp);
+	return (ok);
+}
+
+/*
+ * Replace a single strip (tile) of uncompressed data by
+ * multiple strips (tiles), each approximately 8Kbytes.
+ * This is useful for dealing with large images or
+ * for dealing with machines with a limited amount
+ * memory.
+ */
+static void
+ChopUpSingleUncompressedStrip(TIFF* tif)
+{
+	register TIFFDirectory *td = &tif->tif_dir;
+	uint32 bytecount = td->td_stripbytecount[0];
+	uint32 offset = td->td_stripoffset[0];
+	tsize_t rowbytes = TIFFVTileSize(tif, 1), stripbytes;
+	tstrip_t strip, nstrips, rowsperstrip;
+	uint32* newcounts;
+	uint32* newoffsets;
+
+	/*
+	 * Make the rows hold at least one scanline, but fill specified amount
+	 * of data if possible.
+	 */
+	if (rowbytes > STRIP_SIZE_DEFAULT) {
+		stripbytes = rowbytes;
+		rowsperstrip = 1;
+	} else if (rowbytes > 0 ) {
+		rowsperstrip = STRIP_SIZE_DEFAULT / rowbytes;
+		stripbytes = rowbytes * rowsperstrip;
+	}
+        else
+            return;
+
+	/* 
+	 * never increase the number of strips in an image
+	 */
+	if (rowsperstrip >= td->td_rowsperstrip)
+		return;
+	nstrips = (tstrip_t) TIFFhowmany(bytecount, stripbytes);
+        if( nstrips == 0 ) /* something is wonky, do nothing. */
+            return;
+
+	newcounts = (uint32*) _TIFFCheckMalloc(tif, nstrips, sizeof (uint32),
+				"for chopped \"StripByteCounts\" array");
+	newoffsets = (uint32*) _TIFFCheckMalloc(tif, nstrips, sizeof (uint32),
+				"for chopped \"StripOffsets\" array");
+	if (newcounts == NULL || newoffsets == NULL) {
+	        /*
+		 * Unable to allocate new strip information, give
+		 * up and use the original one strip information.
+		 */
+		if (newcounts != NULL)
+			_TIFFfree(newcounts);
+		if (newoffsets != NULL)
+			_TIFFfree(newoffsets);
+		return;
+	}
+	/*
+	 * Fill the strip information arrays with new bytecounts and offsets
+	 * that reflect the broken-up format.
+	 */
+	for (strip = 0; strip < nstrips; strip++) {
+		if (stripbytes > (tsize_t) bytecount)
+			stripbytes = bytecount;
+		newcounts[strip] = stripbytes;
+		newoffsets[strip] = offset;
+		offset += stripbytes;
+		bytecount -= stripbytes;
+	}
+	/*
+	 * Replace old single strip info with multi-strip info.
+	 */
+	td->td_stripsperimage = td->td_nstrips = nstrips;
+	TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
+
+	_TIFFfree(td->td_stripbytecount);
+	_TIFFfree(td->td_stripoffset);
+	td->td_stripbytecount = newcounts;
+	td->td_stripoffset = newoffsets;
+	td->td_stripbytecountsorted = 1;
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_dirwrite.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_dirwrite.c
new file mode 100644
index 0000000000..c482a7d3ba
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_dirwrite.c
@@ -0,0 +1,1242 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ *
+ * Directory Write Support Routines.
+ */
+#include "tiffiop.h"
+
+#ifdef HAVE_IEEEFP
+# define	TIFFCvtNativeToIEEEFloat(tif, n, fp)
+# define	TIFFCvtNativeToIEEEDouble(tif, n, dp)
+#else
+extern	void TIFFCvtNativeToIEEEFloat(TIFF*, uint32, float*);
+extern	void TIFFCvtNativeToIEEEDouble(TIFF*, uint32, double*);
+#endif
+
+static	int TIFFWriteNormalTag(TIFF*, TIFFDirEntry*, const TIFFFieldInfo*);
+static	void TIFFSetupShortLong(TIFF*, ttag_t, TIFFDirEntry*, uint32);
+static	void TIFFSetupShort(TIFF*, ttag_t, TIFFDirEntry*, uint16);
+static	int TIFFSetupShortPair(TIFF*, ttag_t, TIFFDirEntry*);
+static	int TIFFWritePerSampleShorts(TIFF*, ttag_t, TIFFDirEntry*);
+static	int TIFFWritePerSampleAnys(TIFF*, TIFFDataType, ttag_t, TIFFDirEntry*);
+static	int TIFFWriteShortTable(TIFF*, ttag_t, TIFFDirEntry*, uint32, uint16**);
+static	int TIFFWriteShortArray(TIFF*, TIFFDirEntry*, uint16*);
+static	int TIFFWriteLongArray(TIFF *, TIFFDirEntry*, uint32*);
+static	int TIFFWriteRationalArray(TIFF *, TIFFDirEntry*, float*);
+static	int TIFFWriteFloatArray(TIFF *, TIFFDirEntry*, float*);
+static	int TIFFWriteDoubleArray(TIFF *, TIFFDirEntry*, double*);
+static	int TIFFWriteByteArray(TIFF*, TIFFDirEntry*, char*);
+static	int TIFFWriteAnyArray(TIFF*,
+	    TIFFDataType, ttag_t, TIFFDirEntry*, uint32, double*);
+static	int TIFFWriteTransferFunction(TIFF*, TIFFDirEntry*);
+static	int TIFFWriteInkNames(TIFF*, TIFFDirEntry*);
+static	int TIFFWriteData(TIFF*, TIFFDirEntry*, char*);
+static	int TIFFLinkDirectory(TIFF*);
+
+#define	WriteRationalPair(type, tag1, v1, tag2, v2) {		\
+	TIFFWriteRational((tif), (type), (tag1), (dir), (v1))	\
+	TIFFWriteRational((tif), (type), (tag2), (dir)+1, (v2))	\
+	(dir)++;						\
+}
+#define	TIFFWriteRational(tif, type, tag, dir, v)		\
+	(dir)->tdir_tag = (tag);				\
+	(dir)->tdir_type = (type);				\
+	(dir)->tdir_count = 1;					\
+	if (!TIFFWriteRationalArray((tif), (dir), &(v)))	\
+		goto bad;
+
+/*
+ * Write the contents of the current directory
+ * to the specified file.  This routine doesn't
+ * handle overwriting a directory with auxiliary
+ * storage that's been changed.
+ */
+static int
+_TIFFWriteDirectory(TIFF* tif, int done)
+{
+	uint16 dircount;
+	toff_t diroff;
+	ttag_t tag;
+	uint32 nfields;
+	tsize_t dirsize;
+	char* data;
+	TIFFDirEntry* dir;
+	TIFFDirectory* td;
+	unsigned long b, fields[FIELD_SETLONGS];
+	int fi, nfi;
+
+	if (tif->tif_mode == O_RDONLY)
+		return (1);
+	/*
+	 * Clear write state so that subsequent images with
+	 * different characteristics get the right buffers
+	 * setup for them.
+	 */
+	if (done)
+	{
+		if (tif->tif_flags & TIFF_POSTENCODE) {
+			tif->tif_flags &= ~TIFF_POSTENCODE;
+			if (!(*tif->tif_postencode)(tif)) {
+				TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+				    "Error post-encoding before directory write");
+				return (0);
+			}
+		}
+		(*tif->tif_close)(tif);		/* shutdown encoder */
+		/*
+		 * Flush any data that might have been written
+		 * by the compression close+cleanup routines.
+		 */
+		if (tif->tif_rawcc > 0 && !TIFFFlushData1(tif)) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			    "Error flushing data before directory write");
+			return (0);
+		}
+		if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata) {
+			_TIFFfree(tif->tif_rawdata);
+			tif->tif_rawdata = NULL;
+			tif->tif_rawcc = 0;
+			tif->tif_rawdatasize = 0;
+		}
+		tif->tif_flags &= ~(TIFF_BEENWRITING|TIFF_BUFFERSETUP);
+	}
+
+	td = &tif->tif_dir;
+	/*
+	 * Size the directory so that we can calculate
+	 * offsets for the data items that aren't kept
+	 * in-place in each field.
+	 */
+	nfields = 0;
+	for (b = 0; b <= FIELD_LAST; b++)
+		if (TIFFFieldSet(tif, b) && b != FIELD_CUSTOM)
+			nfields += (b < FIELD_SUBFILETYPE ? 2 : 1);
+	nfields += td->td_customValueCount;
+	dirsize = nfields * sizeof (TIFFDirEntry);
+	data = (char*) _TIFFmalloc(dirsize);
+	if (data == NULL) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		    "Cannot write directory, out of space");
+		return (0);
+	}
+	/*
+	 * Directory hasn't been placed yet, put
+	 * it at the end of the file and link it
+	 * into the existing directory structure.
+	 */
+	if (tif->tif_diroff == 0 && !TIFFLinkDirectory(tif))
+		goto bad;
+	tif->tif_dataoff = (toff_t)(
+	    tif->tif_diroff + sizeof (uint16) + dirsize + sizeof (toff_t));
+	if (tif->tif_dataoff & 1)
+		tif->tif_dataoff++;
+	(void) TIFFSeekFile(tif, tif->tif_dataoff, SEEK_SET);
+	tif->tif_curdir++;
+	dir = (TIFFDirEntry*) data;
+	/*
+	 * Setup external form of directory
+	 * entries and write data items.
+	 */
+	_TIFFmemcpy(fields, td->td_fieldsset, sizeof (fields));
+	/*
+	 * Write out ExtraSamples tag only if
+	 * extra samples are present in the data.
+	 */
+	if (FieldSet(fields, FIELD_EXTRASAMPLES) && !td->td_extrasamples) {
+		ResetFieldBit(fields, FIELD_EXTRASAMPLES);
+		nfields--;
+		dirsize -= sizeof (TIFFDirEntry);
+	}								/*XXX*/
+	for (fi = 0, nfi = tif->tif_nfields; nfi > 0; nfi--, fi++) {
+		const TIFFFieldInfo* fip = tif->tif_fieldinfo[fi];
+
+		/*
+		 * For custom fields, we test to see if the custom field
+		 * is set or not.  For normal fields, we just use the
+		 * FieldSet test.
+		*/
+		if( fip->field_bit == FIELD_CUSTOM )
+		{
+			int ci, is_set = FALSE;
+
+			for( ci = 0; ci < td->td_customValueCount; ci++ )
+				is_set |= (td->td_customValues[ci].info == fip);
+
+			if( !is_set )
+				continue;
+		}
+		else if (!FieldSet(fields, fip->field_bit))
+			continue;
+
+
+		/*
+		 * Handle other fields.
+		 */
+		switch (fip->field_bit)
+		{
+		case FIELD_STRIPOFFSETS:
+			/*
+			 * We use one field bit for both strip and tile
+
+			 * offsets, and so must be careful in selecting
+			 * the appropriate field descriptor (so that tags
+			 * are written in sorted order).
+			 */
+			tag = isTiled(tif) ?
+			    TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS;
+			if (tag != fip->field_tag)
+				continue;
+			
+			dir->tdir_tag = (uint16) tag;
+			dir->tdir_type = (uint16) TIFF_LONG;
+			dir->tdir_count = (uint32) td->td_nstrips;
+			if (!TIFFWriteLongArray(tif, dir, td->td_stripoffset))
+				goto bad;
+			break;
+		case FIELD_STRIPBYTECOUNTS:
+			/*
+			 * We use one field bit for both strip and tile
+			 * byte counts, and so must be careful in selecting
+			 * the appropriate field descriptor (so that tags
+			 * are written in sorted order).
+			 */
+			tag = isTiled(tif) ?
+			    TIFFTAG_TILEBYTECOUNTS : TIFFTAG_STRIPBYTECOUNTS;
+			if (tag != fip->field_tag)
+				continue;
+			
+			dir->tdir_tag = (uint16) tag;
+			dir->tdir_type = (uint16) TIFF_LONG;
+			dir->tdir_count = (uint32) td->td_nstrips;
+			if (!TIFFWriteLongArray(tif, dir, td->td_stripbytecount))
+				goto bad;
+			break;
+		case FIELD_ROWSPERSTRIP:
+			TIFFSetupShortLong(tif, TIFFTAG_ROWSPERSTRIP,
+			    dir, td->td_rowsperstrip);
+			break;
+		case FIELD_COLORMAP:
+			if (!TIFFWriteShortTable(tif, TIFFTAG_COLORMAP, dir,
+			    3, td->td_colormap))
+				goto bad;
+			break;
+		case FIELD_IMAGEDIMENSIONS:
+			TIFFSetupShortLong(tif, TIFFTAG_IMAGEWIDTH,
+			    dir++, td->td_imagewidth);
+			TIFFSetupShortLong(tif, TIFFTAG_IMAGELENGTH,
+			    dir, td->td_imagelength);
+			break;
+		case FIELD_TILEDIMENSIONS:
+			TIFFSetupShortLong(tif, TIFFTAG_TILEWIDTH,
+			    dir++, td->td_tilewidth);
+			TIFFSetupShortLong(tif, TIFFTAG_TILELENGTH,
+			    dir, td->td_tilelength);
+			break;
+		case FIELD_COMPRESSION:
+			TIFFSetupShort(tif, TIFFTAG_COMPRESSION,
+			    dir, td->td_compression);
+			break;
+		case FIELD_PHOTOMETRIC:
+			TIFFSetupShort(tif, TIFFTAG_PHOTOMETRIC,
+			    dir, td->td_photometric);
+			break;
+		case FIELD_POSITION:
+			WriteRationalPair(TIFF_RATIONAL,
+			    TIFFTAG_XPOSITION, td->td_xposition,
+			    TIFFTAG_YPOSITION, td->td_yposition);
+			break;
+		case FIELD_RESOLUTION:
+			WriteRationalPair(TIFF_RATIONAL,
+			    TIFFTAG_XRESOLUTION, td->td_xresolution,
+			    TIFFTAG_YRESOLUTION, td->td_yresolution);
+			break;
+		case FIELD_BITSPERSAMPLE:
+		case FIELD_MINSAMPLEVALUE:
+		case FIELD_MAXSAMPLEVALUE:
+		case FIELD_SAMPLEFORMAT:
+			if (!TIFFWritePerSampleShorts(tif, fip->field_tag, dir))
+				goto bad;
+			break;
+		case FIELD_SMINSAMPLEVALUE:
+		case FIELD_SMAXSAMPLEVALUE:
+			if (!TIFFWritePerSampleAnys(tif,
+			    _TIFFSampleToTagType(tif), fip->field_tag, dir))
+				goto bad;
+			break;
+		case FIELD_PAGENUMBER:
+		case FIELD_HALFTONEHINTS:
+		case FIELD_YCBCRSUBSAMPLING:
+			if (!TIFFSetupShortPair(tif, fip->field_tag, dir))
+				goto bad;
+			break;
+		case FIELD_INKNAMES:
+			if (!TIFFWriteInkNames(tif, dir))
+				goto bad;
+			break;
+		case FIELD_TRANSFERFUNCTION:
+			if (!TIFFWriteTransferFunction(tif, dir))
+				goto bad;
+			break;
+		case FIELD_SUBIFD:
+			/*
+			 * XXX: Always write this field using LONG type
+			 * for backward compatibility.
+			 */
+			dir->tdir_tag = (uint16) fip->field_tag;
+			dir->tdir_type = (uint16) TIFF_LONG;
+			dir->tdir_count = (uint32) td->td_nsubifd;
+			if (!TIFFWriteLongArray(tif, dir, td->td_subifd))
+				goto bad;
+			/*
+			 * Total hack: if this directory includes a SubIFD
+			 * tag then force the next <n> directories to be
+			 * written as ``sub directories'' of this one.  This
+			 * is used to write things like thumbnails and
+			 * image masks that one wants to keep out of the
+			 * normal directory linkage access mechanism.
+			 */
+			if (dir->tdir_count > 0) {
+				tif->tif_flags |= TIFF_INSUBIFD;
+				tif->tif_nsubifd = (uint16) dir->tdir_count;
+				if (dir->tdir_count > 1)
+					tif->tif_subifdoff = dir->tdir_offset;
+				else
+					tif->tif_subifdoff = (uint32)(
+					      tif->tif_diroff
+					    + sizeof (uint16)
+					    + ((char*)&dir->tdir_offset-data));
+			}
+			break;
+		default:
+			/* XXX: Should be fixed and removed. */
+			if (fip->field_tag == TIFFTAG_DOTRANGE) {
+				if (!TIFFSetupShortPair(tif, fip->field_tag, dir))
+					goto bad;
+			}
+			else if (!TIFFWriteNormalTag(tif, dir, fip))
+				goto bad;
+			break;
+		}
+		dir++;
+                
+		if( fip->field_bit != FIELD_CUSTOM )
+			ResetFieldBit(fields, fip->field_bit);
+	}
+
+	/*
+	 * Write directory.
+	 */
+	dircount = (uint16) nfields;
+	diroff = (uint32) tif->tif_nextdiroff;
+	if (tif->tif_flags & TIFF_SWAB) {
+		/*
+		 * The file's byte order is opposite to the
+		 * native machine architecture.  We overwrite
+		 * the directory information with impunity
+		 * because it'll be released below after we
+		 * write it to the file.  Note that all the
+		 * other tag construction routines assume that
+		 * we do this byte-swapping; i.e. they only
+		 * byte-swap indirect data.
+		 */
+		for (dir = (TIFFDirEntry*) data; dircount; dir++, dircount--) {
+			TIFFSwabArrayOfShort(&dir->tdir_tag, 2);
+			TIFFSwabArrayOfLong(&dir->tdir_count, 2);
+		}
+		dircount = (uint16) nfields;
+		TIFFSwabShort(&dircount);
+		TIFFSwabLong(&diroff);
+	}
+	(void) TIFFSeekFile(tif, tif->tif_diroff, SEEK_SET);
+	if (!WriteOK(tif, &dircount, sizeof (dircount))) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error writing directory count");
+		goto bad;
+	}
+	if (!WriteOK(tif, data, dirsize)) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error writing directory contents");
+		goto bad;
+	}
+	if (!WriteOK(tif, &diroff, sizeof (diroff))) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error writing directory link");
+		goto bad;
+	}
+	if (done) {
+		TIFFFreeDirectory(tif);
+		tif->tif_flags &= ~TIFF_DIRTYDIRECT;
+		(*tif->tif_cleanup)(tif);
+
+		/*
+		* Reset directory-related state for subsequent
+		* directories.
+		*/
+		TIFFCreateDirectory(tif);
+	}
+	_TIFFfree(data);
+	return (1);
+bad:
+	_TIFFfree(data);
+	return (0);
+}
+#undef WriteRationalPair
+
+int
+TIFFWriteDirectory(TIFF* tif)
+{
+	return _TIFFWriteDirectory(tif, TRUE);
+}
+
+/*
+ * Similar to TIFFWriteDirectory(), writes the directory out
+ * but leaves all data structures in memory so that it can be
+ * written again.  This will make a partially written TIFF file
+ * readable before it is successfully completed/closed.
+ */ 
+int
+TIFFCheckpointDirectory(TIFF* tif)
+{
+	int rc;
+	/* Setup the strips arrays, if they haven't already been. */
+	if (tif->tif_dir.td_stripoffset == NULL)
+	    (void) TIFFSetupStrips(tif);
+	rc = _TIFFWriteDirectory(tif, FALSE);
+	(void) TIFFSetWriteOffset(tif, TIFFSeekFile(tif, 0, SEEK_END));
+	return rc;
+}
+
+/*
+ * Process tags that are not special cased.
+ */
+static int
+TIFFWriteNormalTag(TIFF* tif, TIFFDirEntry* dir, const TIFFFieldInfo* fip)
+{
+	uint16 wc = (uint16) fip->field_writecount;
+	uint32 wc2;
+
+	dir->tdir_tag = (uint16) fip->field_tag;
+	dir->tdir_type = (uint16) fip->field_type;
+	dir->tdir_count = wc;
+	
+	switch (fip->field_type) {
+	case TIFF_SHORT:
+	case TIFF_SSHORT:
+		if (fip->field_passcount) {
+			uint16* wp;
+			if (wc == (uint16) TIFF_VARIABLE2) {
+				TIFFGetField(tif, fip->field_tag, &wc2, &wp);
+				dir->tdir_count = wc2;
+			} else {	/* Assume TIFF_VARIABLE */
+				TIFFGetField(tif, fip->field_tag, &wc, &wp);
+				dir->tdir_count = wc;
+			}
+			if (!TIFFWriteShortArray(tif, dir, wp))
+				return 0;
+		} else {
+			if (wc == 1) {
+				uint16 sv;
+				TIFFGetField(tif, fip->field_tag, &sv);
+				dir->tdir_offset =
+					TIFFInsertData(tif, dir->tdir_type, sv);
+			} else {
+				uint16* wp;
+				TIFFGetField(tif, fip->field_tag, &wp);
+				if (!TIFFWriteShortArray(tif, dir, wp))
+					return 0;
+			}
+		}
+		break;
+	case TIFF_LONG:
+	case TIFF_SLONG:
+	case TIFF_IFD:
+		if (fip->field_passcount) {
+			uint32* lp;
+			if (wc == (uint16) TIFF_VARIABLE2) {
+				TIFFGetField(tif, fip->field_tag, &wc2, &lp);
+				dir->tdir_count = wc2;
+			} else {	/* Assume TIFF_VARIABLE */
+				TIFFGetField(tif, fip->field_tag, &wc, &lp);
+				dir->tdir_count = wc;
+			}
+			if (!TIFFWriteLongArray(tif, dir, lp))
+				return 0;
+		} else {
+			if (wc == 1) {
+				/* XXX handle LONG->SHORT conversion */
+				TIFFGetField(tif, fip->field_tag,
+					     &dir->tdir_offset);
+			} else {
+				uint32* lp;
+				TIFFGetField(tif, fip->field_tag, &lp);
+				if (!TIFFWriteLongArray(tif, dir, lp))
+					return 0;
+			}
+		}
+		break;
+	case TIFF_RATIONAL:
+	case TIFF_SRATIONAL:
+		if (fip->field_passcount) {
+			float* fp;
+			if (wc == (uint16) TIFF_VARIABLE2) {
+				TIFFGetField(tif, fip->field_tag, &wc2, &fp);
+				dir->tdir_count = wc2;
+			} else {	/* Assume TIFF_VARIABLE */
+				TIFFGetField(tif, fip->field_tag, &wc, &fp);
+				dir->tdir_count = wc;
+			}
+			if (!TIFFWriteRationalArray(tif, dir, fp))
+				return 0;
+		} else {
+			if (wc == 1) {
+				float fv;
+				TIFFGetField(tif, fip->field_tag, &fv);
+				if (!TIFFWriteRationalArray(tif, dir, &fv))
+					return 0;
+			} else {
+				float* fp;
+				TIFFGetField(tif, fip->field_tag, &fp);
+				if (!TIFFWriteRationalArray(tif, dir, fp))
+					return 0;
+			}
+		}
+		break;
+	case TIFF_FLOAT:
+		if (fip->field_passcount) {
+			float* fp;
+			if (wc == (uint16) TIFF_VARIABLE2) {
+				TIFFGetField(tif, fip->field_tag, &wc2, &fp);
+				dir->tdir_count = wc2;
+			} else {	/* Assume TIFF_VARIABLE */
+				TIFFGetField(tif, fip->field_tag, &wc, &fp);
+				dir->tdir_count = wc;
+			}
+			if (!TIFFWriteFloatArray(tif, dir, fp))
+				return 0;
+		} else {
+			if (wc == 1) {
+				float fv;
+				TIFFGetField(tif, fip->field_tag, &fv);
+				if (!TIFFWriteFloatArray(tif, dir, &fv))
+					return 0;
+			} else {
+				float* fp;
+				TIFFGetField(tif, fip->field_tag, &fp);
+				if (!TIFFWriteFloatArray(tif, dir, fp))
+					return 0;
+			}
+		}
+		break;
+	case TIFF_DOUBLE:
+		if (fip->field_passcount) {
+			double* dp;
+			if (wc == (uint16) TIFF_VARIABLE2) {
+				TIFFGetField(tif, fip->field_tag, &wc2, &dp);
+				dir->tdir_count = wc2;
+			} else {	/* Assume TIFF_VARIABLE */
+				TIFFGetField(tif, fip->field_tag, &wc, &dp);
+				dir->tdir_count = wc;
+			}
+			if (!TIFFWriteDoubleArray(tif, dir, dp))
+				return 0;
+		} else {
+			if (wc == 1) {
+				double dv;
+				TIFFGetField(tif, fip->field_tag, &dv);
+				if (!TIFFWriteDoubleArray(tif, dir, &dv))
+					return 0;
+			} else {
+				double* dp;
+				TIFFGetField(tif, fip->field_tag, &dp);
+				if (!TIFFWriteDoubleArray(tif, dir, dp))
+					return 0;
+			}
+		}
+		break;
+	case TIFF_ASCII:
+		{ 
+                    char* cp;
+                    if (fip->field_passcount)
+                        TIFFGetField(tif, fip->field_tag, &wc, &cp);
+                    else
+                        TIFFGetField(tif, fip->field_tag, &cp);
+
+                    dir->tdir_count = (uint32) (strlen(cp) + 1);
+                    if (!TIFFWriteByteArray(tif, dir, cp))
+                        return (0);
+		}
+		break;
+
+        case TIFF_BYTE:
+        case TIFF_SBYTE:          
+		if (fip->field_passcount) {
+			char* cp;
+			if (wc == (uint16) TIFF_VARIABLE2) {
+				TIFFGetField(tif, fip->field_tag, &wc2, &cp);
+				dir->tdir_count = wc2;
+			} else {	/* Assume TIFF_VARIABLE */
+				TIFFGetField(tif, fip->field_tag, &wc, &cp);
+				dir->tdir_count = wc;
+			}
+			if (!TIFFWriteByteArray(tif, dir, cp))
+				return 0;
+		} else {
+			if (wc == 1) {
+				char cv;
+				TIFFGetField(tif, fip->field_tag, &cv);
+				if (!TIFFWriteByteArray(tif, dir, &cv))
+					return 0;
+			} else {
+				char* cp;
+				TIFFGetField(tif, fip->field_tag, &cp);
+				if (!TIFFWriteByteArray(tif, dir, cp))
+					return 0;
+			}
+		}
+                break;
+
+	case TIFF_UNDEFINED:
+		{ char* cp;
+		  if (wc == (unsigned short) TIFF_VARIABLE) {
+			TIFFGetField(tif, fip->field_tag, &wc, &cp);
+			dir->tdir_count = wc;
+		  } else if (wc == (unsigned short) TIFF_VARIABLE2) {
+			TIFFGetField(tif, fip->field_tag, &wc2, &cp);
+			dir->tdir_count = wc2;
+		  } else 
+			TIFFGetField(tif, fip->field_tag, &cp);
+		  if (!TIFFWriteByteArray(tif, dir, cp))
+			return (0);
+		}
+		break;
+
+        case TIFF_NOTYPE:
+                break;
+	}
+	return (1);
+}
+
+/*
+ * Setup a directory entry with either a SHORT
+ * or LONG type according to the value.
+ */
+static void
+TIFFSetupShortLong(TIFF* tif, ttag_t tag, TIFFDirEntry* dir, uint32 v)
+{
+	dir->tdir_tag = (uint16) tag;
+	dir->tdir_count = 1;
+	if (v > 0xffffL) {
+		dir->tdir_type = (short) TIFF_LONG;
+		dir->tdir_offset = v;
+	} else {
+		dir->tdir_type = (short) TIFF_SHORT;
+		dir->tdir_offset = TIFFInsertData(tif, (int) TIFF_SHORT, v);
+	}
+}
+
+/*
+ * Setup a SHORT directory entry
+ */
+static void
+TIFFSetupShort(TIFF* tif, ttag_t tag, TIFFDirEntry* dir, uint16 v)
+{
+	dir->tdir_tag = (uint16) tag;
+	dir->tdir_count = 1;
+	dir->tdir_type = (short) TIFF_SHORT;
+	dir->tdir_offset = TIFFInsertData(tif, (int) TIFF_SHORT, v);
+}
+#undef MakeShortDirent
+
+#define	NITEMS(x)	(sizeof (x) / sizeof (x[0]))
+/*
+ * Setup a directory entry that references a
+ * samples/pixel array of SHORT values and
+ * (potentially) write the associated indirect
+ * values.
+ */
+static int
+TIFFWritePerSampleShorts(TIFF* tif, ttag_t tag, TIFFDirEntry* dir)
+{
+	uint16 buf[10], v;
+	uint16* w = buf;
+	uint16 i, samples = tif->tif_dir.td_samplesperpixel;
+	int status;
+
+	if (samples > NITEMS(buf)) {
+		w = (uint16*) _TIFFmalloc(samples * sizeof (uint16));
+		if (w == NULL) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			    "No space to write per-sample shorts");
+			return (0);
+		}
+	}
+	TIFFGetField(tif, tag, &v);
+	for (i = 0; i < samples; i++)
+		w[i] = v;
+	
+	dir->tdir_tag = (uint16) tag;
+	dir->tdir_type = (uint16) TIFF_SHORT;
+	dir->tdir_count = samples;
+	status = TIFFWriteShortArray(tif, dir, w);
+	if (w != buf)
+		_TIFFfree((char*) w);
+	return (status);
+}
+
+/*
+ * Setup a directory entry that references a samples/pixel array of ``type''
+ * values and (potentially) write the associated indirect values.  The source
+ * data from TIFFGetField() for the specified tag must be returned as double.
+ */
+static int
+TIFFWritePerSampleAnys(TIFF* tif,
+    TIFFDataType type, ttag_t tag, TIFFDirEntry* dir)
+{
+	double buf[10], v;
+	double* w = buf;
+	uint16 i, samples = tif->tif_dir.td_samplesperpixel;
+	int status;
+
+	if (samples > NITEMS(buf)) {
+		w = (double*) _TIFFmalloc(samples * sizeof (double));
+		if (w == NULL) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			    "No space to write per-sample values");
+			return (0);
+		}
+	}
+	TIFFGetField(tif, tag, &v);
+	for (i = 0; i < samples; i++)
+		w[i] = v;
+	status = TIFFWriteAnyArray(tif, type, tag, dir, samples, w);
+	if (w != buf)
+		_TIFFfree(w);
+	return (status);
+}
+#undef NITEMS
+
+/*
+ * Setup a pair of shorts that are returned by
+ * value, rather than as a reference to an array.
+ */
+static int
+TIFFSetupShortPair(TIFF* tif, ttag_t tag, TIFFDirEntry* dir)
+{
+	uint16 v[2];
+
+	TIFFGetField(tif, tag, &v[0], &v[1]);
+
+	dir->tdir_tag = (uint16) tag;
+	dir->tdir_type = (uint16) TIFF_SHORT;
+	dir->tdir_count = 2;
+	return (TIFFWriteShortArray(tif, dir, v));
+}
+
+/*
+ * Setup a directory entry for an NxM table of shorts,
+ * where M is known to be 2**bitspersample, and write
+ * the associated indirect data.
+ */
+static int
+TIFFWriteShortTable(TIFF* tif,
+    ttag_t tag, TIFFDirEntry* dir, uint32 n, uint16** table)
+{
+	uint32 i, off;
+
+	dir->tdir_tag = (uint16) tag;
+	dir->tdir_type = (short) TIFF_SHORT;
+	/* XXX -- yech, fool TIFFWriteData */
+	dir->tdir_count = (uint32) (1L<<tif->tif_dir.td_bitspersample);
+	off = tif->tif_dataoff;
+	for (i = 0; i < n; i++)
+		if (!TIFFWriteData(tif, dir, (char *)table[i]))
+			return (0);
+	dir->tdir_count *= n;
+	dir->tdir_offset = off;
+	return (1);
+}
+
+/*
+ * Write/copy data associated with an ASCII or opaque tag value.
+ */
+static int
+TIFFWriteByteArray(TIFF* tif, TIFFDirEntry* dir, char* cp)
+{
+	if (dir->tdir_count > 4) {
+		if (!TIFFWriteData(tif, dir, cp))
+			return (0);
+	} else
+		_TIFFmemcpy(&dir->tdir_offset, cp, dir->tdir_count);
+	return (1);
+}
+
+/*
+ * Setup a directory entry of an array of SHORT
+ * or SSHORT and write the associated indirect values.
+ */
+static int
+TIFFWriteShortArray(TIFF* tif, TIFFDirEntry* dir, uint16* v)
+{
+	if (dir->tdir_count <= 2) {
+		if (tif->tif_header.tiff_magic == TIFF_BIGENDIAN) {
+			dir->tdir_offset = (uint32) ((long) v[0] << 16);
+			if (dir->tdir_count == 2)
+				dir->tdir_offset |= v[1] & 0xffff;
+		} else {
+			dir->tdir_offset = v[0] & 0xffff;
+			if (dir->tdir_count == 2)
+				dir->tdir_offset |= (long) v[1] << 16;
+		}
+		return (1);
+	} else
+		return (TIFFWriteData(tif, dir, (char*) v));
+}
+
+/*
+ * Setup a directory entry of an array of LONG
+ * or SLONG and write the associated indirect values.
+ */
+static int
+TIFFWriteLongArray(TIFF* tif, TIFFDirEntry* dir, uint32* v)
+{
+	if (dir->tdir_count == 1) {
+		dir->tdir_offset = v[0];
+		return (1);
+	} else
+		return (TIFFWriteData(tif, dir, (char*) v));
+}
+
+/*
+ * Setup a directory entry of an array of RATIONAL
+ * or SRATIONAL and write the associated indirect values.
+ */
+static int
+TIFFWriteRationalArray(TIFF* tif, TIFFDirEntry* dir, float* v)
+{
+	uint32 i;
+	uint32* t;
+	int status;
+
+	t = (uint32*) _TIFFmalloc(2 * dir->tdir_count * sizeof (uint32));
+	if (t == NULL) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		    "No space to write RATIONAL array");
+		return (0);
+	}
+	for (i = 0; i < dir->tdir_count; i++) {
+		float fv = v[i];
+		int sign = 1;
+		uint32 den;
+
+		if (fv < 0) {
+			if (dir->tdir_type == TIFF_RATIONAL) {
+				TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
+	"\"%s\": Information lost writing value (%g) as (unsigned) RATIONAL",
+				_TIFFFieldWithTag(tif,dir->tdir_tag)->field_name,
+				fv);
+				fv = 0;
+			} else
+				fv = -fv, sign = -1;
+		}
+		den = 1L;
+		if (fv > 0) {
+			while (fv < 1L<<(31-3) && den < 1L<<(31-3))
+				fv *= 1<<3, den *= 1L<<3;
+		}
+		t[2*i+0] = (uint32) (sign * (fv + 0.5));
+		t[2*i+1] = den;
+	}
+	status = TIFFWriteData(tif, dir, (char *)t);
+	_TIFFfree((char*) t);
+	return (status);
+}
+
+static int
+TIFFWriteFloatArray(TIFF* tif, TIFFDirEntry* dir, float* v)
+{
+	TIFFCvtNativeToIEEEFloat(tif, dir->tdir_count, v);
+	if (dir->tdir_count == 1) {
+		dir->tdir_offset = *(uint32*) &v[0];
+		return (1);
+	} else
+		return (TIFFWriteData(tif, dir, (char*) v));
+}
+
+static int
+TIFFWriteDoubleArray(TIFF* tif, TIFFDirEntry* dir, double* v)
+{
+	TIFFCvtNativeToIEEEDouble(tif, dir->tdir_count, v);
+	return (TIFFWriteData(tif, dir, (char*) v));
+}
+
+/*
+ * Write an array of ``type'' values for a specified tag (i.e. this is a tag
+ * which is allowed to have different types, e.g. SMaxSampleType).
+ * Internally the data values are represented as double since a double can
+ * hold any of the TIFF tag types (yes, this should really be an abstract
+ * type tany_t for portability).  The data is converted into the specified
+ * type in a temporary buffer and then handed off to the appropriate array
+ * writer.
+ */
+static int
+TIFFWriteAnyArray(TIFF* tif,
+    TIFFDataType type, ttag_t tag, TIFFDirEntry* dir, uint32 n, double* v)
+{
+	char buf[10 * sizeof(double)];
+	char* w = buf;
+	int i, status = 0;
+
+	if (n * TIFFDataWidth(type) > sizeof buf) {
+		w = (char*) _TIFFmalloc(n * TIFFDataWidth(type));
+		if (w == NULL) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			    "No space to write array");
+			return (0);
+		}
+	}
+
+	dir->tdir_tag = (uint16) tag;
+	dir->tdir_type = (uint16) type;
+	dir->tdir_count = n;
+
+	switch (type) {
+	case TIFF_BYTE:
+		{ 
+			uint8* bp = (uint8*) w;
+			for (i = 0; i < (int) n; i++)
+				bp[i] = (uint8) v[i];
+			if (!TIFFWriteByteArray(tif, dir, (char*) bp))
+				goto out;
+		}
+		break;
+	case TIFF_SBYTE:
+		{ 
+			int8* bp = (int8*) w;
+			for (i = 0; i < (int) n; i++)
+				bp[i] = (int8) v[i];
+			if (!TIFFWriteByteArray(tif, dir, (char*) bp))
+				goto out;
+		}
+		break;
+	case TIFF_SHORT:
+		{
+			uint16* bp = (uint16*) w;
+			for (i = 0; i < (int) n; i++)
+				bp[i] = (uint16) v[i];
+			if (!TIFFWriteShortArray(tif, dir, (uint16*)bp))
+				goto out;
+		}
+		break;
+	case TIFF_SSHORT:
+		{ 
+			int16* bp = (int16*) w;
+			for (i = 0; i < (int) n; i++)
+				bp[i] = (int16) v[i];
+			if (!TIFFWriteShortArray(tif, dir, (uint16*)bp))
+				goto out;
+		}
+		break;
+	case TIFF_LONG:
+		{
+			uint32* bp = (uint32*) w;
+			for (i = 0; i < (int) n; i++)
+				bp[i] = (uint32) v[i];
+			if (!TIFFWriteLongArray(tif, dir, bp))
+				goto out;
+		}
+		break;
+	case TIFF_SLONG:
+		{
+			int32* bp = (int32*) w;
+			for (i = 0; i < (int) n; i++)
+				bp[i] = (int32) v[i];
+			if (!TIFFWriteLongArray(tif, dir, (uint32*) bp))
+				goto out;
+		}
+		break;
+	case TIFF_FLOAT:
+		{ 
+			float* bp = (float*) w;
+			for (i = 0; i < (int) n; i++)
+				bp[i] = (float) v[i];
+			if (!TIFFWriteFloatArray(tif, dir, bp))
+				goto out;
+		}
+		break;
+	case TIFF_DOUBLE:
+		return (TIFFWriteDoubleArray(tif, dir, v));
+	default:
+		/* TIFF_NOTYPE */
+		/* TIFF_ASCII */
+		/* TIFF_UNDEFINED */
+		/* TIFF_RATIONAL */
+		/* TIFF_SRATIONAL */
+		goto out;
+	}
+	status = 1;
+ out:
+	if (w != buf)
+		_TIFFfree(w);
+	return (status);
+}
+
+static int
+TIFFWriteTransferFunction(TIFF* tif, TIFFDirEntry* dir)
+{
+	TIFFDirectory* td = &tif->tif_dir;
+	tsize_t n = (1L<<td->td_bitspersample) * sizeof (uint16);
+	uint16** tf = td->td_transferfunction;
+	int ncols;
+
+	/*
+	 * Check if the table can be written as a single column,
+	 * or if it must be written as 3 columns.  Note that we
+	 * write a 3-column tag if there are 2 samples/pixel and
+	 * a single column of data won't suffice--hmm.
+	 */
+	switch (td->td_samplesperpixel - td->td_extrasamples) {
+	default:	if (_TIFFmemcmp(tf[0], tf[2], n)) { ncols = 3; break; }
+	case 2:		if (_TIFFmemcmp(tf[0], tf[1], n)) { ncols = 3; break; }
+	case 1: case 0:	ncols = 1;
+	}
+	return (TIFFWriteShortTable(tif,
+	    TIFFTAG_TRANSFERFUNCTION, dir, ncols, tf));
+}
+
+static int
+TIFFWriteInkNames(TIFF* tif, TIFFDirEntry* dir)
+{
+	TIFFDirectory* td = &tif->tif_dir;
+
+	dir->tdir_tag = TIFFTAG_INKNAMES;
+	dir->tdir_type = (short) TIFF_ASCII;
+	dir->tdir_count = td->td_inknameslen;
+	return (TIFFWriteByteArray(tif, dir, td->td_inknames));
+}
+
+/*
+ * Write a contiguous directory item.
+ */
+static int
+TIFFWriteData(TIFF* tif, TIFFDirEntry* dir, char* cp)
+{
+	tsize_t cc;
+
+	if (tif->tif_flags & TIFF_SWAB) {
+		switch (dir->tdir_type) {
+		case TIFF_SHORT:
+		case TIFF_SSHORT:
+			TIFFSwabArrayOfShort((uint16*) cp, dir->tdir_count);
+			break;
+		case TIFF_LONG:
+		case TIFF_SLONG:
+		case TIFF_FLOAT:
+			TIFFSwabArrayOfLong((uint32*) cp, dir->tdir_count);
+			break;
+		case TIFF_RATIONAL:
+		case TIFF_SRATIONAL:
+			TIFFSwabArrayOfLong((uint32*) cp, 2*dir->tdir_count);
+			break;
+		case TIFF_DOUBLE:
+			TIFFSwabArrayOfDouble((double*) cp, dir->tdir_count);
+			break;
+		}
+	}
+	dir->tdir_offset = tif->tif_dataoff;
+	cc = dir->tdir_count * TIFFDataWidth((TIFFDataType) dir->tdir_type);
+	if (SeekOK(tif, dir->tdir_offset) &&
+	    WriteOK(tif, cp, cc)) {
+		tif->tif_dataoff += (cc + 1) & ~1;
+		return (1);
+	}
+	TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error writing data for field \"%s\"",
+	    _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name);
+	return (0);
+}
+
+/*
+ * Similar to TIFFWriteDirectory(), but if the directory has already
+ * been written once, it is relocated to the end of the file, in case it
+ * has changed in size.  Note that this will result in the loss of the 
+ * previously used directory space. 
+ */ 
+
+int 
+TIFFRewriteDirectory( TIFF *tif )
+{
+    static const char module[] = "TIFFRewriteDirectory";
+
+    /* We don't need to do anything special if it hasn't been written. */
+    if( tif->tif_diroff == 0 )
+        return TIFFWriteDirectory( tif );
+
+    /*
+    ** Find and zero the pointer to this directory, so that TIFFLinkDirectory
+    ** will cause it to be added after this directories current pre-link.
+    */
+    
+    /* Is it the first directory in the file? */
+    if (tif->tif_header.tiff_diroff == tif->tif_diroff) 
+    {
+        tif->tif_header.tiff_diroff = 0;
+        tif->tif_diroff = 0;
+
+        TIFFSeekFile(tif, (toff_t)(TIFF_MAGIC_SIZE+TIFF_VERSION_SIZE),
+		     SEEK_SET);
+        if (!WriteOK(tif, &(tif->tif_header.tiff_diroff), 
+                     sizeof (tif->tif_diroff))) 
+        {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error updating TIFF header");
+            return (0);
+        }
+    }
+    else
+    {
+        toff_t  nextdir, off;
+
+	nextdir = tif->tif_header.tiff_diroff;
+	do {
+		uint16 dircount;
+
+		if (!SeekOK(tif, nextdir) ||
+		    !ReadOK(tif, &dircount, sizeof (dircount))) {
+			TIFFErrorExt(tif->tif_clientdata, module, "Error fetching directory count");
+			return (0);
+		}
+		if (tif->tif_flags & TIFF_SWAB)
+			TIFFSwabShort(&dircount);
+		(void) TIFFSeekFile(tif,
+		    dircount * sizeof (TIFFDirEntry), SEEK_CUR);
+		if (!ReadOK(tif, &nextdir, sizeof (nextdir))) {
+			TIFFErrorExt(tif->tif_clientdata, module, "Error fetching directory link");
+			return (0);
+		}
+		if (tif->tif_flags & TIFF_SWAB)
+			TIFFSwabLong(&nextdir);
+	} while (nextdir != tif->tif_diroff && nextdir != 0);
+        off = TIFFSeekFile(tif, 0, SEEK_CUR); /* get current offset */
+        (void) TIFFSeekFile(tif, off - (toff_t)sizeof(nextdir), SEEK_SET);
+        tif->tif_diroff = 0;
+	if (!WriteOK(tif, &(tif->tif_diroff), sizeof (nextdir))) {
+		TIFFErrorExt(tif->tif_clientdata, module, "Error writing directory link");
+		return (0);
+	}
+    }
+
+    /*
+    ** Now use TIFFWriteDirectory() normally.
+    */
+
+    return TIFFWriteDirectory( tif );
+}
+
+
+/*
+ * Link the current directory into the
+ * directory chain for the file.
+ */
+static int
+TIFFLinkDirectory(TIFF* tif)
+{
+	static const char module[] = "TIFFLinkDirectory";
+	toff_t nextdir;
+	toff_t diroff, off;
+
+	tif->tif_diroff = (TIFFSeekFile(tif, (toff_t) 0, SEEK_END)+1) &~ 1;
+	diroff = tif->tif_diroff;
+	if (tif->tif_flags & TIFF_SWAB)
+		TIFFSwabLong(&diroff);
+
+	/*
+	 * Handle SubIFDs
+	 */
+        if (tif->tif_flags & TIFF_INSUBIFD) {
+		(void) TIFFSeekFile(tif, tif->tif_subifdoff, SEEK_SET);
+		if (!WriteOK(tif, &diroff, sizeof (diroff))) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+			    "%s: Error writing SubIFD directory link",
+			    tif->tif_name);
+			return (0);
+		}
+		/*
+		 * Advance to the next SubIFD or, if this is
+		 * the last one configured, revert back to the
+		 * normal directory linkage.
+		 */
+		if (--tif->tif_nsubifd)
+			tif->tif_subifdoff += sizeof (diroff);
+		else
+			tif->tif_flags &= ~TIFF_INSUBIFD;
+		return (1);
+	}
+
+	if (tif->tif_header.tiff_diroff == 0) {
+		/*
+		 * First directory, overwrite offset in header.
+		 */
+		tif->tif_header.tiff_diroff = tif->tif_diroff;
+		(void) TIFFSeekFile(tif,
+				    (toff_t)(TIFF_MAGIC_SIZE+TIFF_VERSION_SIZE),
+                                    SEEK_SET);
+		if (!WriteOK(tif, &diroff, sizeof (diroff))) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error writing TIFF header");
+			return (0);
+		}
+		return (1);
+	}
+	/*
+	 * Not the first directory, search to the last and append.
+	 */
+	nextdir = tif->tif_header.tiff_diroff;
+	do {
+		uint16 dircount;
+
+		if (!SeekOK(tif, nextdir) ||
+		    !ReadOK(tif, &dircount, sizeof (dircount))) {
+			TIFFErrorExt(tif->tif_clientdata, module, "Error fetching directory count");
+			return (0);
+		}
+		if (tif->tif_flags & TIFF_SWAB)
+			TIFFSwabShort(&dircount);
+		(void) TIFFSeekFile(tif,
+		    dircount * sizeof (TIFFDirEntry), SEEK_CUR);
+		if (!ReadOK(tif, &nextdir, sizeof (nextdir))) {
+			TIFFErrorExt(tif->tif_clientdata, module, "Error fetching directory link");
+			return (0);
+		}
+		if (tif->tif_flags & TIFF_SWAB)
+			TIFFSwabLong(&nextdir);
+	} while (nextdir != 0);
+        off = TIFFSeekFile(tif, 0, SEEK_CUR); /* get current offset */
+        (void) TIFFSeekFile(tif, off - (toff_t)sizeof(nextdir), SEEK_SET);
+	if (!WriteOK(tif, &diroff, sizeof (diroff))) {
+		TIFFErrorExt(tif->tif_clientdata, module, "Error writing directory link");
+		return (0);
+	}
+	return (1);
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_dumpmode.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_dumpmode.c
new file mode 100644
index 0000000000..2e6450bdc4
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_dumpmode.c
@@ -0,0 +1,117 @@
+/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_dumpmode.c,v 1.5 2006/03/25 03:09:24 joris Exp $ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ *
+ * "Null" Compression Algorithm Support.
+ */
+#include "tiffiop.h"
+
+/*
+ * Encode a hunk of pixels.
+ */
+static int
+DumpModeEncode(TIFF* tif, tidata_t pp, tsize_t cc, tsample_t s)
+{
+	(void) s;
+	while (cc > 0) {
+		tsize_t n;
+
+		n = cc;
+		if (tif->tif_rawcc + n > tif->tif_rawdatasize)
+			n = tif->tif_rawdatasize - tif->tif_rawcc;
+
+		assert( n > 0 );
+                
+		/*
+		 * Avoid copy if client has setup raw
+		 * data buffer to avoid extra copy.
+		 */
+		if (tif->tif_rawcp != pp)
+			_TIFFmemcpy(tif->tif_rawcp, pp, n);
+		tif->tif_rawcp += n;
+		tif->tif_rawcc += n;
+		pp += n;
+		cc -= n;
+		if (tif->tif_rawcc >= tif->tif_rawdatasize &&
+		    !TIFFFlushData1(tif))
+			return (-1);
+	}
+	return (1);
+}
+
+/*
+ * Decode a hunk of pixels.
+ */
+static int
+DumpModeDecode(TIFF* tif, tidata_t buf, tsize_t cc, tsample_t s)
+{
+	(void) s;
+	if (tif->tif_rawcc < cc) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		    "DumpModeDecode: Not enough data for scanline %d",
+		    tif->tif_row);
+		return (0);
+	}
+	/*
+	 * Avoid copy if client has setup raw
+	 * data buffer to avoid extra copy.
+	 */
+	if (tif->tif_rawcp != buf)
+		_TIFFmemcpy(buf, tif->tif_rawcp, cc);
+	tif->tif_rawcp += cc;
+	tif->tif_rawcc -= cc;
+	return (1);
+}
+
+/*
+ * Seek forwards nrows in the current strip.
+ */
+static int
+DumpModeSeek(TIFF* tif, uint32 nrows)
+{
+	tif->tif_rawcp += nrows * tif->tif_scanlinesize;
+	tif->tif_rawcc -= nrows * tif->tif_scanlinesize;
+	return (1);
+}
+
+/*
+ * Initialize dump mode.
+ */
+int
+TIFFInitDumpMode(TIFF* tif, int scheme)
+{
+	(void) scheme;
+	tif->tif_decoderow = DumpModeDecode;
+	tif->tif_decodestrip = DumpModeDecode;
+	tif->tif_decodetile = DumpModeDecode;
+	tif->tif_encoderow = DumpModeEncode;
+	tif->tif_encodestrip = DumpModeEncode;
+	tif->tif_encodetile = DumpModeEncode;
+	tif->tif_seek = DumpModeSeek;
+	return (1);
+}
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_error.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_error.c
new file mode 100644
index 0000000000..7025080ed3
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_error.c
@@ -0,0 +1,73 @@
+/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_error.c,v 1.4 2005/12/23 01:18:59 joris Exp $ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ */
+#include "tiffiop.h"
+
+TIFFErrorHandlerExt _TIFFerrorHandlerExt = NULL;
+
+TIFFErrorHandler
+TIFFSetErrorHandler(TIFFErrorHandler handler)
+{
+	TIFFErrorHandler prev = _TIFFerrorHandler;
+	_TIFFerrorHandler = handler;
+	return (prev);
+}
+
+TIFFErrorHandlerExt
+TIFFSetErrorHandlerExt(TIFFErrorHandlerExt handler)
+{
+	TIFFErrorHandlerExt prev = _TIFFerrorHandlerExt;
+	_TIFFerrorHandlerExt = handler;
+	return (prev);
+}
+
+void
+TIFFError(const char* module, const char* fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	if (_TIFFerrorHandler)
+		(*_TIFFerrorHandler)(module, fmt, ap);
+	if (_TIFFerrorHandlerExt)
+		(*_TIFFerrorHandlerExt)(0, module, fmt, ap);
+	va_end(ap);
+}
+
+void
+TIFFErrorExt(thandle_t fd, const char* module, const char* fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	if (_TIFFerrorHandler)
+		(*_TIFFerrorHandler)(module, fmt, ap);
+	if (_TIFFerrorHandlerExt)
+		(*_TIFFerrorHandlerExt)(fd, module, fmt, ap);
+	va_end(ap);
+}
+
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_extension.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_extension.c
new file mode 100644
index 0000000000..6861522285
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_extension.c
@@ -0,0 +1,111 @@
+/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_extension.c,v 1.4 2004/10/02 13:29:41 dron Exp $ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ *
+ * Various routines support external extension of the tag set, and other
+ * application extension capabilities. 
+ */
+
+#include "tiffiop.h"
+
+int TIFFGetTagListCount( TIFF *tif )
+
+{
+    TIFFDirectory* td = &tif->tif_dir;
+    
+    return td->td_customValueCount;
+}
+
+ttag_t TIFFGetTagListEntry( TIFF *tif, int tag_index )
+
+{
+    TIFFDirectory* td = &tif->tif_dir;
+
+    if( tag_index < 0 || tag_index >= td->td_customValueCount )
+        return (ttag_t) -1;
+    else
+        return td->td_customValues[tag_index].info->field_tag;
+}
+
+/*
+** This provides read/write access to the TIFFTagMethods within the TIFF
+** structure to application code without giving access to the private
+** TIFF structure.
+*/
+TIFFTagMethods *TIFFAccessTagMethods( TIFF *tif )
+
+{
+    return &(tif->tif_tagmethods);
+}
+
+void *TIFFGetClientInfo( TIFF *tif, const char *name )
+
+{
+    TIFFClientInfoLink *link = tif->tif_clientinfo;
+
+    while( link != NULL && strcmp(link->name,name) != 0 )
+        link = link->next;
+
+    if( link != NULL )
+        return link->data;
+    else
+        return NULL;
+}
+
+void TIFFSetClientInfo( TIFF *tif, void *data, const char *name )
+
+{
+    TIFFClientInfoLink *link = tif->tif_clientinfo;
+
+    /*
+    ** Do we have an existing link with this name?  If so, just
+    ** set it.
+    */
+    while( link != NULL && strcmp(link->name,name) != 0 )
+        link = link->next;
+
+    if( link != NULL )
+    {
+        link->data = data;
+        return;
+    }
+
+    /*
+    ** Create a new link.
+    */
+
+    link = (TIFFClientInfoLink *) _TIFFmalloc(sizeof(TIFFClientInfoLink));
+    assert (link != NULL);
+    link->next = tif->tif_clientinfo;
+    link->name = (char *) _TIFFmalloc(strlen(name)+1);
+    assert (link->name != NULL);
+    strcpy(link->name, name);
+    link->data = data;
+
+    tif->tif_clientinfo = link;
+}
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_fax3.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_fax3.c
new file mode 100644
index 0000000000..ed05633296
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_fax3.c
@@ -0,0 +1,1566 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1990-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#include "tiffiop.h"
+#ifdef CCITT_SUPPORT
+/*
+ * TIFF Library.
+ *
+ * CCITT Group 3 (T.4) and Group 4 (T.6) Compression Support.
+ *
+ * This file contains support for decoding and encoding TIFF
+ * compression algorithms 2, 3, 4, and 32771.
+ *
+ * Decoder support is derived, with permission, from the code
+ * in Frank Cringle's viewfax program;
+ *      Copyright (C) 1990, 1995  Frank D. Cringle.
+ */
+#include "tif_fax3.h"
+#define	G3CODES
+#include "t4.h"
+#include <stdio.h>
+
+/*
+ * Compression+decompression state blocks are
+ * derived from this ``base state'' block.
+ */
+typedef struct {
+        int     rw_mode;                /* O_RDONLY for decode, else encode */
+	int	mode;			/* operating mode */
+	uint32	rowbytes;		/* bytes in a decoded scanline */
+	uint32	rowpixels;		/* pixels in a scanline */
+
+	uint16	cleanfaxdata;		/* CleanFaxData tag */
+	uint32	badfaxrun;		/* BadFaxRun tag */
+	uint32	badfaxlines;		/* BadFaxLines tag */
+	uint32	groupoptions;		/* Group 3/4 options tag */
+	uint32	recvparams;		/* encoded Class 2 session params */
+	char*	subaddress;		/* subaddress string */
+	uint32	recvtime;		/* time spent receiving (secs) */
+	char*	faxdcs;			/* Table 2/T.30 encoded session params */
+	TIFFVGetMethod vgetparent;	/* super-class method */
+	TIFFVSetMethod vsetparent;	/* super-class method */
+} Fax3BaseState;
+#define	Fax3State(tif)		((Fax3BaseState*) (tif)->tif_data)
+
+typedef enum { G3_1D, G3_2D } Ttag;
+typedef struct {
+	Fax3BaseState b;
+
+	/* Decoder state info */
+	const unsigned char* bitmap;	/* bit reversal table */
+	uint32	data;			/* current i/o byte/word */
+	int	bit;			/* current i/o bit in byte */
+	int	EOLcnt;			/* count of EOL codes recognized */
+	TIFFFaxFillFunc fill;		/* fill routine */
+	uint32*	runs;			/* b&w runs for current/previous row */
+	uint32*	refruns;		/* runs for reference line */
+	uint32*	curruns;		/* runs for current line */
+
+	/* Encoder state info */
+	Ttag    tag;			/* encoding state */
+	unsigned char*	refline;	/* reference line for 2d decoding */
+	int	k;			/* #rows left that can be 2d encoded */
+	int	maxk;			/* max #rows that can be 2d encoded */
+} Fax3CodecState;
+#define	DecoderState(tif)	((Fax3CodecState*) Fax3State(tif))
+#define	EncoderState(tif)	((Fax3CodecState*) Fax3State(tif))
+
+#define	is2DEncoding(sp) \
+	(sp->b.groupoptions & GROUP3OPT_2DENCODING)
+#define	isAligned(p,t)	((((unsigned long)(p)) & (sizeof (t)-1)) == 0)
+
+/*
+ * Group 3 and Group 4 Decoding.
+ */
+
+/*
+ * These macros glue the TIFF library state to
+ * the state expected by Frank's decoder.
+ */
+#define	DECLARE_STATE(tif, sp, mod)					\
+    static const char module[] = mod;					\
+    Fax3CodecState* sp = DecoderState(tif);				\
+    int a0;				/* reference element */		\
+    int lastx = sp->b.rowpixels;	/* last element in row */	\
+    uint32 BitAcc;			/* bit accumulator */		\
+    int BitsAvail;			/* # valid bits in BitAcc */	\
+    int RunLength;			/* length of current run */	\
+    unsigned char* cp;			/* next byte of input data */	\
+    unsigned char* ep;			/* end of input data */		\
+    uint32* pa;				/* place to stuff next run */	\
+    uint32* thisrun;			/* current row's run array */	\
+    int EOLcnt;				/* # EOL codes recognized */	\
+    const unsigned char* bitmap = sp->bitmap;	/* input data bit reverser */	\
+    const TIFFFaxTabEnt* TabEnt
+#define	DECLARE_STATE_2D(tif, sp, mod)					\
+    DECLARE_STATE(tif, sp, mod);					\
+    int b1;				/* next change on prev line */	\
+    uint32* pb				/* next run in reference line */\
+/*
+ * Load any state that may be changed during decoding.
+ */
+#define	CACHE_STATE(tif, sp) do {					\
+    BitAcc = sp->data;							\
+    BitsAvail = sp->bit;						\
+    EOLcnt = sp->EOLcnt;						\
+    cp = (unsigned char*) tif->tif_rawcp;				\
+    ep = cp + tif->tif_rawcc;						\
+} while (0)
+/*
+ * Save state possibly changed during decoding.
+ */
+#define	UNCACHE_STATE(tif, sp) do {					\
+    sp->bit = BitsAvail;						\
+    sp->data = BitAcc;							\
+    sp->EOLcnt = EOLcnt;						\
+    tif->tif_rawcc -= (tidata_t) cp - tif->tif_rawcp;			\
+    tif->tif_rawcp = (tidata_t) cp;					\
+} while (0)
+
+/*
+ * Setup state for decoding a strip.
+ */
+static int
+Fax3PreDecode(TIFF* tif, tsample_t s)
+{
+	Fax3CodecState* sp = DecoderState(tif);
+
+	(void) s;
+	assert(sp != NULL);
+	sp->bit = 0;			/* force initial read */
+	sp->data = 0;
+	sp->EOLcnt = 0;			/* force initial scan for EOL */
+	/*
+	 * Decoder assumes lsb-to-msb bit order.  Note that we select
+	 * this here rather than in Fax3SetupState so that viewers can
+	 * hold the image open, fiddle with the FillOrder tag value,
+	 * and then re-decode the image.  Otherwise they'd need to close
+	 * and open the image to get the state reset.
+	 */
+	sp->bitmap =
+	    TIFFGetBitRevTable(tif->tif_dir.td_fillorder != FILLORDER_LSB2MSB);
+	if (sp->refruns) {		/* init reference line to white */
+		sp->refruns[0] = (uint32) sp->b.rowpixels;
+		sp->refruns[1] = 0;
+	}
+	return (1);
+}
+
+/*
+ * Routine for handling various errors/conditions.
+ * Note how they are "glued into the decoder" by
+ * overriding the definitions used by the decoder.
+ */
+
+static void
+Fax3Unexpected(const char* module, TIFF* tif, uint32 line, uint32 a0)
+{
+	TIFFErrorExt(tif->tif_clientdata, module, "%s: Bad code word at line %lu of %s %lu (x %lu)",
+		tif->tif_name, (unsigned long) line, isTiled(tif) ? "tile" : "strip",
+	   (unsigned long) (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip),
+	   (unsigned long) a0);
+}
+#define	unexpected(table, a0)	Fax3Unexpected(module, tif, line, a0)
+
+static void
+Fax3Extension(const char* module, TIFF* tif, uint32 line, uint32 a0)
+{
+	TIFFErrorExt(tif->tif_clientdata, module,
+	    "%s: Uncompressed data (not supported) at line %lu of %s %lu (x %lu)",
+	    tif->tif_name, (unsigned long) line, isTiled(tif) ? "tile" : "strip",
+       (unsigned long) (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip),
+       (unsigned long) a0);
+}
+#define	extension(a0)	Fax3Extension(module, tif, line, a0)
+
+static void
+Fax3BadLength(const char* module, TIFF* tif, uint32 line, uint32 a0, uint32 lastx)
+{
+	TIFFWarningExt(tif->tif_clientdata, module, "%s: %s at line %lu of %s %lu (got %lu, expected %lu)",
+	    tif->tif_name,
+	    a0 < lastx ? "Premature EOL" : "Line length mismatch",
+	    (unsigned long) line, isTiled(tif) ? "tile" : "strip",
+        (unsigned long) (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip),
+        (unsigned long) a0, lastx);
+}
+#define	badlength(a0,lastx)	Fax3BadLength(module, tif, line, a0, lastx)
+
+static void
+Fax3PrematureEOF(const char* module, TIFF* tif, uint32 line, uint32 a0)
+{
+	TIFFWarningExt(tif->tif_clientdata, module, "%s: Premature EOF at line %lu of %s %lu (x %lu)",
+	    tif->tif_name,
+	    (unsigned long) line, isTiled(tif) ? "tile" : "strip",
+        (unsigned long) (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip),
+        (unsigned long) a0);
+}
+#define	prematureEOF(a0)	Fax3PrematureEOF(module, tif, line, a0)
+
+#define	Nop
+
+/*
+ * Decode the requested amount of G3 1D-encoded data.
+ */
+static int
+Fax3Decode1D(TIFF* tif, tidata_t buf, tsize_t occ, tsample_t s)
+{
+	DECLARE_STATE(tif, sp, "Fax3Decode1D");
+        int line = 0;
+
+	(void) s;
+	CACHE_STATE(tif, sp);
+	thisrun = sp->curruns;
+	while ((long)occ > 0) {
+		a0 = 0;
+		RunLength = 0;
+		pa = thisrun;
+#ifdef FAX3_DEBUG
+		printf("\nBitAcc=%08X, BitsAvail = %d\n", BitAcc, BitsAvail);
+		printf("-------------------- %d\n", tif->tif_row);
+		fflush(stdout);
+#endif
+		SYNC_EOL(EOF1D);
+		EXPAND1D(EOF1Da);
+		(*sp->fill)(buf, thisrun, pa, lastx);
+		buf += sp->b.rowbytes;
+		occ -= sp->b.rowbytes;
+                line++;
+		continue;
+	EOF1D:				/* premature EOF */
+		CLEANUP_RUNS();
+	EOF1Da:				/* premature EOF */
+		(*sp->fill)(buf, thisrun, pa, lastx);
+		UNCACHE_STATE(tif, sp);
+		return (-1);
+	}
+	UNCACHE_STATE(tif, sp);
+	return (1);
+}
+
+#define	SWAP(t,a,b)	{ t x; x = (a); (a) = (b); (b) = x; }
+/*
+ * Decode the requested amount of G3 2D-encoded data.
+ */
+static int
+Fax3Decode2D(TIFF* tif, tidata_t buf, tsize_t occ, tsample_t s)
+{
+	DECLARE_STATE_2D(tif, sp, "Fax3Decode2D");
+        int line = 0;
+	int is1D;			/* current line is 1d/2d-encoded */
+
+	(void) s;
+	CACHE_STATE(tif, sp);
+	while ((long)occ > 0) {
+		a0 = 0;
+		RunLength = 0;
+		pa = thisrun = sp->curruns;
+#ifdef FAX3_DEBUG
+		printf("\nBitAcc=%08X, BitsAvail = %d EOLcnt = %d",
+		    BitAcc, BitsAvail, EOLcnt);
+#endif
+		SYNC_EOL(EOF2D);
+		NeedBits8(1, EOF2D);
+		is1D = GetBits(1);	/* 1D/2D-encoding tag bit */
+		ClrBits(1);
+#ifdef FAX3_DEBUG
+		printf(" %s\n-------------------- %d\n",
+		    is1D ? "1D" : "2D", tif->tif_row);
+		fflush(stdout);
+#endif
+		pb = sp->refruns;
+		b1 = *pb++;
+		if (is1D)
+			EXPAND1D(EOF2Da);
+		else
+			EXPAND2D(EOF2Da);
+		(*sp->fill)(buf, thisrun, pa, lastx);
+		SETVALUE(0);		/* imaginary change for reference */
+		SWAP(uint32*, sp->curruns, sp->refruns);
+		buf += sp->b.rowbytes;
+		occ -= sp->b.rowbytes;
+                line++;
+		continue;
+	EOF2D:				/* premature EOF */
+		CLEANUP_RUNS();
+	EOF2Da:				/* premature EOF */
+		(*sp->fill)(buf, thisrun, pa, lastx);
+		UNCACHE_STATE(tif, sp);
+		return (-1);
+	}
+	UNCACHE_STATE(tif, sp);
+	return (1);
+}
+#undef SWAP
+
+/*
+ * The ZERO & FILL macros must handle spans < 2*sizeof(long) bytes.
+ * For machines with 64-bit longs this is <16 bytes; otherwise
+ * this is <8 bytes.  We optimize the code here to reflect the
+ * machine characteristics.
+ */
+#if SIZEOF_LONG == 8
+# define FILL(n, cp)							    \
+    switch (n) {							    \
+    case 15:(cp)[14] = 0xff; case 14:(cp)[13] = 0xff; case 13: (cp)[12] = 0xff;\
+    case 12:(cp)[11] = 0xff; case 11:(cp)[10] = 0xff; case 10: (cp)[9] = 0xff;\
+    case  9: (cp)[8] = 0xff; case  8: (cp)[7] = 0xff; case  7: (cp)[6] = 0xff;\
+    case  6: (cp)[5] = 0xff; case  5: (cp)[4] = 0xff; case  4: (cp)[3] = 0xff;\
+    case  3: (cp)[2] = 0xff; case  2: (cp)[1] = 0xff;			      \
+    case  1: (cp)[0] = 0xff; (cp) += (n); case 0:  ;			      \
+    }
+# define ZERO(n, cp)							\
+    switch (n) {							\
+    case 15:(cp)[14] = 0; case 14:(cp)[13] = 0; case 13: (cp)[12] = 0;	\
+    case 12:(cp)[11] = 0; case 11:(cp)[10] = 0; case 10: (cp)[9] = 0;	\
+    case  9: (cp)[8] = 0; case  8: (cp)[7] = 0; case  7: (cp)[6] = 0;	\
+    case  6: (cp)[5] = 0; case  5: (cp)[4] = 0; case  4: (cp)[3] = 0;	\
+    case  3: (cp)[2] = 0; case  2: (cp)[1] = 0;				\
+    case  1: (cp)[0] = 0; (cp) += (n); case 0:  ;			\
+    }
+#else
+# define FILL(n, cp)							    \
+    switch (n) {							    \
+    case 7: (cp)[6] = 0xff; case 6: (cp)[5] = 0xff; case 5: (cp)[4] = 0xff; \
+    case 4: (cp)[3] = 0xff; case 3: (cp)[2] = 0xff; case 2: (cp)[1] = 0xff; \
+    case 1: (cp)[0] = 0xff; (cp) += (n); case 0:  ;			    \
+    }
+# define ZERO(n, cp)							\
+    switch (n) {							\
+    case 7: (cp)[6] = 0; case 6: (cp)[5] = 0; case 5: (cp)[4] = 0;	\
+    case 4: (cp)[3] = 0; case 3: (cp)[2] = 0; case 2: (cp)[1] = 0;	\
+    case 1: (cp)[0] = 0; (cp) += (n); case 0:  ;			\
+    }
+#endif
+
+/*
+ * Bit-fill a row according to the white/black
+ * runs generated during G3/G4 decoding.
+ */
+void
+_TIFFFax3fillruns(unsigned char* buf, uint32* runs, uint32* erun, uint32 lastx)
+{
+	static const unsigned char _fillmasks[] =
+	    { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+	unsigned char* cp;
+	uint32 x, bx, run;
+	int32 n, nw;
+	long* lp;
+
+	if ((erun-runs)&1)
+	    *erun++ = 0;
+	x = 0;
+	for (; runs < erun; runs += 2) {
+	    run = runs[0];
+	    if (x+run > lastx || run > lastx )
+		run = runs[0] = (uint32) (lastx - x);
+	    if (run) {
+		cp = buf + (x>>3);
+		bx = x&7;
+		if (run > 8-bx) {
+		    if (bx) {			/* align to byte boundary */
+			*cp++ &= 0xff << (8-bx);
+			run -= 8-bx;
+		    }
+		    if( (n = run >> 3) != 0 ) {	/* multiple bytes to fill */
+			if ((n/sizeof (long)) > 1) {
+			    /*
+			     * Align to longword boundary and fill.
+			     */
+			    for (; n && !isAligned(cp, long); n--)
+				    *cp++ = 0x00;
+			    lp = (long*) cp;
+			    nw = (int32)(n / sizeof (long));
+			    n -= nw * sizeof (long);
+			    do {
+				    *lp++ = 0L;
+			    } while (--nw);
+			    cp = (unsigned char*) lp;
+			}
+			ZERO(n, cp);
+			run &= 7;
+		    }
+		    if (run)
+			cp[0] &= 0xff >> run;
+		} else
+		    cp[0] &= ~(_fillmasks[run]>>bx);
+		x += runs[0];
+	    }
+	    run = runs[1];
+	    if (x+run > lastx || run > lastx )
+		run = runs[1] = lastx - x;
+	    if (run) {
+		cp = buf + (x>>3);
+		bx = x&7;
+		if (run > 8-bx) {
+		    if (bx) {			/* align to byte boundary */
+			*cp++ |= 0xff >> bx;
+			run -= 8-bx;
+		    }
+		    if( (n = run>>3) != 0 ) {	/* multiple bytes to fill */
+			if ((n/sizeof (long)) > 1) {
+			    /*
+			     * Align to longword boundary and fill.
+			     */
+			    for (; n && !isAligned(cp, long); n--)
+				*cp++ = 0xff;
+			    lp = (long*) cp;
+			    nw = (int32)(n / sizeof (long));
+			    n -= nw * sizeof (long);
+			    do {
+				*lp++ = -1L;
+			    } while (--nw);
+			    cp = (unsigned char*) lp;
+			}
+			FILL(n, cp);
+			run &= 7;
+		    }
+		    if (run)
+			cp[0] |= 0xff00 >> run;
+		} else
+		    cp[0] |= _fillmasks[run]>>bx;
+		x += runs[1];
+	    }
+	}
+	assert(x == lastx);
+}
+#undef	ZERO
+#undef	FILL
+
+/*
+ * Setup G3/G4-related compression/decompression state
+ * before data is processed.  This routine is called once
+ * per image -- it sets up different state based on whether
+ * or not decoding or encoding is being done and whether
+ * 1D- or 2D-encoded data is involved.
+ */
+static int
+Fax3SetupState(TIFF* tif)
+{
+	TIFFDirectory* td = &tif->tif_dir;
+	Fax3BaseState* sp = Fax3State(tif);
+	int needsRefLine;
+	Fax3CodecState* dsp = (Fax3CodecState*) Fax3State(tif);
+	uint32 rowbytes, rowpixels, nruns;
+
+	if (td->td_bitspersample != 1) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		    "Bits/sample must be 1 for Group 3/4 encoding/decoding");
+		return (0);
+	}
+	/*
+	 * Calculate the scanline/tile widths.
+	 */
+	if (isTiled(tif)) {
+		rowbytes = TIFFTileRowSize(tif);
+		rowpixels = td->td_tilewidth;
+	} else {
+		rowbytes = TIFFScanlineSize(tif);
+		rowpixels = td->td_imagewidth;
+	}
+	sp->rowbytes = (uint32) rowbytes;
+	sp->rowpixels = (uint32) rowpixels;
+	/*
+	 * Allocate any additional space required for decoding/encoding.
+	 */
+	needsRefLine = (
+	    (sp->groupoptions & GROUP3OPT_2DENCODING) ||
+	    td->td_compression == COMPRESSION_CCITTFAX4
+	);
+
+	nruns = needsRefLine ? 2*TIFFroundup(rowpixels,32) : rowpixels;
+
+	dsp->runs = (uint32*) _TIFFCheckMalloc(tif, 2*nruns+3, sizeof (uint32),
+					  "for Group 3/4 run arrays");
+	if (dsp->runs == NULL)
+		return (0);
+	dsp->curruns = dsp->runs;
+	if (needsRefLine)
+		dsp->refruns = dsp->runs + (nruns>>1);
+	else
+		dsp->refruns = NULL;
+	if (td->td_compression == COMPRESSION_CCITTFAX3
+	    && is2DEncoding(dsp)) {	/* NB: default is 1D routine */
+		tif->tif_decoderow = Fax3Decode2D;
+		tif->tif_decodestrip = Fax3Decode2D;
+		tif->tif_decodetile = Fax3Decode2D;
+	}
+
+	if (needsRefLine) {		/* 2d encoding */
+		Fax3CodecState* esp = EncoderState(tif);
+		/*
+		 * 2d encoding requires a scanline
+		 * buffer for the ``reference line''; the
+		 * scanline against which delta encoding
+		 * is referenced.  The reference line must
+		 * be initialized to be ``white'' (done elsewhere).
+		 */
+		esp->refline = (unsigned char*) _TIFFmalloc(rowbytes);
+		if (esp->refline == NULL) {
+			TIFFErrorExt(tif->tif_clientdata, "Fax3SetupState",
+			    "%s: No space for Group 3/4 reference line",
+			    tif->tif_name);
+			return (0);
+		}
+	} else					/* 1d encoding */
+		EncoderState(tif)->refline = NULL;
+
+	return (1);
+}
+
+/*
+ * CCITT Group 3 FAX Encoding.
+ */
+
+#define	Fax3FlushBits(tif, sp) {				\
+	if ((tif)->tif_rawcc >= (tif)->tif_rawdatasize)		\
+		(void) TIFFFlushData1(tif);			\
+	*(tif)->tif_rawcp++ = (tidataval_t) (sp)->data;		\
+	(tif)->tif_rawcc++;					\
+	(sp)->data = 0, (sp)->bit = 8;				\
+}
+#define	_FlushBits(tif) {					\
+	if ((tif)->tif_rawcc >= (tif)->tif_rawdatasize)		\
+		(void) TIFFFlushData1(tif);			\
+	*(tif)->tif_rawcp++ = (tidataval_t) data;		\
+	(tif)->tif_rawcc++;					\
+	data = 0, bit = 8;					\
+}
+static const int _msbmask[9] =
+    { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
+#define	_PutBits(tif, bits, length) {				\
+	while (length > bit) {					\
+		data |= bits >> (length - bit);			\
+		length -= bit;					\
+		_FlushBits(tif);				\
+	}							\
+	data |= (bits & _msbmask[length]) << (bit - length);	\
+	bit -= length;						\
+	if (bit == 0)						\
+		_FlushBits(tif);				\
+}
+	
+/*
+ * Write a variable-length bit-value to
+ * the output stream.  Values are
+ * assumed to be at most 16 bits.
+ */
+static void
+Fax3PutBits(TIFF* tif, unsigned int bits, unsigned int length)
+{
+	Fax3CodecState* sp = EncoderState(tif);
+	unsigned int bit = sp->bit;
+	int data = sp->data;
+
+	_PutBits(tif, bits, length);
+
+	sp->data = data;
+	sp->bit = bit;
+}
+
+/*
+ * Write a code to the output stream.
+ */
+#define putcode(tif, te)	Fax3PutBits(tif, (te)->code, (te)->length)
+
+#ifdef FAX3_DEBUG
+#define	DEBUG_COLOR(w) (tab == TIFFFaxWhiteCodes ? w "W" : w "B")
+#define	DEBUG_PRINT(what,len) {						\
+    int t;								\
+    printf("%08X/%-2d: %s%5d\t", data, bit, DEBUG_COLOR(what), len);	\
+    for (t = length-1; t >= 0; t--)					\
+	putchar(code & (1<<t) ? '1' : '0');				\
+    putchar('\n');							\
+}
+#endif
+
+/*
+ * Write the sequence of codes that describes
+ * the specified span of zero's or one's.  The
+ * appropriate table that holds the make-up and
+ * terminating codes is supplied.
+ */
+static void
+putspan(TIFF* tif, int32 span, const tableentry* tab)
+{
+	Fax3CodecState* sp = EncoderState(tif);
+	unsigned int bit = sp->bit;
+	int data = sp->data;
+	unsigned int code, length;
+
+	while (span >= 2624) {
+		const tableentry* te = &tab[63 + (2560>>6)];
+		code = te->code, length = te->length;
+#ifdef FAX3_DEBUG
+		DEBUG_PRINT("MakeUp", te->runlen);
+#endif
+		_PutBits(tif, code, length);
+		span -= te->runlen;
+	}
+	if (span >= 64) {
+		const tableentry* te = &tab[63 + (span>>6)];
+		assert(te->runlen == 64*(span>>6));
+		code = te->code, length = te->length;
+#ifdef FAX3_DEBUG
+		DEBUG_PRINT("MakeUp", te->runlen);
+#endif
+		_PutBits(tif, code, length);
+		span -= te->runlen;
+	}
+	code = tab[span].code, length = tab[span].length;
+#ifdef FAX3_DEBUG
+	DEBUG_PRINT("  Term", tab[span].runlen);
+#endif
+	_PutBits(tif, code, length);
+
+	sp->data = data;
+	sp->bit = bit;
+}
+
+/*
+ * Write an EOL code to the output stream.  The zero-fill
+ * logic for byte-aligning encoded scanlines is handled
+ * here.  We also handle writing the tag bit for the next
+ * scanline when doing 2d encoding.
+ */
+static void
+Fax3PutEOL(TIFF* tif)
+{
+	Fax3CodecState* sp = EncoderState(tif);
+	unsigned int bit = sp->bit;
+	int data = sp->data;
+	unsigned int code, length, tparm;
+
+	if (sp->b.groupoptions & GROUP3OPT_FILLBITS) {
+		/*
+		 * Force bit alignment so EOL will terminate on
+		 * a byte boundary.  That is, force the bit alignment
+		 * to 16-12 = 4 before putting out the EOL code.
+		 */
+		int align = 8 - 4;
+		if (align != sp->bit) {
+			if (align > sp->bit)
+				align = sp->bit + (8 - align);
+			else
+				align = sp->bit - align;
+			code = 0;
+			tparm=align; 
+			_PutBits(tif, 0, tparm);
+		}
+	}
+	code = EOL, length = 12;
+	if (is2DEncoding(sp))
+		code = (code<<1) | (sp->tag == G3_1D), length++;
+	_PutBits(tif, code, length);
+
+	sp->data = data;
+	sp->bit = bit;
+}
+
+/*
+ * Reset encoding state at the start of a strip.
+ */
+static int
+Fax3PreEncode(TIFF* tif, tsample_t s)
+{
+	Fax3CodecState* sp = EncoderState(tif);
+
+	(void) s;
+	assert(sp != NULL);
+	sp->bit = 8;
+	sp->data = 0;
+	sp->tag = G3_1D;
+	/*
+	 * This is necessary for Group 4; otherwise it isn't
+	 * needed because the first scanline of each strip ends
+	 * up being copied into the refline.
+	 */
+	if (sp->refline)
+		_TIFFmemset(sp->refline, 0x00, sp->b.rowbytes);
+	if (is2DEncoding(sp)) {
+		float res = tif->tif_dir.td_yresolution;
+		/*
+		 * The CCITT spec says that when doing 2d encoding, you
+		 * should only do it on K consecutive scanlines, where K
+		 * depends on the resolution of the image being encoded
+		 * (2 for <= 200 lpi, 4 for > 200 lpi).  Since the directory
+		 * code initializes td_yresolution to 0, this code will
+		 * select a K of 2 unless the YResolution tag is set
+		 * appropriately.  (Note also that we fudge a little here
+		 * and use 150 lpi to avoid problems with units conversion.)
+		 */
+		if (tif->tif_dir.td_resolutionunit == RESUNIT_CENTIMETER)
+			res *= 2.54f;		/* convert to inches */
+		sp->maxk = (res > 150 ? 4 : 2);
+		sp->k = sp->maxk-1;
+	} else
+		sp->k = sp->maxk = 0;
+	return (1);
+}
+
+static const unsigned char zeroruns[256] = {
+    8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,	/* 0x00 - 0x0f */
+    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,	/* 0x10 - 0x1f */
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0x20 - 0x2f */
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0x30 - 0x3f */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x40 - 0x4f */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x50 - 0x5f */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x60 - 0x6f */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x70 - 0x7f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x80 - 0x8f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x90 - 0x9f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xa0 - 0xaf */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xb0 - 0xbf */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xc0 - 0xcf */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xd0 - 0xdf */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xe0 - 0xef */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xf0 - 0xff */
+};
+static const unsigned char oneruns[256] = {
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x00 - 0x0f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x10 - 0x1f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x20 - 0x2f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x30 - 0x3f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x40 - 0x4f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x50 - 0x5f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x60 - 0x6f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x70 - 0x7f */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x80 - 0x8f */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x90 - 0x9f */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0xa0 - 0xaf */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0xb0 - 0xbf */
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0xc0 - 0xcf */
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0xd0 - 0xdf */
+    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,	/* 0xe0 - 0xef */
+    4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8,	/* 0xf0 - 0xff */
+};
+
+/*
+ * On certain systems it pays to inline
+ * the routines that find pixel spans.
+ */
+#ifdef VAXC
+static	int32 find0span(unsigned char*, int32, int32);
+static	int32 find1span(unsigned char*, int32, int32);
+#pragma inline(find0span,find1span)
+#endif
+
+/*
+ * Find a span of ones or zeros using the supplied
+ * table.  The ``base'' of the bit string is supplied
+ * along with the start+end bit indices.
+ */
+inline static int32
+find0span(unsigned char* bp, int32 bs, int32 be)
+{
+	int32 bits = be - bs;
+	int32 n, span;
+
+	bp += bs>>3;
+	/*
+	 * Check partial byte on lhs.
+	 */
+	if (bits > 0 && (n = (bs & 7))) {
+		span = zeroruns[(*bp << n) & 0xff];
+		if (span > 8-n)		/* table value too generous */
+			span = 8-n;
+		if (span > bits)	/* constrain span to bit range */
+			span = bits;
+		if (n+span < 8)		/* doesn't extend to edge of byte */
+			return (span);
+		bits -= span;
+		bp++;
+	} else
+		span = 0;
+	if (bits >= (int32)(2 * 8 * sizeof(long))) {
+		long* lp;
+		/*
+		 * Align to longword boundary and check longwords.
+		 */
+		while (!isAligned(bp, long)) {
+			if (*bp != 0x00)
+				return (span + zeroruns[*bp]);
+			span += 8, bits -= 8;
+			bp++;
+		}
+		lp = (long*) bp;
+		while ((bits >= (int32)(8 * sizeof(long))) && (0 == *lp)) {
+			span += 8*sizeof (long), bits -= 8*sizeof (long);
+			lp++;
+		}
+		bp = (unsigned char*) lp;
+	}
+	/*
+	 * Scan full bytes for all 0's.
+	 */
+	while (bits >= 8) {
+		if (*bp != 0x00)	/* end of run */
+			return (span + zeroruns[*bp]);
+		span += 8, bits -= 8;
+		bp++;
+	}
+	/*
+	 * Check partial byte on rhs.
+	 */
+	if (bits > 0) {
+		n = zeroruns[*bp];
+		span += (n > bits ? bits : n);
+	}
+	return (span);
+}
+
+inline static int32
+find1span(unsigned char* bp, int32 bs, int32 be)
+{
+	int32 bits = be - bs;
+	int32 n, span;
+
+	bp += bs>>3;
+	/*
+	 * Check partial byte on lhs.
+	 */
+	if (bits > 0 && (n = (bs & 7))) {
+		span = oneruns[(*bp << n) & 0xff];
+		if (span > 8-n)		/* table value too generous */
+			span = 8-n;
+		if (span > bits)	/* constrain span to bit range */
+			span = bits;
+		if (n+span < 8)		/* doesn't extend to edge of byte */
+			return (span);
+		bits -= span;
+		bp++;
+	} else
+		span = 0;
+	if (bits >= (int32)(2 * 8 * sizeof(long))) {
+		long* lp;
+		/*
+		 * Align to longword boundary and check longwords.
+		 */
+		while (!isAligned(bp, long)) {
+			if (*bp != 0xff)
+				return (span + oneruns[*bp]);
+			span += 8, bits -= 8;
+			bp++;
+		}
+		lp = (long*) bp;
+		while ((bits >= (int32)(8 * sizeof(long))) && (~0 == *lp)) {
+			span += 8*sizeof (long), bits -= 8*sizeof (long);
+			lp++;
+		}
+		bp = (unsigned char*) lp;
+	}
+	/*
+	 * Scan full bytes for all 1's.
+	 */
+	while (bits >= 8) {
+		if (*bp != 0xff)	/* end of run */
+			return (span + oneruns[*bp]);
+		span += 8, bits -= 8;
+		bp++;
+	}
+	/*
+	 * Check partial byte on rhs.
+	 */
+	if (bits > 0) {
+		n = oneruns[*bp];
+		span += (n > bits ? bits : n);
+	}
+	return (span);
+}
+
+/*
+ * Return the offset of the next bit in the range
+ * [bs..be] that is different from the specified
+ * color.  The end, be, is returned if no such bit
+ * exists.
+ */
+#define	finddiff(_cp, _bs, _be, _color)	\
+	(_bs + (_color ? find1span(_cp,_bs,_be) : find0span(_cp,_bs,_be)))
+/*
+ * Like finddiff, but also check the starting bit
+ * against the end in case start > end.
+ */
+#define	finddiff2(_cp, _bs, _be, _color) \
+	(_bs < _be ? finddiff(_cp,_bs,_be,_color) : _be)
+
+/*
+ * 1d-encode a row of pixels.  The encoding is
+ * a sequence of all-white or all-black spans
+ * of pixels encoded with Huffman codes.
+ */
+static int
+Fax3Encode1DRow(TIFF* tif, unsigned char* bp, uint32 bits)
+{
+	Fax3CodecState* sp = EncoderState(tif);
+	int32 span;
+        uint32 bs = 0;
+
+	for (;;) {
+		span = find0span(bp, bs, bits);		/* white span */
+		putspan(tif, span, TIFFFaxWhiteCodes);
+		bs += span;
+		if (bs >= bits)
+			break;
+		span = find1span(bp, bs, bits);		/* black span */
+		putspan(tif, span, TIFFFaxBlackCodes);
+		bs += span;
+		if (bs >= bits)
+			break;
+	}
+	if (sp->b.mode & (FAXMODE_BYTEALIGN|FAXMODE_WORDALIGN)) {
+		if (sp->bit != 8)			/* byte-align */
+			Fax3FlushBits(tif, sp);
+		if ((sp->b.mode&FAXMODE_WORDALIGN) &&
+		    !isAligned(tif->tif_rawcp, uint16))
+			Fax3FlushBits(tif, sp);
+	}
+	return (1);
+}
+
+static const tableentry horizcode =
+    { 3, 0x1, 0 };	/* 001 */
+static const tableentry passcode =
+    { 4, 0x1, 0 };	/* 0001 */
+static const tableentry vcodes[7] = {
+    { 7, 0x03, 0 },	/* 0000 011 */
+    { 6, 0x03, 0 },	/* 0000 11 */
+    { 3, 0x03, 0 },	/* 011 */
+    { 1, 0x1, 0 },	/* 1 */
+    { 3, 0x2, 0 },	/* 010 */
+    { 6, 0x02, 0 },	/* 0000 10 */
+    { 7, 0x02, 0 }	/* 0000 010 */
+};
+
+/*
+ * 2d-encode a row of pixels.  Consult the CCITT
+ * documentation for the algorithm.
+ */
+static int
+Fax3Encode2DRow(TIFF* tif, unsigned char* bp, unsigned char* rp, uint32 bits)
+{
+#define	PIXEL(buf,ix)	((((buf)[(ix)>>3]) >> (7-((ix)&7))) & 1)
+        uint32 a0 = 0;
+	uint32 a1 = (PIXEL(bp, 0) != 0 ? 0 : finddiff(bp, 0, bits, 0));
+	uint32 b1 = (PIXEL(rp, 0) != 0 ? 0 : finddiff(rp, 0, bits, 0));
+	uint32 a2, b2;
+
+	for (;;) {
+		b2 = finddiff2(rp, b1, bits, PIXEL(rp,b1));
+		if (b2 >= a1) {
+			int32 d = b1 - a1;
+			if (!(-3 <= d && d <= 3)) {	/* horizontal mode */
+				a2 = finddiff2(bp, a1, bits, PIXEL(bp,a1));
+				putcode(tif, &horizcode);
+				if (a0+a1 == 0 || PIXEL(bp, a0) == 0) {
+					putspan(tif, a1-a0, TIFFFaxWhiteCodes);
+					putspan(tif, a2-a1, TIFFFaxBlackCodes);
+				} else {
+					putspan(tif, a1-a0, TIFFFaxBlackCodes);
+					putspan(tif, a2-a1, TIFFFaxWhiteCodes);
+				}
+				a0 = a2;
+			} else {			/* vertical mode */
+				putcode(tif, &vcodes[d+3]);
+				a0 = a1;
+			}
+		} else {				/* pass mode */
+			putcode(tif, &passcode);
+			a0 = b2;
+		}
+		if (a0 >= bits)
+			break;
+		a1 = finddiff(bp, a0, bits, PIXEL(bp,a0));
+		b1 = finddiff(rp, a0, bits, !PIXEL(bp,a0));
+		b1 = finddiff(rp, b1, bits, PIXEL(bp,a0));
+	}
+	return (1);
+#undef PIXEL
+}
+
+/*
+ * Encode a buffer of pixels.
+ */
+static int
+Fax3Encode(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s)
+{
+	Fax3CodecState* sp = EncoderState(tif);
+
+	(void) s;
+	while ((long)cc > 0) {
+		if ((sp->b.mode & FAXMODE_NOEOL) == 0)
+			Fax3PutEOL(tif);
+		if (is2DEncoding(sp)) {
+			if (sp->tag == G3_1D) {
+				if (!Fax3Encode1DRow(tif, bp, sp->b.rowpixels))
+					return (0);
+				sp->tag = G3_2D;
+			} else {
+				if (!Fax3Encode2DRow(tif, bp, sp->refline,
+                                                     sp->b.rowpixels))
+					return (0);
+				sp->k--;
+			}
+			if (sp->k == 0) {
+				sp->tag = G3_1D;
+				sp->k = sp->maxk-1;
+			} else
+				_TIFFmemcpy(sp->refline, bp, sp->b.rowbytes);
+		} else {
+			if (!Fax3Encode1DRow(tif, bp, sp->b.rowpixels))
+				return (0);
+		}
+		bp += sp->b.rowbytes;
+		cc -= sp->b.rowbytes;
+	}
+	return (1);
+}
+
+static int
+Fax3PostEncode(TIFF* tif)
+{
+	Fax3CodecState* sp = EncoderState(tif);
+
+	if (sp->bit != 8)
+		Fax3FlushBits(tif, sp);
+	return (1);
+}
+
+static void
+Fax3Close(TIFF* tif)
+{
+	if ((Fax3State(tif)->mode & FAXMODE_NORTC) == 0) {
+		Fax3CodecState* sp = EncoderState(tif);
+		unsigned int code = EOL;
+		unsigned int length = 12;
+		int i;
+
+		if (is2DEncoding(sp))
+			code = (code<<1) | (sp->tag == G3_1D), length++;
+		for (i = 0; i < 6; i++)
+			Fax3PutBits(tif, code, length);
+		Fax3FlushBits(tif, sp);
+	}
+}
+
+static void
+Fax3Cleanup(TIFF* tif)
+{
+	Fax3CodecState* sp = DecoderState(tif);
+	
+	assert(sp != 0);
+
+	tif->tif_tagmethods.vgetfield = sp->b.vgetparent;
+	tif->tif_tagmethods.vsetfield = sp->b.vsetparent;
+
+	if (sp->runs)
+		_TIFFfree(sp->runs);
+	if (sp->refline)
+		_TIFFfree(sp->refline);
+
+	if (Fax3State(tif)->subaddress)
+		_TIFFfree(Fax3State(tif)->subaddress);
+	_TIFFfree(tif->tif_data);
+	tif->tif_data = NULL;
+
+	_TIFFSetDefaultCompressionState(tif);
+}
+
+#define	FIELD_BADFAXLINES	(FIELD_CODEC+0)
+#define	FIELD_CLEANFAXDATA	(FIELD_CODEC+1)
+#define	FIELD_BADFAXRUN		(FIELD_CODEC+2)
+#define	FIELD_RECVPARAMS	(FIELD_CODEC+3)
+#define	FIELD_SUBADDRESS	(FIELD_CODEC+4)
+#define	FIELD_RECVTIME		(FIELD_CODEC+5)
+#define	FIELD_FAXDCS		(FIELD_CODEC+6)
+
+#define	FIELD_OPTIONS		(FIELD_CODEC+7)
+
+static const TIFFFieldInfo faxFieldInfo[] = {
+    { TIFFTAG_FAXMODE,		 0, 0,	TIFF_ANY,	FIELD_PSEUDO,
+      FALSE,	FALSE,	"FaxMode" },
+    { TIFFTAG_FAXFILLFUNC,	 0, 0,	TIFF_ANY,	FIELD_PSEUDO,
+      FALSE,	FALSE,	"FaxFillFunc" },
+    { TIFFTAG_BADFAXLINES,	 1, 1,	TIFF_LONG,	FIELD_BADFAXLINES,
+      TRUE,	FALSE,	"BadFaxLines" },
+    { TIFFTAG_BADFAXLINES,	 1, 1,	TIFF_SHORT,	FIELD_BADFAXLINES,
+      TRUE,	FALSE,	"BadFaxLines" },
+    { TIFFTAG_CLEANFAXDATA,	 1, 1,	TIFF_SHORT,	FIELD_CLEANFAXDATA,
+      TRUE,	FALSE,	"CleanFaxData" },
+    { TIFFTAG_CONSECUTIVEBADFAXLINES,1,1, TIFF_LONG,	FIELD_BADFAXRUN,
+      TRUE,	FALSE,	"ConsecutiveBadFaxLines" },
+    { TIFFTAG_CONSECUTIVEBADFAXLINES,1,1, TIFF_SHORT,	FIELD_BADFAXRUN,
+      TRUE,	FALSE,	"ConsecutiveBadFaxLines" },
+    { TIFFTAG_FAXRECVPARAMS,	 1, 1, TIFF_LONG,	FIELD_RECVPARAMS,
+      TRUE,	FALSE,	"FaxRecvParams" },
+    { TIFFTAG_FAXSUBADDRESS,	-1,-1, TIFF_ASCII,	FIELD_SUBADDRESS,
+      TRUE,	FALSE,	"FaxSubAddress" },
+    { TIFFTAG_FAXRECVTIME,	 1, 1, TIFF_LONG,	FIELD_RECVTIME,
+      TRUE,	FALSE,	"FaxRecvTime" },
+    { TIFFTAG_FAXDCS,		-1,-1, TIFF_ASCII,	FIELD_FAXDCS,
+      TRUE,	FALSE,	"FaxDcs" },
+};
+static const TIFFFieldInfo fax3FieldInfo[] = {
+    { TIFFTAG_GROUP3OPTIONS,	 1, 1,	TIFF_LONG,	FIELD_OPTIONS,
+      FALSE,	FALSE,	"Group3Options" },
+};
+static const TIFFFieldInfo fax4FieldInfo[] = {
+    { TIFFTAG_GROUP4OPTIONS,	 1, 1,	TIFF_LONG,	FIELD_OPTIONS,
+      FALSE,	FALSE,	"Group4Options" },
+};
+#define	N(a)	(sizeof (a) / sizeof (a[0]))
+
+static int
+Fax3VSetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+	Fax3BaseState* sp = Fax3State(tif);
+
+	assert(sp != 0);
+	assert(sp->vsetparent != 0);
+
+	switch (tag) {
+	case TIFFTAG_FAXMODE:
+		sp->mode = va_arg(ap, int);
+		return (1);			/* NB: pseudo tag */
+	case TIFFTAG_FAXFILLFUNC:
+		DecoderState(tif)->fill = va_arg(ap, TIFFFaxFillFunc);
+		return (1);			/* NB: pseudo tag */
+	case TIFFTAG_GROUP3OPTIONS:
+		/* XXX: avoid reading options if compression mismatches. */
+		if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX3)
+			sp->groupoptions = va_arg(ap, uint32);
+		break;
+	case TIFFTAG_GROUP4OPTIONS:
+		/* XXX: avoid reading options if compression mismatches. */
+		if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX4)
+			sp->groupoptions = va_arg(ap, uint32);
+		break;
+	case TIFFTAG_BADFAXLINES:
+		sp->badfaxlines = va_arg(ap, uint32);
+		break;
+	case TIFFTAG_CLEANFAXDATA:
+		sp->cleanfaxdata = (uint16) va_arg(ap, int);
+		break;
+	case TIFFTAG_CONSECUTIVEBADFAXLINES:
+		sp->badfaxrun = va_arg(ap, uint32);
+		break;
+	case TIFFTAG_FAXRECVPARAMS:
+		sp->recvparams = va_arg(ap, uint32);
+		break;
+	case TIFFTAG_FAXSUBADDRESS:
+		_TIFFsetString(&sp->subaddress, va_arg(ap, char*));
+		break;
+	case TIFFTAG_FAXRECVTIME:
+		sp->recvtime = va_arg(ap, uint32);
+		break;
+	case TIFFTAG_FAXDCS:
+		_TIFFsetString(&sp->faxdcs, va_arg(ap, char*));
+		break;
+	default:
+		return (*sp->vsetparent)(tif, tag, ap);
+	}
+	TIFFSetFieldBit(tif, _TIFFFieldWithTag(tif, tag)->field_bit);
+	tif->tif_flags |= TIFF_DIRTYDIRECT;
+	return (1);
+}
+
+static int
+Fax3VGetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+	Fax3BaseState* sp = Fax3State(tif);
+
+	switch (tag) {
+	case TIFFTAG_FAXMODE:
+		*va_arg(ap, int*) = sp->mode;
+		break;
+	case TIFFTAG_FAXFILLFUNC:
+		*va_arg(ap, TIFFFaxFillFunc*) = DecoderState(tif)->fill;
+		break;
+	case TIFFTAG_GROUP3OPTIONS:
+	case TIFFTAG_GROUP4OPTIONS:
+		*va_arg(ap, uint32*) = sp->groupoptions;
+		break;
+	case TIFFTAG_BADFAXLINES:
+		*va_arg(ap, uint32*) = sp->badfaxlines;
+		break;
+	case TIFFTAG_CLEANFAXDATA:
+		*va_arg(ap, uint16*) = sp->cleanfaxdata;
+		break;
+	case TIFFTAG_CONSECUTIVEBADFAXLINES:
+		*va_arg(ap, uint32*) = sp->badfaxrun;
+		break;
+	case TIFFTAG_FAXRECVPARAMS:
+		*va_arg(ap, uint32*) = sp->recvparams;
+		break;
+	case TIFFTAG_FAXSUBADDRESS:
+		*va_arg(ap, char**) = sp->subaddress;
+		break;
+	case TIFFTAG_FAXRECVTIME:
+		*va_arg(ap, uint32*) = sp->recvtime;
+		break;
+	case TIFFTAG_FAXDCS:
+		*va_arg(ap, char**) = sp->faxdcs;
+		break;
+	default:
+		return (*sp->vgetparent)(tif, tag, ap);
+	}
+	return (1);
+}
+
+static void
+Fax3PrintDir(TIFF* tif, FILE* fd, long flags)
+{
+	Fax3BaseState* sp = Fax3State(tif);
+
+	(void) flags;
+	if (TIFFFieldSet(tif,FIELD_OPTIONS)) {
+		const char* sep = " ";
+		if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX4) {
+			fprintf(fd, "  Group 4 Options:");
+			if (sp->groupoptions & GROUP4OPT_UNCOMPRESSED)
+				fprintf(fd, "%suncompressed data", sep);
+		} else {
+
+			fprintf(fd, "  Group 3 Options:");
+			if (sp->groupoptions & GROUP3OPT_2DENCODING)
+				fprintf(fd, "%s2-d encoding", sep), sep = "+";
+			if (sp->groupoptions & GROUP3OPT_FILLBITS)
+				fprintf(fd, "%sEOL padding", sep), sep = "+";
+			if (sp->groupoptions & GROUP3OPT_UNCOMPRESSED)
+				fprintf(fd, "%suncompressed data", sep);
+		}
+		fprintf(fd, " (%lu = 0x%lx)\n",
+                        (unsigned long) sp->groupoptions,
+                        (unsigned long) sp->groupoptions);
+	}
+	if (TIFFFieldSet(tif,FIELD_CLEANFAXDATA)) {
+		fprintf(fd, "  Fax Data:");
+		switch (sp->cleanfaxdata) {
+		case CLEANFAXDATA_CLEAN:
+			fprintf(fd, " clean");
+			break;
+		case CLEANFAXDATA_REGENERATED:
+			fprintf(fd, " receiver regenerated");
+			break;
+		case CLEANFAXDATA_UNCLEAN:
+			fprintf(fd, " uncorrected errors");
+			break;
+		}
+		fprintf(fd, " (%u = 0x%x)\n",
+		    sp->cleanfaxdata, sp->cleanfaxdata);
+	}
+	if (TIFFFieldSet(tif,FIELD_BADFAXLINES))
+		fprintf(fd, "  Bad Fax Lines: %lu\n",
+                        (unsigned long) sp->badfaxlines);
+	if (TIFFFieldSet(tif,FIELD_BADFAXRUN))
+		fprintf(fd, "  Consecutive Bad Fax Lines: %lu\n",
+		    (unsigned long) sp->badfaxrun);
+	if (TIFFFieldSet(tif,FIELD_RECVPARAMS))
+		fprintf(fd, "  Fax Receive Parameters: %08lx\n",
+		   (unsigned long) sp->recvparams);
+	if (TIFFFieldSet(tif,FIELD_SUBADDRESS))
+		fprintf(fd, "  Fax SubAddress: %s\n", sp->subaddress);
+	if (TIFFFieldSet(tif,FIELD_RECVTIME))
+		fprintf(fd, "  Fax Receive Time: %lu secs\n",
+		    (unsigned long) sp->recvtime);
+	if (TIFFFieldSet(tif,FIELD_FAXDCS))
+		fprintf(fd, "  Fax DCS: %s\n", sp->faxdcs);
+}
+
+static int
+InitCCITTFax3(TIFF* tif)
+{
+	Fax3BaseState* sp;
+
+	/*
+	 * Allocate state block so tag methods have storage to record values.
+	 */
+	tif->tif_data = (tidata_t)
+		_TIFFmalloc(sizeof (Fax3CodecState));
+
+	if (tif->tif_data == NULL) {
+		TIFFErrorExt(tif->tif_clientdata, "TIFFInitCCITTFax3",
+		    "%s: No space for state block", tif->tif_name);
+		return (0);
+	}
+
+	sp = Fax3State(tif);
+        sp->rw_mode = tif->tif_mode;
+
+	/*
+	 * Merge codec-specific tag information and
+	 * override parent get/set field methods.
+	 */
+	_TIFFMergeFieldInfo(tif, faxFieldInfo, N(faxFieldInfo));
+	sp->vgetparent = tif->tif_tagmethods.vgetfield;
+	tif->tif_tagmethods.vgetfield = Fax3VGetField; /* hook for codec tags */
+	sp->vsetparent = tif->tif_tagmethods.vsetfield;
+	tif->tif_tagmethods.vsetfield = Fax3VSetField; /* hook for codec tags */
+	tif->tif_tagmethods.printdir = Fax3PrintDir;   /* hook for codec tags */
+	sp->groupoptions = 0;	
+	sp->recvparams = 0;
+	sp->subaddress = NULL;
+	sp->faxdcs = NULL;
+
+	if (sp->rw_mode == O_RDONLY) /* FIXME: improve for in place update */
+		tif->tif_flags |= TIFF_NOBITREV; /* decoder does bit reversal */
+	DecoderState(tif)->runs = NULL;
+	TIFFSetField(tif, TIFFTAG_FAXFILLFUNC, _TIFFFax3fillruns);
+	EncoderState(tif)->refline = NULL;
+
+	/*
+	 * Install codec methods.
+	 */
+	tif->tif_setupdecode = Fax3SetupState;
+	tif->tif_predecode = Fax3PreDecode;
+	tif->tif_decoderow = Fax3Decode1D;
+	tif->tif_decodestrip = Fax3Decode1D;
+	tif->tif_decodetile = Fax3Decode1D;
+	tif->tif_setupencode = Fax3SetupState;
+	tif->tif_preencode = Fax3PreEncode;
+	tif->tif_postencode = Fax3PostEncode;
+	tif->tif_encoderow = Fax3Encode;
+	tif->tif_encodestrip = Fax3Encode;
+	tif->tif_encodetile = Fax3Encode;
+	tif->tif_close = Fax3Close;
+	tif->tif_cleanup = Fax3Cleanup;
+
+	return (1);
+}
+
+int
+TIFFInitCCITTFax3(TIFF* tif, int scheme)
+{
+	(void) scheme;
+	if (InitCCITTFax3(tif)) {
+		_TIFFMergeFieldInfo(tif, fax3FieldInfo, N(fax3FieldInfo));
+
+		/*
+		 * The default format is Class/F-style w/o RTC.
+		 */
+		return TIFFSetField(tif, TIFFTAG_FAXMODE, FAXMODE_CLASSF);
+	} else
+		return (0);
+}
+
+/*
+ * CCITT Group 4 (T.6) Facsimile-compatible
+ * Compression Scheme Support.
+ */
+
+#define	SWAP(t,a,b)	{ t x; x = (a); (a) = (b); (b) = x; }
+/*
+ * Decode the requested amount of G4-encoded data.
+ */
+static int
+Fax4Decode(TIFF* tif, tidata_t buf, tsize_t occ, tsample_t s)
+{
+	DECLARE_STATE_2D(tif, sp, "Fax4Decode");
+        int line = 0;
+
+	(void) s;
+	CACHE_STATE(tif, sp);
+	while ((long)occ > 0) {
+		a0 = 0;
+		RunLength = 0;
+		pa = thisrun = sp->curruns;
+		pb = sp->refruns;
+		b1 = *pb++;
+#ifdef FAX3_DEBUG
+		printf("\nBitAcc=%08X, BitsAvail = %d\n", BitAcc, BitsAvail);
+		printf("-------------------- %d\n", tif->tif_row);
+		fflush(stdout);
+#endif
+		EXPAND2D(EOFG4);
+                if (EOLcnt)
+                    goto EOFG4;
+		(*sp->fill)(buf, thisrun, pa, lastx);
+		SETVALUE(0);		/* imaginary change for reference */
+		SWAP(uint32*, sp->curruns, sp->refruns);
+		buf += sp->b.rowbytes;
+		occ -= sp->b.rowbytes;
+                line++;
+		continue;
+	EOFG4:
+                NeedBits16( 13, BADG4 );
+        BADG4:
+#ifdef FAX3_DEBUG
+                if( GetBits(13) != 0x1001 )
+                    fputs( "Bad RTC\n", stderr );
+#endif                
+                ClrBits( 13 );
+		(*sp->fill)(buf, thisrun, pa, lastx);
+		UNCACHE_STATE(tif, sp);
+		return (-1);
+	}
+	UNCACHE_STATE(tif, sp);
+	return (1);
+}
+#undef	SWAP
+
+/*
+ * Encode the requested amount of data.
+ */
+static int
+Fax4Encode(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s)
+{
+	Fax3CodecState *sp = EncoderState(tif);
+
+	(void) s;
+	while ((long)cc > 0) {
+		if (!Fax3Encode2DRow(tif, bp, sp->refline, sp->b.rowpixels))
+			return (0);
+		_TIFFmemcpy(sp->refline, bp, sp->b.rowbytes);
+		bp += sp->b.rowbytes;
+		cc -= sp->b.rowbytes;
+	}
+	return (1);
+}
+
+static int
+Fax4PostEncode(TIFF* tif)
+{
+	Fax3CodecState *sp = EncoderState(tif);
+
+	/* terminate strip w/ EOFB */
+	Fax3PutBits(tif, EOL, 12);
+	Fax3PutBits(tif, EOL, 12);
+	if (sp->bit != 8)
+		Fax3FlushBits(tif, sp);
+	return (1);
+}
+
+int
+TIFFInitCCITTFax4(TIFF* tif, int scheme)
+{
+	(void) scheme;
+	if (InitCCITTFax3(tif)) {		/* reuse G3 support */
+		_TIFFMergeFieldInfo(tif, fax4FieldInfo, N(fax4FieldInfo));
+
+		tif->tif_decoderow = Fax4Decode;
+		tif->tif_decodestrip = Fax4Decode;
+		tif->tif_decodetile = Fax4Decode;
+		tif->tif_encoderow = Fax4Encode;
+		tif->tif_encodestrip = Fax4Encode;
+		tif->tif_encodetile = Fax4Encode;
+		tif->tif_postencode = Fax4PostEncode;
+		/*
+		 * Suppress RTC at the end of each strip.
+		 */
+		return TIFFSetField(tif, TIFFTAG_FAXMODE, FAXMODE_NORTC);
+	} else
+		return (0);
+}
+
+/*
+ * CCITT Group 3 1-D Modified Huffman RLE Compression Support.
+ * (Compression algorithms 2 and 32771)
+ */
+
+/*
+ * Decode the requested amount of RLE-encoded data.
+ */
+static int
+Fax3DecodeRLE(TIFF* tif, tidata_t buf, tsize_t occ, tsample_t s)
+{
+	DECLARE_STATE(tif, sp, "Fax3DecodeRLE");
+	int mode = sp->b.mode;
+        int line = 0;
+
+	(void) s;
+	CACHE_STATE(tif, sp);
+	thisrun = sp->curruns;
+	while ((long)occ > 0) {
+		a0 = 0;
+		RunLength = 0;
+		pa = thisrun;
+#ifdef FAX3_DEBUG
+		printf("\nBitAcc=%08X, BitsAvail = %d\n", BitAcc, BitsAvail);
+		printf("-------------------- %d\n", tif->tif_row);
+		fflush(stdout);
+#endif
+		EXPAND1D(EOFRLE);
+		(*sp->fill)(buf, thisrun, pa, lastx);
+		/*
+		 * Cleanup at the end of the row.
+		 */
+		if (mode & FAXMODE_BYTEALIGN) {
+			int n = BitsAvail - (BitsAvail &~ 7);
+			ClrBits(n);
+		} else if (mode & FAXMODE_WORDALIGN) {
+			int n = BitsAvail - (BitsAvail &~ 15);
+			ClrBits(n);
+			if (BitsAvail == 0 && !isAligned(cp, uint16))
+			    cp++;
+		}
+		buf += sp->b.rowbytes;
+		occ -= sp->b.rowbytes;
+                line++;
+		continue;
+	EOFRLE:				/* premature EOF */
+		(*sp->fill)(buf, thisrun, pa, lastx);
+		UNCACHE_STATE(tif, sp);
+		return (-1);
+	}
+	UNCACHE_STATE(tif, sp);
+	return (1);
+}
+
+int
+TIFFInitCCITTRLE(TIFF* tif, int scheme)
+{
+	(void) scheme;
+	if (InitCCITTFax3(tif)) {		/* reuse G3 support */
+		tif->tif_decoderow = Fax3DecodeRLE;
+		tif->tif_decodestrip = Fax3DecodeRLE;
+		tif->tif_decodetile = Fax3DecodeRLE;
+		/*
+		 * Suppress RTC+EOLs when encoding and byte-align data.
+		 */
+		return TIFFSetField(tif, TIFFTAG_FAXMODE,
+		    FAXMODE_NORTC|FAXMODE_NOEOL|FAXMODE_BYTEALIGN);
+	} else
+		return (0);
+}
+
+int
+TIFFInitCCITTRLEW(TIFF* tif, int scheme)
+{
+	(void) scheme;
+	if (InitCCITTFax3(tif)) {		/* reuse G3 support */
+		tif->tif_decoderow = Fax3DecodeRLE;
+		tif->tif_decodestrip = Fax3DecodeRLE;
+		tif->tif_decodetile = Fax3DecodeRLE;
+		/*
+		 * Suppress RTC+EOLs when encoding and word-align data.
+		 */
+		return TIFFSetField(tif, TIFFTAG_FAXMODE,
+		    FAXMODE_NORTC|FAXMODE_NOEOL|FAXMODE_WORDALIGN);
+	} else
+		return (0);
+}
+#endif /* CCITT_SUPPORT */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_fax3.h b/Utilities/GDAL/frmts/gtiff/libtiff/tif_fax3.h
new file mode 100644
index 0000000000..b218961dbe
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_fax3.h
@@ -0,0 +1,525 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1990-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _FAX3_
+#define	_FAX3_
+/*
+ * TIFF Library.
+ *
+ * CCITT Group 3 (T.4) and Group 4 (T.6) Decompression Support.
+ *
+ * Decoder support is derived, with permission, from the code
+ * in Frank Cringle's viewfax program;
+ *      Copyright (C) 1990, 1995  Frank D. Cringle.
+ */
+#include "tiff.h"
+
+/*
+ * To override the default routine used to image decoded
+ * spans one can use the pseduo tag TIFFTAG_FAXFILLFUNC.
+ * The routine must have the type signature given below;
+ * for example:
+ *
+ * fillruns(unsigned char* buf, uint32* runs, uint32* erun, uint32 lastx)
+ *
+ * where buf is place to set the bits, runs is the array of b&w run
+ * lengths (white then black), erun is the last run in the array, and
+ * lastx is the width of the row in pixels.  Fill routines can assume
+ * the run array has room for at least lastx runs and can overwrite
+ * data in the run array as needed (e.g. to append zero runs to bring
+ * the count up to a nice multiple).
+ */
+typedef	void (*TIFFFaxFillFunc)(unsigned char*, uint32*, uint32*, uint32);
+
+/*
+ * The default run filler; made external for other decoders.
+ */
+#if defined(__cplusplus)
+extern "C" {
+#endif
+extern	void _TIFFFax3fillruns(unsigned char*, uint32*, uint32*, uint32);
+#if defined(__cplusplus)
+}
+#endif
+
+
+/* finite state machine codes */
+#define S_Null		0
+#define S_Pass		1
+#define S_Horiz		2
+#define S_V0		3
+#define S_VR		4
+#define S_VL		5
+#define S_Ext		6
+#define S_TermW		7
+#define S_TermB		8
+#define S_MakeUpW	9
+#define S_MakeUpB	10
+#define S_MakeUp	11
+#define S_EOL		12
+
+typedef struct {		/* state table entry */
+	unsigned char State;	/* see above */
+	unsigned char Width;	/* width of code in bits */
+	uint32	Param;		/* unsigned 32-bit run length in bits */
+} TIFFFaxTabEnt;
+
+extern	const TIFFFaxTabEnt TIFFFaxMainTable[];
+extern	const TIFFFaxTabEnt TIFFFaxWhiteTable[];
+extern	const TIFFFaxTabEnt TIFFFaxBlackTable[];
+
+/*
+ * The following macros define the majority of the G3/G4 decoder
+ * algorithm using the state tables defined elsewhere.  To build
+ * a decoder you need some setup code and some glue code. Note
+ * that you may also need/want to change the way the NeedBits*
+ * macros get input data if, for example, you know the data to be
+ * decoded is properly aligned and oriented (doing so before running
+ * the decoder can be a big performance win).
+ *
+ * Consult the decoder in the TIFF library for an idea of what you
+ * need to define and setup to make use of these definitions.
+ *
+ * NB: to enable a debugging version of these macros define FAX3_DEBUG
+ *     before including this file.  Trace output goes to stdout.
+ */
+
+#ifndef EndOfData
+#define EndOfData()	(cp >= ep)
+#endif
+/*
+ * Need <=8 or <=16 bits of input data.  Unlike viewfax we
+ * cannot use/assume a word-aligned, properly bit swizzled
+ * input data set because data may come from an arbitrarily
+ * aligned, read-only source such as a memory-mapped file.
+ * Note also that the viewfax decoder does not check for
+ * running off the end of the input data buffer.  This is
+ * possible for G3-encoded data because it prescans the input
+ * data to count EOL markers, but can cause problems for G4
+ * data.  In any event, we don't prescan and must watch for
+ * running out of data since we can't permit the library to
+ * scan past the end of the input data buffer.
+ *
+ * Finally, note that we must handle remaindered data at the end
+ * of a strip specially.  The coder asks for a fixed number of
+ * bits when scanning for the next code.  This may be more bits
+ * than are actually present in the data stream.  If we appear
+ * to run out of data but still have some number of valid bits
+ * remaining then we makeup the requested amount with zeros and
+ * return successfully.  If the returned data is incorrect then
+ * we should be called again and get a premature EOF error;
+ * otherwise we should get the right answer.
+ */
+#ifndef NeedBits8
+#define NeedBits8(n,eoflab) do {					\
+    if (BitsAvail < (n)) {						\
+	if (EndOfData()) {						\
+	    if (BitsAvail == 0)			/* no valid bits */	\
+		goto eoflab;						\
+	    BitsAvail = (n);			/* pad with zeros */	\
+	} else {							\
+	    BitAcc |= ((uint32) bitmap[*cp++])<<BitsAvail;		\
+	    BitsAvail += 8;						\
+	}								\
+    }									\
+} while (0)
+#endif
+#ifndef NeedBits16
+#define NeedBits16(n,eoflab) do {					\
+    if (BitsAvail < (n)) {						\
+	if (EndOfData()) {						\
+	    if (BitsAvail == 0)			/* no valid bits */	\
+		goto eoflab;						\
+	    BitsAvail = (n);			/* pad with zeros */	\
+	} else {							\
+	    BitAcc |= ((uint32) bitmap[*cp++])<<BitsAvail;		\
+	    if ((BitsAvail += 8) < (n)) {				\
+		if (EndOfData()) {					\
+		    /* NB: we know BitsAvail is non-zero here */	\
+		    BitsAvail = (n);		/* pad with zeros */	\
+		} else {						\
+		    BitAcc |= ((uint32) bitmap[*cp++])<<BitsAvail;	\
+		    BitsAvail += 8;					\
+		}							\
+	    }								\
+	}								\
+    }									\
+} while (0)
+#endif
+#define GetBits(n)	(BitAcc & ((1<<(n))-1))
+#define ClrBits(n) do {							\
+    BitsAvail -= (n);							\
+    BitAcc >>= (n);							\
+} while (0)
+
+#ifdef FAX3_DEBUG
+static const char* StateNames[] = {
+    "Null   ",
+    "Pass   ",
+    "Horiz  ",
+    "V0     ",
+    "VR     ",
+    "VL     ",
+    "Ext    ",
+    "TermW  ",
+    "TermB  ",
+    "MakeUpW",
+    "MakeUpB",
+    "MakeUp ",
+    "EOL    ",
+};
+#define DEBUG_SHOW putchar(BitAcc & (1 << t) ? '1' : '0')
+#define LOOKUP8(wid,tab,eoflab) do {					\
+    int t;								\
+    NeedBits8(wid,eoflab);						\
+    TabEnt = tab + GetBits(wid);					\
+    printf("%08lX/%d: %s%5d\t", (long) BitAcc, BitsAvail,		\
+	   StateNames[TabEnt->State], TabEnt->Param);			\
+    for (t = 0; t < TabEnt->Width; t++)					\
+	DEBUG_SHOW;							\
+    putchar('\n');							\
+    fflush(stdout);							\
+    ClrBits(TabEnt->Width);						\
+} while (0)
+#define LOOKUP16(wid,tab,eoflab) do {					\
+    int t;								\
+    NeedBits16(wid,eoflab);						\
+    TabEnt = tab + GetBits(wid);					\
+    printf("%08lX/%d: %s%5d\t", (long) BitAcc, BitsAvail,		\
+	   StateNames[TabEnt->State], TabEnt->Param);			\
+    for (t = 0; t < TabEnt->Width; t++)					\
+	DEBUG_SHOW;							\
+    putchar('\n');							\
+    fflush(stdout);							\
+    ClrBits(TabEnt->Width);						\
+} while (0)
+
+#define SETVALUE(x) do {							\
+    *pa++ = RunLength + (x);						\
+    printf("SETVALUE: %d\t%d\n", RunLength + (x), a0);			\
+    a0 += x;								\
+    RunLength = 0;							\
+} while (0)
+#else
+#define LOOKUP8(wid,tab,eoflab) do {					\
+    NeedBits8(wid,eoflab);						\
+    TabEnt = tab + GetBits(wid);					\
+    ClrBits(TabEnt->Width);						\
+} while (0)
+#define LOOKUP16(wid,tab,eoflab) do {					\
+    NeedBits16(wid,eoflab);						\
+    TabEnt = tab + GetBits(wid);					\
+    ClrBits(TabEnt->Width);						\
+} while (0)
+
+/*
+ * Append a run to the run length array for the
+ * current row and reset decoding state.
+ */
+#define SETVALUE(x) do {							\
+    *pa++ = RunLength + (x);						\
+    a0 += (x);								\
+    RunLength = 0;							\
+} while (0)
+#endif
+
+/*
+ * Synchronize input decoding at the start of each
+ * row by scanning for an EOL (if appropriate) and
+ * skipping any trash data that might be present
+ * after a decoding error.  Note that the decoding
+ * done elsewhere that recognizes an EOL only consumes
+ * 11 consecutive zero bits.  This means that if EOLcnt
+ * is non-zero then we still need to scan for the final flag
+ * bit that is part of the EOL code.
+ */
+#define	SYNC_EOL(eoflab) do {						\
+    if (EOLcnt == 0) {							\
+	for (;;) {							\
+	    NeedBits16(11,eoflab);					\
+	    if (GetBits(11) == 0)					\
+		break;							\
+	    ClrBits(1);							\
+	}								\
+    }									\
+    for (;;) {								\
+	NeedBits8(8,eoflab);						\
+	if (GetBits(8))							\
+	    break;							\
+	ClrBits(8);							\
+    }									\
+    while (GetBits(1) == 0)						\
+	ClrBits(1);							\
+    ClrBits(1);				/* EOL bit */			\
+    EOLcnt = 0;				/* reset EOL counter/flag */	\
+} while (0)
+
+/*
+ * Cleanup the array of runs after decoding a row.
+ * We adjust final runs to insure the user buffer is not
+ * overwritten and/or undecoded area is white filled.
+ */
+#define	CLEANUP_RUNS() do {						\
+    if (RunLength)							\
+	SETVALUE(0);							\
+    if (a0 != lastx) {							\
+	badlength(a0, lastx);						\
+	while (a0 > lastx && pa > thisrun)				\
+	    a0 -= *--pa;						\
+	if (a0 < lastx) {						\
+	    if (a0 < 0)							\
+		a0 = 0;							\
+	    if ((pa-thisrun)&1)						\
+		SETVALUE(0);						\
+	    SETVALUE(lastx - a0);						\
+	} else if (a0 > lastx) {					\
+	    SETVALUE(lastx);						\
+	    SETVALUE(0);							\
+	}								\
+    }									\
+} while (0)
+
+/*
+ * Decode a line of 1D-encoded data.
+ *
+ * The line expanders are written as macros so that they can be reused
+ * but still have direct access to the local variables of the "calling"
+ * function.
+ *
+ * Note that unlike the original version we have to explicitly test for
+ * a0 >= lastx after each black/white run is decoded.  This is because
+ * the original code depended on the input data being zero-padded to
+ * insure the decoder recognized an EOL before running out of data.
+ */
+#define EXPAND1D(eoflab) do {						\
+    for (;;) {								\
+	for (;;) {							\
+	    LOOKUP16(12, TIFFFaxWhiteTable, eof1d);			\
+	    switch (TabEnt->State) {					\
+	    case S_EOL:							\
+		EOLcnt = 1;						\
+		goto done1d;						\
+	    case S_TermW:						\
+		SETVALUE(TabEnt->Param);					\
+		goto doneWhite1d;					\
+	    case S_MakeUpW:						\
+	    case S_MakeUp:						\
+		a0 += TabEnt->Param;					\
+		RunLength += TabEnt->Param;				\
+		break;							\
+	    default:							\
+		unexpected("WhiteTable", a0);				\
+		goto done1d;						\
+	    }								\
+	}								\
+    doneWhite1d:							\
+	if (a0 >= lastx)						\
+	    goto done1d;						\
+	for (;;) {							\
+	    LOOKUP16(13, TIFFFaxBlackTable, eof1d);			\
+	    switch (TabEnt->State) {					\
+	    case S_EOL:							\
+		EOLcnt = 1;						\
+		goto done1d;						\
+	    case S_TermB:						\
+		SETVALUE(TabEnt->Param);					\
+		goto doneBlack1d;					\
+	    case S_MakeUpB:						\
+	    case S_MakeUp:						\
+		a0 += TabEnt->Param;					\
+		RunLength += TabEnt->Param;				\
+		break;							\
+	    default:							\
+		unexpected("BlackTable", a0);				\
+		goto done1d;						\
+	    }								\
+	}								\
+    doneBlack1d:							\
+	if (a0 >= lastx)						\
+	    goto done1d;						\
+        if( *(pa-1) == 0 && *(pa-2) == 0 )				\
+            pa -= 2;                                                    \
+    }									\
+eof1d:									\
+    prematureEOF(a0);							\
+    CLEANUP_RUNS();							\
+    goto eoflab;							\
+done1d:									\
+    CLEANUP_RUNS();							\
+} while (0)
+
+/*
+ * Update the value of b1 using the array
+ * of runs for the reference line.
+ */
+#define CHECK_b1 do {							\
+    if (pa != thisrun) while (b1 <= a0 && b1 < lastx) {			\
+	b1 += pb[0] + pb[1];						\
+	pb += 2;							\
+    }									\
+} while (0)
+
+/*
+ * Expand a row of 2D-encoded data.
+ */
+#define EXPAND2D(eoflab) do {						\
+    while (a0 < lastx) {						\
+	LOOKUP8(7, TIFFFaxMainTable, eof2d);				\
+	switch (TabEnt->State) {					\
+	case S_Pass:							\
+	    CHECK_b1;							\
+	    b1 += *pb++;						\
+	    RunLength += b1 - a0;					\
+	    a0 = b1;							\
+	    b1 += *pb++;						\
+	    break;							\
+	case S_Horiz:							\
+	    if ((pa-thisrun)&1) {					\
+		for (;;) {	/* black first */			\
+		    LOOKUP16(13, TIFFFaxBlackTable, eof2d);		\
+		    switch (TabEnt->State) {				\
+		    case S_TermB:					\
+			SETVALUE(TabEnt->Param);				\
+			goto doneWhite2da;				\
+		    case S_MakeUpB:					\
+		    case S_MakeUp:					\
+			a0 += TabEnt->Param;				\
+			RunLength += TabEnt->Param;			\
+			break;						\
+		    default:						\
+			goto badBlack2d;				\
+		    }							\
+		}							\
+	    doneWhite2da:;						\
+		for (;;) {	/* then white */			\
+		    LOOKUP16(12, TIFFFaxWhiteTable, eof2d);		\
+		    switch (TabEnt->State) {				\
+		    case S_TermW:					\
+			SETVALUE(TabEnt->Param);				\
+			goto doneBlack2da;				\
+		    case S_MakeUpW:					\
+		    case S_MakeUp:					\
+			a0 += TabEnt->Param;				\
+			RunLength += TabEnt->Param;			\
+			break;						\
+		    default:						\
+			goto badWhite2d;				\
+		    }							\
+		}							\
+	    doneBlack2da:;						\
+	    } else {							\
+		for (;;) {	/* white first */			\
+		    LOOKUP16(12, TIFFFaxWhiteTable, eof2d);		\
+		    switch (TabEnt->State) {				\
+		    case S_TermW:					\
+			SETVALUE(TabEnt->Param);				\
+			goto doneWhite2db;				\
+		    case S_MakeUpW:					\
+		    case S_MakeUp:					\
+			a0 += TabEnt->Param;				\
+			RunLength += TabEnt->Param;			\
+			break;						\
+		    default:						\
+			goto badWhite2d;				\
+		    }							\
+		}							\
+	    doneWhite2db:;						\
+		for (;;) {	/* then black */			\
+		    LOOKUP16(13, TIFFFaxBlackTable, eof2d);		\
+		    switch (TabEnt->State) {				\
+		    case S_TermB:					\
+			SETVALUE(TabEnt->Param);				\
+			goto doneBlack2db;				\
+		    case S_MakeUpB:					\
+		    case S_MakeUp:					\
+			a0 += TabEnt->Param;				\
+			RunLength += TabEnt->Param;			\
+			break;						\
+		    default:						\
+			goto badBlack2d;				\
+		    }							\
+		}							\
+	    doneBlack2db:;						\
+	    }								\
+	    CHECK_b1;							\
+	    break;							\
+	case S_V0:							\
+	    CHECK_b1;							\
+	    SETVALUE(b1 - a0);						\
+	    b1 += *pb++;						\
+	    break;							\
+	case S_VR:							\
+	    CHECK_b1;							\
+	    SETVALUE(b1 - a0 + TabEnt->Param);				\
+	    b1 += *pb++;						\
+	    break;							\
+	case S_VL:							\
+	    CHECK_b1;							\
+	    SETVALUE(b1 - a0 - TabEnt->Param);				\
+	    b1 -= *--pb;						\
+	    break;							\
+	case S_Ext:							\
+	    *pa++ = lastx - a0;						\
+	    extension(a0);						\
+	    goto eol2d;							\
+	case S_EOL:							\
+	    *pa++ = lastx - a0;						\
+	    NeedBits8(4,eof2d);						\
+	    if (GetBits(4))						\
+		unexpected("EOL", a0);					\
+            ClrBits(4);                                                 \
+	    EOLcnt = 1;							\
+	    goto eol2d;							\
+	default:							\
+	badMain2d:							\
+	    unexpected("MainTable", a0);				\
+	    goto eol2d;							\
+	badBlack2d:							\
+	    unexpected("BlackTable", a0);				\
+	    goto eol2d;							\
+	badWhite2d:							\
+	    unexpected("WhiteTable", a0);				\
+	    goto eol2d;							\
+	eof2d:								\
+	    prematureEOF(a0);						\
+	    CLEANUP_RUNS();						\
+	    goto eoflab;						\
+	}								\
+    }									\
+    if (RunLength) {							\
+	if (RunLength + a0 < lastx) {					\
+	    /* expect a final V0 */					\
+	    NeedBits8(1,eof2d);						\
+	    if (!GetBits(1))						\
+		goto badMain2d;						\
+	    ClrBits(1);							\
+	}								\
+	SETVALUE(0);							\
+    }									\
+eol2d:									\
+    CLEANUP_RUNS();							\
+} while (0)
+#endif /* _FAX3_ */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_fax3sm.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_fax3sm.c
new file mode 100644
index 0000000000..08ce1ad608
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_fax3sm.c
@@ -0,0 +1,1253 @@
+/* WARNING, this file was automatically generated by the
+    mkg3states program */
+#include "tiff.h"
+#include "tif_fax3.h"
+ const TIFFFaxTabEnt TIFFFaxMainTable[128] = {
+{12,7,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0},
+{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{5,6,2},{3,1,0},{5,3,1},{3,1,0},
+{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},
+{4,3,1},{3,1,0},{5,7,3},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},
+{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{4,6,2},{3,1,0},
+{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0},{5,3,1},{3,1,0},
+{2,3,0},{3,1,0},{4,3,1},{3,1,0},{6,7,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},
+{4,3,1},{3,1,0},{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},
+{5,6,2},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0},
+{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{4,7,3},{3,1,0},{5,3,1},{3,1,0},
+{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},
+{4,3,1},{3,1,0},{4,6,2},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},
+{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0}
+};
+ const TIFFFaxTabEnt TIFFFaxWhiteTable[4096] = {
+{12,11,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},
+{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},
+{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},
+{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},
+{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},
+{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},
+{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6},{7,8,31},{7,5,8},
+{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},
+{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},
+{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},
+{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,11,1792},{7,4,3},
+{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},
+{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},
+{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},
+{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1600},{7,4,5},
+{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},
+{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},
+{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},
+{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},
+{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},
+{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},
+{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},
+{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},
+{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},
+{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},
+{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5},{7,8,43},{7,6,17},
+{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},
+{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},
+{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,768},{7,4,6},
+{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},
+{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,11,1856},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},
+{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},
+{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6},{7,7,19},{7,5,8},
+{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},
+{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},
+{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17},{9,9,1408},{7,4,6},
+{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},
+{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},
+{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},
+{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},
+{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},
+{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},
+{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},
+{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},
+{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},
+{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8},
+{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},
+{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},
+{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6},
+{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},
+{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},
+{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},
+{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{11,12,2112},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},
+{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},
+{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},
+{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},
+{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},
+{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},
+{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8},
+{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},
+{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},
+{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},
+{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},
+{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},
+{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},
+{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},
+{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5},
+{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},
+{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},
+{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},
+{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},
+{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},
+{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2368},{7,4,3},{7,5,11},{7,4,5},
+{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},
+{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6},
+{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},
+{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},
+{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17},
+{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},
+{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},
+{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},
+{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},
+{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},
+{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},
+{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},
+{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},
+{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},
+{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6},
+{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},
+{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},
+{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},
+{9,9,960},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},
+{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},
+{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{11,12,1984},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},
+{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},
+{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},
+{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},
+{7,6,13},{7,4,3},{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},
+{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},
+{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},
+{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6},
+{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},
+{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},
+{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},
+{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},
+{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},
+{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},
+{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},
+{9,9,1536},{7,4,5},{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},
+{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},
+{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},
+{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},
+{7,7,26},{7,5,9},{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},
+{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},
+{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,11,1920},{7,4,3},
+{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},
+{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},
+{9,9,896},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},
+{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5},
+{7,8,44},{7,6,17},{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},
+{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},
+{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},
+{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},
+{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},
+{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},
+{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},
+{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},
+{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},
+{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},
+{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17},
+{9,9,1216},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},
+{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},
+{7,8,41},{7,6,16},{9,9,960},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6},
+{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},
+{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2240},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},
+{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},
+{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},
+{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},
+{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},
+{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6},
+{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},
+{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},
+{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},
+{9,9,1088},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},
+{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},
+{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},
+{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},
+{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},
+{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},
+{7,6,13},{7,4,3},{9,9,1536},{7,4,5},{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8},
+{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},
+{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},
+{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6},
+{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},
+{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},
+{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},
+{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{11,12,2496},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},
+{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},
+{7,8,40},{7,6,16},{9,9,896},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},
+{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},
+{9,9,1728},{7,4,5},{7,8,44},{7,6,17},{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},
+{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},
+{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8},
+{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},
+{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},
+{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},
+{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{12,11,0},{7,4,3},
+{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},
+{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},
+{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},
+{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1472},{7,4,5},
+{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},
+{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},
+{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},
+{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},
+{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},
+{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,11,1792},{7,4,3},{7,5,11},{7,4,5},
+{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},
+{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,832},{7,4,6},
+{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},
+{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},
+{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1600},{7,4,5},{7,8,44},{7,6,17},
+{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},
+{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},
+{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},
+{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},
+{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},
+{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},
+{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},
+{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},
+{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},
+{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5},{7,8,43},{7,6,17},{9,9,1280},{7,4,6},
+{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},
+{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},
+{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},
+{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,768},{7,4,6},{7,8,37},{9,5,128},
+{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},
+{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{11,11,1856},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},
+{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},
+{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},
+{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},
+{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17},{9,9,1408},{7,4,6},{7,6,1},{7,5,8},
+{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},
+{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},
+{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1152},{7,4,6},
+{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},
+{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},
+{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},
+{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},
+{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},
+{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},
+{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},
+{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},
+{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},
+{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6},{7,8,31},{7,5,8},
+{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},
+{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},
+{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},
+{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2176},{7,4,3},
+{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},
+{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},
+{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},
+{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1600},{7,4,5},
+{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},
+{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},
+{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},
+{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},
+{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},
+{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},
+{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},
+{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},
+{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},
+{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},
+{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5},{7,8,43},{7,6,17},
+{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},
+{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},
+{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,768},{7,4,6},
+{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},
+{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2432},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},
+{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},
+{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6},{7,7,19},{7,5,8},
+{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},
+{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},
+{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17},{9,9,1408},{7,4,6},
+{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},
+{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},
+{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},
+{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},
+{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},
+{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},
+{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},
+{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},
+{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},
+{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8},
+{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},
+{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},
+{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6},
+{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},
+{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},
+{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},
+{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{11,12,2048},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},
+{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},
+{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},
+{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},
+{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},
+{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},
+{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8},
+{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},
+{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},
+{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},
+{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},
+{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},
+{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},
+{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},
+{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5},
+{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},
+{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},
+{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},
+{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},
+{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},
+{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,11,1920},{7,4,3},{7,5,11},{7,4,5},
+{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},
+{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6},
+{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},
+{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},
+{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17},
+{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},
+{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},
+{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},
+{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},
+{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},
+{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},
+{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},
+{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},
+{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},
+{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6},
+{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},
+{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},
+{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},
+{9,9,960},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},
+{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},
+{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{11,12,2304},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},
+{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},
+{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},
+{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},
+{7,6,13},{7,4,3},{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},
+{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},
+{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},
+{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6},
+{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},
+{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},
+{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},
+{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},
+{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},
+{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},
+{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},
+{9,9,1536},{7,4,5},{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},
+{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},
+{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},
+{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},
+{7,7,26},{7,5,9},{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},
+{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},
+{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2560},{7,4,3},
+{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},
+{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},
+{9,9,896},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},
+{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},
+{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5},
+{7,8,44},{7,6,17},{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},
+{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},
+{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},
+{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},
+{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},
+{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},
+{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},
+{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}
+};
+ const TIFFFaxTabEnt TIFFFaxBlackTable[8192] = {
+{12,11,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1792},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,11,23},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,20},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,11,25},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,128},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,12,56},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,30},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1856},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,57},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,11,21},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,54},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,52},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,48},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{11,12,2112},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,44},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,36},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,384},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,28},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,60},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,40},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2368},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{11,12,1984},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,50},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,12,34},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1664},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,26},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1408},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,32},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1920},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,12,61},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,42},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{10,13,1024},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{10,13,768},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,62},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2240},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,46},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,38},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,512},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,11,19},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,24},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,22},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{11,12,2496},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{12,11,0},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1792},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,23},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,20},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,11,25},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{10,12,192},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1280},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,12,31},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{11,11,1856},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,58},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,11,21},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,896},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,640},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,49},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2176},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,12,45},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,37},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{10,12,448},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,29},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{10,13,1536},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,41},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2432},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{11,12,2048},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,51},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,35},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,320},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,27},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,59},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,33},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1920},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,256},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,43},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{10,13,1152},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,55},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,12,63},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{11,12,2304},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,47},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,12,39},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,53},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,19},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,24},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,22},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2560},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,10,16},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{12,11,0},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1792},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,23},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,11,20},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,25},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{10,12,128},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,56},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,30},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{11,11,1856},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,57},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,21},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,54},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,52},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,48},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2112},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,44},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,36},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{10,12,384},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,28},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,60},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,12,40},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{11,12,2368},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,1984},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,12,50},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,34},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{10,13,1728},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,26},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{10,13,1472},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,32},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1920},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,61},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,42},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1088},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,832},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,62},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{11,12,2240},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,46},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,38},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,576},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,19},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,11,24},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,22},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2496},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{12,11,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{11,11,1792},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,23},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,11,20},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,25},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,192},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1344},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,31},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1856},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,12,58},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,21},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{10,13,960},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{10,13,704},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,49},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2176},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,45},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,37},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,448},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,12,29},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1600},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,41},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{11,12,2432},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2048},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,51},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,35},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{10,12,320},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,27},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,59},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,12,33},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{11,11,1920},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,256},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,12,43},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1216},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,55},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,63},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2304},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,12,47},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,39},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,12,53},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,19},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,11,24},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,11,22},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2560},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},
+{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3},
+{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},
+{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},
+{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},
+{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},
+{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},
+{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},
+{8,3,4},{8,2,2}
+};
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_flush.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_flush.c
new file mode 100644
index 0000000000..5ff56e9a81
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_flush.c
@@ -0,0 +1,67 @@
+/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_flush.c,v 1.3 2000/09/15 20:52:42 warmerda Exp $ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ */
+#include "tiffiop.h"
+
+int
+TIFFFlush(TIFF* tif)
+{
+
+	if (tif->tif_mode != O_RDONLY) {
+		if (!TIFFFlushData(tif))
+			return (0);
+		if ((tif->tif_flags & TIFF_DIRTYDIRECT) &&
+		    !TIFFWriteDirectory(tif))
+			return (0);
+	}
+	return (1);
+}
+
+/*
+ * Flush buffered data to the file.
+ *
+ * Frank Warmerdam'2000: I modified this to return 1 if TIFF_BEENWRITING
+ * is not set, so that TIFFFlush() will proceed to write out the directory.
+ * The documentation says returning 1 is an error indicator, but not having
+ * been writing isn't exactly a an error.  Hopefully this doesn't cause
+ * problems for other people. 
+ */
+int
+TIFFFlushData(TIFF* tif)
+{
+	if ((tif->tif_flags & TIFF_BEENWRITING) == 0)
+		return (0);
+	if (tif->tif_flags & TIFF_POSTENCODE) {
+		tif->tif_flags &= ~TIFF_POSTENCODE;
+		if (!(*tif->tif_postencode)(tif))
+			return (0);
+	}
+	return (TIFFFlushData1(tif));
+}
+
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_getimage.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_getimage.c
new file mode 100644
index 0000000000..96c1fcf05b
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_getimage.c
@@ -0,0 +1,2741 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1991-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library
+ *
+ * Read and return a packed RGBA image.
+ */
+#include "tiffiop.h"
+#include <stdio.h>
+
+static int gtTileContig(TIFFRGBAImage*, uint32*, uint32, uint32);
+static int gtTileSeparate(TIFFRGBAImage*, uint32*, uint32, uint32);
+static int gtStripContig(TIFFRGBAImage*, uint32*, uint32, uint32);
+static int gtStripSeparate(TIFFRGBAImage*, uint32*, uint32, uint32);
+static int PickContigCase(TIFFRGBAImage*);
+static int PickSeparateCase(TIFFRGBAImage*);
+
+static int BuildMapUaToAa(TIFFRGBAImage* img);
+static int BuildMapBitdepth16To8(TIFFRGBAImage* img);
+
+static const char photoTag[] = "PhotometricInterpretation";
+
+/* 
+ * Helper constants used in Orientation tag handling
+ */
+#define FLIP_VERTICALLY 0x01
+#define FLIP_HORIZONTALLY 0x02
+
+/*
+ * Color conversion constants. We will define display types here.
+ */
+
+TIFFDisplay display_sRGB = {
+	{			/* XYZ -> luminance matrix */
+		{  3.2410F, -1.5374F, -0.4986F },
+		{  -0.9692F, 1.8760F, 0.0416F },
+		{  0.0556F, -0.2040F, 1.0570F }
+	},	
+	100.0F, 100.0F, 100.0F,	/* Light o/p for reference white */
+	255, 255, 255,		/* Pixel values for ref. white */
+	1.0F, 1.0F, 1.0F,	/* Residual light o/p for black pixel */
+	2.4F, 2.4F, 2.4F,	/* Gamma values for the three guns */
+};
+
+/*
+ * Check the image to see if TIFFReadRGBAImage can deal with it.
+ * 1/0 is returned according to whether or not the image can
+ * be handled.  If 0 is returned, emsg contains the reason
+ * why it is being rejected.
+ */
+int
+TIFFRGBAImageOK(TIFF* tif, char emsg[1024])
+{
+	TIFFDirectory* td = &tif->tif_dir;
+	uint16 photometric;
+	int colorchannels;
+
+	if (!tif->tif_decodestatus) {
+		sprintf(emsg, "Sorry, requested compression method is not configured");
+		return (0);
+	}
+	switch (td->td_bitspersample) {
+		case 1:
+		case 2:
+		case 4:
+		case 8:
+		case 16:
+			break;
+		default:
+			sprintf(emsg, "Sorry, can not handle images with %d-bit samples",
+			    td->td_bitspersample);
+			return (0);
+	}
+	colorchannels = td->td_samplesperpixel - td->td_extrasamples;
+	if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric)) {
+		switch (colorchannels) {
+			case 1:
+				photometric = PHOTOMETRIC_MINISBLACK;
+				break;
+			case 3:
+				photometric = PHOTOMETRIC_RGB;
+				break;
+			default:
+				sprintf(emsg, "Missing needed %s tag", photoTag);
+				return (0);
+		}
+	}
+	switch (photometric) {
+		case PHOTOMETRIC_MINISWHITE:
+		case PHOTOMETRIC_MINISBLACK:
+		case PHOTOMETRIC_PALETTE:
+			if (td->td_planarconfig == PLANARCONFIG_CONTIG
+			    && td->td_samplesperpixel != 1
+			    && td->td_bitspersample < 8 ) {
+				sprintf(emsg,
+				    "Sorry, can not handle contiguous data with %s=%d, "
+				    "and %s=%d and Bits/Sample=%d",
+				    photoTag, photometric,
+				    "Samples/pixel", td->td_samplesperpixel,
+				    td->td_bitspersample);
+				return (0);
+			}
+			/*
+			 * We should likely validate that any extra samples are either
+			 * to be ignored, or are alpha, and if alpha we should try to use
+			 * them.  But for now we won't bother with this.
+			*/
+			break;
+		case PHOTOMETRIC_YCBCR:
+			/*
+			 * TODO: if at all meaningful and useful, make more complete
+			 * support check here, or better still, refactor to let supporting
+			 * code decide whether there is support and what meaningfull
+			 * error to return
+			 */
+			break;
+		case PHOTOMETRIC_RGB:
+			if (colorchannels < 3) {
+				sprintf(emsg, "Sorry, can not handle RGB image with %s=%d",
+				    "Color channels", colorchannels);
+				return (0);
+			}
+			break;
+		case PHOTOMETRIC_SEPARATED:
+			{
+				uint16 inkset;
+				TIFFGetFieldDefaulted(tif, TIFFTAG_INKSET, &inkset);
+				if (inkset != INKSET_CMYK) {
+					sprintf(emsg,
+					    "Sorry, can not handle separated image with %s=%d",
+					    "InkSet", inkset);
+					return 0;
+				}
+				if (td->td_samplesperpixel < 4) {
+					sprintf(emsg,
+					    "Sorry, can not handle separated image with %s=%d",
+					    "Samples/pixel", td->td_samplesperpixel);
+					return 0;
+				}
+				break;
+			}
+		case PHOTOMETRIC_LOGL:
+			if (td->td_compression != COMPRESSION_SGILOG) {
+				sprintf(emsg, "Sorry, LogL data must have %s=%d",
+				    "Compression", COMPRESSION_SGILOG);
+				return (0);
+			}
+			break;
+		case PHOTOMETRIC_LOGLUV:
+			if (td->td_compression != COMPRESSION_SGILOG &&
+			    td->td_compression != COMPRESSION_SGILOG24) {
+				sprintf(emsg, "Sorry, LogLuv data must have %s=%d or %d",
+				    "Compression", COMPRESSION_SGILOG, COMPRESSION_SGILOG24);
+				return (0);
+			}
+			if (td->td_planarconfig != PLANARCONFIG_CONTIG) {
+				sprintf(emsg, "Sorry, can not handle LogLuv images with %s=%d",
+				    "Planarconfiguration", td->td_planarconfig);
+				return (0);
+			}
+			break;
+		case PHOTOMETRIC_CIELAB:
+			break;
+		default:
+			sprintf(emsg, "Sorry, can not handle image with %s=%d",
+			    photoTag, photometric);
+			return (0);
+	}
+	return (1);
+}
+
+void
+TIFFRGBAImageEnd(TIFFRGBAImage* img)
+{
+	if (img->Map)
+		_TIFFfree(img->Map), img->Map = NULL;
+	if (img->BWmap)
+		_TIFFfree(img->BWmap), img->BWmap = NULL;
+	if (img->PALmap)
+		_TIFFfree(img->PALmap), img->PALmap = NULL;
+	if (img->ycbcr)
+		_TIFFfree(img->ycbcr), img->ycbcr = NULL;
+	if (img->cielab)
+		_TIFFfree(img->cielab), img->cielab = NULL;
+	if (img->UaToAa)
+		_TIFFfree(img->UaToAa), img->UaToAa = NULL;
+	if (img->Bitdepth16To8)
+		_TIFFfree(img->Bitdepth16To8), img->Bitdepth16To8 = NULL;
+
+	if( img->redcmap ) {
+		_TIFFfree( img->redcmap );
+		_TIFFfree( img->greencmap );
+		_TIFFfree( img->bluecmap );
+	}
+}
+
+static int
+isCCITTCompression(TIFF* tif)
+{
+    uint16 compress;
+    TIFFGetField(tif, TIFFTAG_COMPRESSION, &compress);
+    return (compress == COMPRESSION_CCITTFAX3 ||
+	    compress == COMPRESSION_CCITTFAX4 ||
+	    compress == COMPRESSION_CCITTRLE ||
+	    compress == COMPRESSION_CCITTRLEW);
+}
+
+int
+TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024])
+{
+	uint16* sampleinfo;
+	uint16 extrasamples;
+	uint16 planarconfig;
+	uint16 compress;
+	int colorchannels;
+	uint16 *red_orig, *green_orig, *blue_orig;
+	int n_color;
+
+	/* Initialize to normal values */
+	img->row_offset = 0;
+	img->col_offset = 0;
+	img->redcmap = NULL;
+	img->greencmap = NULL;
+	img->bluecmap = NULL;
+	img->req_orientation = ORIENTATION_BOTLEFT;     /* It is the default */
+
+	img->tif = tif;
+	img->stoponerr = stop;
+	TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &img->bitspersample);
+	switch (img->bitspersample) {
+		case 1:
+		case 2:
+		case 4:
+		case 8:
+		case 16:
+			break;
+		default:
+			sprintf(emsg, "Sorry, can not handle images with %d-bit samples",
+			    img->bitspersample);
+			return (0);
+	}
+	img->alpha = 0;
+	TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &img->samplesperpixel);
+	TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES,
+	    &extrasamples, &sampleinfo);
+	if (extrasamples >= 1)
+	{
+		switch (sampleinfo[0]) {
+			case EXTRASAMPLE_UNSPECIFIED:          /* Workaround for some images without */
+				if (img->samplesperpixel > 3)  /* correct info about alpha channel */
+					img->alpha = EXTRASAMPLE_ASSOCALPHA;
+				break;
+			case EXTRASAMPLE_ASSOCALPHA:           /* data is pre-multiplied */
+			case EXTRASAMPLE_UNASSALPHA:           /* data is not pre-multiplied */
+				img->alpha = sampleinfo[0];
+				break;
+		}
+	}
+
+#ifdef DEFAULT_EXTRASAMPLE_AS_ALPHA
+	if( !TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &img->photometric))
+		img->photometric = PHOTOMETRIC_MINISWHITE;
+
+	if( extrasamples == 0
+	    && img->samplesperpixel == 4
+	    && img->photometric == PHOTOMETRIC_RGB )
+	{
+		img->alpha = EXTRASAMPLE_ASSOCALPHA;
+		extrasamples = 1;
+	}
+#endif
+
+	colorchannels = img->samplesperpixel - extrasamples;
+	TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compress);
+	TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planarconfig);
+	if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &img->photometric)) {
+		switch (colorchannels) {
+			case 1:
+				if (isCCITTCompression(tif))
+					img->photometric = PHOTOMETRIC_MINISWHITE;
+				else
+					img->photometric = PHOTOMETRIC_MINISBLACK;
+				break;
+			case 3:
+				img->photometric = PHOTOMETRIC_RGB;
+				break;
+			default:
+				sprintf(emsg, "Missing needed %s tag", photoTag);
+				return (0);
+		}
+	}
+	switch (img->photometric) {
+		case PHOTOMETRIC_PALETTE:
+			if (!TIFFGetField(tif, TIFFTAG_COLORMAP,
+			    &red_orig, &green_orig, &blue_orig)) {
+				sprintf(emsg, "Missing required \"Colormap\" tag");
+				return (0);
+			}
+
+			/* copy the colormaps so we can modify them */
+			n_color = (1L << img->bitspersample);
+			img->redcmap = (uint16 *) _TIFFmalloc(sizeof(uint16)*n_color);
+			img->greencmap = (uint16 *) _TIFFmalloc(sizeof(uint16)*n_color);
+			img->bluecmap = (uint16 *) _TIFFmalloc(sizeof(uint16)*n_color);
+			if( !img->redcmap || !img->greencmap || !img->bluecmap ) {
+				sprintf(emsg, "Out of memory for colormap copy");
+				return (0);
+			}
+
+			_TIFFmemcpy( img->redcmap, red_orig, n_color * 2 );
+			_TIFFmemcpy( img->greencmap, green_orig, n_color * 2 );
+			_TIFFmemcpy( img->bluecmap, blue_orig, n_color * 2 );
+
+			/* fall thru... */
+		case PHOTOMETRIC_MINISWHITE:
+		case PHOTOMETRIC_MINISBLACK:
+			if (planarconfig == PLANARCONFIG_CONTIG
+			    && img->samplesperpixel != 1
+			    && img->bitspersample < 8 ) {
+				sprintf(emsg,
+				    "Sorry, can not handle contiguous data with %s=%d, "
+				    "and %s=%d and Bits/Sample=%d",
+				    photoTag, img->photometric,
+				    "Samples/pixel", img->samplesperpixel,
+				    img->bitspersample);
+				return (0);
+			}
+			break;
+		case PHOTOMETRIC_YCBCR:
+			/* It would probably be nice to have a reality check here. */
+			if (planarconfig == PLANARCONFIG_CONTIG)
+				/* can rely on libjpeg to convert to RGB */
+				/* XXX should restore current state on exit */
+				switch (compress) {
+					case COMPRESSION_JPEG:
+						/*
+						 * TODO: when complete tests verify complete desubsampling
+						 * and YCbCr handling, remove use of TIFFTAG_JPEGCOLORMODE in
+						 * favor of tif_getimage.c native handling
+						 */
+						TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
+						img->photometric = PHOTOMETRIC_RGB;
+						break;
+					default:
+						/* do nothing */;
+						break;
+				}
+			/*
+			 * TODO: if at all meaningful and useful, make more complete
+			 * support check here, or better still, refactor to let supporting
+			 * code decide whether there is support and what meaningfull
+			 * error to return
+			 */
+			break;
+		case PHOTOMETRIC_RGB:
+			if (colorchannels < 3) {
+				sprintf(emsg, "Sorry, can not handle RGB image with %s=%d",
+				    "Color channels", colorchannels);
+				return (0);
+			}
+			break;
+		case PHOTOMETRIC_SEPARATED:
+			{
+				uint16 inkset;
+				TIFFGetFieldDefaulted(tif, TIFFTAG_INKSET, &inkset);
+				if (inkset != INKSET_CMYK) {
+					sprintf(emsg, "Sorry, can not handle separated image with %s=%d",
+					    "InkSet", inkset);
+					return (0);
+				}
+				if (img->samplesperpixel < 4) {
+					sprintf(emsg, "Sorry, can not handle separated image with %s=%d",
+					    "Samples/pixel", img->samplesperpixel);
+					return (0);
+				}
+			}
+			break;
+		case PHOTOMETRIC_LOGL:
+			if (compress != COMPRESSION_SGILOG) {
+				sprintf(emsg, "Sorry, LogL data must have %s=%d",
+				    "Compression", COMPRESSION_SGILOG);
+				return (0);
+			}
+			TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_8BIT);
+			img->photometric = PHOTOMETRIC_MINISBLACK;	/* little white lie */
+			img->bitspersample = 8;
+			break;
+		case PHOTOMETRIC_LOGLUV:
+			if (compress != COMPRESSION_SGILOG && compress != COMPRESSION_SGILOG24) {
+				sprintf(emsg, "Sorry, LogLuv data must have %s=%d or %d",
+				    "Compression", COMPRESSION_SGILOG, COMPRESSION_SGILOG24);
+				return (0);
+			}
+			if (planarconfig != PLANARCONFIG_CONTIG) {
+				sprintf(emsg, "Sorry, can not handle LogLuv images with %s=%d",
+				    "Planarconfiguration", planarconfig);
+				return (0);
+			}
+			TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_8BIT);
+			img->photometric = PHOTOMETRIC_RGB;		/* little white lie */
+			img->bitspersample = 8;
+			break;
+		case PHOTOMETRIC_CIELAB:
+			break;
+		default:
+			sprintf(emsg, "Sorry, can not handle image with %s=%d",
+			    photoTag, img->photometric);
+			return (0);
+	}
+	img->Map = NULL;
+	img->BWmap = NULL;
+	img->PALmap = NULL;
+	img->ycbcr = NULL;
+	img->cielab = NULL;
+	img->UaToAa = NULL;
+	img->Bitdepth16To8 = NULL;
+	TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &img->width);
+	TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &img->height);
+	TIFFGetFieldDefaulted(tif, TIFFTAG_ORIENTATION, &img->orientation);
+	img->isContig =
+	    !(planarconfig == PLANARCONFIG_SEPARATE && colorchannels > 1);
+	if (img->isContig) {
+		if (!PickContigCase(img)) {
+			sprintf(emsg, "Sorry, can not handle image");
+			return 0;
+		}
+	} else {
+		if (!PickSeparateCase(img)) {
+			sprintf(emsg, "Sorry, can not handle image");
+			return 0;
+		}
+	}
+	return 1;
+}
+
+int
+TIFFRGBAImageGet(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
+{
+    if (img->get == NULL) {
+		TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif), "No \"get\" routine setup");
+		return (0);
+	}
+	if (img->put.any == NULL) {
+		TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif),
+		"No \"put\" routine setupl; probably can not handle image format");
+		return (0);
+    }
+    return (*img->get)(img, raster, w, h);
+}
+
+/*
+ * Read the specified image into an ABGR-format rastertaking in account
+ * specified orientation.
+ */
+int
+TIFFReadRGBAImageOriented(TIFF* tif,
+			  uint32 rwidth, uint32 rheight, uint32* raster,
+			  int orientation, int stop)
+{
+    char emsg[1024] = "";
+    TIFFRGBAImage img;
+    int ok;
+
+	if (TIFFRGBAImageOK(tif, emsg) && TIFFRGBAImageBegin(&img, tif, stop, emsg)) {
+		img.req_orientation = orientation;
+		/* XXX verify rwidth and rheight against width and height */
+		ok = TIFFRGBAImageGet(&img, raster+(rheight-img.height)*rwidth,
+			rwidth, img.height);
+		TIFFRGBAImageEnd(&img);
+	} else {
+		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), emsg);
+		ok = 0;
+    }
+    return (ok);
+}
+
+/*
+ * Read the specified image into an ABGR-format raster. Use bottom left
+ * origin for raster by default.
+ */
+int
+TIFFReadRGBAImage(TIFF* tif,
+		  uint32 rwidth, uint32 rheight, uint32* raster, int stop)
+{
+	return TIFFReadRGBAImageOriented(tif, rwidth, rheight, raster,
+					 ORIENTATION_BOTLEFT, stop);
+}
+
+static int 
+setorientation(TIFFRGBAImage* img)
+{
+	switch (img->orientation) {
+		case ORIENTATION_TOPLEFT:
+		case ORIENTATION_LEFTTOP:
+			if (img->req_orientation == ORIENTATION_TOPRIGHT ||
+			    img->req_orientation == ORIENTATION_RIGHTTOP)
+				return FLIP_HORIZONTALLY;
+			else if (img->req_orientation == ORIENTATION_BOTRIGHT ||
+			    img->req_orientation == ORIENTATION_RIGHTBOT)
+				return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
+			else if (img->req_orientation == ORIENTATION_BOTLEFT ||
+			    img->req_orientation == ORIENTATION_LEFTBOT)
+				return FLIP_VERTICALLY;
+			else
+				return 0;
+		case ORIENTATION_TOPRIGHT:
+		case ORIENTATION_RIGHTTOP:
+			if (img->req_orientation == ORIENTATION_TOPLEFT ||
+			    img->req_orientation == ORIENTATION_LEFTTOP)
+				return FLIP_HORIZONTALLY;
+			else if (img->req_orientation == ORIENTATION_BOTRIGHT ||
+			    img->req_orientation == ORIENTATION_RIGHTBOT)
+				return FLIP_VERTICALLY;
+			else if (img->req_orientation == ORIENTATION_BOTLEFT ||
+			    img->req_orientation == ORIENTATION_LEFTBOT)
+				return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
+			else
+				return 0;
+		case ORIENTATION_BOTRIGHT:
+		case ORIENTATION_RIGHTBOT:
+			if (img->req_orientation == ORIENTATION_TOPLEFT ||
+			    img->req_orientation == ORIENTATION_LEFTTOP)
+				return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
+			else if (img->req_orientation == ORIENTATION_TOPRIGHT ||
+			    img->req_orientation == ORIENTATION_RIGHTTOP)
+				return FLIP_VERTICALLY;
+			else if (img->req_orientation == ORIENTATION_BOTLEFT ||
+			    img->req_orientation == ORIENTATION_LEFTBOT)
+				return FLIP_HORIZONTALLY;
+			else
+				return 0;
+		case ORIENTATION_BOTLEFT:
+		case ORIENTATION_LEFTBOT:
+			if (img->req_orientation == ORIENTATION_TOPLEFT ||
+			    img->req_orientation == ORIENTATION_LEFTTOP)
+				return FLIP_VERTICALLY;
+			else if (img->req_orientation == ORIENTATION_TOPRIGHT ||
+			    img->req_orientation == ORIENTATION_RIGHTTOP)
+				return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
+			else if (img->req_orientation == ORIENTATION_BOTRIGHT ||
+			    img->req_orientation == ORIENTATION_RIGHTBOT)
+				return FLIP_HORIZONTALLY;
+			else
+				return 0;
+		default:	/* NOTREACHED */
+			return 0;
+	}
+}
+
+/*
+ * Get an tile-organized image that has
+ *	PlanarConfiguration contiguous if SamplesPerPixel > 1
+ * or
+ *	SamplesPerPixel == 1
+ */	
+static int
+gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
+{
+    TIFF* tif = img->tif;
+    tileContigRoutine put = img->put.contig;
+    uint32 col, row, y, rowstoread;
+    uint32 pos;
+    uint32 tw, th;
+    unsigned char* buf;
+    int32 fromskew, toskew;
+    uint32 nrow;
+    int ret = 1, flip;
+
+    buf = (unsigned char*) _TIFFmalloc(TIFFTileSize(tif));
+    if (buf == 0) {
+		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "No space for tile buffer");
+		return (0);
+    }
+    _TIFFmemset(buf, 0, TIFFTileSize(tif));
+    TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw);
+    TIFFGetField(tif, TIFFTAG_TILELENGTH, &th);
+
+    flip = setorientation(img);
+    if (flip & FLIP_VERTICALLY) {
+	    y = h - 1;
+	    toskew = -(int32)(tw + w);
+    }
+    else {
+	    y = 0;
+	    toskew = -(int32)(tw - w);
+    }
+     
+    for (row = 0; row < h; row += nrow)
+    {
+        rowstoread = th - (row + img->row_offset) % th;
+    	nrow = (row + rowstoread > h ? h - row : rowstoread);
+	for (col = 0; col < w; col += tw) 
+        {
+            if (TIFFReadTile(tif, buf, col+img->col_offset,
+                             row+img->row_offset, 0, 0) < 0 && img->stoponerr)
+            {
+                ret = 0;
+                break;
+            }
+	    
+            pos = ((row+img->row_offset) % th) * TIFFTileRowSize(tif);
+
+    	    if (col + tw > w) 
+            {
+                /*
+                 * Tile is clipped horizontally.  Calculate
+                 * visible portion and skewing factors.
+                 */
+                uint32 npix = w - col;
+                fromskew = tw - npix;
+                (*put)(img, raster+y*w+col, col, y,
+                       npix, nrow, fromskew, toskew + fromskew, buf + pos);
+            }
+            else 
+            {
+                (*put)(img, raster+y*w+col, col, y, tw, nrow, 0, toskew, buf + pos);
+            }
+        }
+
+        y += (flip & FLIP_VERTICALLY ? -(int32) nrow : (int32) nrow);
+    }
+    _TIFFfree(buf);
+
+    if (flip & FLIP_HORIZONTALLY) {
+	    uint32 line;
+
+	    for (line = 0; line < h; line++) {
+		    uint32 *left = raster + (line * w);
+		    uint32 *right = left + w - 1;
+		    
+		    while ( left < right ) {
+			    uint32 temp = *left;
+			    *left = *right;
+			    *right = temp;
+			    left++, right--;
+		    }
+	    }
+    }
+
+    return (ret);
+}
+
+/*
+ * Get an tile-organized image that has
+ *	 SamplesPerPixel > 1
+ *	 PlanarConfiguration separated
+ * We assume that all such images are RGB.
+ */	
+static int
+gtTileSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
+{
+	TIFF* tif = img->tif;
+	tileSeparateRoutine put = img->put.separate;
+	uint32 col, row, y, rowstoread;
+	uint32 pos;
+	uint32 tw, th;
+	unsigned char* buf;
+	unsigned char* p0;
+	unsigned char* p1;
+	unsigned char* p2;
+	unsigned char* pa;
+	tsize_t tilesize;
+	int32 fromskew, toskew;
+	int alpha = img->alpha;
+	uint32 nrow;
+	int ret = 1, flip;
+
+	tilesize = TIFFTileSize(tif);
+	buf = (unsigned char*) _TIFFmalloc((alpha?4:3)*tilesize);
+	if (buf == 0) {
+		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "No space for tile buffer");
+		return (0);
+	}
+	_TIFFmemset(buf, 0, (alpha?4:3)*tilesize);
+	p0 = buf;
+	p1 = p0 + tilesize;
+	p2 = p1 + tilesize;
+	pa = (alpha?(p2+tilesize):NULL);
+	TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw);
+	TIFFGetField(tif, TIFFTAG_TILELENGTH, &th);
+
+	flip = setorientation(img);
+	if (flip & FLIP_VERTICALLY) {
+		y = h - 1;
+		toskew = -(int32)(tw + w);
+	}
+	else {
+		y = 0;
+		toskew = -(int32)(tw - w);
+	}
+
+	for (row = 0; row < h; row += nrow)
+	{
+		rowstoread = th - (row + img->row_offset) % th;
+		nrow = (row + rowstoread > h ? h - row : rowstoread);
+		for (col = 0; col < w; col += tw)
+		{
+			if (TIFFReadTile(tif, p0, col+img->col_offset,
+			    row+img->row_offset,0,0) < 0 && img->stoponerr)
+			{
+				ret = 0;
+				break;
+			}
+			if (TIFFReadTile(tif, p1, col+img->col_offset,
+			    row+img->row_offset,0,1) < 0 && img->stoponerr)
+			{
+				ret = 0;
+				break;
+			}
+			if (TIFFReadTile(tif, p2, col+img->col_offset,
+			    row+img->row_offset,0,2) < 0 && img->stoponerr)
+			{
+				ret = 0;
+				break;
+			}
+			if (alpha)
+			{
+				if (TIFFReadTile(tif,pa,col+img->col_offset,
+				    row+img->row_offset,0,3) < 0 && img->stoponerr)
+				{
+					ret = 0;
+					break;
+				}
+			}
+
+			pos = ((row+img->row_offset) % th) * TIFFTileRowSize(tif);
+
+			if (col + tw > w)
+			{
+				/*
+				 * Tile is clipped horizontally.  Calculate
+				 * visible portion and skewing factors.
+				 */
+				uint32 npix = w - col;
+				fromskew = tw - npix;
+				(*put)(img, raster+y*w+col, col, y,
+				    npix, nrow, fromskew, toskew + fromskew,
+				    p0 + pos, p1 + pos, p2 + pos, (alpha?(pa+pos):NULL));
+			} else {
+				(*put)(img, raster+y*w+col, col, y,
+				    tw, nrow, 0, toskew, p0 + pos, p1 + pos, p2 + pos, (alpha?(pa+pos):NULL));
+			}
+		}
+
+		y += (flip & FLIP_VERTICALLY ?-(int32) nrow : (int32) nrow);
+	}
+
+	if (flip & FLIP_HORIZONTALLY) {
+		uint32 line;
+
+		for (line = 0; line < h; line++) {
+			uint32 *left = raster + (line * w);
+			uint32 *right = left + w - 1;
+
+			while ( left < right ) {
+				uint32 temp = *left;
+				*left = *right;
+				*right = temp;
+				left++, right--;
+			}
+		}
+	}
+
+	_TIFFfree(buf);
+	return (ret);
+}
+
+/*
+ * Get a strip-organized image that has
+ *	PlanarConfiguration contiguous if SamplesPerPixel > 1
+ * or
+ *	SamplesPerPixel == 1
+ */	
+static int
+gtStripContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
+{
+    TIFF* tif = img->tif;
+    tileContigRoutine put = img->put.contig;
+    uint32 row, y, nrow, rowstoread;
+    uint32 pos;
+    unsigned char* buf;
+    uint32 rowsperstrip;
+    uint32 imagewidth = img->width;
+    tsize_t scanline;
+    int32 fromskew, toskew;
+    int ret = 1, flip;
+
+    buf = (unsigned char*) _TIFFmalloc(TIFFStripSize(tif));
+    if (buf == 0) {
+		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "No space for strip buffer");
+		return (0);
+    }
+    _TIFFmemset(buf, 0, TIFFStripSize(tif));
+
+    flip = setorientation(img);
+    if (flip & FLIP_VERTICALLY) {
+	    y = h - 1;
+	    toskew = -(int32)(w + w);
+    } else {
+	    y = 0;
+	    toskew = -(int32)(w - w);
+    }
+
+    TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
+    scanline = TIFFNewScanlineSize(tif);
+    fromskew = (w < imagewidth ? imagewidth - w : 0);
+    for (row = 0; row < h; row += nrow) 
+    {
+        rowstoread = rowsperstrip - (row + img->row_offset) % rowsperstrip;
+        nrow = (row + rowstoread > h ? h - row : rowstoread);
+        if (TIFFReadEncodedStrip(tif,
+                                 TIFFComputeStrip(tif,row+img->row_offset, 0),
+                                 buf, 
+                                 ((row + img->row_offset)%rowsperstrip + nrow) * scanline) < 0
+            && img->stoponerr)
+        {
+            ret = 0;
+            break;
+        }
+
+        pos = ((row + img->row_offset) % rowsperstrip) * scanline;
+        (*put)(img, raster+y*w, 0, y, w, nrow, fromskew, toskew, buf + pos);
+        y += (flip & FLIP_VERTICALLY ? -(int32) nrow : (int32) nrow);
+    }
+
+    if (flip & FLIP_HORIZONTALLY) {
+	    uint32 line;
+
+	    for (line = 0; line < h; line++) {
+		    uint32 *left = raster + (line * w);
+		    uint32 *right = left + w - 1;
+		    
+		    while ( left < right ) {
+			    uint32 temp = *left;
+			    *left = *right;
+			    *right = temp;
+			    left++, right--;
+		    }
+	    }
+    }
+
+    _TIFFfree(buf);
+    return (ret);
+}
+
+/*
+ * Get a strip-organized image with
+ *	 SamplesPerPixel > 1
+ *	 PlanarConfiguration separated
+ * We assume that all such images are RGB.
+ */
+static int
+gtStripSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
+{
+	TIFF* tif = img->tif;
+	tileSeparateRoutine put = img->put.separate;
+	unsigned char *buf;
+	unsigned char *p0, *p1, *p2, *pa;
+	uint32 row, y, nrow, rowstoread;
+	uint32 pos;
+	tsize_t scanline;
+	uint32 rowsperstrip, offset_row;
+	uint32 imagewidth = img->width;
+	tsize_t stripsize;
+	int32 fromskew, toskew;
+	int alpha = img->alpha;
+	int ret = 1, flip;
+
+	stripsize = TIFFStripSize(tif);
+	p0 = buf = (unsigned char *)_TIFFmalloc((alpha?4:3)*stripsize);
+	if (buf == 0) {
+		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "No space for tile buffer");
+		return (0);
+	}
+	_TIFFmemset(buf, 0, (alpha?4:3)*stripsize);
+	p1 = p0 + stripsize;
+	p2 = p1 + stripsize;
+	pa = (alpha?(p2+stripsize):NULL);
+
+	flip = setorientation(img);
+	if (flip & FLIP_VERTICALLY) {
+		y = h - 1;
+		toskew = -(int32)(w + w);
+	}
+	else {
+		y = 0;
+		toskew = -(int32)(w - w);
+	}
+
+	TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
+	scanline = TIFFScanlineSize(tif);
+	fromskew = (w < imagewidth ? imagewidth - w : 0);
+	for (row = 0; row < h; row += nrow)
+	{
+		rowstoread = rowsperstrip - (row + img->row_offset) % rowsperstrip;
+		nrow = (row + rowstoread > h ? h - row : rowstoread);
+		offset_row = row + img->row_offset;
+		if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 0),
+		    p0, ((row + img->row_offset)%rowsperstrip + nrow) * scanline) < 0
+		    && img->stoponerr)
+		{
+			ret = 0;
+			break;
+		}
+		if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 1),
+		    p1, ((row + img->row_offset)%rowsperstrip + nrow) * scanline) < 0
+		    && img->stoponerr)
+		{
+			ret = 0;
+			break;
+		}
+		if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 2),
+		    p2, ((row + img->row_offset)%rowsperstrip + nrow) * scanline) < 0
+		    && img->stoponerr)
+		{
+			ret = 0;
+			break;
+		}
+		if (alpha)
+		{
+			if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 3),
+			    pa, ((row + img->row_offset)%rowsperstrip + nrow) * scanline) < 0
+			    && img->stoponerr)
+			{
+				ret = 0;
+				break;
+			}
+		}
+
+		pos = ((row + img->row_offset) % rowsperstrip) * scanline;
+		(*put)(img, raster+y*w, 0, y, w, nrow, fromskew, toskew, p0 + pos, p1 + pos,
+		    p2 + pos, (alpha?(pa+pos):NULL));
+		y += (flip & FLIP_VERTICALLY ? -(int32) nrow : (int32) nrow);
+	}
+
+	if (flip & FLIP_HORIZONTALLY) {
+		uint32 line;
+
+		for (line = 0; line < h; line++) {
+			uint32 *left = raster + (line * w);
+			uint32 *right = left + w - 1;
+
+			while ( left < right ) {
+				uint32 temp = *left;
+				*left = *right;
+				*right = temp;
+				left++, right--;
+			}
+		}
+	}
+
+	_TIFFfree(buf);
+	return (ret);
+}
+
+/*
+ * The following routines move decoded data returned
+ * from the TIFF library into rasters filled with packed
+ * ABGR pixels (i.e. suitable for passing to lrecwrite.)
+ *
+ * The routines have been created according to the most
+ * important cases and optimized.  PickContigCase and
+ * PickSeparateCase analyze the parameters and select
+ * the appropriate "get" and "put" routine to use.
+ */
+#define	REPEAT8(op)	REPEAT4(op); REPEAT4(op)
+#define	REPEAT4(op)	REPEAT2(op); REPEAT2(op)
+#define	REPEAT2(op)	op; op
+#define	CASE8(x,op)			\
+    switch (x) {			\
+    case 7: op; case 6: op; case 5: op;	\
+    case 4: op; case 3: op; case 2: op;	\
+    case 1: op;				\
+    }
+#define	CASE4(x,op)	switch (x) { case 3: op; case 2: op; case 1: op; }
+#define	NOP
+
+#define	UNROLL8(w, op1, op2) {		\
+    uint32 _x;				\
+    for (_x = w; _x >= 8; _x -= 8) {	\
+	op1;				\
+	REPEAT8(op2);			\
+    }					\
+    if (_x > 0) {			\
+	op1;				\
+	CASE8(_x,op2);			\
+    }					\
+}
+#define	UNROLL4(w, op1, op2) {		\
+    uint32 _x;				\
+    for (_x = w; _x >= 4; _x -= 4) {	\
+	op1;				\
+	REPEAT4(op2);			\
+    }					\
+    if (_x > 0) {			\
+	op1;				\
+	CASE4(_x,op2);			\
+    }					\
+}
+#define	UNROLL2(w, op1, op2) {		\
+    uint32 _x;				\
+    for (_x = w; _x >= 2; _x -= 2) {	\
+	op1;				\
+	REPEAT2(op2);			\
+    }					\
+    if (_x) {				\
+	op1;				\
+	op2;				\
+    }					\
+}
+    
+#define	SKEW(r,g,b,skew)	{ r += skew; g += skew; b += skew; }
+#define	SKEW4(r,g,b,a,skew)	{ r += skew; g += skew; b += skew; a+= skew; }
+
+#define A1 (((uint32)0xffL)<<24)
+#define	PACK(r,g,b)	\
+	((uint32)(r)|((uint32)(g)<<8)|((uint32)(b)<<16)|A1)
+#define	PACK4(r,g,b,a)	\
+	((uint32)(r)|((uint32)(g)<<8)|((uint32)(b)<<16)|((uint32)(a)<<24))
+#define W2B(v) (((v)>>8)&0xff)
+/* TODO: PACKW should have be made redundant in favor of Bitdepth16To8 LUT */
+#define	PACKW(r,g,b)	\
+	((uint32)W2B(r)|((uint32)W2B(g)<<8)|((uint32)W2B(b)<<16)|A1)
+#define	PACKW4(r,g,b,a)	\
+	((uint32)W2B(r)|((uint32)W2B(g)<<8)|((uint32)W2B(b)<<16)|((uint32)W2B(a)<<24))
+
+#define	DECLAREContigPutFunc(name) \
+static void name(\
+    TIFFRGBAImage* img, \
+    uint32* cp, \
+    uint32 x, uint32 y, \
+    uint32 w, uint32 h, \
+    int32 fromskew, int32 toskew, \
+    unsigned char* pp \
+)
+
+/*
+ * 8-bit palette => colormap/RGB
+ */
+DECLAREContigPutFunc(put8bitcmaptile)
+{
+    uint32** PALmap = img->PALmap;
+    int samplesperpixel = img->samplesperpixel;
+
+    (void) y;
+    while (h-- > 0) {
+	for (x = w; x-- > 0;)
+        {
+	    *cp++ = PALmap[*pp][0];
+            pp += samplesperpixel;
+        }
+	cp += toskew;
+	pp += fromskew;
+    }
+}
+
+/*
+ * 4-bit palette => colormap/RGB
+ */
+DECLAREContigPutFunc(put4bitcmaptile)
+{
+    uint32** PALmap = img->PALmap;
+
+    (void) x; (void) y;
+    fromskew /= 2;
+    while (h-- > 0) {
+	uint32* bw;
+	UNROLL2(w, bw = PALmap[*pp++], *cp++ = *bw++);
+	cp += toskew;
+	pp += fromskew;
+    }
+}
+
+/*
+ * 2-bit palette => colormap/RGB
+ */
+DECLAREContigPutFunc(put2bitcmaptile)
+{
+    uint32** PALmap = img->PALmap;
+
+    (void) x; (void) y;
+    fromskew /= 4;
+    while (h-- > 0) {
+	uint32* bw;
+	UNROLL4(w, bw = PALmap[*pp++], *cp++ = *bw++);
+	cp += toskew;
+	pp += fromskew;
+    }
+}
+
+/*
+ * 1-bit palette => colormap/RGB
+ */
+DECLAREContigPutFunc(put1bitcmaptile)
+{
+    uint32** PALmap = img->PALmap;
+
+    (void) x; (void) y;
+    fromskew /= 8;
+    while (h-- > 0) {
+	uint32* bw;
+	UNROLL8(w, bw = PALmap[*pp++], *cp++ = *bw++);
+	cp += toskew;
+	pp += fromskew;
+    }
+}
+
+/*
+ * 8-bit greyscale => colormap/RGB
+ */
+DECLAREContigPutFunc(putgreytile)
+{
+    int samplesperpixel = img->samplesperpixel;
+    uint32** BWmap = img->BWmap;
+
+    (void) y;
+    while (h-- > 0) {
+	for (x = w; x-- > 0;)
+        {
+	    *cp++ = BWmap[*pp][0];
+            pp += samplesperpixel;
+        }
+	cp += toskew;
+	pp += fromskew;
+    }
+}
+
+/*
+ * 16-bit greyscale => colormap/RGB
+ */
+DECLAREContigPutFunc(put16bitbwtile)
+{
+    int samplesperpixel = img->samplesperpixel;
+    uint32** BWmap = img->BWmap;
+
+    (void) y;
+    while (h-- > 0) {
+        uint16 *wp = (uint16 *) pp;
+
+	for (x = w; x-- > 0;)
+        {
+            /* use high order byte of 16bit value */
+
+	    *cp++ = BWmap[*wp >> 8][0];
+            pp += 2 * samplesperpixel;
+            wp += samplesperpixel;
+        }
+	cp += toskew;
+	pp += fromskew;
+    }
+}
+
+/*
+ * 1-bit bilevel => colormap/RGB
+ */
+DECLAREContigPutFunc(put1bitbwtile)
+{
+    uint32** BWmap = img->BWmap;
+
+    (void) x; (void) y;
+    fromskew /= 8;
+    while (h-- > 0) {
+	uint32* bw;
+	UNROLL8(w, bw = BWmap[*pp++], *cp++ = *bw++);
+	cp += toskew;
+	pp += fromskew;
+    }
+}
+
+/*
+ * 2-bit greyscale => colormap/RGB
+ */
+DECLAREContigPutFunc(put2bitbwtile)
+{
+    uint32** BWmap = img->BWmap;
+
+    (void) x; (void) y;
+    fromskew /= 4;
+    while (h-- > 0) {
+	uint32* bw;
+	UNROLL4(w, bw = BWmap[*pp++], *cp++ = *bw++);
+	cp += toskew;
+	pp += fromskew;
+    }
+}
+
+/*
+ * 4-bit greyscale => colormap/RGB
+ */
+DECLAREContigPutFunc(put4bitbwtile)
+{
+    uint32** BWmap = img->BWmap;
+
+    (void) x; (void) y;
+    fromskew /= 2;
+    while (h-- > 0) {
+	uint32* bw;
+	UNROLL2(w, bw = BWmap[*pp++], *cp++ = *bw++);
+	cp += toskew;
+	pp += fromskew;
+    }
+}
+
+/*
+ * 8-bit packed samples, no Map => RGB
+ */
+DECLAREContigPutFunc(putRGBcontig8bittile)
+{
+    int samplesperpixel = img->samplesperpixel;
+
+    (void) x; (void) y;
+    fromskew *= samplesperpixel;
+    while (h-- > 0) {
+	UNROLL8(w, NOP,
+	    *cp++ = PACK(pp[0], pp[1], pp[2]);
+	    pp += samplesperpixel);
+	cp += toskew;
+	pp += fromskew;
+    }
+}
+
+/*
+ * 8-bit packed samples => RGBA w/ associated alpha
+ * (known to have Map == NULL)
+ */
+DECLAREContigPutFunc(putRGBAAcontig8bittile)
+{
+    int samplesperpixel = img->samplesperpixel;
+
+    (void) x; (void) y;
+    fromskew *= samplesperpixel;
+    while (h-- > 0) {
+	UNROLL8(w, NOP,
+	    *cp++ = PACK4(pp[0], pp[1], pp[2], pp[3]);
+	    pp += samplesperpixel);
+	cp += toskew;
+	pp += fromskew;
+    }
+}
+
+/*
+ * 8-bit packed samples => RGBA w/ unassociated alpha
+ * (known to have Map == NULL)
+ */
+DECLAREContigPutFunc(putRGBUAcontig8bittile)
+{
+	int samplesperpixel = img->samplesperpixel;
+	(void) y;
+	fromskew *= samplesperpixel;
+	while (h-- > 0) {
+		uint32 r, g, b, a;
+		uint8* m;
+		for (x = w; x-- > 0;) {
+			a = pp[3];
+			m = img->UaToAa+(a<<8);
+			r = m[pp[0]];
+			g = m[pp[1]];
+			b = m[pp[2]];
+			*cp++ = PACK4(r,g,b,a);
+			pp += samplesperpixel;
+		}
+		cp += toskew;
+		pp += fromskew;
+	}
+}
+
+/*
+ * 16-bit packed samples => RGB
+ */
+DECLAREContigPutFunc(putRGBcontig16bittile)
+{
+	int samplesperpixel = img->samplesperpixel;
+	uint16 *wp = (uint16 *)pp;
+	(void) y;
+	fromskew *= samplesperpixel;
+	while (h-- > 0) {
+		for (x = w; x-- > 0;) {
+			*cp++ = PACK(img->Bitdepth16To8[wp[0]],
+			    img->Bitdepth16To8[wp[1]],
+			    img->Bitdepth16To8[wp[2]]);
+			wp += samplesperpixel;
+		}
+		cp += toskew;
+		wp += fromskew;
+	}
+}
+
+/*
+ * 16-bit packed samples => RGBA w/ associated alpha
+ * (known to have Map == NULL)
+ */
+DECLAREContigPutFunc(putRGBAAcontig16bittile)
+{
+	int samplesperpixel = img->samplesperpixel;
+	uint16 *wp = (uint16 *)pp;
+	(void) y;
+	fromskew *= samplesperpixel;
+	while (h-- > 0) {
+		for (x = w; x-- > 0;) {
+			*cp++ = PACK4(img->Bitdepth16To8[wp[0]],
+			    img->Bitdepth16To8[wp[1]],
+			    img->Bitdepth16To8[wp[2]],
+			    img->Bitdepth16To8[wp[3]]);
+			wp += samplesperpixel;
+		}
+		cp += toskew;
+		wp += fromskew;
+	}
+}
+
+/*
+ * 16-bit packed samples => RGBA w/ unassociated alpha
+ * (known to have Map == NULL)
+ */
+DECLAREContigPutFunc(putRGBUAcontig16bittile)
+{
+	int samplesperpixel = img->samplesperpixel;
+	uint16 *wp = (uint16 *)pp;
+	(void) y;
+	fromskew *= samplesperpixel;
+	while (h-- > 0) {
+		uint32 r,g,b,a;
+		uint8* m;
+		for (x = w; x-- > 0;) {
+			a = img->Bitdepth16To8[wp[3]];
+			m = img->UaToAa+(a<<8);
+			r = m[img->Bitdepth16To8[wp[0]]];
+			g = m[img->Bitdepth16To8[wp[1]]];
+			b = m[img->Bitdepth16To8[wp[2]]];
+			*cp++ = PACK4(r,g,b,a);
+			wp += samplesperpixel;
+		}
+		cp += toskew;
+		wp += fromskew;
+	}
+}
+
+/*
+ * 8-bit packed CMYK samples w/o Map => RGB
+ *
+ * NB: The conversion of CMYK->RGB is *very* crude.
+ */
+DECLAREContigPutFunc(putRGBcontig8bitCMYKtile)
+{
+    int samplesperpixel = img->samplesperpixel;
+    uint16 r, g, b, k;
+
+    (void) x; (void) y;
+    fromskew *= samplesperpixel;
+    while (h-- > 0) {
+	UNROLL8(w, NOP,
+	    k = 255 - pp[3];
+	    r = (k*(255-pp[0]))/255;
+	    g = (k*(255-pp[1]))/255;
+	    b = (k*(255-pp[2]))/255;
+	    *cp++ = PACK(r, g, b);
+	    pp += samplesperpixel);
+	cp += toskew;
+	pp += fromskew;
+    }
+}
+
+/*
+ * 8-bit packed CMYK samples w/Map => RGB
+ *
+ * NB: The conversion of CMYK->RGB is *very* crude.
+ */
+DECLAREContigPutFunc(putRGBcontig8bitCMYKMaptile)
+{
+    int samplesperpixel = img->samplesperpixel;
+    TIFFRGBValue* Map = img->Map;
+    uint16 r, g, b, k;
+
+    (void) y;
+    fromskew *= samplesperpixel;
+    while (h-- > 0) {
+	for (x = w; x-- > 0;) {
+	    k = 255 - pp[3];
+	    r = (k*(255-pp[0]))/255;
+	    g = (k*(255-pp[1]))/255;
+	    b = (k*(255-pp[2]))/255;
+	    *cp++ = PACK(Map[r], Map[g], Map[b]);
+	    pp += samplesperpixel;
+	}
+	pp += fromskew;
+	cp += toskew;
+    }
+}
+
+#define	DECLARESepPutFunc(name) \
+static void name(\
+    TIFFRGBAImage* img,\
+    uint32* cp,\
+    uint32 x, uint32 y, \
+    uint32 w, uint32 h,\
+    int32 fromskew, int32 toskew,\
+    unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a\
+)
+
+/*
+ * 8-bit unpacked samples => RGB
+ */
+DECLARESepPutFunc(putRGBseparate8bittile)
+{
+    (void) img; (void) x; (void) y; (void) a;
+    while (h-- > 0) {
+	UNROLL8(w, NOP, *cp++ = PACK(*r++, *g++, *b++));
+	SKEW(r, g, b, fromskew);
+	cp += toskew;
+    }
+}
+
+/*
+ * 8-bit unpacked samples => RGBA w/ associated alpha
+ */
+DECLARESepPutFunc(putRGBAAseparate8bittile)
+{
+	(void) img; (void) x; (void) y;
+	while (h-- > 0) {
+		UNROLL8(w, NOP, *cp++ = PACK4(*r++, *g++, *b++, *a++));
+		SKEW4(r, g, b, a, fromskew);
+		cp += toskew;
+	}
+}
+
+/*
+ * 8-bit unpacked samples => RGBA w/ unassociated alpha
+ */
+DECLARESepPutFunc(putRGBUAseparate8bittile)
+{
+	(void) img; (void) y;
+	while (h-- > 0) {
+		uint32 rv, gv, bv, av;
+		uint8* m;
+		for (x = w; x-- > 0;) {
+			av = *a++;
+			m = img->UaToAa+(av<<8);
+			rv = m[*r++];
+			gv = m[*g++];
+			bv = m[*b++];
+			*cp++ = PACK4(rv,gv,bv,av);
+		}
+		SKEW4(r, g, b, a, fromskew);
+		cp += toskew;
+	}
+}
+
+/*
+ * 16-bit unpacked samples => RGB
+ */
+DECLARESepPutFunc(putRGBseparate16bittile)
+{
+	uint16 *wr = (uint16*) r;
+	uint16 *wg = (uint16*) g;
+	uint16 *wb = (uint16*) b;
+	(void) img; (void) y; (void) a;
+	while (h-- > 0) {
+		for (x = 0; x < w; x++)
+			*cp++ = PACK(img->Bitdepth16To8[*wr++],
+			    img->Bitdepth16To8[*wg++],
+			    img->Bitdepth16To8[*wb++]);
+		SKEW(wr, wg, wb, fromskew);
+		cp += toskew;
+	}
+}
+
+/*
+ * 16-bit unpacked samples => RGBA w/ associated alpha
+ */
+DECLARESepPutFunc(putRGBAAseparate16bittile)
+{
+	uint16 *wr = (uint16*) r;
+	uint16 *wg = (uint16*) g;
+	uint16 *wb = (uint16*) b;
+	uint16 *wa = (uint16*) a;
+	(void) img; (void) y;
+	while (h-- > 0) {
+		for (x = 0; x < w; x++)
+			*cp++ = PACK4(img->Bitdepth16To8[*wr++],
+			    img->Bitdepth16To8[*wg++],
+			    img->Bitdepth16To8[*wb++],
+			    img->Bitdepth16To8[*wa++]);
+		SKEW4(wr, wg, wb, wa, fromskew);
+		cp += toskew;
+	}
+}
+
+/*
+ * 16-bit unpacked samples => RGBA w/ unassociated alpha
+ */
+DECLARESepPutFunc(putRGBUAseparate16bittile)
+{
+	uint16 *wr = (uint16*) r;
+	uint16 *wg = (uint16*) g;
+	uint16 *wb = (uint16*) b;
+	uint16 *wa = (uint16*) a;
+	(void) img; (void) y;
+	while (h-- > 0) {
+		uint32 r,g,b,a;
+		uint8* m;
+		for (x = w; x-- > 0;) {
+			a = img->Bitdepth16To8[*wa++];
+			m = img->UaToAa+(a<<8);
+			r = m[img->Bitdepth16To8[*wr++]];
+			g = m[img->Bitdepth16To8[*wg++]];
+			b = m[img->Bitdepth16To8[*wb++]];
+			*cp++ = PACK4(r,g,b,a);
+		}
+		SKEW4(wr, wg, wb, wa, fromskew);
+		cp += toskew;
+	}
+}
+
+/*
+ * 8-bit packed CIE L*a*b 1976 samples => RGB
+ */
+DECLAREContigPutFunc(putcontig8bitCIELab)
+{
+	float X, Y, Z;
+	uint32 r, g, b;
+	(void) y;
+	fromskew *= 3;
+	while (h-- > 0) {
+		for (x = w; x-- > 0;) {
+			TIFFCIELabToXYZ(img->cielab,
+					(unsigned char)pp[0],
+					(signed char)pp[1],
+					(signed char)pp[2],
+					&X, &Y, &Z);
+			TIFFXYZToRGB(img->cielab, X, Y, Z, &r, &g, &b);
+			*cp++ = PACK(r, g, b);
+			pp += 3;
+		}
+		cp += toskew;
+		pp += fromskew;
+	}
+}
+
+/*
+ * YCbCr -> RGB conversion and packing routines.
+ */
+
+#define	YCbCrtoRGB(dst, Y) {						\
+	uint32 r, g, b;							\
+	TIFFYCbCrtoRGB(img->ycbcr, (Y), Cb, Cr, &r, &g, &b);		\
+	dst = PACK(r, g, b);						\
+}
+
+/*
+ * 8-bit packed YCbCr samples => RGB 
+ * This function is generic for different sampling sizes, 
+ * and can handle blocks sizes that aren't multiples of the
+ * sampling size.  However, it is substantially less optimized
+ * than the specific sampling cases.  It is used as a fallback
+ * for difficult blocks.
+ */
+#ifdef notdef
+static void putcontig8bitYCbCrGenericTile( 
+    TIFFRGBAImage* img, 
+    uint32* cp, 
+    uint32 x, uint32 y, 
+    uint32 w, uint32 h, 
+    int32 fromskew, int32 toskew, 
+    unsigned char* pp,
+    int h_group, 
+    int v_group )
+
+{
+    uint32* cp1 = cp+w+toskew;
+    uint32* cp2 = cp1+w+toskew;
+    uint32* cp3 = cp2+w+toskew;
+    int32 incr = 3*w+4*toskew;
+    int32   Cb, Cr;
+    int     group_size = v_group * h_group + 2;
+
+    (void) y;
+    fromskew = (fromskew * group_size) / h_group;
+
+    for( yy = 0; yy < h; yy++ )
+    {
+        unsigned char *pp_line;
+        int     y_line_group = yy / v_group;
+        int     y_remainder = yy - y_line_group * v_group;
+
+        pp_line = pp + v_line_group * 
+
+        
+        for( xx = 0; xx < w; xx++ )
+        {
+            Cb = pp
+        }
+    }
+    for (; h >= 4; h -= 4) {
+	x = w>>2;
+	do {
+	    Cb = pp[16];
+	    Cr = pp[17];
+
+	    YCbCrtoRGB(cp [0], pp[ 0]);
+	    YCbCrtoRGB(cp [1], pp[ 1]);
+	    YCbCrtoRGB(cp [2], pp[ 2]);
+	    YCbCrtoRGB(cp [3], pp[ 3]);
+	    YCbCrtoRGB(cp1[0], pp[ 4]);
+	    YCbCrtoRGB(cp1[1], pp[ 5]);
+	    YCbCrtoRGB(cp1[2], pp[ 6]);
+	    YCbCrtoRGB(cp1[3], pp[ 7]);
+	    YCbCrtoRGB(cp2[0], pp[ 8]);
+	    YCbCrtoRGB(cp2[1], pp[ 9]);
+	    YCbCrtoRGB(cp2[2], pp[10]);
+	    YCbCrtoRGB(cp2[3], pp[11]);
+	    YCbCrtoRGB(cp3[0], pp[12]);
+	    YCbCrtoRGB(cp3[1], pp[13]);
+	    YCbCrtoRGB(cp3[2], pp[14]);
+	    YCbCrtoRGB(cp3[3], pp[15]);
+
+	    cp += 4, cp1 += 4, cp2 += 4, cp3 += 4;
+	    pp += 18;
+	} while (--x);
+	cp += incr, cp1 += incr, cp2 += incr, cp3 += incr;
+	pp += fromskew;
+    }
+}
+#endif
+
+/*
+ * 8-bit packed YCbCr samples w/ 4,4 subsampling => RGB
+ */
+DECLAREContigPutFunc(putcontig8bitYCbCr44tile)
+{
+    uint32* cp1 = cp+w+toskew;
+    uint32* cp2 = cp1+w+toskew;
+    uint32* cp3 = cp2+w+toskew;
+    int32 incr = 3*w+4*toskew;
+
+    (void) y;
+    /* adjust fromskew */
+    fromskew = (fromskew * 18) / 4;
+    if ((h & 3) == 0 && (w & 3) == 0) {				        
+        for (; h >= 4; h -= 4) {
+            x = w>>2;
+            do {
+                int32 Cb = pp[16];
+                int32 Cr = pp[17];
+
+                YCbCrtoRGB(cp [0], pp[ 0]);
+                YCbCrtoRGB(cp [1], pp[ 1]);
+                YCbCrtoRGB(cp [2], pp[ 2]);
+                YCbCrtoRGB(cp [3], pp[ 3]);
+                YCbCrtoRGB(cp1[0], pp[ 4]);
+                YCbCrtoRGB(cp1[1], pp[ 5]);
+                YCbCrtoRGB(cp1[2], pp[ 6]);
+                YCbCrtoRGB(cp1[3], pp[ 7]);
+                YCbCrtoRGB(cp2[0], pp[ 8]);
+                YCbCrtoRGB(cp2[1], pp[ 9]);
+                YCbCrtoRGB(cp2[2], pp[10]);
+                YCbCrtoRGB(cp2[3], pp[11]);
+                YCbCrtoRGB(cp3[0], pp[12]);
+                YCbCrtoRGB(cp3[1], pp[13]);
+                YCbCrtoRGB(cp3[2], pp[14]);
+                YCbCrtoRGB(cp3[3], pp[15]);
+
+                cp += 4, cp1 += 4, cp2 += 4, cp3 += 4;
+                pp += 18;
+            } while (--x);
+            cp += incr, cp1 += incr, cp2 += incr, cp3 += incr;
+            pp += fromskew;
+        }
+    } else {
+        while (h > 0) {
+            for (x = w; x > 0;) {
+                int32 Cb = pp[16];
+                int32 Cr = pp[17];
+                switch (x) {
+                default:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp3[3], pp[15]); /* FALLTHROUGH */
+                    case 3:  YCbCrtoRGB(cp2[3], pp[11]); /* FALLTHROUGH */
+                    case 2:  YCbCrtoRGB(cp1[3], pp[ 7]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [3], pp[ 3]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                case 3:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp3[2], pp[14]); /* FALLTHROUGH */
+                    case 3:  YCbCrtoRGB(cp2[2], pp[10]); /* FALLTHROUGH */
+                    case 2:  YCbCrtoRGB(cp1[2], pp[ 6]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [2], pp[ 2]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                case 2:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp3[1], pp[13]); /* FALLTHROUGH */
+                    case 3:  YCbCrtoRGB(cp2[1], pp[ 9]); /* FALLTHROUGH */
+                    case 2:  YCbCrtoRGB(cp1[1], pp[ 5]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [1], pp[ 1]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                case 1:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp3[0], pp[12]); /* FALLTHROUGH */
+                    case 3:  YCbCrtoRGB(cp2[0], pp[ 8]); /* FALLTHROUGH */
+                    case 2:  YCbCrtoRGB(cp1[0], pp[ 4]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [0], pp[ 0]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                }
+                if (x < 4) {
+                    cp += x; cp1 += x; cp2 += x; cp3 += x;
+                    x = 0;
+                }
+                else {
+                    cp += 4; cp1 += 4; cp2 += 4; cp3 += 4;
+                    x -= 4;
+                }
+                pp += 18;
+            }
+            if (h <= 4)
+                break;
+            h -= 4;
+            cp += incr, cp1 += incr, cp2 += incr, cp3 += incr;
+            pp += fromskew;
+        }
+    }
+}
+
+/*
+ * 8-bit packed YCbCr samples w/ 4,2 subsampling => RGB
+ */
+DECLAREContigPutFunc(putcontig8bitYCbCr42tile)
+{
+    uint32* cp1 = cp+w+toskew;
+    int32 incr = 2*toskew+w;
+
+    (void) y;
+    fromskew = (fromskew * 10) / 4;
+    if ((h & 3) == 0 && (w & 1) == 0) {
+        for (; h >= 2; h -= 2) {
+            x = w>>2;
+            do {
+                int32 Cb = pp[8];
+                int32 Cr = pp[9];
+                
+                YCbCrtoRGB(cp [0], pp[0]);
+                YCbCrtoRGB(cp [1], pp[1]);
+                YCbCrtoRGB(cp [2], pp[2]);
+                YCbCrtoRGB(cp [3], pp[3]);
+                YCbCrtoRGB(cp1[0], pp[4]);
+                YCbCrtoRGB(cp1[1], pp[5]);
+                YCbCrtoRGB(cp1[2], pp[6]);
+                YCbCrtoRGB(cp1[3], pp[7]);
+                
+                cp += 4, cp1 += 4;
+                pp += 10;
+            } while (--x);
+            cp += incr, cp1 += incr;
+            pp += fromskew;
+        }
+    } else {
+        while (h > 0) {
+            for (x = w; x > 0;) {
+                int32 Cb = pp[8];
+                int32 Cr = pp[9];
+                switch (x) {
+                default:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp1[3], pp[ 7]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [3], pp[ 3]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                case 3:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp1[2], pp[ 6]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [2], pp[ 2]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                case 2:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp1[1], pp[ 5]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [1], pp[ 1]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                case 1:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp1[0], pp[ 4]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [0], pp[ 0]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                }
+                if (x < 4) {
+                    cp += x; cp1 += x;
+                    x = 0;
+                }
+                else {
+                    cp += 4; cp1 += 4;
+                    x -= 4;
+                }
+                pp += 10;
+            }
+            if (h <= 2)
+                break;
+            h -= 2;
+            cp += incr, cp1 += incr;
+            pp += fromskew;
+        }
+    }
+}
+
+/*
+ * 8-bit packed YCbCr samples w/ 4,1 subsampling => RGB
+ */
+DECLAREContigPutFunc(putcontig8bitYCbCr41tile)
+{
+    (void) y;
+    /* XXX adjust fromskew */
+    do {
+	x = w>>2;
+	do {
+	    int32 Cb = pp[4];
+	    int32 Cr = pp[5];
+
+	    YCbCrtoRGB(cp [0], pp[0]);
+	    YCbCrtoRGB(cp [1], pp[1]);
+	    YCbCrtoRGB(cp [2], pp[2]);
+	    YCbCrtoRGB(cp [3], pp[3]);
+
+	    cp += 4;
+	    pp += 6;
+	} while (--x);
+
+        if( (w&3) != 0 )
+        {
+	    int32 Cb = pp[4];
+	    int32 Cr = pp[5];
+
+            switch( (w&3) ) {
+              case 3: YCbCrtoRGB(cp [2], pp[2]);
+              case 2: YCbCrtoRGB(cp [1], pp[1]);
+              case 1: YCbCrtoRGB(cp [0], pp[0]);
+              case 0: break;
+            }
+
+            cp += (w&3);
+            pp += 6;
+        }
+
+	cp += toskew;
+	pp += fromskew;
+    } while (--h);
+
+}
+
+/*
+ * 8-bit packed YCbCr samples w/ 2,2 subsampling => RGB
+ */
+DECLAREContigPutFunc(putcontig8bitYCbCr22tile)
+{
+	uint32* cp2;
+	fromskew = (fromskew / 2) * 6;
+	cp2 = cp+w+toskew;
+	while (h>=2) {
+		x = w;
+		while (x>=2) {
+			uint32 Cb = pp[4];
+			uint32 Cr = pp[5];
+			YCbCrtoRGB(cp[0], pp[0]);
+			YCbCrtoRGB(cp[1], pp[1]);
+			YCbCrtoRGB(cp2[0], pp[2]);
+			YCbCrtoRGB(cp2[1], pp[3]);
+			cp += 2;
+			cp2 += 2;
+			pp += 6;
+			x -= 2;
+		}
+		if (x==1) {
+			uint32 Cb = pp[4];
+			uint32 Cr = pp[5];
+			YCbCrtoRGB(cp[0], pp[0]);
+			YCbCrtoRGB(cp2[0], pp[2]);
+			cp ++ ;
+			cp2 ++ ;
+			pp += 6;
+		}
+		cp += toskew*2+w;
+		cp2 += toskew*2+w;
+		pp += fromskew;
+		h-=2;
+	}
+	if (h==1) {
+		x = w;
+		while (x>=2) {
+			uint32 Cb = pp[4];
+			uint32 Cr = pp[5];
+			YCbCrtoRGB(cp[0], pp[0]);
+			YCbCrtoRGB(cp[1], pp[1]);
+			cp += 2;
+			cp2 += 2;
+			pp += 6;
+			x -= 2;
+		}
+		if (x==1) {
+			uint32 Cb = pp[4];
+			uint32 Cr = pp[5];
+			YCbCrtoRGB(cp[0], pp[0]);
+		}
+	}
+}
+
+/*
+ * 8-bit packed YCbCr samples w/ 2,1 subsampling => RGB
+ */
+DECLAREContigPutFunc(putcontig8bitYCbCr21tile)
+{
+	(void) y;
+	fromskew = (fromskew * 4) / 2;
+	do {
+		x = w>>1;
+		do {
+			int32 Cb = pp[2];
+			int32 Cr = pp[3];
+
+			YCbCrtoRGB(cp[0], pp[0]);
+			YCbCrtoRGB(cp[1], pp[1]);
+
+			cp += 2;
+			pp += 4;
+		} while (--x);
+
+		if( (w&1) != 0 )
+		{
+			int32 Cb = pp[2];
+			int32 Cr = pp[3];
+
+			YCbCrtoRGB(cp[0], pp[0]);
+
+			cp += 1;
+			pp += 4;
+		}
+
+		cp += toskew;
+		pp += fromskew;
+	} while (--h);
+}
+
+/*
+ * 8-bit packed YCbCr samples w/ 1,2 subsampling => RGB
+ */
+DECLAREContigPutFunc(putcontig8bitYCbCr12tile)
+{
+	uint32* cp2;
+	fromskew = (fromskew / 2) * 4;
+	cp2 = cp+w+toskew;
+	while (h>=2) {
+		x = w;
+		do {
+			uint32 Cb = pp[2];
+			uint32 Cr = pp[3];
+			YCbCrtoRGB(cp[0], pp[0]);
+			YCbCrtoRGB(cp2[0], pp[1]);
+			cp ++;
+			cp2 ++;
+			pp += 4;
+		} while (--x);
+		cp += toskew*2+w;
+		cp2 += toskew*2+w;
+		pp += fromskew;
+		h-=2;
+	}
+	if (h==1) {
+		x = w;
+		do {
+			uint32 Cb = pp[2];
+			uint32 Cr = pp[3];
+			YCbCrtoRGB(cp[0], pp[0]);
+			cp ++;
+			pp += 4;
+		} while (--x);
+	}
+}
+
+/*
+ * 8-bit packed YCbCr samples w/ no subsampling => RGB
+ */
+DECLAREContigPutFunc(putcontig8bitYCbCr11tile)
+{
+	(void) y;
+	fromskew *= 3;
+	do {
+		x = w; /* was x = w>>1; patched 2000/09/25 warmerda@home.com */
+		do {
+			int32 Cb = pp[1];
+			int32 Cr = pp[2];
+
+			YCbCrtoRGB(*cp++, pp[0]);
+
+			pp += 3;
+		} while (--x);
+		cp += toskew;
+		pp += fromskew;
+	} while (--h);
+}
+
+/*
+ * 8-bit packed YCbCr samples w/ no subsampling => RGB
+ */
+DECLARESepPutFunc(putseparate8bitYCbCr11tile)
+{
+	/* TODO: naming of input vars is still off, change obfuscating declaration inside define, or resolve obfuscation */
+	while (h-- > 0) {
+		x = w;
+		do {
+			uint32 dr, dg, db;
+			TIFFYCbCrtoRGB(img->ycbcr,*r++,*g++,*b++,&dr,&dg,&db);
+			*cp++ = PACK(dr,dg,db);
+		} while (--x);
+		SKEW(r, g, b, fromskew);
+		cp += toskew;
+	}
+}
+#undef YCbCrtoRGB
+
+static int
+initYCbCrConversion(TIFFRGBAImage* img)
+{
+	static char module[] = "initYCbCrConversion";
+
+	float *luma, *refBlackWhite;
+
+	if (img->ycbcr == NULL) {
+		img->ycbcr = (TIFFYCbCrToRGB*) _TIFFmalloc(
+		    TIFFroundup(sizeof (TIFFYCbCrToRGB), sizeof (long))
+		    + 4*256*sizeof (TIFFRGBValue)
+		    + 2*256*sizeof (int)
+		    + 3*256*sizeof (int32)
+		    );
+		if (img->ycbcr == NULL) {
+			TIFFErrorExt(img->tif->tif_clientdata, module,
+			    "No space for YCbCr->RGB conversion state");
+			return (0);
+		}
+	}
+
+	TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRCOEFFICIENTS, &luma);
+	TIFFGetFieldDefaulted(img->tif, TIFFTAG_REFERENCEBLACKWHITE,
+	    &refBlackWhite);
+	if (TIFFYCbCrToRGBInit(img->ycbcr, luma, refBlackWhite) < 0)
+		return(0);
+	return (1);
+}
+
+static tileContigRoutine
+initCIELabConversion(TIFFRGBAImage* img)
+{
+	static char module[] = "initCIELabConversion";
+
+	float   *whitePoint;
+	float   refWhite[3];
+
+	if (!img->cielab) {
+		img->cielab = (TIFFCIELabToRGB *)
+			_TIFFmalloc(sizeof(TIFFCIELabToRGB));
+		if (!img->cielab) {
+			TIFFErrorExt(img->tif->tif_clientdata, module,
+			    "No space for CIE L*a*b*->RGB conversion state.");
+			return NULL;
+		}
+	}
+
+	TIFFGetFieldDefaulted(img->tif, TIFFTAG_WHITEPOINT, &whitePoint);
+	refWhite[1] = 100.0F;
+	refWhite[0] = whitePoint[0] / whitePoint[1] * refWhite[1];
+	refWhite[2] = (1.0F - whitePoint[0] - whitePoint[1])
+		      / whitePoint[1] * refWhite[1];
+	if (TIFFCIELabToRGBInit(img->cielab, &display_sRGB, refWhite) < 0) {
+		TIFFErrorExt(img->tif->tif_clientdata, module,
+		    "Failed to initialize CIE L*a*b*->RGB conversion state.");
+		_TIFFfree(img->cielab);
+		return NULL;
+	}
+
+	return putcontig8bitCIELab;
+}
+
+/*
+ * Greyscale images with less than 8 bits/sample are handled
+ * with a table to avoid lots of shifts and masks.  The table
+ * is setup so that put*bwtile (below) can retrieve 8/bitspersample
+ * pixel values simply by indexing into the table with one
+ * number.
+ */
+static int
+makebwmap(TIFFRGBAImage* img)
+{
+    TIFFRGBValue* Map = img->Map;
+    int bitspersample = img->bitspersample;
+    int nsamples = 8 / bitspersample;
+    int i;
+    uint32* p;
+
+    if( nsamples == 0 )
+        nsamples = 1;
+
+    img->BWmap = (uint32**) _TIFFmalloc(
+	256*sizeof (uint32 *)+(256*nsamples*sizeof(uint32)));
+    if (img->BWmap == NULL) {
+		TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif), "No space for B&W mapping table");
+		return (0);
+    }
+    p = (uint32*)(img->BWmap + 256);
+    for (i = 0; i < 256; i++) {
+	TIFFRGBValue c;
+	img->BWmap[i] = p;
+	switch (bitspersample) {
+#define	GREY(x)	c = Map[x]; *p++ = PACK(c,c,c);
+	case 1:
+	    GREY(i>>7);
+	    GREY((i>>6)&1);
+	    GREY((i>>5)&1);
+	    GREY((i>>4)&1);
+	    GREY((i>>3)&1);
+	    GREY((i>>2)&1);
+	    GREY((i>>1)&1);
+	    GREY(i&1);
+	    break;
+	case 2:
+	    GREY(i>>6);
+	    GREY((i>>4)&3);
+	    GREY((i>>2)&3);
+	    GREY(i&3);
+	    break;
+	case 4:
+	    GREY(i>>4);
+	    GREY(i&0xf);
+	    break;
+	case 8:
+        case 16:
+	    GREY(i);
+	    break;
+	}
+#undef	GREY
+    }
+    return (1);
+}
+
+/*
+ * Construct a mapping table to convert from the range
+ * of the data samples to [0,255] --for display.  This
+ * process also handles inverting B&W images when needed.
+ */ 
+static int
+setupMap(TIFFRGBAImage* img)
+{
+    int32 x, range;
+
+    range = (int32)((1L<<img->bitspersample)-1);
+    
+    /* treat 16 bit the same as eight bit */
+    if( img->bitspersample == 16 )
+        range = (int32) 255;
+
+    img->Map = (TIFFRGBValue*) _TIFFmalloc((range+1) * sizeof (TIFFRGBValue));
+    if (img->Map == NULL) {
+		TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif),
+			"No space for photometric conversion table");
+		return (0);
+    }
+    if (img->photometric == PHOTOMETRIC_MINISWHITE) {
+	for (x = 0; x <= range; x++)
+	    img->Map[x] = (TIFFRGBValue) (((range - x) * 255) / range);
+    } else {
+	for (x = 0; x <= range; x++)
+	    img->Map[x] = (TIFFRGBValue) ((x * 255) / range);
+    }
+    if (img->bitspersample <= 16 &&
+	(img->photometric == PHOTOMETRIC_MINISBLACK ||
+	 img->photometric == PHOTOMETRIC_MINISWHITE)) {
+	/*
+	 * Use photometric mapping table to construct
+	 * unpacking tables for samples <= 8 bits.
+	 */
+	if (!makebwmap(img))
+	    return (0);
+	/* no longer need Map, free it */
+	_TIFFfree(img->Map), img->Map = NULL;
+    }
+    return (1);
+}
+
+static int
+checkcmap(TIFFRGBAImage* img)
+{
+    uint16* r = img->redcmap;
+    uint16* g = img->greencmap;
+    uint16* b = img->bluecmap;
+    long n = 1L<<img->bitspersample;
+
+    while (n-- > 0)
+	if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256)
+	    return (16);
+    return (8);
+}
+
+static void
+cvtcmap(TIFFRGBAImage* img)
+{
+    uint16* r = img->redcmap;
+    uint16* g = img->greencmap;
+    uint16* b = img->bluecmap;
+    long i;
+
+    for (i = (1L<<img->bitspersample)-1; i >= 0; i--) {
+#define	CVT(x)		((uint16)((x)>>8))
+	r[i] = CVT(r[i]);
+	g[i] = CVT(g[i]);
+	b[i] = CVT(b[i]);
+#undef	CVT
+    }
+}
+
+/*
+ * Palette images with <= 8 bits/sample are handled
+ * with a table to avoid lots of shifts and masks.  The table
+ * is setup so that put*cmaptile (below) can retrieve 8/bitspersample
+ * pixel values simply by indexing into the table with one
+ * number.
+ */
+static int
+makecmap(TIFFRGBAImage* img)
+{
+    int bitspersample = img->bitspersample;
+    int nsamples = 8 / bitspersample;
+    uint16* r = img->redcmap;
+    uint16* g = img->greencmap;
+    uint16* b = img->bluecmap;
+    uint32 *p;
+    int i;
+
+    img->PALmap = (uint32**) _TIFFmalloc(
+	256*sizeof (uint32 *)+(256*nsamples*sizeof(uint32)));
+    if (img->PALmap == NULL) {
+		TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif), "No space for Palette mapping table");
+		return (0);
+	}
+    p = (uint32*)(img->PALmap + 256);
+    for (i = 0; i < 256; i++) {
+	TIFFRGBValue c;
+	img->PALmap[i] = p;
+#define	CMAP(x)	c = (TIFFRGBValue) x; *p++ = PACK(r[c]&0xff, g[c]&0xff, b[c]&0xff);
+	switch (bitspersample) {
+	case 1:
+	    CMAP(i>>7);
+	    CMAP((i>>6)&1);
+	    CMAP((i>>5)&1);
+	    CMAP((i>>4)&1);
+	    CMAP((i>>3)&1);
+	    CMAP((i>>2)&1);
+	    CMAP((i>>1)&1);
+	    CMAP(i&1);
+	    break;
+	case 2:
+	    CMAP(i>>6);
+	    CMAP((i>>4)&3);
+	    CMAP((i>>2)&3);
+	    CMAP(i&3);
+	    break;
+	case 4:
+	    CMAP(i>>4);
+	    CMAP(i&0xf);
+	    break;
+	case 8:
+	    CMAP(i);
+	    break;
+	}
+#undef CMAP
+    }
+    return (1);
+}
+
+/* 
+ * Construct any mapping table used
+ * by the associated put routine.
+ */
+static int
+buildMap(TIFFRGBAImage* img)
+{
+    switch (img->photometric) {
+    case PHOTOMETRIC_RGB:
+    case PHOTOMETRIC_YCBCR:
+    case PHOTOMETRIC_SEPARATED:
+	if (img->bitspersample == 8)
+	    break;
+	/* fall thru... */
+    case PHOTOMETRIC_MINISBLACK:
+    case PHOTOMETRIC_MINISWHITE:
+	if (!setupMap(img))
+	    return (0);
+	break;
+    case PHOTOMETRIC_PALETTE:
+	/*
+	 * Convert 16-bit colormap to 8-bit (unless it looks
+	 * like an old-style 8-bit colormap).
+	 */
+	if (checkcmap(img) == 16)
+	    cvtcmap(img);
+	else
+	    TIFFWarningExt(img->tif->tif_clientdata, TIFFFileName(img->tif), "Assuming 8-bit colormap");
+	/*
+	 * Use mapping table and colormap to construct
+	 * unpacking tables for samples < 8 bits.
+	 */
+	if (img->bitspersample <= 8 && !makecmap(img))
+	    return (0);
+	break;
+    }
+    return (1);
+}
+
+/*
+ * Select the appropriate conversion routine for packed data.
+ */
+static int
+PickContigCase(TIFFRGBAImage* img)
+{
+	img->get = TIFFIsTiled(img->tif) ? gtTileContig : gtStripContig;
+	img->put.contig = NULL;
+	switch (img->photometric) {
+		case PHOTOMETRIC_RGB:
+			switch (img->bitspersample) {
+				case 8:
+					if (img->alpha == EXTRASAMPLE_ASSOCALPHA)
+						img->put.contig = putRGBAAcontig8bittile;
+					else if (img->alpha == EXTRASAMPLE_UNASSALPHA)
+					{
+						if (BuildMapUaToAa(img))
+							img->put.contig = putRGBUAcontig8bittile;
+					}
+					else
+						img->put.contig = putRGBcontig8bittile;
+					break;
+				case 16:
+					if (img->alpha == EXTRASAMPLE_ASSOCALPHA)
+					{
+						if (BuildMapBitdepth16To8(img))
+							img->put.contig = putRGBAAcontig16bittile;
+					}
+					else if (img->alpha == EXTRASAMPLE_UNASSALPHA)
+					{
+						if (BuildMapBitdepth16To8(img) &&
+						    BuildMapUaToAa(img))
+							img->put.contig = putRGBUAcontig16bittile;
+					}
+					else
+					{
+						if (BuildMapBitdepth16To8(img))
+							img->put.contig = putRGBcontig16bittile;
+					}
+					break;
+			}
+			break;
+		case PHOTOMETRIC_SEPARATED:
+			if (buildMap(img)) {
+				if (img->bitspersample == 8) {
+					if (!img->Map)
+						img->put.contig = putRGBcontig8bitCMYKtile;
+					else
+						img->put.contig = putRGBcontig8bitCMYKMaptile;
+				}
+			}
+			break;
+		case PHOTOMETRIC_PALETTE:
+			if (buildMap(img)) {
+				switch (img->bitspersample) {
+					case 8:
+						img->put.contig = put8bitcmaptile;
+						break;
+					case 4:
+						img->put.contig = put4bitcmaptile;
+						break;
+					case 2:
+						img->put.contig = put2bitcmaptile;
+						break;
+					case 1:
+						img->put.contig = put1bitcmaptile;
+						break;
+				}
+			}
+			break;
+		case PHOTOMETRIC_MINISWHITE:
+		case PHOTOMETRIC_MINISBLACK:
+			if (buildMap(img)) {
+				switch (img->bitspersample) {
+					case 16:
+						img->put.contig = put16bitbwtile;
+						break;
+					case 8:
+						img->put.contig = putgreytile;
+						break;
+					case 4:
+						img->put.contig = put4bitbwtile;
+						break;
+					case 2:
+						img->put.contig = put2bitbwtile;
+						break;
+					case 1:
+						img->put.contig = put1bitbwtile;
+						break;
+				}
+			}
+			break;
+		case PHOTOMETRIC_YCBCR:
+			if (img->bitspersample == 8)
+			{
+				if (initYCbCrConversion(img)!=0)
+				{
+					uint16 hs, vs;
+					/*
+					 * The 6.0 spec says that subsampling must be
+					 * one of 1, 2, or 4, and that vertical subsampling
+					 * must always be <= horizontal subsampling; so
+					 * there are only a few possibilities and we just
+					 * enumerate the cases.
+					 * Joris: added support for the [1,2] case, nonetheless, to accomodate
+					 * some OJPEG files
+					 */
+					TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRSUBSAMPLING, &hs, &vs);
+					switch ((hs<<4)|vs) {
+						case 0x44:
+							img->put.contig = putcontig8bitYCbCr44tile;
+							break;
+						case 0x42:
+							img->put.contig = putcontig8bitYCbCr42tile;
+							break;
+						case 0x41:
+							img->put.contig = putcontig8bitYCbCr41tile;
+							break;
+						case 0x22:
+							img->put.contig = putcontig8bitYCbCr22tile;
+							break;
+						case 0x21:
+							img->put.contig = putcontig8bitYCbCr21tile;
+							break;
+						case 0x12:
+							img->put.contig = putcontig8bitYCbCr12tile;
+							break;
+						case 0x11:
+							img->put.contig = putcontig8bitYCbCr11tile;
+							break;
+					}
+				}
+			}
+			break;
+		case PHOTOMETRIC_CIELAB:
+			if (buildMap(img)) {
+				if (img->bitspersample == 8)
+					img->put.contig = initCIELabConversion(img);
+				break;
+			}
+	}
+	return ((img->get!=NULL) && (img->put.contig!=NULL));
+}
+
+/*
+ * Select the appropriate conversion routine for unpacked data.
+ *
+ * NB: we assume that unpacked single channel data is directed
+ *	 to the "packed routines.
+ */
+static int
+PickSeparateCase(TIFFRGBAImage* img)
+{
+	img->get = TIFFIsTiled(img->tif) ? gtTileSeparate : gtStripSeparate;
+	img->put.separate = NULL;
+	switch (img->photometric) {
+		case PHOTOMETRIC_RGB:
+			switch (img->bitspersample) {
+				case 8:
+					if (img->alpha == EXTRASAMPLE_ASSOCALPHA)
+						img->put.separate = putRGBAAseparate8bittile;
+					else if (img->alpha == EXTRASAMPLE_UNASSALPHA)
+					{
+						if (BuildMapUaToAa(img))
+							img->put.separate = putRGBUAseparate8bittile;
+					}
+					else
+						img->put.separate = putRGBseparate8bittile;
+					break;
+				case 16:
+					if (img->alpha == EXTRASAMPLE_ASSOCALPHA)
+					{
+						if (BuildMapBitdepth16To8(img))
+							img->put.separate = putRGBAAseparate16bittile;
+					}
+					else if (img->alpha == EXTRASAMPLE_UNASSALPHA)
+					{
+						if (BuildMapBitdepth16To8(img) &&
+						    BuildMapUaToAa(img))
+							img->put.separate = putRGBUAseparate16bittile;
+					}
+					else
+					{
+						if (BuildMapBitdepth16To8(img))
+							img->put.separate = putRGBseparate16bittile;
+					}
+					break;
+			}
+			break;
+		case PHOTOMETRIC_YCBCR:
+			if ((img->bitspersample==8) && (img->samplesperpixel==3))
+			{
+				if (initYCbCrConversion(img)!=0)
+				{
+					uint16 hs, vs;
+					TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRSUBSAMPLING, &hs, &vs);
+					switch ((hs<<4)|vs) {
+						case 0x11:
+							img->put.separate = putseparate8bitYCbCr11tile;
+							break;
+						/* TODO: add other cases here */
+					}
+				}
+			}
+			break;
+	}
+	return ((img->get!=NULL) && (img->put.separate!=NULL));
+}
+
+static int
+BuildMapUaToAa(TIFFRGBAImage* img)
+{
+	static const char module[]="BuildMapUaToAa";
+	uint8* m;
+	uint16 na,nv;
+	assert(img->UaToAa==NULL);
+	img->UaToAa=_TIFFmalloc(65536);
+	if (img->UaToAa==NULL)
+	{
+		TIFFErrorExt(img->tif,module,"Out of memory");
+		return(0);
+	}
+	m=img->UaToAa;
+	for (na=0; na<256; na++)
+	{
+		for (nv=0; nv<256; nv++)
+			*m++=(nv*na+127)/255;
+	}
+	return(1);
+}
+
+static int
+BuildMapBitdepth16To8(TIFFRGBAImage* img)
+{
+	static const char module[]="BuildMapBitdepth16To8";
+	uint8* m;
+	uint32 n;
+	assert(img->Bitdepth16To8==NULL);
+	img->Bitdepth16To8=_TIFFmalloc(65536);
+	if (img->Bitdepth16To8==NULL)
+	{
+		TIFFErrorExt(img->tif,module,"Out of memory");
+		return(0);
+	}
+	m=img->Bitdepth16To8;
+	for (n=0; n<65536; n++)
+		*m++=(n+128)/257;
+	return(1);
+}
+
+
+/*
+ * Read a whole strip off data from the file, and convert to RGBA form.
+ * If this is the last strip, then it will only contain the portion of
+ * the strip that is actually within the image space.  The result is
+ * organized in bottom to top form.
+ */
+
+
+int
+TIFFReadRGBAStrip(TIFF* tif, uint32 row, uint32 * raster )
+
+{
+    char 	emsg[1024] = "";
+    TIFFRGBAImage img;
+    int 	ok;
+    uint32	rowsperstrip, rows_to_read;
+
+    if( TIFFIsTiled( tif ) )
+    {
+		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif),
+                  "Can't use TIFFReadRGBAStrip() with tiled file.");
+	return (0);
+    }
+    
+    TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
+    if( (row % rowsperstrip) != 0 )
+    {
+		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif),
+				"Row passed to TIFFReadRGBAStrip() must be first in a strip.");
+		return (0);
+    }
+
+    if (TIFFRGBAImageOK(tif, emsg) && TIFFRGBAImageBegin(&img, tif, 0, emsg)) {
+
+        img.row_offset = row;
+        img.col_offset = 0;
+
+        if( row + rowsperstrip > img.height )
+            rows_to_read = img.height - row;
+        else
+            rows_to_read = rowsperstrip;
+        
+	ok = TIFFRGBAImageGet(&img, raster, img.width, rows_to_read );
+        
+	TIFFRGBAImageEnd(&img);
+    } else {
+		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), emsg);
+		ok = 0;
+    }
+    
+    return (ok);
+}
+
+/*
+ * Read a whole tile off data from the file, and convert to RGBA form.
+ * The returned RGBA data is organized from bottom to top of tile,
+ * and may include zeroed areas if the tile extends off the image.
+ */
+
+int
+TIFFReadRGBATile(TIFF* tif, uint32 col, uint32 row, uint32 * raster)
+
+{
+    char 	emsg[1024] = "";
+    TIFFRGBAImage img;
+    int 	ok;
+    uint32	tile_xsize, tile_ysize;
+    uint32	read_xsize, read_ysize;
+    uint32	i_row;
+
+    /*
+     * Verify that our request is legal - on a tile file, and on a
+     * tile boundary.
+     */
+    
+    if( !TIFFIsTiled( tif ) )
+    {
+		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif),
+				  "Can't use TIFFReadRGBATile() with stripped file.");
+		return (0);
+    }
+    
+    TIFFGetFieldDefaulted(tif, TIFFTAG_TILEWIDTH, &tile_xsize);
+    TIFFGetFieldDefaulted(tif, TIFFTAG_TILELENGTH, &tile_ysize);
+    if( (col % tile_xsize) != 0 || (row % tile_ysize) != 0 )
+    {
+		TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif),
+                  "Row/col passed to TIFFReadRGBATile() must be top"
+                  "left corner of a tile.");
+	return (0);
+    }
+
+    /*
+     * Setup the RGBA reader.
+     */
+    
+    if (!TIFFRGBAImageOK(tif, emsg) 
+	|| !TIFFRGBAImageBegin(&img, tif, 0, emsg)) {
+	    TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), emsg);
+	    return( 0 );
+    }
+
+    /*
+     * The TIFFRGBAImageGet() function doesn't allow us to get off the
+     * edge of the image, even to fill an otherwise valid tile.  So we
+     * figure out how much we can read, and fix up the tile buffer to
+     * a full tile configuration afterwards.
+     */
+
+    if( row + tile_ysize > img.height )
+        read_ysize = img.height - row;
+    else
+        read_ysize = tile_ysize;
+    
+    if( col + tile_xsize > img.width )
+        read_xsize = img.width - col;
+    else
+        read_xsize = tile_xsize;
+
+    /*
+     * Read the chunk of imagery.
+     */
+    
+    img.row_offset = row;
+    img.col_offset = col;
+
+    ok = TIFFRGBAImageGet(&img, raster, read_xsize, read_ysize );
+        
+    TIFFRGBAImageEnd(&img);
+
+    /*
+     * If our read was incomplete we will need to fix up the tile by
+     * shifting the data around as if a full tile of data is being returned.
+     *
+     * This is all the more complicated because the image is organized in
+     * bottom to top format. 
+     */
+
+    if( read_xsize == tile_xsize && read_ysize == tile_ysize )
+        return( ok );
+
+    for( i_row = 0; i_row < read_ysize; i_row++ ) {
+        memmove( raster + (tile_ysize - i_row - 1) * tile_xsize,
+                 raster + (read_ysize - i_row - 1) * read_xsize,
+                 read_xsize * sizeof(uint32) );
+        _TIFFmemset( raster + (tile_ysize - i_row - 1) * tile_xsize+read_xsize,
+                     0, sizeof(uint32) * (tile_xsize - read_xsize) );
+    }
+
+    for( i_row = read_ysize; i_row < tile_ysize; i_row++ ) {
+        _TIFFmemset( raster + (tile_ysize - i_row - 1) * tile_xsize,
+                     0, sizeof(uint32) * tile_xsize );
+    }
+
+    return (ok);
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_jpeg.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_jpeg.c
new file mode 100644
index 0000000000..0555c7a52e
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_jpeg.c
@@ -0,0 +1,1970 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1994-1997 Sam Leffler
+ * Copyright (c) 1994-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#define VC_EXTRALEAN
+
+#include "tiffiop.h"
+#ifdef JPEG_SUPPORT
+
+/*
+ * TIFF Library
+ *
+ * JPEG Compression support per TIFF Technical Note #2
+ * (*not* per the original TIFF 6.0 spec).
+ *
+ * This file is simply an interface to the libjpeg library written by
+ * the Independent JPEG Group.  You need release 5 or later of the IJG
+ * code, which you can find on the Internet at ftp.uu.net:/graphics/jpeg/.
+ *
+ * Contributed by Tom Lane <tgl@sss.pgh.pa.us>.
+ */
+#include <setjmp.h>
+
+int TIFFFillStrip(TIFF*, tstrip_t);
+int TIFFFillTile(TIFF*, ttile_t);
+
+/* We undefine FAR to avoid conflict with JPEG definition */
+
+#ifdef FAR
+#undef FAR
+#endif
+
+/*
+  Libjpeg's jmorecfg.h defines INT16 and INT32, but only if XMD_H is
+  not defined.  Unfortunately, the MinGW and Borland compilers include
+  a typedef for INT32, which causes a conflict.  MSVC does not include
+  a conficting typedef given the headers which are included.
+*/
+#if defined(__BORLANDC__) || defined(__MINGW32__)
+# define XMD_H 1
+#endif
+
+/*
+   The windows RPCNDR.H file defines boolean, but defines it with the
+   unsigned char size.  You should compile JPEG library using appropriate
+   definitions in jconfig.h header, but many users compile library in wrong
+   way. That causes errors of the following type:
+
+   "JPEGLib: JPEG parameter struct mismatch: library thinks size is 432,
+   caller expects 464"
+
+   For such users we wil fix the problem here. See install.doc file from
+   the JPEG library distribution for details.
+*/
+
+/* Define "boolean" as unsigned char, not int, per Windows custom. */
+#if defined(WIN32) && !defined(__MINGW32__)
+# ifndef __RPCNDR_H__            /* don't conflict if rpcndr.h already read */
+   typedef unsigned char boolean;
+# endif
+# define HAVE_BOOLEAN            /* prevent jmorecfg.h from redefining it */
+#endif
+
+#include "jpeglib.h"
+#include "jerror.h"
+
+/*
+ * We are using width_in_blocks which is supposed to be private to
+ * libjpeg. Unfortunately, the libjpeg delivered with Cygwin has
+ * renamed this member to width_in_data_units.  Since the header has
+ * also renamed a define, use that unique define name in order to
+ * detect the problem header and adjust to suit.
+ */
+#if defined(D_MAX_DATA_UNITS_IN_MCU)
+#define width_in_blocks width_in_data_units
+#endif
+
+/*
+ * On some machines it may be worthwhile to use _setjmp or sigsetjmp
+ * in place of plain setjmp.  These macros will make it easier.
+ */
+#define SETJMP(jbuf)		setjmp(jbuf)
+#define LONGJMP(jbuf,code)	longjmp(jbuf,code)
+#define JMP_BUF			jmp_buf
+
+typedef struct jpeg_destination_mgr jpeg_destination_mgr;
+typedef struct jpeg_source_mgr jpeg_source_mgr;
+typedef	struct jpeg_error_mgr jpeg_error_mgr;
+
+/*
+ * State block for each open TIFF file using
+ * libjpeg to do JPEG compression/decompression.
+ *
+ * libjpeg's visible state is either a jpeg_compress_struct
+ * or jpeg_decompress_struct depending on which way we
+ * are going.  comm can be used to refer to the fields
+ * which are common to both.
+ *
+ * NB: cinfo is required to be the first member of JPEGState,
+ *     so we can safely cast JPEGState* -> jpeg_xxx_struct*
+ *     and vice versa!
+ */
+typedef	struct {
+	union {
+		struct jpeg_compress_struct c;
+		struct jpeg_decompress_struct d;
+		struct jpeg_common_struct comm;
+	} cinfo;			/* NB: must be first */
+        int             cinfo_initialized;
+
+	jpeg_error_mgr	err;		/* libjpeg error manager */
+	JMP_BUF		exit_jmpbuf;	/* for catching libjpeg failures */
+	/*
+	 * The following two members could be a union, but
+	 * they're small enough that it's not worth the effort.
+	 */
+	jpeg_destination_mgr dest;	/* data dest for compression */
+	jpeg_source_mgr	src;		/* data source for decompression */
+					/* private state */
+	TIFF*		tif;		/* back link needed by some code */
+	uint16		photometric;	/* copy of PhotometricInterpretation */
+	uint16		h_sampling;	/* luminance sampling factors */
+	uint16		v_sampling;
+	tsize_t		bytesperline;	/* decompressed bytes per scanline */
+	/* pointers to intermediate buffers when processing downsampled data */
+	JSAMPARRAY	ds_buffer[MAX_COMPONENTS];
+	int		scancount;	/* number of "scanlines" accumulated */
+	int		samplesperclump;
+
+	TIFFVGetMethod	vgetparent;	/* super-class method */
+	TIFFVSetMethod	vsetparent;	/* super-class method */
+	TIFFStripMethod	defsparent;	/* super-class method */
+	TIFFTileMethod	deftparent;	/* super-class method */
+					/* pseudo-tag fields */
+	void*		jpegtables;	/* JPEGTables tag value, or NULL */
+	uint32		jpegtables_length; /* number of bytes in same */
+	int		jpegquality;	/* Compression quality level */
+	int		jpegcolormode;	/* Auto RGB<=>YCbCr convert? */
+	int		jpegtablesmode;	/* What to put in JPEGTables */
+
+        int             ycbcrsampling_fetched;
+	uint32		recvparams;	/* encoded Class 2 session params */
+	char*		subaddress;	/* subaddress string */
+	uint32		recvtime;	/* time spent receiving (secs) */
+	char*		faxdcs;		/* encoded fax parameters (DCS, Table 2/T.30) */
+} JPEGState;
+
+#define	JState(tif)	((JPEGState*)(tif)->tif_data)
+
+static	int JPEGDecode(TIFF*, tidata_t, tsize_t, tsample_t);
+static	int JPEGDecodeRaw(TIFF*, tidata_t, tsize_t, tsample_t);
+static	int JPEGEncode(TIFF*, tidata_t, tsize_t, tsample_t);
+static	int JPEGEncodeRaw(TIFF*, tidata_t, tsize_t, tsample_t);
+static  int JPEGInitializeLibJPEG( TIFF * tif,
+								   int force_encode, int force_decode );
+
+#define	FIELD_JPEGTABLES	(FIELD_CODEC+0)
+#define	FIELD_RECVPARAMS	(FIELD_CODEC+1)
+#define	FIELD_SUBADDRESS	(FIELD_CODEC+2)
+#define	FIELD_RECVTIME		(FIELD_CODEC+3)
+#define	FIELD_FAXDCS		(FIELD_CODEC+4)
+
+static const TIFFFieldInfo jpegFieldInfo[] = {
+    { TIFFTAG_JPEGTABLES,	 -3,-3,	TIFF_UNDEFINED,	FIELD_JPEGTABLES,
+      FALSE,	TRUE,	"JPEGTables" },
+    { TIFFTAG_JPEGQUALITY,	 0, 0,	TIFF_ANY,	FIELD_PSEUDO,
+      TRUE,	FALSE,	"" },
+    { TIFFTAG_JPEGCOLORMODE,	 0, 0,	TIFF_ANY,	FIELD_PSEUDO,
+      FALSE,	FALSE,	"" },
+    { TIFFTAG_JPEGTABLESMODE,	 0, 0,	TIFF_ANY,	FIELD_PSEUDO,
+      FALSE,	FALSE,	"" },
+    /* Specific for JPEG in faxes */
+    { TIFFTAG_FAXRECVPARAMS,	 1, 1, TIFF_LONG,	FIELD_RECVPARAMS,
+      TRUE,	FALSE,	"FaxRecvParams" },
+    { TIFFTAG_FAXSUBADDRESS,	-1,-1, TIFF_ASCII,	FIELD_SUBADDRESS,
+      TRUE,	FALSE,	"FaxSubAddress" },
+    { TIFFTAG_FAXRECVTIME,	 1, 1, TIFF_LONG,	FIELD_RECVTIME,
+      TRUE,	FALSE,	"FaxRecvTime" },
+    { TIFFTAG_FAXDCS,		-1, -1, TIFF_ASCII,	FIELD_FAXDCS,
+	  TRUE,	FALSE,	"FaxDcs" },
+};
+#define	N(a)	(sizeof (a) / sizeof (a[0]))
+
+/*
+ * libjpeg interface layer.
+ *
+ * We use setjmp/longjmp to return control to libtiff
+ * when a fatal error is encountered within the JPEG
+ * library.  We also direct libjpeg error and warning
+ * messages through the appropriate libtiff handlers.
+ */
+
+/*
+ * Error handling routines (these replace corresponding
+ * IJG routines from jerror.c).  These are used for both
+ * compression and decompression.
+ */
+static void
+TIFFjpeg_error_exit(j_common_ptr cinfo)
+{
+	JPEGState *sp = (JPEGState *) cinfo;	/* NB: cinfo assumed first */
+	char buffer[JMSG_LENGTH_MAX];
+
+	(*cinfo->err->format_message) (cinfo, buffer);
+	TIFFErrorExt(sp->tif->tif_clientdata, "JPEGLib", buffer);		/* display the error message */
+	jpeg_abort(cinfo);			/* clean up libjpeg state */
+	LONGJMP(sp->exit_jmpbuf, 1);		/* return to libtiff caller */
+}
+
+/*
+ * This routine is invoked only for warning messages,
+ * since error_exit does its own thing and trace_level
+ * is never set > 0.
+ */
+static void
+TIFFjpeg_output_message(j_common_ptr cinfo)
+{
+	char buffer[JMSG_LENGTH_MAX];
+
+	(*cinfo->err->format_message) (cinfo, buffer);
+	TIFFWarningExt(((JPEGState *) cinfo)->tif->tif_clientdata, "JPEGLib", buffer);
+}
+
+/*
+ * Interface routines.  This layer of routines exists
+ * primarily to limit side-effects from using setjmp.
+ * Also, normal/error returns are converted into return
+ * values per libtiff practice.
+ */
+#define	CALLJPEG(sp, fail, op)	(SETJMP((sp)->exit_jmpbuf) ? (fail) : (op))
+#define	CALLVJPEG(sp, op)	CALLJPEG(sp, 0, ((op),1))
+
+static int
+TIFFjpeg_create_compress(JPEGState* sp)
+{
+	/* initialize JPEG error handling */
+	sp->cinfo.c.err = jpeg_std_error(&sp->err);
+	sp->err.error_exit = TIFFjpeg_error_exit;
+	sp->err.output_message = TIFFjpeg_output_message;
+
+	return CALLVJPEG(sp, jpeg_create_compress(&sp->cinfo.c));
+}
+
+static int
+TIFFjpeg_create_decompress(JPEGState* sp)
+{
+	/* initialize JPEG error handling */
+	sp->cinfo.d.err = jpeg_std_error(&sp->err);
+	sp->err.error_exit = TIFFjpeg_error_exit;
+	sp->err.output_message = TIFFjpeg_output_message;
+
+	return CALLVJPEG(sp, jpeg_create_decompress(&sp->cinfo.d));
+}
+
+static int
+TIFFjpeg_set_defaults(JPEGState* sp)
+{
+	return CALLVJPEG(sp, jpeg_set_defaults(&sp->cinfo.c));
+}
+
+static int
+TIFFjpeg_set_colorspace(JPEGState* sp, J_COLOR_SPACE colorspace)
+{
+	return CALLVJPEG(sp, jpeg_set_colorspace(&sp->cinfo.c, colorspace));
+}
+
+static int
+TIFFjpeg_set_quality(JPEGState* sp, int quality, boolean force_baseline)
+{
+	return CALLVJPEG(sp,
+	    jpeg_set_quality(&sp->cinfo.c, quality, force_baseline));
+}
+
+static int
+TIFFjpeg_suppress_tables(JPEGState* sp, boolean suppress)
+{
+	return CALLVJPEG(sp, jpeg_suppress_tables(&sp->cinfo.c, suppress));
+}
+
+static int
+TIFFjpeg_start_compress(JPEGState* sp, boolean write_all_tables)
+{
+	return CALLVJPEG(sp,
+	    jpeg_start_compress(&sp->cinfo.c, write_all_tables));
+}
+
+static int
+TIFFjpeg_write_scanlines(JPEGState* sp, JSAMPARRAY scanlines, int num_lines)
+{
+	return CALLJPEG(sp, -1, (int) jpeg_write_scanlines(&sp->cinfo.c,
+	    scanlines, (JDIMENSION) num_lines));
+}
+
+static int
+TIFFjpeg_write_raw_data(JPEGState* sp, JSAMPIMAGE data, int num_lines)
+{
+	return CALLJPEG(sp, -1, (int) jpeg_write_raw_data(&sp->cinfo.c,
+	    data, (JDIMENSION) num_lines));
+}
+
+static int
+TIFFjpeg_finish_compress(JPEGState* sp)
+{
+	return CALLVJPEG(sp, jpeg_finish_compress(&sp->cinfo.c));
+}
+
+static int
+TIFFjpeg_write_tables(JPEGState* sp)
+{
+	return CALLVJPEG(sp, jpeg_write_tables(&sp->cinfo.c));
+}
+
+static int
+TIFFjpeg_read_header(JPEGState* sp, boolean require_image)
+{
+	return CALLJPEG(sp, -1, jpeg_read_header(&sp->cinfo.d, require_image));
+}
+
+static int
+TIFFjpeg_start_decompress(JPEGState* sp)
+{
+	return CALLVJPEG(sp, jpeg_start_decompress(&sp->cinfo.d));
+}
+
+static int
+TIFFjpeg_read_scanlines(JPEGState* sp, JSAMPARRAY scanlines, int max_lines)
+{
+	return CALLJPEG(sp, -1, (int) jpeg_read_scanlines(&sp->cinfo.d,
+	    scanlines, (JDIMENSION) max_lines));
+}
+
+static int
+TIFFjpeg_read_raw_data(JPEGState* sp, JSAMPIMAGE data, int max_lines)
+{
+	return CALLJPEG(sp, -1, (int) jpeg_read_raw_data(&sp->cinfo.d,
+	    data, (JDIMENSION) max_lines));
+}
+
+static int
+TIFFjpeg_finish_decompress(JPEGState* sp)
+{
+	return CALLJPEG(sp, -1, (int) jpeg_finish_decompress(&sp->cinfo.d));
+}
+
+static int
+TIFFjpeg_abort(JPEGState* sp)
+{
+	return CALLVJPEG(sp, jpeg_abort(&sp->cinfo.comm));
+}
+
+static int
+TIFFjpeg_destroy(JPEGState* sp)
+{
+	return CALLVJPEG(sp, jpeg_destroy(&sp->cinfo.comm));
+}
+
+static JSAMPARRAY
+TIFFjpeg_alloc_sarray(JPEGState* sp, int pool_id,
+		      JDIMENSION samplesperrow, JDIMENSION numrows)
+{
+	return CALLJPEG(sp, (JSAMPARRAY) NULL,
+	    (*sp->cinfo.comm.mem->alloc_sarray)
+		(&sp->cinfo.comm, pool_id, samplesperrow, numrows));
+}
+
+/*
+ * JPEG library destination data manager.
+ * These routines direct compressed data from libjpeg into the
+ * libtiff output buffer.
+ */
+
+static void
+std_init_destination(j_compress_ptr cinfo)
+{
+	JPEGState* sp = (JPEGState*) cinfo;
+	TIFF* tif = sp->tif;
+
+	sp->dest.next_output_byte = (JOCTET*) tif->tif_rawdata;
+	sp->dest.free_in_buffer = (size_t) tif->tif_rawdatasize;
+}
+
+static boolean
+std_empty_output_buffer(j_compress_ptr cinfo)
+{
+	JPEGState* sp = (JPEGState*) cinfo;
+	TIFF* tif = sp->tif;
+
+	/* the entire buffer has been filled */
+	tif->tif_rawcc = tif->tif_rawdatasize;
+	TIFFFlushData1(tif);
+	sp->dest.next_output_byte = (JOCTET*) tif->tif_rawdata;
+	sp->dest.free_in_buffer = (size_t) tif->tif_rawdatasize;
+
+	return (TRUE);
+}
+
+static void
+std_term_destination(j_compress_ptr cinfo)
+{
+	JPEGState* sp = (JPEGState*) cinfo;
+	TIFF* tif = sp->tif;
+
+	tif->tif_rawcp = (tidata_t) sp->dest.next_output_byte;
+	tif->tif_rawcc =
+	    tif->tif_rawdatasize - (tsize_t) sp->dest.free_in_buffer;
+	/* NB: libtiff does the final buffer flush */
+}
+
+static void
+TIFFjpeg_data_dest(JPEGState* sp, TIFF* tif)
+{
+	(void) tif;
+	sp->cinfo.c.dest = &sp->dest;
+	sp->dest.init_destination = std_init_destination;
+	sp->dest.empty_output_buffer = std_empty_output_buffer;
+	sp->dest.term_destination = std_term_destination;
+}
+
+/*
+ * Alternate destination manager for outputting to JPEGTables field.
+ */
+
+static void
+tables_init_destination(j_compress_ptr cinfo)
+{
+	JPEGState* sp = (JPEGState*) cinfo;
+
+	/* while building, jpegtables_length is allocated buffer size */
+	sp->dest.next_output_byte = (JOCTET*) sp->jpegtables;
+	sp->dest.free_in_buffer = (size_t) sp->jpegtables_length;
+}
+
+static boolean
+tables_empty_output_buffer(j_compress_ptr cinfo)
+{
+	JPEGState* sp = (JPEGState*) cinfo;
+	void* newbuf;
+
+	/* the entire buffer has been filled; enlarge it by 1000 bytes */
+	newbuf = _TIFFrealloc((tdata_t) sp->jpegtables,
+			      (tsize_t) (sp->jpegtables_length + 1000));
+	if (newbuf == NULL)
+		ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 100);
+	sp->dest.next_output_byte = (JOCTET*) newbuf + sp->jpegtables_length;
+	sp->dest.free_in_buffer = (size_t) 1000;
+	sp->jpegtables = newbuf;
+	sp->jpegtables_length += 1000;
+	return (TRUE);
+}
+
+static void
+tables_term_destination(j_compress_ptr cinfo)
+{
+	JPEGState* sp = (JPEGState*) cinfo;
+
+	/* set tables length to number of bytes actually emitted */
+	sp->jpegtables_length -= sp->dest.free_in_buffer;
+}
+
+static int
+TIFFjpeg_tables_dest(JPEGState* sp, TIFF* tif)
+{
+	(void) tif;
+	/*
+	 * Allocate a working buffer for building tables.
+	 * Initial size is 1000 bytes, which is usually adequate.
+	 */
+	if (sp->jpegtables)
+		_TIFFfree(sp->jpegtables);
+	sp->jpegtables_length = 1000;
+	sp->jpegtables = (void*) _TIFFmalloc((tsize_t) sp->jpegtables_length);
+	if (sp->jpegtables == NULL) {
+		sp->jpegtables_length = 0;
+		TIFFErrorExt(sp->tif->tif_clientdata, "TIFFjpeg_tables_dest", "No space for JPEGTables");
+		return (0);
+	}
+	sp->cinfo.c.dest = &sp->dest;
+	sp->dest.init_destination = tables_init_destination;
+	sp->dest.empty_output_buffer = tables_empty_output_buffer;
+	sp->dest.term_destination = tables_term_destination;
+	return (1);
+}
+
+/*
+ * JPEG library source data manager.
+ * These routines supply compressed data to libjpeg.
+ */
+
+static void
+std_init_source(j_decompress_ptr cinfo)
+{
+	JPEGState* sp = (JPEGState*) cinfo;
+	TIFF* tif = sp->tif;
+
+	sp->src.next_input_byte = (const JOCTET*) tif->tif_rawdata;
+	sp->src.bytes_in_buffer = (size_t) tif->tif_rawcc;
+}
+
+static boolean
+std_fill_input_buffer(j_decompress_ptr cinfo)
+{
+	JPEGState* sp = (JPEGState* ) cinfo;
+	static const JOCTET dummy_EOI[2] = { 0xFF, JPEG_EOI };
+
+	/*
+	 * Should never get here since entire strip/tile is
+	 * read into memory before the decompressor is called,
+	 * and thus was supplied by init_source.
+	 */
+	WARNMS(cinfo, JWRN_JPEG_EOF);
+	/* insert a fake EOI marker */
+	sp->src.next_input_byte = dummy_EOI;
+	sp->src.bytes_in_buffer = 2;
+	return (TRUE);
+}
+
+static void
+std_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
+{
+	JPEGState* sp = (JPEGState*) cinfo;
+
+	if (num_bytes > 0) {
+		if (num_bytes > (long) sp->src.bytes_in_buffer) {
+			/* oops, buffer overrun */
+			(void) std_fill_input_buffer(cinfo);
+		} else {
+			sp->src.next_input_byte += (size_t) num_bytes;
+			sp->src.bytes_in_buffer -= (size_t) num_bytes;
+		}
+	}
+}
+
+static void
+std_term_source(j_decompress_ptr cinfo)
+{
+	/* No work necessary here */
+	/* Or must we update tif->tif_rawcp, tif->tif_rawcc ??? */
+	/* (if so, need empty tables_term_source!) */
+	(void) cinfo;
+}
+
+static void
+TIFFjpeg_data_src(JPEGState* sp, TIFF* tif)
+{
+	(void) tif;
+	sp->cinfo.d.src = &sp->src;
+	sp->src.init_source = std_init_source;
+	sp->src.fill_input_buffer = std_fill_input_buffer;
+	sp->src.skip_input_data = std_skip_input_data;
+	sp->src.resync_to_restart = jpeg_resync_to_restart;
+	sp->src.term_source = std_term_source;
+	sp->src.bytes_in_buffer = 0;		/* for safety */
+	sp->src.next_input_byte = NULL;
+}
+
+/*
+ * Alternate source manager for reading from JPEGTables.
+ * We can share all the code except for the init routine.
+ */
+
+static void
+tables_init_source(j_decompress_ptr cinfo)
+{
+	JPEGState* sp = (JPEGState*) cinfo;
+
+	sp->src.next_input_byte = (const JOCTET*) sp->jpegtables;
+	sp->src.bytes_in_buffer = (size_t) sp->jpegtables_length;
+}
+
+static void
+TIFFjpeg_tables_src(JPEGState* sp, TIFF* tif)
+{
+	TIFFjpeg_data_src(sp, tif);
+	sp->src.init_source = tables_init_source;
+}
+
+/*
+ * Allocate downsampled-data buffers needed for downsampled I/O.
+ * We use values computed in jpeg_start_compress or jpeg_start_decompress.
+ * We use libjpeg's allocator so that buffers will be released automatically
+ * when done with strip/tile.
+ * This is also a handy place to compute samplesperclump, bytesperline.
+ */
+static int
+alloc_downsampled_buffers(TIFF* tif, jpeg_component_info* comp_info,
+			  int num_components)
+{
+	JPEGState* sp = JState(tif);
+	int ci;
+	jpeg_component_info* compptr;
+	JSAMPARRAY buf;
+	int samples_per_clump = 0;
+
+	for (ci = 0, compptr = comp_info; ci < num_components;
+	     ci++, compptr++) {
+		samples_per_clump += compptr->h_samp_factor *
+			compptr->v_samp_factor;
+		buf = TIFFjpeg_alloc_sarray(sp, JPOOL_IMAGE,
+				compptr->width_in_blocks * DCTSIZE,
+				(JDIMENSION) (compptr->v_samp_factor*DCTSIZE));
+		if (buf == NULL)
+			return (0);
+		sp->ds_buffer[ci] = buf;
+	}
+	sp->samplesperclump = samples_per_clump;
+	return (1);
+}
+
+
+/*
+ * JPEG Decoding.
+ */
+
+static int
+JPEGSetupDecode(TIFF* tif)
+{
+	JPEGState* sp = JState(tif);
+	TIFFDirectory *td = &tif->tif_dir;
+
+        JPEGInitializeLibJPEG( tif, 0, 1 );
+
+	assert(sp != NULL);
+	assert(sp->cinfo.comm.is_decompressor);
+
+	/* Read JPEGTables if it is present */
+	if (TIFFFieldSet(tif,FIELD_JPEGTABLES)) {
+		TIFFjpeg_tables_src(sp, tif);
+		if(TIFFjpeg_read_header(sp,FALSE) != JPEG_HEADER_TABLES_ONLY) {
+			TIFFErrorExt(tif->tif_clientdata, "JPEGSetupDecode", "Bogus JPEGTables field");
+			return (0);
+		}
+	}
+
+	/* Grab parameters that are same for all strips/tiles */
+	sp->photometric = td->td_photometric;
+	switch (sp->photometric) {
+	case PHOTOMETRIC_YCBCR:
+		sp->h_sampling = td->td_ycbcrsubsampling[0];
+		sp->v_sampling = td->td_ycbcrsubsampling[1];
+		break;
+	default:
+		/* TIFF 6.0 forbids subsampling of all other color spaces */
+		sp->h_sampling = 1;
+		sp->v_sampling = 1;
+		break;
+	}
+
+	/* Set up for reading normal data */
+	TIFFjpeg_data_src(sp, tif);
+	tif->tif_postdecode = _TIFFNoPostDecode; /* override byte swapping */
+	return (1);
+}
+
+/*
+ * Set up for decoding a strip or tile.
+ */
+static int
+JPEGPreDecode(TIFF* tif, tsample_t s)
+{
+	JPEGState *sp = JState(tif);
+	TIFFDirectory *td = &tif->tif_dir;
+	static const char module[] = "JPEGPreDecode";
+	uint32 segment_width, segment_height;
+	int downsampled_output;
+	int ci;
+
+	assert(sp != NULL);
+	assert(sp->cinfo.comm.is_decompressor);
+	/*
+	 * Reset decoder state from any previous strip/tile,
+	 * in case application didn't read the whole strip.
+	 */
+	if (!TIFFjpeg_abort(sp))
+		return (0);
+	/*
+	 * Read the header for this strip/tile.
+	 */
+	if (TIFFjpeg_read_header(sp, TRUE) != JPEG_HEADER_OK)
+		return (0);
+	/*
+	 * Check image parameters and set decompression parameters.
+	 */
+	segment_width = td->td_imagewidth;
+	segment_height = td->td_imagelength - tif->tif_row;
+	if (isTiled(tif)) {
+                segment_width = td->td_tilewidth;
+                segment_height = td->td_tilelength;
+		sp->bytesperline = TIFFTileRowSize(tif);
+	} else {
+		if (segment_height > td->td_rowsperstrip)
+			segment_height = td->td_rowsperstrip;
+		sp->bytesperline = TIFFOldScanlineSize(tif);
+	}
+	if (td->td_planarconfig == PLANARCONFIG_SEPARATE && s > 0) {
+		/*
+		 * For PC 2, scale down the expected strip/tile size
+		 * to match a downsampled component
+		 */
+		segment_width = TIFFhowmany(segment_width, sp->h_sampling);
+		segment_height = TIFFhowmany(segment_height, sp->v_sampling);
+	}
+	if (sp->cinfo.d.image_width != segment_width ||
+	    sp->cinfo.d.image_height != segment_height) {
+		TIFFWarningExt(tif->tif_clientdata, module,
+                 "Improper JPEG strip/tile size, expected %dx%d, got %dx%d",
+                          segment_width, 
+                          segment_height,
+                          sp->cinfo.d.image_width, 
+                          sp->cinfo.d.image_height);
+	}
+	if (sp->cinfo.d.num_components !=
+	    (td->td_planarconfig == PLANARCONFIG_CONTIG ?
+	     td->td_samplesperpixel : 1)) {
+		TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG component count");
+		return (0);
+	}
+#ifdef JPEG_LIB_MK1
+	if (12 != td->td_bitspersample && 8 != td->td_bitspersample) {
+			TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG data precision");
+            return (0);
+	}
+        sp->cinfo.d.data_precision = td->td_bitspersample;
+        sp->cinfo.d.bits_in_jsample = td->td_bitspersample;
+#else
+	if (sp->cinfo.d.data_precision != td->td_bitspersample) {
+			TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG data precision");
+            return (0);
+	}
+#endif
+	if (td->td_planarconfig == PLANARCONFIG_CONTIG) {
+		/* Component 0 should have expected sampling factors */
+		if (sp->cinfo.d.comp_info[0].h_samp_factor != sp->h_sampling ||
+		    sp->cinfo.d.comp_info[0].v_samp_factor != sp->v_sampling) {
+				TIFFWarningExt(tif->tif_clientdata, module,
+                                    "Improper JPEG sampling factors %d,%d\n"
+                                    "Apparently should be %d,%d.",
+                                    sp->cinfo.d.comp_info[0].h_samp_factor,
+                                    sp->cinfo.d.comp_info[0].v_samp_factor,
+                                    sp->h_sampling, sp->v_sampling);
+
+			    /*
+			     * XXX: Files written by the Intergraph software
+			     * has different sampling factors stored in the
+			     * TIFF tags and in the JPEG structures. We will
+			     * try to deduce Intergraph files by the presense
+			     * of the tag 33918.
+			     */
+			    if (!_TIFFFindFieldInfo(tif, 33918, TIFF_ANY)) {
+					TIFFWarningExt(tif->tif_clientdata, module,
+					"Decompressor will try reading with "
+					"sampling %d,%d.",
+					sp->cinfo.d.comp_info[0].h_samp_factor,
+					sp->cinfo.d.comp_info[0].v_samp_factor);
+
+				    sp->h_sampling = (uint16)
+					sp->cinfo.d.comp_info[0].h_samp_factor;
+				    sp->v_sampling = (uint16)
+					sp->cinfo.d.comp_info[0].v_samp_factor;
+			    }
+		}
+		/* Rest should have sampling factors 1,1 */
+		for (ci = 1; ci < sp->cinfo.d.num_components; ci++) {
+			if (sp->cinfo.d.comp_info[ci].h_samp_factor != 1 ||
+			    sp->cinfo.d.comp_info[ci].v_samp_factor != 1) {
+				TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG sampling factors");
+				return (0);
+			}
+		}
+	} else {
+		/* PC 2's single component should have sampling factors 1,1 */
+		if (sp->cinfo.d.comp_info[0].h_samp_factor != 1 ||
+		    sp->cinfo.d.comp_info[0].v_samp_factor != 1) {
+			TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG sampling factors");
+			return (0);
+		}
+	}
+	downsampled_output = FALSE;
+	if (td->td_planarconfig == PLANARCONFIG_CONTIG &&
+	    sp->photometric == PHOTOMETRIC_YCBCR &&
+	    sp->jpegcolormode == JPEGCOLORMODE_RGB) {
+	/* Convert YCbCr to RGB */
+		sp->cinfo.d.jpeg_color_space = JCS_YCbCr;
+		sp->cinfo.d.out_color_space = JCS_RGB;
+	} else {
+			/* Suppress colorspace handling */
+		sp->cinfo.d.jpeg_color_space = JCS_UNKNOWN;
+		sp->cinfo.d.out_color_space = JCS_UNKNOWN;
+		if (td->td_planarconfig == PLANARCONFIG_CONTIG &&
+		    (sp->h_sampling != 1 || sp->v_sampling != 1))
+			downsampled_output = TRUE;
+		/* XXX what about up-sampling? */
+	}
+	if (downsampled_output) {
+		/* Need to use raw-data interface to libjpeg */
+		sp->cinfo.d.raw_data_out = TRUE;
+		tif->tif_decoderow = JPEGDecodeRaw;
+		tif->tif_decodestrip = JPEGDecodeRaw;
+		tif->tif_decodetile = JPEGDecodeRaw;
+	} else {
+		/* Use normal interface to libjpeg */
+		sp->cinfo.d.raw_data_out = FALSE;
+		tif->tif_decoderow = JPEGDecode;
+		tif->tif_decodestrip = JPEGDecode;
+		tif->tif_decodetile = JPEGDecode;
+	}
+	/* Start JPEG decompressor */
+	if (!TIFFjpeg_start_decompress(sp))
+		return (0);
+	/* Allocate downsampled-data buffers if needed */
+	if (downsampled_output) {
+		if (!alloc_downsampled_buffers(tif, sp->cinfo.d.comp_info,
+					       sp->cinfo.d.num_components))
+			return (0);
+		sp->scancount = DCTSIZE;	/* mark buffer empty */
+	}
+	return (1);
+}
+
+/*
+ * Decode a chunk of pixels.
+ * "Standard" case: returned data is not downsampled.
+ */
+/*ARGSUSED*/ static int
+JPEGDecode(TIFF* tif, tidata_t buf, tsize_t cc, tsample_t s)
+{
+    JPEGState *sp = JState(tif);
+    tsize_t nrows;
+    (void) s;
+
+    nrows = cc / sp->bytesperline;
+    if (cc % sp->bytesperline)
+		TIFFWarningExt(tif->tif_clientdata, tif->tif_name, "fractional scanline not read");
+
+    if( nrows > (int) sp->cinfo.d.image_height )
+        nrows = sp->cinfo.d.image_height;
+
+    /* data is expected to be read in multiples of a scanline */
+    if (nrows)
+    {
+        JSAMPROW line_work_buf = NULL;
+
+        /*
+        ** For 6B, only use temporary buffer for 12 bit imagery. 
+        ** For Mk1 always use it. 
+        */
+#if !defined(JPEG_LIB_MK1)        
+        if( sp->cinfo.d.data_precision == 12 )
+#endif
+        {
+            line_work_buf = (JSAMPROW) 
+                _TIFFmalloc(sizeof(short) * sp->cinfo.d.output_width 
+                            * sp->cinfo.d.num_components );
+        }
+
+        do {
+            if( line_work_buf != NULL )
+            {
+                /* 
+                ** In the MK1 case, we aways read into a 16bit buffer, and then
+                ** pack down to 12bit or 8bit.  In 6B case we only read into 16
+                ** bit buffer for 12bit data, which we need to repack. 
+                */
+                if (TIFFjpeg_read_scanlines(sp, &line_work_buf, 1) != 1)
+                    return (0);
+
+                if( sp->cinfo.d.data_precision == 12 )
+                {
+                    int value_pairs = (sp->cinfo.d.output_width 
+                                       * sp->cinfo.d.num_components) / 2;
+                    int iPair;
+
+                    for( iPair = 0; iPair < value_pairs; iPair++ )
+                    {
+                        unsigned char *out_ptr = 
+                            ((unsigned char *) buf) + iPair * 3;
+                        JSAMPLE *in_ptr = line_work_buf + iPair * 2;
+
+                        out_ptr[0] = (in_ptr[0] & 0xff0) >> 4;
+                        out_ptr[1] = ((in_ptr[0] & 0xf) << 4)
+                            | ((in_ptr[1] & 0xf00) >> 8);
+                        out_ptr[2] = ((in_ptr[1] & 0xff) >> 0);
+                    }
+                }
+                else if( sp->cinfo.d.data_precision == 8 )
+                {
+                    int value_count = (sp->cinfo.d.output_width 
+                                       * sp->cinfo.d.num_components);
+                    int iValue;
+
+                    for( iValue = 0; iValue < value_count; iValue++ )
+                    {
+                        ((unsigned char *) buf)[iValue] = 
+                            line_work_buf[iValue] & 0xff;
+                    }
+                }
+            }
+            else
+            {
+                /*
+                ** In the libjpeg6b 8bit case.  We read directly into the 
+                ** TIFF buffer.
+                */
+                JSAMPROW bufptr = (JSAMPROW)buf;
+  
+                if (TIFFjpeg_read_scanlines(sp, &bufptr, 1) != 1)
+                    return (0);
+            }
+
+            ++tif->tif_row;
+            buf += sp->bytesperline;
+            cc -= sp->bytesperline;
+        } while (--nrows > 0);
+
+        if( line_work_buf != NULL )
+            _TIFFfree( line_work_buf );
+    }
+
+    /* Close down the decompressor if we've finished the strip or tile. */
+    return sp->cinfo.d.output_scanline < sp->cinfo.d.output_height
+        || TIFFjpeg_finish_decompress(sp);
+}
+
+/*
+ * Decode a chunk of pixels.
+ * Returned data is downsampled per sampling factors.
+ */
+/*ARGSUSED*/ static int
+JPEGDecodeRaw(TIFF* tif, tidata_t buf, tsize_t cc, tsample_t s)
+{
+	JPEGState *sp = JState(tif);
+	tsize_t nrows;
+	(void) s;
+
+	/* data is expected to be read in multiples of a scanline */
+	if ( (nrows = sp->cinfo.d.image_height) ) {
+		/* Cb,Cr both have sampling factors 1, so this is correct */
+		JDIMENSION clumps_per_line = sp->cinfo.d.comp_info[1].downsampled_width;            
+		int samples_per_clump = sp->samplesperclump;
+
+#ifdef JPEG_LIB_MK1
+		unsigned short* tmpbuf = _TIFFmalloc(sizeof(unsigned short) *
+		    sp->cinfo.d.output_width *
+		    sp->cinfo.d.num_components);
+#endif
+
+		do {
+			jpeg_component_info *compptr;
+			int ci, clumpoffset;
+
+			/* Reload downsampled-data buffer if needed */
+			if (sp->scancount >= DCTSIZE) {
+				int n = sp->cinfo.d.max_v_samp_factor * DCTSIZE;
+				if (TIFFjpeg_read_raw_data(sp, sp->ds_buffer, n) != n)
+					return (0);
+				sp->scancount = 0;
+			}
+			/*
+			 * Fastest way to unseparate data is to make one pass
+			 * over the scanline for each row of each component.
+			 */
+			clumpoffset = 0;    /* first sample in clump */
+			for (ci = 0, compptr = sp->cinfo.d.comp_info;
+			    ci < sp->cinfo.d.num_components;
+			    ci++, compptr++) {
+				int hsamp = compptr->h_samp_factor;
+				int vsamp = compptr->v_samp_factor;
+				int ypos;
+
+				for (ypos = 0; ypos < vsamp; ypos++) {
+					JSAMPLE *inptr = sp->ds_buffer[ci][sp->scancount*vsamp + ypos];
+#ifdef JPEG_LIB_MK1
+					JSAMPLE *outptr = (JSAMPLE*)tmpbuf + clumpoffset;
+#else
+					JSAMPLE *outptr = (JSAMPLE*)buf + clumpoffset;
+#endif
+					JDIMENSION nclump;
+
+					if (hsamp == 1) {
+						/* fast path for at least Cb and Cr */
+						for (nclump = clumps_per_line; nclump-- > 0; ) {
+							outptr[0] = *inptr++;
+							outptr += samples_per_clump;
+						}
+					} else {
+						int xpos;
+
+			/* general case */
+						for (nclump = clumps_per_line; nclump-- > 0; ) {
+							for (xpos = 0; xpos < hsamp; xpos++)
+								outptr[xpos] = *inptr++;
+							outptr += samples_per_clump;
+						}
+					}
+					clumpoffset += hsamp;
+				}
+			}
+
+#ifdef JPEG_LIB_MK1
+			{
+				if (sp->cinfo.d.data_precision == 8)
+				{
+					int i=0;
+					int len = sp->cinfo.d.output_width * sp->cinfo.d.num_components;
+					for (i=0; i<len; i++)
+					{
+						((unsigned char*)buf)[i] = tmpbuf[i] & 0xff;
+					}
+				}
+				else
+				{         // 12-bit
+					int value_pairs = (sp->cinfo.d.output_width
+					    * sp->cinfo.d.num_components) / 2;
+					int iPair;
+					for( iPair = 0; iPair < value_pairs; iPair++ )
+					{
+						unsigned char *out_ptr = ((unsigned char *) buf) + iPair * 3;
+						JSAMPLE *in_ptr = tmpbuf + iPair * 2;
+						out_ptr[0] = (in_ptr[0] & 0xff0) >> 4;
+						out_ptr[1] = ((in_ptr[0] & 0xf) << 4)
+						    | ((in_ptr[1] & 0xf00) >> 8);
+						out_ptr[2] = ((in_ptr[1] & 0xff) >> 0);
+					}
+				}
+			}
+#endif
+
+			sp->scancount ++;
+			tif->tif_row += sp->v_sampling;
+			/* increment/decrement of buf and cc is still incorrect, but should not matter
+			 * TODO: resolve this */
+			buf += sp->bytesperline;
+			cc -= sp->bytesperline;
+			nrows -= sp->v_sampling;
+		} while (nrows > 0);
+
+#ifdef JPEG_LIB_MK1
+		_TIFFfree(tmpbuf);
+#endif
+
+	}
+
+	/* Close down the decompressor if done. */
+	return sp->cinfo.d.output_scanline < sp->cinfo.d.output_height
+	    || TIFFjpeg_finish_decompress(sp);
+}
+
+
+/*
+ * JPEG Encoding.
+ */
+
+static void
+unsuppress_quant_table (JPEGState* sp, int tblno)
+{
+	JQUANT_TBL* qtbl;
+
+	if ((qtbl = sp->cinfo.c.quant_tbl_ptrs[tblno]) != NULL)
+		qtbl->sent_table = FALSE;
+}
+
+static void
+unsuppress_huff_table (JPEGState* sp, int tblno)
+{
+	JHUFF_TBL* htbl;
+
+	if ((htbl = sp->cinfo.c.dc_huff_tbl_ptrs[tblno]) != NULL)
+		htbl->sent_table = FALSE;
+	if ((htbl = sp->cinfo.c.ac_huff_tbl_ptrs[tblno]) != NULL)
+		htbl->sent_table = FALSE;
+}
+
+static int
+prepare_JPEGTables(TIFF* tif)
+{
+	JPEGState* sp = JState(tif);
+
+        JPEGInitializeLibJPEG( tif, 0, 0 );
+
+	/* Initialize quant tables for current quality setting */
+	if (!TIFFjpeg_set_quality(sp, sp->jpegquality, FALSE))
+		return (0);
+	/* Mark only the tables we want for output */
+	/* NB: chrominance tables are currently used only with YCbCr */
+	if (!TIFFjpeg_suppress_tables(sp, TRUE))
+		return (0);
+	if (sp->jpegtablesmode & JPEGTABLESMODE_QUANT) {
+		unsuppress_quant_table(sp, 0);
+		if (sp->photometric == PHOTOMETRIC_YCBCR)
+			unsuppress_quant_table(sp, 1);
+	}
+	if (sp->jpegtablesmode & JPEGTABLESMODE_HUFF) {
+		unsuppress_huff_table(sp, 0);
+		if (sp->photometric == PHOTOMETRIC_YCBCR)
+			unsuppress_huff_table(sp, 1);
+	}
+	/* Direct libjpeg output into jpegtables */
+	if (!TIFFjpeg_tables_dest(sp, tif))
+		return (0);
+	/* Emit tables-only datastream */
+	if (!TIFFjpeg_write_tables(sp))
+		return (0);
+
+	return (1);
+}
+
+static int
+JPEGSetupEncode(TIFF* tif)
+{
+	JPEGState* sp = JState(tif);
+	TIFFDirectory *td = &tif->tif_dir;
+	static const char module[] = "JPEGSetupEncode";
+
+        JPEGInitializeLibJPEG( tif, 1, 0 );
+
+	assert(sp != NULL);
+	assert(!sp->cinfo.comm.is_decompressor);
+
+	/*
+	 * Initialize all JPEG parameters to default values.
+	 * Note that jpeg_set_defaults needs legal values for
+	 * in_color_space and input_components.
+	 */
+	sp->cinfo.c.in_color_space = JCS_UNKNOWN;
+	sp->cinfo.c.input_components = 1;
+	if (!TIFFjpeg_set_defaults(sp))
+		return (0);
+	/* Set per-file parameters */
+	sp->photometric = td->td_photometric;
+	switch (sp->photometric) {
+	case PHOTOMETRIC_YCBCR:
+		sp->h_sampling = td->td_ycbcrsubsampling[0];
+		sp->v_sampling = td->td_ycbcrsubsampling[1];
+		/*
+		 * A ReferenceBlackWhite field *must* be present since the
+		 * default value is inappropriate for YCbCr.  Fill in the
+		 * proper value if application didn't set it.
+		 */
+		{
+			float *ref;
+			if (!TIFFGetField(tif, TIFFTAG_REFERENCEBLACKWHITE,
+					  &ref)) {
+				float refbw[6];
+				long top = 1L << td->td_bitspersample;
+				refbw[0] = 0;
+				refbw[1] = (float)(top-1L);
+				refbw[2] = (float)(top>>1);
+				refbw[3] = refbw[1];
+				refbw[4] = refbw[2];
+				refbw[5] = refbw[1];
+				TIFFSetField(tif, TIFFTAG_REFERENCEBLACKWHITE,
+					     refbw);
+			}
+		}
+		break;
+	case PHOTOMETRIC_PALETTE:		/* disallowed by Tech Note */
+	case PHOTOMETRIC_MASK:
+		TIFFErrorExt(tif->tif_clientdata, module,
+			  "PhotometricInterpretation %d not allowed for JPEG",
+			  (int) sp->photometric);
+		return (0);
+	default:
+		/* TIFF 6.0 forbids subsampling of all other color spaces */
+		sp->h_sampling = 1;
+		sp->v_sampling = 1;
+		break;
+	}
+
+	/* Verify miscellaneous parameters */
+
+	/*
+	 * This would need work if libtiff ever supports different
+	 * depths for different components, or if libjpeg ever supports
+	 * run-time selection of depth.  Neither is imminent.
+	 */
+#ifdef JPEG_LIB_MK1
+        /* BITS_IN_JSAMPLE now permits 8 and 12 --- dgilbert */
+	if (td->td_bitspersample != 8 && td->td_bitspersample != 12) 
+#else
+	if (td->td_bitspersample != BITS_IN_JSAMPLE )
+#endif
+	{
+		TIFFErrorExt(tif->tif_clientdata, module, "BitsPerSample %d not allowed for JPEG",
+			  (int) td->td_bitspersample);
+		return (0);
+	}
+	sp->cinfo.c.data_precision = td->td_bitspersample;
+#ifdef JPEG_LIB_MK1
+        sp->cinfo.c.bits_in_jsample = td->td_bitspersample;
+#endif
+	if (isTiled(tif)) {
+		if ((td->td_tilelength % (sp->v_sampling * DCTSIZE)) != 0) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+				  "JPEG tile height must be multiple of %d",
+				  sp->v_sampling * DCTSIZE);
+			return (0);
+		}
+		if ((td->td_tilewidth % (sp->h_sampling * DCTSIZE)) != 0) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+				  "JPEG tile width must be multiple of %d",
+				  sp->h_sampling * DCTSIZE);
+			return (0);
+		}
+	} else {
+		if (td->td_rowsperstrip < td->td_imagelength &&
+		    (td->td_rowsperstrip % (sp->v_sampling * DCTSIZE)) != 0) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+				  "RowsPerStrip must be multiple of %d for JPEG",
+				  sp->v_sampling * DCTSIZE);
+			return (0);
+		}
+	}
+
+	/* Create a JPEGTables field if appropriate */
+	if (sp->jpegtablesmode & (JPEGTABLESMODE_QUANT|JPEGTABLESMODE_HUFF)) {
+		if (!prepare_JPEGTables(tif))
+			return (0);
+		/* Mark the field present */
+		/* Can't use TIFFSetField since BEENWRITING is already set! */
+		TIFFSetFieldBit(tif, FIELD_JPEGTABLES);
+		tif->tif_flags |= TIFF_DIRTYDIRECT;
+	} else {
+		/* We do not support application-supplied JPEGTables, */
+		/* so mark the field not present */
+		TIFFClrFieldBit(tif, FIELD_JPEGTABLES);
+	}
+
+	/* Direct libjpeg output to libtiff's output buffer */
+	TIFFjpeg_data_dest(sp, tif);
+
+	return (1);
+}
+
+/*
+ * Set encoding state at the start of a strip or tile.
+ */
+static int
+JPEGPreEncode(TIFF* tif, tsample_t s)
+{
+	JPEGState *sp = JState(tif);
+	TIFFDirectory *td = &tif->tif_dir;
+	static const char module[] = "JPEGPreEncode";
+	uint32 segment_width, segment_height;
+	int downsampled_input;
+
+	assert(sp != NULL);
+	assert(!sp->cinfo.comm.is_decompressor);
+	/*
+	 * Set encoding parameters for this strip/tile.
+	 */
+	if (isTiled(tif)) {
+		segment_width = td->td_tilewidth;
+		segment_height = td->td_tilelength;
+		sp->bytesperline = TIFFTileRowSize(tif);
+	} else {
+		segment_width = td->td_imagewidth;
+		segment_height = td->td_imagelength - tif->tif_row;
+		if (segment_height > td->td_rowsperstrip)
+			segment_height = td->td_rowsperstrip;
+		sp->bytesperline = TIFFOldScanlineSize(tif);
+	}
+	if (td->td_planarconfig == PLANARCONFIG_SEPARATE && s > 0) {
+		/* for PC 2, scale down the strip/tile size
+		 * to match a downsampled component
+		 */
+		segment_width = TIFFhowmany(segment_width, sp->h_sampling);
+		segment_height = TIFFhowmany(segment_height, sp->v_sampling);
+	}
+	if (segment_width > 65535 || segment_height > 65535) {
+		TIFFErrorExt(tif->tif_clientdata, module, "Strip/tile too large for JPEG");
+		return (0);
+	}
+	sp->cinfo.c.image_width = segment_width;
+	sp->cinfo.c.image_height = segment_height;
+	downsampled_input = FALSE;
+	if (td->td_planarconfig == PLANARCONFIG_CONTIG) {
+		sp->cinfo.c.input_components = td->td_samplesperpixel;
+		if (sp->photometric == PHOTOMETRIC_YCBCR) {
+			if (sp->jpegcolormode == JPEGCOLORMODE_RGB) {
+				sp->cinfo.c.in_color_space = JCS_RGB;
+			} else {
+				sp->cinfo.c.in_color_space = JCS_YCbCr;
+				if (sp->h_sampling != 1 || sp->v_sampling != 1)
+					downsampled_input = TRUE;
+			}
+			if (!TIFFjpeg_set_colorspace(sp, JCS_YCbCr))
+				return (0);
+			/*
+			 * Set Y sampling factors;
+			 * we assume jpeg_set_colorspace() set the rest to 1
+			 */
+			sp->cinfo.c.comp_info[0].h_samp_factor = sp->h_sampling;
+			sp->cinfo.c.comp_info[0].v_samp_factor = sp->v_sampling;
+		} else {
+			sp->cinfo.c.in_color_space = JCS_UNKNOWN;
+			if (!TIFFjpeg_set_colorspace(sp, JCS_UNKNOWN))
+				return (0);
+			/* jpeg_set_colorspace set all sampling factors to 1 */
+		}
+	} else {
+		sp->cinfo.c.input_components = 1;
+		sp->cinfo.c.in_color_space = JCS_UNKNOWN;
+		if (!TIFFjpeg_set_colorspace(sp, JCS_UNKNOWN))
+			return (0);
+		sp->cinfo.c.comp_info[0].component_id = s;
+		/* jpeg_set_colorspace() set sampling factors to 1 */
+		if (sp->photometric == PHOTOMETRIC_YCBCR && s > 0) {
+			sp->cinfo.c.comp_info[0].quant_tbl_no = 1;
+			sp->cinfo.c.comp_info[0].dc_tbl_no = 1;
+			sp->cinfo.c.comp_info[0].ac_tbl_no = 1;
+		}
+	}
+	/* ensure libjpeg won't write any extraneous markers */
+	sp->cinfo.c.write_JFIF_header = FALSE;
+	sp->cinfo.c.write_Adobe_marker = FALSE;
+	/* set up table handling correctly */
+	if (! (sp->jpegtablesmode & JPEGTABLESMODE_QUANT)) {
+		if (!TIFFjpeg_set_quality(sp, sp->jpegquality, FALSE))
+			return (0);
+		unsuppress_quant_table(sp, 0);
+		unsuppress_quant_table(sp, 1);
+	}
+	if (sp->jpegtablesmode & JPEGTABLESMODE_HUFF)
+		sp->cinfo.c.optimize_coding = FALSE;
+	else
+		sp->cinfo.c.optimize_coding = TRUE;
+	if (downsampled_input) {
+		/* Need to use raw-data interface to libjpeg */
+		sp->cinfo.c.raw_data_in = TRUE;
+		tif->tif_encoderow = JPEGEncodeRaw;
+		tif->tif_encodestrip = JPEGEncodeRaw;
+		tif->tif_encodetile = JPEGEncodeRaw;
+	} else {
+		/* Use normal interface to libjpeg */
+		sp->cinfo.c.raw_data_in = FALSE;
+		tif->tif_encoderow = JPEGEncode;
+		tif->tif_encodestrip = JPEGEncode;
+		tif->tif_encodetile = JPEGEncode;
+	}
+	/* Start JPEG compressor */
+	if (!TIFFjpeg_start_compress(sp, FALSE))
+		return (0);
+	/* Allocate downsampled-data buffers if needed */
+	if (downsampled_input) {
+		if (!alloc_downsampled_buffers(tif, sp->cinfo.c.comp_info,
+					       sp->cinfo.c.num_components))
+			return (0);
+	}
+	sp->scancount = 0;
+
+	return (1);
+}
+
+/*
+ * Encode a chunk of pixels.
+ * "Standard" case: incoming data is not downsampled.
+ */
+static int
+JPEGEncode(TIFF* tif, tidata_t buf, tsize_t cc, tsample_t s)
+{
+	JPEGState *sp = JState(tif);
+	tsize_t nrows;
+	JSAMPROW bufptr[1];
+
+	(void) s;
+	assert(sp != NULL);
+	/* data is expected to be supplied in multiples of a scanline */
+	nrows = cc / sp->bytesperline;
+	if (cc % sp->bytesperline)
+		TIFFWarningExt(tif->tif_clientdata, tif->tif_name, "fractional scanline discarded");
+
+	while (nrows-- > 0) {
+		bufptr[0] = (JSAMPROW) buf;
+		if (TIFFjpeg_write_scanlines(sp, bufptr, 1) != 1)
+			return (0);
+		if (nrows > 0)
+			tif->tif_row++;
+		buf += sp->bytesperline;
+	}
+	return (1);
+}
+
+/*
+ * Encode a chunk of pixels.
+ * Incoming data is expected to be downsampled per sampling factors.
+ */
+static int
+JPEGEncodeRaw(TIFF* tif, tidata_t buf, tsize_t cc, tsample_t s)
+{
+	JPEGState *sp = JState(tif);
+	JSAMPLE* inptr;
+	JSAMPLE* outptr;
+	tsize_t nrows;
+	JDIMENSION clumps_per_line, nclump;
+	int clumpoffset, ci, xpos, ypos;
+	jpeg_component_info* compptr;
+	int samples_per_clump = sp->samplesperclump;
+	tsize_t bytesperclumpline;
+
+	(void) s;
+	assert(sp != NULL);
+	/* data is expected to be supplied in multiples of a clumpline */
+	/* a clumpline is equivalent to v_sampling desubsampled scanlines */
+	/* TODO: the following calculation of bytesperclumpline, should substitute calculation of sp->bytesperline, except that it is per v_sampling lines */
+	bytesperclumpline = (((sp->cinfo.c.image_width+sp->h_sampling-1)/sp->h_sampling)
+			     *(sp->h_sampling*sp->v_sampling+2)*sp->cinfo.c.data_precision+7)
+			    /8;
+
+	nrows = ( cc / bytesperclumpline ) * sp->v_sampling;
+	if (cc % bytesperclumpline)
+		TIFFWarningExt(tif->tif_clientdata, tif->tif_name, "fractional scanline discarded");
+
+	/* Cb,Cr both have sampling factors 1, so this is correct */
+	clumps_per_line = sp->cinfo.c.comp_info[1].downsampled_width;
+
+	while (nrows > 0) {
+		/*
+		 * Fastest way to separate the data is to make one pass
+		 * over the scanline for each row of each component.
+		 */
+		clumpoffset = 0;		/* first sample in clump */
+		for (ci = 0, compptr = sp->cinfo.c.comp_info;
+		     ci < sp->cinfo.c.num_components;
+		     ci++, compptr++) {
+		    int hsamp = compptr->h_samp_factor;
+		    int vsamp = compptr->v_samp_factor;
+		    int padding = (int) (compptr->width_in_blocks * DCTSIZE -
+					 clumps_per_line * hsamp);
+		    for (ypos = 0; ypos < vsamp; ypos++) {
+			inptr = ((JSAMPLE*) buf) + clumpoffset;
+			outptr = sp->ds_buffer[ci][sp->scancount*vsamp + ypos];
+			if (hsamp == 1) {
+			    /* fast path for at least Cb and Cr */
+			    for (nclump = clumps_per_line; nclump-- > 0; ) {
+				*outptr++ = inptr[0];
+				inptr += samples_per_clump;
+			    }
+			} else {
+			    /* general case */
+			    for (nclump = clumps_per_line; nclump-- > 0; ) {
+				for (xpos = 0; xpos < hsamp; xpos++)
+				    *outptr++ = inptr[xpos];
+				inptr += samples_per_clump;
+			    }
+			}
+			/* pad each scanline as needed */
+			for (xpos = 0; xpos < padding; xpos++) {
+			    *outptr = outptr[-1];
+			    outptr++;
+			}
+			clumpoffset += hsamp;
+		    }
+		}
+		sp->scancount++;
+		if (sp->scancount >= DCTSIZE) {
+			int n = sp->cinfo.c.max_v_samp_factor * DCTSIZE;
+			if (TIFFjpeg_write_raw_data(sp, sp->ds_buffer, n) != n)
+				return (0);
+			sp->scancount = 0;
+		}
+		tif->tif_row += sp->v_sampling;
+		buf += sp->bytesperline;
+		nrows -= sp->v_sampling;
+	}
+	return (1);
+}
+
+/*
+ * Finish up at the end of a strip or tile.
+ */
+static int
+JPEGPostEncode(TIFF* tif)
+{
+	JPEGState *sp = JState(tif);
+
+	if (sp->scancount > 0) {
+		/*
+		 * Need to emit a partial bufferload of downsampled data.
+		 * Pad the data vertically.
+		 */
+		int ci, ypos, n;
+		jpeg_component_info* compptr;
+
+		for (ci = 0, compptr = sp->cinfo.c.comp_info;
+		     ci < sp->cinfo.c.num_components;
+		     ci++, compptr++) {
+			int vsamp = compptr->v_samp_factor;
+			tsize_t row_width = compptr->width_in_blocks * DCTSIZE
+				* sizeof(JSAMPLE);
+			for (ypos = sp->scancount * vsamp;
+			     ypos < DCTSIZE * vsamp; ypos++) {
+				_TIFFmemcpy((tdata_t)sp->ds_buffer[ci][ypos],
+					    (tdata_t)sp->ds_buffer[ci][ypos-1],
+					    row_width);
+
+			}
+		}
+		n = sp->cinfo.c.max_v_samp_factor * DCTSIZE;
+		if (TIFFjpeg_write_raw_data(sp, sp->ds_buffer, n) != n)
+			return (0);
+	}
+
+	return (TIFFjpeg_finish_compress(JState(tif)));
+}
+
+static void
+JPEGCleanup(TIFF* tif)
+{
+	JPEGState *sp = JState(tif);
+	
+	assert(sp != 0);
+
+	tif->tif_tagmethods.vgetfield = sp->vgetparent;
+	tif->tif_tagmethods.vsetfield = sp->vsetparent;
+
+	if( sp->cinfo_initialized )
+	    TIFFjpeg_destroy(sp);	/* release libjpeg resources */
+	if (sp->jpegtables)		/* tag value */
+		_TIFFfree(sp->jpegtables);
+	_TIFFfree(tif->tif_data);	/* release local state */
+	tif->tif_data = NULL;
+
+	_TIFFSetDefaultCompressionState(tif);
+}
+
+static void 
+JPEGResetUpsampled( TIFF* tif )
+{
+    JPEGState* sp = JState(tif);
+    TIFFDirectory* td = &tif->tif_dir;
+
+    /*
+     * Mark whether returned data is up-sampled or not
+     * so TIFFStripSize and TIFFTileSize return values
+     * that reflect the true amount of data.
+     */
+    tif->tif_flags &= ~TIFF_UPSAMPLED;
+    if (td->td_planarconfig == PLANARCONFIG_CONTIG) {
+        if (td->td_photometric == PHOTOMETRIC_YCBCR &&
+            sp->jpegcolormode == JPEGCOLORMODE_RGB) {
+            tif->tif_flags |= TIFF_UPSAMPLED;
+        } else {
+#ifdef notdef
+            if (td->td_ycbcrsubsampling[0] != 1 ||
+                td->td_ycbcrsubsampling[1] != 1)
+                ; /* XXX what about up-sampling? */
+#endif
+        }
+    }
+
+    /*
+     * Must recalculate cached tile size
+     * in case sampling state changed.
+     *
+     * Should we really be doing this now if image size isn't set? 
+     */
+    tif->tif_tilesize = isTiled(tif) ? TIFFTileSize(tif) : (tsize_t) -1;
+}
+
+static int
+JPEGVSetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+	JPEGState* sp = JState(tif);
+	uint32 v32;
+
+	assert(sp != NULL);
+
+	switch (tag) {
+	case TIFFTAG_JPEGTABLES:
+		v32 = va_arg(ap, uint32);
+		if (v32 == 0) {
+			/* XXX */
+			return (0);
+		}
+		_TIFFsetByteArray(&sp->jpegtables, va_arg(ap, void*),
+		    (long) v32);
+		sp->jpegtables_length = v32;
+		TIFFSetFieldBit(tif, FIELD_JPEGTABLES);
+		break;
+	case TIFFTAG_JPEGQUALITY:
+		sp->jpegquality = va_arg(ap, int);
+		return (1);			/* pseudo tag */
+	case TIFFTAG_JPEGCOLORMODE:
+		sp->jpegcolormode = va_arg(ap, int);
+                JPEGResetUpsampled( tif );
+		return (1);			/* pseudo tag */
+	case TIFFTAG_PHOTOMETRIC:
+        {
+                int ret_value = (*sp->vsetparent)(tif, tag, ap);
+                JPEGResetUpsampled( tif );
+                return ret_value;
+        }
+	case TIFFTAG_JPEGTABLESMODE:
+		sp->jpegtablesmode = va_arg(ap, int);
+		return (1);			/* pseudo tag */
+	case TIFFTAG_YCBCRSUBSAMPLING:
+                /* mark the fact that we have a real ycbcrsubsampling! */
+		sp->ycbcrsampling_fetched = 1;
+                /* should we be recomputing upsampling info here? */
+		return (*sp->vsetparent)(tif, tag, ap);
+	case TIFFTAG_FAXRECVPARAMS:
+		sp->recvparams = va_arg(ap, uint32);
+		break;
+	case TIFFTAG_FAXSUBADDRESS:
+		_TIFFsetString(&sp->subaddress, va_arg(ap, char*));
+		break;
+	case TIFFTAG_FAXRECVTIME:
+		sp->recvtime = va_arg(ap, uint32);
+		break;
+	case TIFFTAG_FAXDCS:
+		_TIFFsetString(&sp->faxdcs, va_arg(ap, char*));
+		break;
+	default:
+		return (*sp->vsetparent)(tif, tag, ap);
+	}
+	TIFFSetFieldBit(tif, _TIFFFieldWithTag(tif, tag)->field_bit);
+	tif->tif_flags |= TIFF_DIRTYDIRECT;
+	return (1);
+}
+
+/*
+ * Some JPEG-in-TIFF produces do not emit the YCBCRSUBSAMPLING values in
+ * the TIFF tags, but still use non-default (2,2) values within the jpeg
+ * data stream itself.  In order for TIFF applications to work properly
+ * - for instance to get the strip buffer size right - it is imperative
+ * that the subsampling be available before we start reading the image
+ * data normally.  This function will attempt to load the first strip in
+ * order to get the sampling values from the jpeg data stream.  Various
+ * hacks are various places are done to ensure this function gets called
+ * before the td_ycbcrsubsampling values are used from the directory structure,
+ * including calling TIFFGetField() for the YCBCRSUBSAMPLING field from 
+ * TIFFStripSize(), and the printing code in tif_print.c. 
+ *
+ * Note that JPEGPreDeocode() will produce a fairly loud warning when the
+ * discovered sampling does not match the default sampling (2,2) or whatever
+ * was actually in the tiff tags. 
+ *
+ * Problems:
+ *  o This code will cause one whole strip/tile of compressed data to be
+ *    loaded just to get the tags right, even if the imagery is never read.
+ *    It would be more efficient to just load a bit of the header, and
+ *    initialize things from that. 
+ *
+ * See the bug in bugzilla for details:
+ *
+ * http://bugzilla.remotesensing.org/show_bug.cgi?id=168
+ *
+ * Frank Warmerdam, July 2002
+ */
+
+static void 
+JPEGFixupTestSubsampling( TIFF * tif )
+{
+#ifdef CHECK_JPEG_YCBCR_SUBSAMPLING
+    JPEGState *sp = JState(tif);
+    TIFFDirectory *td = &tif->tif_dir;
+
+    JPEGInitializeLibJPEG( tif, 0, 0 );
+
+    /*
+     * Some JPEG-in-TIFF files don't provide the ycbcrsampling tags, 
+     * and use a sampling schema other than the default 2,2.  To handle
+     * this we actually have to scan the header of a strip or tile of
+     * jpeg data to get the sampling.  
+     */
+    if( !sp->cinfo.comm.is_decompressor 
+        || sp->ycbcrsampling_fetched  
+        || td->td_photometric != PHOTOMETRIC_YCBCR )
+        return;
+
+    sp->ycbcrsampling_fetched = 1;
+    if( TIFFIsTiled( tif ) )
+    {
+        if( !TIFFFillTile( tif, 0 ) )
+			return;
+    }
+    else
+	{
+        if( !TIFFFillStrip( tif, 0 ) )
+            return;
+    }
+
+    TIFFSetField( tif, TIFFTAG_YCBCRSUBSAMPLING, 
+                  (uint16) sp->h_sampling, (uint16) sp->v_sampling );
+#endif /* CHECK_JPEG_YCBCR_SUBSAMPLING */
+}
+
+static int
+JPEGVGetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+	JPEGState* sp = JState(tif);
+
+	assert(sp != NULL);
+
+	switch (tag) {
+		case TIFFTAG_JPEGTABLES:
+			*va_arg(ap, uint32*) = sp->jpegtables_length;
+			*va_arg(ap, void**) = sp->jpegtables;
+			break;
+		case TIFFTAG_JPEGQUALITY:
+			*va_arg(ap, int*) = sp->jpegquality;
+			break;
+		case TIFFTAG_JPEGCOLORMODE:
+			*va_arg(ap, int*) = sp->jpegcolormode;
+			break;
+		case TIFFTAG_JPEGTABLESMODE:
+			*va_arg(ap, int*) = sp->jpegtablesmode;
+			break;
+		case TIFFTAG_YCBCRSUBSAMPLING:
+			JPEGFixupTestSubsampling( tif );
+			return (*sp->vgetparent)(tif, tag, ap);
+		case TIFFTAG_FAXRECVPARAMS:
+			*va_arg(ap, uint32*) = sp->recvparams;
+			break;
+		case TIFFTAG_FAXSUBADDRESS:
+			*va_arg(ap, char**) = sp->subaddress;
+			break;
+		case TIFFTAG_FAXRECVTIME:
+			*va_arg(ap, uint32*) = sp->recvtime;
+			break;
+		case TIFFTAG_FAXDCS:
+			*va_arg(ap, char**) = sp->faxdcs;
+			break;
+		default:
+			return (*sp->vgetparent)(tif, tag, ap);
+	}
+	return (1);
+}
+
+static void
+JPEGPrintDir(TIFF* tif, FILE* fd, long flags)
+{
+	JPEGState* sp = JState(tif);
+
+	assert(sp != NULL);
+
+	(void) flags;
+	if (TIFFFieldSet(tif,FIELD_JPEGTABLES))
+		fprintf(fd, "  JPEG Tables: (%lu bytes)\n",
+			(unsigned long) sp->jpegtables_length);
+        if (TIFFFieldSet(tif,FIELD_RECVPARAMS))
+                fprintf(fd, "  Fax Receive Parameters: %08lx\n",
+                   (unsigned long) sp->recvparams);
+        if (TIFFFieldSet(tif,FIELD_SUBADDRESS))
+                fprintf(fd, "  Fax SubAddress: %s\n", sp->subaddress);
+        if (TIFFFieldSet(tif,FIELD_RECVTIME))
+                fprintf(fd, "  Fax Receive Time: %lu secs\n",
+                    (unsigned long) sp->recvtime);
+        if (TIFFFieldSet(tif,FIELD_FAXDCS))
+                fprintf(fd, "  Fax DCS: %s\n", sp->faxdcs);
+}
+
+static uint32
+JPEGDefaultStripSize(TIFF* tif, uint32 s)
+{
+	JPEGState* sp = JState(tif);
+	TIFFDirectory *td = &tif->tif_dir;
+
+	s = (*sp->defsparent)(tif, s);
+	if (s < td->td_imagelength)
+		s = TIFFroundup(s, td->td_ycbcrsubsampling[1] * DCTSIZE);
+	return (s);
+}
+
+static void
+JPEGDefaultTileSize(TIFF* tif, uint32* tw, uint32* th)
+{
+	JPEGState* sp = JState(tif);
+	TIFFDirectory *td = &tif->tif_dir;
+
+	(*sp->deftparent)(tif, tw, th);
+	*tw = TIFFroundup(*tw, td->td_ycbcrsubsampling[0] * DCTSIZE);
+	*th = TIFFroundup(*th, td->td_ycbcrsubsampling[1] * DCTSIZE);
+}
+
+/*
+ * The JPEG library initialized used to be done in TIFFInitJPEG(), but
+ * now that we allow a TIFF file to be opened in update mode it is necessary
+ * to have some way of deciding whether compression or decompression is
+ * desired other than looking at tif->tif_mode.  We accomplish this by 
+ * examining {TILE/STRIP}BYTECOUNTS to see if there is a non-zero entry.
+ * If so, we assume decompression is desired. 
+ *
+ * This is tricky, because TIFFInitJPEG() is called while the directory is
+ * being read, and generally speaking the BYTECOUNTS tag won't have been read
+ * at that point.  So we try to defer jpeg library initialization till we
+ * do have that tag ... basically any access that might require the compressor
+ * or decompressor that occurs after the reading of the directory. 
+ *
+ * In an ideal world compressors or decompressors would be setup
+ * at the point where a single tile or strip was accessed (for read or write)
+ * so that stuff like update of missing tiles, or replacement of tiles could
+ * be done. However, we aren't trying to crack that nut just yet ...
+ *
+ * NFW, Feb 3rd, 2003.
+ */
+
+static int JPEGInitializeLibJPEG( TIFF * tif, int force_encode, int force_decode )
+{
+    JPEGState* sp = JState(tif);
+    uint32 *byte_counts = NULL;
+    int     data_is_empty = TRUE;
+    int     decompress;
+
+    if( sp->cinfo_initialized )
+        return 1;
+
+    /*
+     * Do we have tile data already?  Make sure we initialize the
+     * the state in decompressor mode if we have tile data, even if we
+     * are not in read-only file access mode. 
+     */
+    if( TIFFIsTiled( tif ) 
+        && TIFFGetField( tif, TIFFTAG_TILEBYTECOUNTS, &byte_counts ) 
+        && byte_counts != NULL )
+    {
+        data_is_empty = byte_counts[0] == 0;
+    }
+    if( !TIFFIsTiled( tif ) 
+        && TIFFGetField( tif, TIFFTAG_STRIPBYTECOUNTS, &byte_counts) 
+        && byte_counts != NULL )
+    {
+        data_is_empty = byte_counts[0] == 0;
+    }
+
+    if( force_decode )
+        decompress = 1;
+    else if( force_encode )
+        decompress = 0;
+    else if( tif->tif_mode == O_RDONLY )
+        decompress = 1;
+    else if( data_is_empty )
+        decompress = 0;
+    else
+        decompress = 1;
+
+    /*
+     * Initialize libjpeg.
+     */
+    if ( decompress ) {
+        if (!TIFFjpeg_create_decompress(sp))
+            return (0);
+
+    } else {
+        if (!TIFFjpeg_create_compress(sp))
+            return (0);
+    }
+
+    sp->cinfo_initialized = TRUE;
+
+    return 1;
+}
+
+int
+TIFFInitJPEG(TIFF* tif, int scheme)
+{
+	JPEGState* sp;
+
+	assert(scheme == COMPRESSION_JPEG);
+
+	/*
+	 * Allocate state block so tag methods have storage to record values.
+	 */
+	tif->tif_data = (tidata_t) _TIFFmalloc(sizeof (JPEGState));
+
+	if (tif->tif_data == NULL) {
+		TIFFErrorExt(tif->tif_clientdata, "TIFFInitJPEG", "No space for JPEG state block");
+		return (0);
+	}
+        _TIFFmemset( tif->tif_data, 0, sizeof(JPEGState));
+
+	sp = JState(tif);
+	sp->tif = tif;				/* back link */
+
+	/*
+	 * Merge codec-specific tag information and override parent get/set
+	 * field methods.
+	 */
+	_TIFFMergeFieldInfo(tif, jpegFieldInfo, N(jpegFieldInfo));
+	sp->vgetparent = tif->tif_tagmethods.vgetfield;
+	tif->tif_tagmethods.vgetfield = JPEGVGetField; /* hook for codec tags */
+	sp->vsetparent = tif->tif_tagmethods.vsetfield;
+	tif->tif_tagmethods.vsetfield = JPEGVSetField; /* hook for codec tags */
+	tif->tif_tagmethods.printdir = JPEGPrintDir;   /* hook for codec tags */
+
+	/* Default values for codec-specific fields */
+	sp->jpegtables = NULL;
+	sp->jpegtables_length = 0;
+	sp->jpegquality = 75;			/* Default IJG quality */
+	sp->jpegcolormode = JPEGCOLORMODE_RAW;
+	sp->jpegtablesmode = JPEGTABLESMODE_QUANT | JPEGTABLESMODE_HUFF;
+
+        sp->recvparams = 0;
+        sp->subaddress = NULL;
+        sp->faxdcs = NULL;
+
+        sp->ycbcrsampling_fetched = 0;
+
+	/*
+	 * Install codec methods.
+	 */
+	tif->tif_setupdecode = JPEGSetupDecode;
+	tif->tif_predecode = JPEGPreDecode;
+	tif->tif_decoderow = JPEGDecode;
+	tif->tif_decodestrip = JPEGDecode;
+	tif->tif_decodetile = JPEGDecode;
+	tif->tif_setupencode = JPEGSetupEncode;
+	tif->tif_preencode = JPEGPreEncode;
+	tif->tif_postencode = JPEGPostEncode;
+	tif->tif_encoderow = JPEGEncode;
+	tif->tif_encodestrip = JPEGEncode;
+	tif->tif_encodetile = JPEGEncode;
+	tif->tif_cleanup = JPEGCleanup;
+	sp->defsparent = tif->tif_defstripsize;
+	tif->tif_defstripsize = JPEGDefaultStripSize;
+	sp->deftparent = tif->tif_deftilesize;
+	tif->tif_deftilesize = JPEGDefaultTileSize;
+	tif->tif_flags |= TIFF_NOBITREV;	/* no bit reversal, please */
+
+        sp->cinfo_initialized = FALSE;
+
+	/*
+        ** Create a JPEGTables field if no directory has yet been created. 
+        ** We do this just to ensure that sufficient space is reserved for
+        ** the JPEGTables field.  It will be properly created the right
+        ** size later. 
+        */
+        if( tif->tif_diroff == 0 )
+        {
+#define SIZE_OF_JPEGTABLES 2000
+            TIFFSetFieldBit(tif, FIELD_JPEGTABLES);
+            sp->jpegtables_length = SIZE_OF_JPEGTABLES;
+            sp->jpegtables = (void *) _TIFFmalloc(sp->jpegtables_length);
+	    _TIFFmemset(sp->jpegtables, 0, SIZE_OF_JPEGTABLES);
+#undef SIZE_OF_JPEGTABLES
+        }
+
+        /*
+         * Mark the TIFFTAG_YCBCRSAMPLES as present even if it is not
+         * see: JPEGFixupTestSubsampling().
+         */
+        TIFFSetFieldBit( tif, FIELD_YCBCRSUBSAMPLING );
+
+	return 1;
+}
+#endif /* JPEG_SUPPORT */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_luv.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_luv.c
new file mode 100644
index 0000000000..7b769c44ca
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_luv.c
@@ -0,0 +1,1606 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1997 Greg Ward Larson
+ * Copyright (c) 1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler, Greg Larson and Silicon Graphics may not be used in any
+ * advertising or publicity relating to the software without the specific,
+ * prior written permission of Sam Leffler, Greg Larson and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER, GREG LARSON OR SILICON GRAPHICS BE LIABLE
+ * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#include "tiffiop.h"
+#ifdef LOGLUV_SUPPORT
+
+/*
+ * TIFF Library.
+ * LogLuv compression support for high dynamic range images.
+ *
+ * Contributed by Greg Larson.
+ *
+ * LogLuv image support uses the TIFF library to store 16 or 10-bit
+ * log luminance values with 8 bits each of u and v or a 14-bit index.
+ *
+ * The codec can take as input and produce as output 32-bit IEEE float values 
+ * as well as 16-bit integer values.  A 16-bit luminance is interpreted
+ * as a sign bit followed by a 15-bit integer that is converted
+ * to and from a linear magnitude using the transformation:
+ *
+ *	L = 2^( (Le+.5)/256 - 64 )		# real from 15-bit
+ *
+ *	Le = floor( 256*(log2(L) + 64) )	# 15-bit from real
+ *
+ * The actual conversion to world luminance units in candelas per sq. meter
+ * requires an additional multiplier, which is stored in the TIFFTAG_STONITS.
+ * This value is usually set such that a reasonable exposure comes from
+ * clamping decoded luminances above 1 to 1 in the displayed image.
+ *
+ * The 16-bit values for u and v may be converted to real values by dividing
+ * each by 32768.  (This allows for negative values, which aren't useful as
+ * far as we know, but are left in case of future improvements in human
+ * color vision.)
+ *
+ * Conversion from (u,v), which is actually the CIE (u',v') system for
+ * you color scientists, is accomplished by the following transformation:
+ *
+ *	u = 4*x / (-2*x + 12*y + 3)
+ *	v = 9*y / (-2*x + 12*y + 3)
+ *
+ *	x = 9*u / (6*u - 16*v + 12)
+ *	y = 4*v / (6*u - 16*v + 12)
+ *
+ * This process is greatly simplified by passing 32-bit IEEE floats
+ * for each of three CIE XYZ coordinates.  The codec then takes care
+ * of conversion to and from LogLuv, though the application is still
+ * responsible for interpreting the TIFFTAG_STONITS calibration factor.
+ *
+ * By definition, a CIE XYZ vector of [1 1 1] corresponds to a neutral white
+ * point of (x,y)=(1/3,1/3).  However, most color systems assume some other
+ * white point, such as D65, and an absolute color conversion to XYZ then
+ * to another color space with a different white point may introduce an
+ * unwanted color cast to the image.  It is often desirable, therefore, to
+ * perform a white point conversion that maps the input white to [1 1 1]
+ * in XYZ, then record the original white point using the TIFFTAG_WHITEPOINT
+ * tag value.  A decoder that demands absolute color calibration may use
+ * this white point tag to get back the original colors, but usually it
+ * will be ignored and the new white point will be used instead that
+ * matches the output color space.
+ *
+ * Pixel information is compressed into one of two basic encodings, depending
+ * on the setting of the compression tag, which is one of COMPRESSION_SGILOG
+ * or COMPRESSION_SGILOG24.  For COMPRESSION_SGILOG, greyscale data is
+ * stored as:
+ *
+ *	 1       15
+ *	|-+---------------|
+ *
+ * COMPRESSION_SGILOG color data is stored as:
+ *
+ *	 1       15           8        8
+ *	|-+---------------|--------+--------|
+ *	 S       Le           ue       ve
+ *
+ * For the 24-bit COMPRESSION_SGILOG24 color format, the data is stored as:
+ *
+ *	     10           14
+ *	|----------|--------------|
+ *	     Le'          Ce
+ *
+ * There is no sign bit in the 24-bit case, and the (u,v) chromaticity is
+ * encoded as an index for optimal color resolution.  The 10 log bits are
+ * defined by the following conversions:
+ *
+ *	L = 2^((Le'+.5)/64 - 12)		# real from 10-bit
+ *
+ *	Le' = floor( 64*(log2(L) + 12) )	# 10-bit from real
+ *
+ * The 10 bits of the smaller format may be converted into the 15 bits of
+ * the larger format by multiplying by 4 and adding 13314.  Obviously,
+ * a smaller range of magnitudes is covered (about 5 orders of magnitude
+ * instead of 38), and the lack of a sign bit means that negative luminances
+ * are not allowed.  (Well, they aren't allowed in the real world, either,
+ * but they are useful for certain types of image processing.)
+ *
+ * The desired user format is controlled by the setting the internal
+ * pseudo tag TIFFTAG_SGILOGDATAFMT to one of:
+ *  SGILOGDATAFMT_FLOAT       = IEEE 32-bit float XYZ values
+ *  SGILOGDATAFMT_16BIT	      = 16-bit integer encodings of logL, u and v
+ * Raw data i/o is also possible using:
+ *  SGILOGDATAFMT_RAW         = 32-bit unsigned integer with encoded pixel
+ * In addition, the following decoding is provided for ease of display:
+ *  SGILOGDATAFMT_8BIT        = 8-bit default RGB gamma-corrected values
+ *
+ * For grayscale images, we provide the following data formats:
+ *  SGILOGDATAFMT_FLOAT       = IEEE 32-bit float Y values
+ *  SGILOGDATAFMT_16BIT       = 16-bit integer w/ encoded luminance
+ *  SGILOGDATAFMT_8BIT        = 8-bit gray monitor values
+ *
+ * Note that the COMPRESSION_SGILOG applies a simple run-length encoding
+ * scheme by separating the logL, u and v bytes for each row and applying
+ * a PackBits type of compression.  Since the 24-bit encoding is not
+ * adaptive, the 32-bit color format takes less space in many cases.
+ *
+ * Further control is provided over the conversion from higher-resolution
+ * formats to final encoded values through the pseudo tag
+ * TIFFTAG_SGILOGENCODE:
+ *  SGILOGENCODE_NODITHER     = do not dither encoded values
+ *  SGILOGENCODE_RANDITHER    = apply random dithering during encoding
+ *
+ * The default value of this tag is SGILOGENCODE_NODITHER for
+ * COMPRESSION_SGILOG to maximize run-length encoding and
+ * SGILOGENCODE_RANDITHER for COMPRESSION_SGILOG24 to turn
+ * quantization errors into noise.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+/*
+ * State block for each open TIFF
+ * file using LogLuv compression/decompression.
+ */
+typedef	struct logLuvState LogLuvState;
+
+struct logLuvState {
+	int			user_datafmt;	/* user data format */
+	int			encode_meth;	/* encoding method */
+	int			pixel_size;	/* bytes per pixel */
+
+	tidata_t*		tbuf;		/* translation buffer */
+	int			tbuflen;	/* buffer length */
+	void (*tfunc)(LogLuvState*, tidata_t, int);
+
+	TIFFVSetMethod		vgetparent;	/* super-class method */
+	TIFFVSetMethod		vsetparent;	/* super-class method */
+};
+
+#define	DecoderState(tif)	((LogLuvState*) (tif)->tif_data)
+#define	EncoderState(tif)	((LogLuvState*) (tif)->tif_data)
+
+#define SGILOGDATAFMT_UNKNOWN	-1
+
+#define MINRUN		4	/* minimum run length */
+
+/*
+ * Decode a string of 16-bit gray pixels.
+ */
+static int
+LogL16Decode(TIFF* tif, tidata_t op, tsize_t occ, tsample_t s)
+{
+	LogLuvState* sp = DecoderState(tif);
+	int shft, i, npixels;
+	unsigned char* bp;
+	int16* tp;
+	int16 b;
+	int cc, rc;
+
+	assert(s == 0);
+	assert(sp != NULL);
+
+	npixels = occ / sp->pixel_size;
+
+	if (sp->user_datafmt == SGILOGDATAFMT_16BIT)
+		tp = (int16*) op;
+	else {
+		assert(sp->tbuflen >= npixels);
+		tp = (int16*) sp->tbuf;
+	}
+	_TIFFmemset((tdata_t) tp, 0, npixels*sizeof (tp[0]));
+
+	bp = (unsigned char*) tif->tif_rawcp;
+	cc = tif->tif_rawcc;
+					/* get each byte string */
+	for (shft = 2*8; (shft -= 8) >= 0; ) {
+		for (i = 0; i < npixels && cc > 0; )
+			if (*bp >= 128) {		/* run */
+				rc = *bp++ + (2-128);
+				b = (int16)(*bp++ << shft);
+				cc -= 2;
+				while (rc-- && i < npixels)
+					tp[i++] |= b;
+			} else {			/* non-run */
+				rc = *bp++;		/* nul is noop */
+				while (--cc && rc-- && i < npixels)
+					tp[i++] |= (int16)*bp++ << shft;
+			}
+		if (i != npixels) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		"LogL16Decode: Not enough data at row %d (short %d pixels)",
+			    tif->tif_row, npixels - i);
+			tif->tif_rawcp = (tidata_t) bp;
+			tif->tif_rawcc = cc;
+			return (0);
+		}
+	}
+	(*sp->tfunc)(sp, op, npixels);
+	tif->tif_rawcp = (tidata_t) bp;
+	tif->tif_rawcc = cc;
+	return (1);
+}
+
+/*
+ * Decode a string of 24-bit pixels.
+ */
+static int
+LogLuvDecode24(TIFF* tif, tidata_t op, tsize_t occ, tsample_t s)
+{
+	LogLuvState* sp = DecoderState(tif);
+	int cc, i, npixels;
+	unsigned char* bp;
+	uint32* tp;
+
+	assert(s == 0);
+	assert(sp != NULL);
+
+	npixels = occ / sp->pixel_size;
+
+	if (sp->user_datafmt == SGILOGDATAFMT_RAW)
+		tp = (uint32 *)op;
+	else {
+		assert(sp->tbuflen >= npixels);
+		tp = (uint32 *) sp->tbuf;
+	}
+					/* copy to array of uint32 */
+	bp = (unsigned char*) tif->tif_rawcp;
+	cc = tif->tif_rawcc;
+	for (i = 0; i < npixels && cc > 0; i++) {
+		tp[i] = bp[0] << 16 | bp[1] << 8 | bp[2];
+		bp += 3;
+		cc -= 3;
+	}
+	tif->tif_rawcp = (tidata_t) bp;
+	tif->tif_rawcc = cc;
+	if (i != npixels) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+	    "LogLuvDecode24: Not enough data at row %d (short %d pixels)",
+		    tif->tif_row, npixels - i);
+		return (0);
+	}
+	(*sp->tfunc)(sp, op, npixels);
+	return (1);
+}
+
+/*
+ * Decode a string of 32-bit pixels.
+ */
+static int
+LogLuvDecode32(TIFF* tif, tidata_t op, tsize_t occ, tsample_t s)
+{
+	LogLuvState* sp;
+	int shft, i, npixels;
+	unsigned char* bp;
+	uint32* tp;
+	uint32 b;
+	int cc, rc;
+
+	assert(s == 0);
+	sp = DecoderState(tif);
+	assert(sp != NULL);
+
+	npixels = occ / sp->pixel_size;
+
+	if (sp->user_datafmt == SGILOGDATAFMT_RAW)
+		tp = (uint32*) op;
+	else {
+		assert(sp->tbuflen >= npixels);
+		tp = (uint32*) sp->tbuf;
+	}
+	_TIFFmemset((tdata_t) tp, 0, npixels*sizeof (tp[0]));
+
+	bp = (unsigned char*) tif->tif_rawcp;
+	cc = tif->tif_rawcc;
+					/* get each byte string */
+	for (shft = 4*8; (shft -= 8) >= 0; ) {
+		for (i = 0; i < npixels && cc > 0; )
+			if (*bp >= 128) {		/* run */
+				rc = *bp++ + (2-128);
+				b = (uint32)*bp++ << shft;
+				cc -= 2;
+				while (rc-- && i < npixels)
+					tp[i++] |= b;
+			} else {			/* non-run */
+				rc = *bp++;		/* nul is noop */
+				while (--cc && rc-- && i < npixels)
+					tp[i++] |= (uint32)*bp++ << shft;
+			}
+		if (i != npixels) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		"LogLuvDecode32: Not enough data at row %d (short %d pixels)",
+			    tif->tif_row, npixels - i);
+			tif->tif_rawcp = (tidata_t) bp;
+			tif->tif_rawcc = cc;
+			return (0);
+		}
+	}
+	(*sp->tfunc)(sp, op, npixels);
+	tif->tif_rawcp = (tidata_t) bp;
+	tif->tif_rawcc = cc;
+	return (1);
+}
+
+/*
+ * Decode a strip of pixels.  We break it into rows to
+ * maintain synchrony with the encode algorithm, which
+ * is row by row.
+ */
+static int
+LogLuvDecodeStrip(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s)
+{
+	tsize_t rowlen = TIFFScanlineSize(tif);
+
+	assert(cc%rowlen == 0);
+	while (cc && (*tif->tif_decoderow)(tif, bp, rowlen, s))
+		bp += rowlen, cc -= rowlen;
+	return (cc == 0);
+}
+
+/*
+ * Decode a tile of pixels.  We break it into rows to
+ * maintain synchrony with the encode algorithm, which
+ * is row by row.
+ */
+static int
+LogLuvDecodeTile(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s)
+{
+	tsize_t rowlen = TIFFTileRowSize(tif);
+
+	assert(cc%rowlen == 0);
+	while (cc && (*tif->tif_decoderow)(tif, bp, rowlen, s))
+		bp += rowlen, cc -= rowlen;
+	return (cc == 0);
+}
+
+/*
+ * Encode a row of 16-bit pixels.
+ */
+static int
+LogL16Encode(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s)
+{
+	LogLuvState* sp = EncoderState(tif);
+	int shft, i, j, npixels;
+	tidata_t op;
+	int16* tp;
+	int16 b;
+	int occ, rc=0, mask, beg;
+
+	assert(s == 0);
+	assert(sp != NULL);
+	npixels = cc / sp->pixel_size;
+
+	if (sp->user_datafmt == SGILOGDATAFMT_16BIT)
+		tp = (int16*) bp;
+	else {
+		tp = (int16*) sp->tbuf;
+		assert(sp->tbuflen >= npixels);
+		(*sp->tfunc)(sp, bp, npixels);
+	}
+					/* compress each byte string */
+	op = tif->tif_rawcp;
+	occ = tif->tif_rawdatasize - tif->tif_rawcc;
+	for (shft = 2*8; (shft -= 8) >= 0; )
+		for (i = 0; i < npixels; i += rc) {
+			if (occ < 4) {
+				tif->tif_rawcp = op;
+				tif->tif_rawcc = tif->tif_rawdatasize - occ;
+				if (!TIFFFlushData1(tif))
+					return (-1);
+				op = tif->tif_rawcp;
+				occ = tif->tif_rawdatasize - tif->tif_rawcc;
+			}
+			mask = 0xff << shft;		/* find next run */
+			for (beg = i; beg < npixels; beg += rc) {
+				b = (int16) (tp[beg] & mask);
+				rc = 1;
+				while (rc < 127+2 && beg+rc < npixels &&
+						(tp[beg+rc] & mask) == b)
+					rc++;
+				if (rc >= MINRUN)
+					break;		/* long enough */
+			}
+			if (beg-i > 1 && beg-i < MINRUN) {
+				b = (int16) (tp[i] & mask);/*check short run */
+				j = i+1;
+				while ((tp[j++] & mask) == b)
+                                    if (j == beg) {
+                                        *op++ = (tidataval_t)(128-2+j-i);
+                                        *op++ = (tidataval_t) (b >> shft);
+                                        occ -= 2;
+                                        i = beg;
+                                        break;
+                                    }
+			}
+			while (i < beg) {		/* write out non-run */
+				if ((j = beg-i) > 127) j = 127;
+				if (occ < j+3) {
+                                    tif->tif_rawcp = op;
+                                    tif->tif_rawcc = tif->tif_rawdatasize - occ;
+                                    if (!TIFFFlushData1(tif))
+                                        return (-1);
+                                    op = tif->tif_rawcp;
+                                    occ = tif->tif_rawdatasize - tif->tif_rawcc;
+				}
+				*op++ = (tidataval_t) j; occ--;
+				while (j--) {
+					*op++ = (tidataval_t) (tp[i++] >> shft & 0xff);
+					occ--;
+				}
+			}
+			if (rc >= MINRUN) {		/* write out run */
+				*op++ = (tidataval_t) (128-2+rc);
+				*op++ = (tidataval_t) (tp[beg] >> shft & 0xff);
+				occ -= 2;
+			} else
+				rc = 0;
+		}
+	tif->tif_rawcp = op;
+	tif->tif_rawcc = tif->tif_rawdatasize - occ;
+
+	return (0);
+}
+
+/*
+ * Encode a row of 24-bit pixels.
+ */
+static int
+LogLuvEncode24(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s)
+{
+	LogLuvState* sp = EncoderState(tif);
+	int i, npixels, occ;
+	tidata_t op;
+	uint32* tp;
+
+	assert(s == 0);
+	assert(sp != NULL);
+	npixels = cc / sp->pixel_size;
+
+	if (sp->user_datafmt == SGILOGDATAFMT_RAW)
+		tp = (uint32*) bp;
+	else {
+		tp = (uint32*) sp->tbuf;
+		assert(sp->tbuflen >= npixels);
+		(*sp->tfunc)(sp, bp, npixels);
+	}
+					/* write out encoded pixels */
+	op = tif->tif_rawcp;
+	occ = tif->tif_rawdatasize - tif->tif_rawcc;
+	for (i = npixels; i--; ) {
+		if (occ < 3) {
+			tif->tif_rawcp = op;
+			tif->tif_rawcc = tif->tif_rawdatasize - occ;
+			if (!TIFFFlushData1(tif))
+				return (-1);
+			op = tif->tif_rawcp;
+			occ = tif->tif_rawdatasize - tif->tif_rawcc;
+		}
+		*op++ = (tidataval_t)(*tp >> 16);
+		*op++ = (tidataval_t)(*tp >> 8 & 0xff);
+		*op++ = (tidataval_t)(*tp++ & 0xff);
+		occ -= 3;
+	}
+	tif->tif_rawcp = op;
+	tif->tif_rawcc = tif->tif_rawdatasize - occ;
+
+	return (0);
+}
+
+/*
+ * Encode a row of 32-bit pixels.
+ */
+static int
+LogLuvEncode32(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s)
+{
+	LogLuvState* sp = EncoderState(tif);
+	int shft, i, j, npixels;
+	tidata_t op;
+	uint32* tp;
+	uint32 b;
+	int occ, rc=0, mask, beg;
+
+	assert(s == 0);
+	assert(sp != NULL);
+
+	npixels = cc / sp->pixel_size;
+
+	if (sp->user_datafmt == SGILOGDATAFMT_RAW)
+		tp = (uint32*) bp;
+	else {
+		tp = (uint32*) sp->tbuf;
+		assert(sp->tbuflen >= npixels);
+		(*sp->tfunc)(sp, bp, npixels);
+	}
+					/* compress each byte string */
+	op = tif->tif_rawcp;
+	occ = tif->tif_rawdatasize - tif->tif_rawcc;
+	for (shft = 4*8; (shft -= 8) >= 0; )
+		for (i = 0; i < npixels; i += rc) {
+			if (occ < 4) {
+				tif->tif_rawcp = op;
+				tif->tif_rawcc = tif->tif_rawdatasize - occ;
+				if (!TIFFFlushData1(tif))
+					return (-1);
+				op = tif->tif_rawcp;
+				occ = tif->tif_rawdatasize - tif->tif_rawcc;
+			}
+			mask = 0xff << shft;		/* find next run */
+			for (beg = i; beg < npixels; beg += rc) {
+				b = tp[beg] & mask;
+				rc = 1;
+				while (rc < 127+2 && beg+rc < npixels &&
+						(tp[beg+rc] & mask) == b)
+					rc++;
+				if (rc >= MINRUN)
+					break;		/* long enough */
+			}
+			if (beg-i > 1 && beg-i < MINRUN) {
+				b = tp[i] & mask;	/* check short run */
+				j = i+1;
+				while ((tp[j++] & mask) == b)
+					if (j == beg) {
+						*op++ = (tidataval_t)(128-2+j-i);
+						*op++ = (tidataval_t)(b >> shft);
+						occ -= 2;
+						i = beg;
+						break;
+					}
+			}
+			while (i < beg) {		/* write out non-run */
+				if ((j = beg-i) > 127) j = 127;
+				if (occ < j+3) {
+					tif->tif_rawcp = op;
+					tif->tif_rawcc = tif->tif_rawdatasize - occ;
+					if (!TIFFFlushData1(tif))
+						return (-1);
+					op = tif->tif_rawcp;
+					occ = tif->tif_rawdatasize - tif->tif_rawcc;
+				}
+				*op++ = (tidataval_t) j; occ--;
+				while (j--) {
+					*op++ = (tidataval_t)(tp[i++] >> shft & 0xff);
+					occ--;
+				}
+			}
+			if (rc >= MINRUN) {		/* write out run */
+				*op++ = (tidataval_t) (128-2+rc);
+				*op++ = (tidataval_t)(tp[beg] >> shft & 0xff);
+				occ -= 2;
+			} else
+				rc = 0;
+		}
+	tif->tif_rawcp = op;
+	tif->tif_rawcc = tif->tif_rawdatasize - occ;
+
+	return (0);
+}
+
+/*
+ * Encode a strip of pixels.  We break it into rows to
+ * avoid encoding runs across row boundaries.
+ */
+static int
+LogLuvEncodeStrip(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s)
+{
+	tsize_t rowlen = TIFFScanlineSize(tif);
+
+	assert(cc%rowlen == 0);
+	while (cc && (*tif->tif_encoderow)(tif, bp, rowlen, s) == 0)
+		bp += rowlen, cc -= rowlen;
+	return (cc == 0);
+}
+
+/*
+ * Encode a tile of pixels.  We break it into rows to
+ * avoid encoding runs across row boundaries.
+ */
+static int
+LogLuvEncodeTile(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s)
+{
+	tsize_t rowlen = TIFFTileRowSize(tif);
+
+	assert(cc%rowlen == 0);
+	while (cc && (*tif->tif_encoderow)(tif, bp, rowlen, s) == 0)
+		bp += rowlen, cc -= rowlen;
+	return (cc == 0);
+}
+
+/*
+ * Encode/Decode functions for converting to and from user formats.
+ */
+
+#include "uvcode.h"
+
+#ifndef UVSCALE
+#define U_NEU		0.210526316
+#define V_NEU		0.473684211
+#define UVSCALE		410.
+#endif
+
+#ifndef	M_LN2
+#define M_LN2		0.69314718055994530942
+#endif
+#ifndef M_PI
+#define M_PI		3.14159265358979323846
+#endif
+#define log2(x)		((1./M_LN2)*log(x))
+#define exp2(x)		exp(M_LN2*(x))
+
+#define itrunc(x,m)	((m)==SGILOGENCODE_NODITHER ? \
+				(int)(x) : \
+				(int)((x) + rand()*(1./RAND_MAX) - .5))
+
+#if !LOGLUV_PUBLIC
+static
+#endif
+double
+LogL16toY(int p16)		/* compute luminance from 16-bit LogL */
+{
+	int	Le = p16 & 0x7fff;
+	double	Y;
+
+	if (!Le)
+		return (0.);
+	Y = exp(M_LN2/256.*(Le+.5) - M_LN2*64.);
+	return (!(p16 & 0x8000) ? Y : -Y);
+}
+
+#if !LOGLUV_PUBLIC
+static
+#endif
+int
+LogL16fromY(double Y, int em)	/* get 16-bit LogL from Y */
+{
+	if (Y >= 1.8371976e19)
+		return (0x7fff);
+	if (Y <= -1.8371976e19)
+		return (0xffff);
+	if (Y > 5.4136769e-20)
+		return itrunc(256.*(log2(Y) + 64.), em);
+	if (Y < -5.4136769e-20)
+		return (~0x7fff | itrunc(256.*(log2(-Y) + 64.), em));
+	return (0);
+}
+
+static void
+L16toY(LogLuvState* sp, tidata_t op, int n)
+{
+	int16* l16 = (int16*) sp->tbuf;
+	float* yp = (float*) op;
+
+	while (n-- > 0)
+		*yp++ = (float)LogL16toY(*l16++);
+}
+
+static void
+L16toGry(LogLuvState* sp, tidata_t op, int n)
+{
+	int16* l16 = (int16*) sp->tbuf;
+	uint8* gp = (uint8*) op;
+
+	while (n-- > 0) {
+		double Y = LogL16toY(*l16++);
+		*gp++ = (uint8) ((Y <= 0.) ? 0 : (Y >= 1.) ? 255 : (int)(256.*sqrt(Y)));
+	}
+}
+
+static void
+L16fromY(LogLuvState* sp, tidata_t op, int n)
+{
+	int16* l16 = (int16*) sp->tbuf;
+	float* yp = (float*) op;
+
+	while (n-- > 0)
+		*l16++ = (int16) (LogL16fromY(*yp++, sp->encode_meth));
+}
+
+#if !LOGLUV_PUBLIC
+static
+#endif
+void
+XYZtoRGB24(float xyz[3], uint8 rgb[3])
+{
+	double	r, g, b;
+					/* assume CCIR-709 primaries */
+	r =  2.690*xyz[0] + -1.276*xyz[1] + -0.414*xyz[2];
+	g = -1.022*xyz[0] +  1.978*xyz[1] +  0.044*xyz[2];
+	b =  0.061*xyz[0] + -0.224*xyz[1] +  1.163*xyz[2];
+					/* assume 2.0 gamma for speed */
+	/* could use integer sqrt approx., but this is probably faster */
+	rgb[0] = (uint8)((r<=0.) ? 0 : (r >= 1.) ? 255 : (int)(256.*sqrt(r)));
+	rgb[1] = (uint8)((g<=0.) ? 0 : (g >= 1.) ? 255 : (int)(256.*sqrt(g)));
+	rgb[2] = (uint8)((b<=0.) ? 0 : (b >= 1.) ? 255 : (int)(256.*sqrt(b)));
+}
+
+#if !LOGLUV_PUBLIC
+static
+#endif
+double
+LogL10toY(int p10)		/* compute luminance from 10-bit LogL */
+{
+	if (p10 == 0)
+		return (0.);
+	return (exp(M_LN2/64.*(p10+.5) - M_LN2*12.));
+}
+
+#if !LOGLUV_PUBLIC
+static
+#endif
+int
+LogL10fromY(double Y, int em)	/* get 10-bit LogL from Y */
+{
+	if (Y >= 15.742)
+		return (0x3ff);
+	else if (Y <= .00024283)
+		return (0);
+	else
+		return itrunc(64.*(log2(Y) + 12.), em);
+}
+
+#define NANGLES		100
+#define uv2ang(u, v)	( (NANGLES*.499999999/M_PI) \
+				* atan2((v)-V_NEU,(u)-U_NEU) + .5*NANGLES )
+
+static int
+oog_encode(double u, double v)		/* encode out-of-gamut chroma */
+{
+	static int	oog_table[NANGLES];
+	static int	initialized = 0;
+	register int	i;
+	
+	if (!initialized) {		/* set up perimeter table */
+		double	eps[NANGLES], ua, va, ang, epsa;
+		int	ui, vi, ustep;
+		for (i = NANGLES; i--; )
+			eps[i] = 2.;
+		for (vi = UV_NVS; vi--; ) {
+			va = UV_VSTART + (vi+.5)*UV_SQSIZ;
+			ustep = uv_row[vi].nus-1;
+			if (vi == UV_NVS-1 || vi == 0 || ustep <= 0)
+				ustep = 1;
+			for (ui = uv_row[vi].nus-1; ui >= 0; ui -= ustep) {
+				ua = uv_row[vi].ustart + (ui+.5)*UV_SQSIZ;
+				ang = uv2ang(ua, va);
+                                i = (int) ang;
+				epsa = fabs(ang - (i+.5));
+				if (epsa < eps[i]) {
+					oog_table[i] = uv_row[vi].ncum + ui;
+					eps[i] = epsa;
+				}
+			}
+		}
+		for (i = NANGLES; i--; )	/* fill any holes */
+			if (eps[i] > 1.5) {
+				int	i1, i2;
+				for (i1 = 1; i1 < NANGLES/2; i1++)
+					if (eps[(i+i1)%NANGLES] < 1.5)
+						break;
+				for (i2 = 1; i2 < NANGLES/2; i2++)
+					if (eps[(i+NANGLES-i2)%NANGLES] < 1.5)
+						break;
+				if (i1 < i2)
+					oog_table[i] =
+						oog_table[(i+i1)%NANGLES];
+				else
+					oog_table[i] =
+						oog_table[(i+NANGLES-i2)%NANGLES];
+			}
+		initialized = 1;
+	}
+	i = (int) uv2ang(u, v);		/* look up hue angle */
+	return (oog_table[i]);
+}
+
+#undef uv2ang
+#undef NANGLES
+
+#if !LOGLUV_PUBLIC
+static
+#endif
+int
+uv_encode(double u, double v, int em)	/* encode (u',v') coordinates */
+{
+	register int	vi, ui;
+
+	if (v < UV_VSTART)
+		return oog_encode(u, v);
+	vi = itrunc((v - UV_VSTART)*(1./UV_SQSIZ), em);
+	if (vi >= UV_NVS)
+		return oog_encode(u, v);
+	if (u < uv_row[vi].ustart)
+		return oog_encode(u, v);
+	ui = itrunc((u - uv_row[vi].ustart)*(1./UV_SQSIZ), em);
+	if (ui >= uv_row[vi].nus)
+		return oog_encode(u, v);
+
+	return (uv_row[vi].ncum + ui);
+}
+
+#if !LOGLUV_PUBLIC
+static
+#endif
+int
+uv_decode(double *up, double *vp, int c)	/* decode (u',v') index */
+{
+	int	upper, lower;
+	register int	ui, vi;
+
+	if (c < 0 || c >= UV_NDIVS)
+		return (-1);
+	lower = 0;				/* binary search */
+	upper = UV_NVS;
+	while (upper - lower > 1) {
+		vi = (lower + upper) >> 1;
+		ui = c - uv_row[vi].ncum;
+		if (ui > 0)
+			lower = vi;
+		else if (ui < 0)
+			upper = vi;
+		else {
+			lower = vi;
+			break;
+		}
+	}
+	vi = lower;
+	ui = c - uv_row[vi].ncum;
+	*up = uv_row[vi].ustart + (ui+.5)*UV_SQSIZ;
+	*vp = UV_VSTART + (vi+.5)*UV_SQSIZ;
+	return (0);
+}
+
+#if !LOGLUV_PUBLIC
+static
+#endif
+void
+LogLuv24toXYZ(uint32 p, float XYZ[3])
+{
+	int	Ce;
+	double	L, u, v, s, x, y;
+					/* decode luminance */
+	L = LogL10toY(p>>14 & 0x3ff);
+	if (L <= 0.) {
+		XYZ[0] = XYZ[1] = XYZ[2] = 0.;
+		return;
+	}
+					/* decode color */
+	Ce = p & 0x3fff;
+	if (uv_decode(&u, &v, Ce) < 0) {
+		u = U_NEU; v = V_NEU;
+	}
+	s = 1./(6.*u - 16.*v + 12.);
+	x = 9.*u * s;
+	y = 4.*v * s;
+					/* convert to XYZ */
+	XYZ[0] = (float)(x/y * L);
+	XYZ[1] = (float)L;
+	XYZ[2] = (float)((1.-x-y)/y * L);
+}
+
+#if !LOGLUV_PUBLIC
+static
+#endif
+uint32
+LogLuv24fromXYZ(float XYZ[3], int em)
+{
+	int	Le, Ce;
+	double	u, v, s;
+					/* encode luminance */
+	Le = LogL10fromY(XYZ[1], em);
+					/* encode color */
+	s = XYZ[0] + 15.*XYZ[1] + 3.*XYZ[2];
+	if (!Le || s <= 0.) {
+		u = U_NEU;
+		v = V_NEU;
+	} else {
+		u = 4.*XYZ[0] / s;
+		v = 9.*XYZ[1] / s;
+	}
+	Ce = uv_encode(u, v, em);
+	if (Ce < 0)			/* never happens */
+		Ce = uv_encode(U_NEU, V_NEU, SGILOGENCODE_NODITHER);
+					/* combine encodings */
+	return (Le << 14 | Ce);
+}
+
+static void
+Luv24toXYZ(LogLuvState* sp, tidata_t op, int n)
+{
+	uint32* luv = (uint32*) sp->tbuf;
+	float* xyz = (float*) op;
+
+	while (n-- > 0) {
+		LogLuv24toXYZ(*luv, xyz);
+		xyz += 3;
+		luv++;
+	}
+}
+
+static void
+Luv24toLuv48(LogLuvState* sp, tidata_t op, int n)
+{
+	uint32* luv = (uint32*) sp->tbuf;
+	int16* luv3 = (int16*) op;
+
+	while (n-- > 0) {
+		double u, v;
+
+		*luv3++ = (int16)((*luv >> 12 & 0xffd) + 13314);
+		if (uv_decode(&u, &v, *luv&0x3fff) < 0) {
+			u = U_NEU;
+			v = V_NEU;
+		}
+		*luv3++ = (int16)(u * (1L<<15));
+		*luv3++ = (int16)(v * (1L<<15));
+		luv++;
+	}
+}
+
+static void
+Luv24toRGB(LogLuvState* sp, tidata_t op, int n)
+{
+	uint32* luv = (uint32*) sp->tbuf;
+	uint8* rgb = (uint8*) op;
+
+	while (n-- > 0) {
+		float xyz[3];
+
+		LogLuv24toXYZ(*luv++, xyz);
+		XYZtoRGB24(xyz, rgb);
+		rgb += 3;
+	}
+}
+
+static void
+Luv24fromXYZ(LogLuvState* sp, tidata_t op, int n)
+{
+	uint32* luv = (uint32*) sp->tbuf;
+	float* xyz = (float*) op;
+
+	while (n-- > 0) {
+		*luv++ = LogLuv24fromXYZ(xyz, sp->encode_meth);
+		xyz += 3;
+	}
+}
+
+static void
+Luv24fromLuv48(LogLuvState* sp, tidata_t op, int n)
+{
+	uint32* luv = (uint32*) sp->tbuf;
+	int16* luv3 = (int16*) op;
+
+	while (n-- > 0) {
+		int Le, Ce;
+
+		if (luv3[0] <= 0)
+			Le = 0;
+		else if (luv3[0] >= (1<<12)+3314)
+			Le = (1<<10) - 1;
+		else if (sp->encode_meth == SGILOGENCODE_NODITHER)
+			Le = (luv3[0]-3314) >> 2;
+		else
+			Le = itrunc(.25*(luv3[0]-3314.), sp->encode_meth);
+
+		Ce = uv_encode((luv3[1]+.5)/(1<<15), (luv3[2]+.5)/(1<<15),
+					sp->encode_meth);
+		if (Ce < 0)	/* never happens */
+			Ce = uv_encode(U_NEU, V_NEU, SGILOGENCODE_NODITHER);
+		*luv++ = (uint32)Le << 14 | Ce;
+		luv3 += 3;
+	}
+}
+
+#if !LOGLUV_PUBLIC
+static
+#endif
+void
+LogLuv32toXYZ(uint32 p, float XYZ[3])
+{
+	double	L, u, v, s, x, y;
+					/* decode luminance */
+	L = LogL16toY((int)p >> 16);
+	if (L <= 0.) {
+		XYZ[0] = XYZ[1] = XYZ[2] = 0.;
+		return;
+	}
+					/* decode color */
+	u = 1./UVSCALE * ((p>>8 & 0xff) + .5);
+	v = 1./UVSCALE * ((p & 0xff) + .5);
+	s = 1./(6.*u - 16.*v + 12.);
+	x = 9.*u * s;
+	y = 4.*v * s;
+					/* convert to XYZ */
+	XYZ[0] = (float)(x/y * L);
+	XYZ[1] = (float)L;
+	XYZ[2] = (float)((1.-x-y)/y * L);
+}
+
+#if !LOGLUV_PUBLIC
+static
+#endif
+uint32
+LogLuv32fromXYZ(float XYZ[3], int em)
+{
+	unsigned int	Le, ue, ve;
+	double	u, v, s;
+					/* encode luminance */
+	Le = (unsigned int)LogL16fromY(XYZ[1], em);
+					/* encode color */
+	s = XYZ[0] + 15.*XYZ[1] + 3.*XYZ[2];
+	if (!Le || s <= 0.) {
+		u = U_NEU;
+		v = V_NEU;
+	} else {
+		u = 4.*XYZ[0] / s;
+		v = 9.*XYZ[1] / s;
+	}
+	if (u <= 0.) ue = 0;
+	else ue = itrunc(UVSCALE*u, em);
+	if (ue > 255) ue = 255;
+	if (v <= 0.) ve = 0;
+	else ve = itrunc(UVSCALE*v, em);
+	if (ve > 255) ve = 255;
+					/* combine encodings */
+	return (Le << 16 | ue << 8 | ve);
+}
+
+static void
+Luv32toXYZ(LogLuvState* sp, tidata_t op, int n)
+{
+	uint32* luv = (uint32*) sp->tbuf;
+	float* xyz = (float*) op;
+
+	while (n-- > 0) {
+		LogLuv32toXYZ(*luv++, xyz);
+		xyz += 3;
+	}
+}
+
+static void
+Luv32toLuv48(LogLuvState* sp, tidata_t op, int n)
+{
+	uint32* luv = (uint32*) sp->tbuf;
+	int16* luv3 = (int16*) op;
+
+	while (n-- > 0) {
+		double u, v;
+
+		*luv3++ = (int16)(*luv >> 16);
+		u = 1./UVSCALE * ((*luv>>8 & 0xff) + .5);
+		v = 1./UVSCALE * ((*luv & 0xff) + .5);
+		*luv3++ = (int16)(u * (1L<<15));
+		*luv3++ = (int16)(v * (1L<<15));
+		luv++;
+	}
+}
+
+static void
+Luv32toRGB(LogLuvState* sp, tidata_t op, int n)
+{
+	uint32* luv = (uint32*) sp->tbuf;
+	uint8* rgb = (uint8*) op;
+
+	while (n-- > 0) {
+		float xyz[3];
+
+		LogLuv32toXYZ(*luv++, xyz);
+		XYZtoRGB24(xyz, rgb);
+		rgb += 3;
+	}
+}
+
+static void
+Luv32fromXYZ(LogLuvState* sp, tidata_t op, int n)
+{
+	uint32* luv = (uint32*) sp->tbuf;
+	float* xyz = (float*) op;
+
+	while (n-- > 0) {
+		*luv++ = LogLuv32fromXYZ(xyz, sp->encode_meth);
+		xyz += 3;
+	}
+}
+
+static void
+Luv32fromLuv48(LogLuvState* sp, tidata_t op, int n)
+{
+	uint32* luv = (uint32*) sp->tbuf;
+	int16* luv3 = (int16*) op;
+
+	if (sp->encode_meth == SGILOGENCODE_NODITHER) {
+		while (n-- > 0) {
+			*luv++ = (uint32)luv3[0] << 16 |
+				(luv3[1]*(uint32)(UVSCALE+.5) >> 7 & 0xff00) |
+				(luv3[2]*(uint32)(UVSCALE+.5) >> 15 & 0xff);
+			luv3 += 3;
+		}
+		return;
+	}
+	while (n-- > 0) {
+		*luv++ = (uint32)luv3[0] << 16 |
+	(itrunc(luv3[1]*(UVSCALE/(1<<15)), sp->encode_meth) << 8 & 0xff00) |
+		(itrunc(luv3[2]*(UVSCALE/(1<<15)), sp->encode_meth) & 0xff);
+		luv3 += 3;
+	}
+}
+
+static void
+_logLuvNop(LogLuvState* sp, tidata_t op, int n)
+{
+	(void) sp; (void) op; (void) n;
+}
+
+static int
+LogL16GuessDataFmt(TIFFDirectory *td)
+{
+#define	PACK(s,b,f)	(((b)<<6)|((s)<<3)|(f))
+	switch (PACK(td->td_samplesperpixel, td->td_bitspersample, td->td_sampleformat)) {
+	case PACK(1, 32, SAMPLEFORMAT_IEEEFP):
+		return (SGILOGDATAFMT_FLOAT);
+	case PACK(1, 16, SAMPLEFORMAT_VOID):
+	case PACK(1, 16, SAMPLEFORMAT_INT):
+	case PACK(1, 16, SAMPLEFORMAT_UINT):
+		return (SGILOGDATAFMT_16BIT);
+	case PACK(1,  8, SAMPLEFORMAT_VOID):
+	case PACK(1,  8, SAMPLEFORMAT_UINT):
+		return (SGILOGDATAFMT_8BIT);
+	}
+#undef PACK
+	return (SGILOGDATAFMT_UNKNOWN);
+}
+
+static uint32
+multiply(size_t m1, size_t m2)
+{
+	uint32	bytes = m1 * m2;
+
+	if (m1 && bytes / m1 != m2)
+		bytes = 0;
+
+	return bytes;
+}
+
+static int
+LogL16InitState(TIFF* tif)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	LogLuvState* sp = DecoderState(tif);
+	static const char module[] = "LogL16InitState";
+
+	assert(sp != NULL);
+	assert(td->td_photometric == PHOTOMETRIC_LOGL);
+
+	/* for some reason, we can't do this in TIFFInitLogL16 */
+	if (sp->user_datafmt == SGILOGDATAFMT_UNKNOWN)
+		sp->user_datafmt = LogL16GuessDataFmt(td);
+	switch (sp->user_datafmt) {
+	case SGILOGDATAFMT_FLOAT:
+		sp->pixel_size = sizeof (float);
+		break;
+	case SGILOGDATAFMT_16BIT:
+		sp->pixel_size = sizeof (int16);
+		break;
+	case SGILOGDATAFMT_8BIT:
+		sp->pixel_size = sizeof (uint8);
+		break;
+	default:
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		    "No support for converting user data format to LogL");
+		return (0);
+	}
+	sp->tbuflen = multiply(td->td_imagewidth, td->td_rowsperstrip);
+	if (multiply(sp->tbuflen, sizeof (int16)) == 0 ||
+	    (sp->tbuf = (tidata_t*) _TIFFmalloc(sp->tbuflen * sizeof (int16))) == NULL) {
+		TIFFErrorExt(tif->tif_clientdata, module, "%s: No space for SGILog translation buffer",
+		    tif->tif_name);
+		return (0);
+	}
+	return (1);
+}
+
+static int
+LogLuvGuessDataFmt(TIFFDirectory *td)
+{
+	int guess;
+
+	/*
+	 * If the user didn't tell us their datafmt,
+	 * take our best guess from the bitspersample.
+	 */
+#define	PACK(a,b)	(((a)<<3)|(b))
+	switch (PACK(td->td_bitspersample, td->td_sampleformat)) {
+	case PACK(32, SAMPLEFORMAT_IEEEFP):
+		guess = SGILOGDATAFMT_FLOAT;
+		break;
+	case PACK(32, SAMPLEFORMAT_VOID):
+	case PACK(32, SAMPLEFORMAT_UINT):
+	case PACK(32, SAMPLEFORMAT_INT):
+		guess = SGILOGDATAFMT_RAW;
+		break;
+	case PACK(16, SAMPLEFORMAT_VOID):
+	case PACK(16, SAMPLEFORMAT_INT):
+	case PACK(16, SAMPLEFORMAT_UINT):
+		guess = SGILOGDATAFMT_16BIT;
+		break;
+	case PACK( 8, SAMPLEFORMAT_VOID):
+	case PACK( 8, SAMPLEFORMAT_UINT):
+		guess = SGILOGDATAFMT_8BIT;
+		break;
+	default:
+		guess = SGILOGDATAFMT_UNKNOWN;
+		break;
+#undef PACK
+	}
+	/*
+	 * Double-check samples per pixel.
+	 */
+	switch (td->td_samplesperpixel) {
+	case 1:
+		if (guess != SGILOGDATAFMT_RAW)
+			guess = SGILOGDATAFMT_UNKNOWN;
+		break;
+	case 3:
+		if (guess == SGILOGDATAFMT_RAW)
+			guess = SGILOGDATAFMT_UNKNOWN;
+		break;
+	default:
+		guess = SGILOGDATAFMT_UNKNOWN;
+		break;
+	}
+	return (guess);
+}
+
+static int
+LogLuvInitState(TIFF* tif)
+{
+	TIFFDirectory* td = &tif->tif_dir;
+	LogLuvState* sp = DecoderState(tif);
+	static const char module[] = "LogLuvInitState";
+
+	assert(sp != NULL);
+	assert(td->td_photometric == PHOTOMETRIC_LOGLUV);
+
+	/* for some reason, we can't do this in TIFFInitLogLuv */
+	if (td->td_planarconfig != PLANARCONFIG_CONTIG) {
+		TIFFErrorExt(tif->tif_clientdata, module,
+		    "SGILog compression cannot handle non-contiguous data");
+		return (0);
+	}
+	if (sp->user_datafmt == SGILOGDATAFMT_UNKNOWN)
+		sp->user_datafmt = LogLuvGuessDataFmt(td);
+	switch (sp->user_datafmt) {
+	case SGILOGDATAFMT_FLOAT:
+		sp->pixel_size = 3*sizeof (float);
+		break;
+	case SGILOGDATAFMT_16BIT:
+		sp->pixel_size = 3*sizeof (int16);
+		break;
+	case SGILOGDATAFMT_RAW:
+		sp->pixel_size = sizeof (uint32);
+		break;
+	case SGILOGDATAFMT_8BIT:
+		sp->pixel_size = 3*sizeof (uint8);
+		break;
+	default:
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		    "No support for converting user data format to LogLuv");
+		return (0);
+	}
+	sp->tbuflen = multiply(td->td_imagewidth, td->td_rowsperstrip);
+	if (multiply(sp->tbuflen, sizeof (uint32)) == 0 ||
+	    (sp->tbuf = (tidata_t*) _TIFFmalloc(sp->tbuflen * sizeof (uint32))) == NULL) {
+		TIFFErrorExt(tif->tif_clientdata, module, "%s: No space for SGILog translation buffer",
+		    tif->tif_name);
+		return (0);
+	}
+	return (1);
+}
+
+static int
+LogLuvSetupDecode(TIFF* tif)
+{
+	LogLuvState* sp = DecoderState(tif);
+	TIFFDirectory* td = &tif->tif_dir;
+
+	tif->tif_postdecode = _TIFFNoPostDecode;
+	switch (td->td_photometric) {
+	case PHOTOMETRIC_LOGLUV:
+		if (!LogLuvInitState(tif))
+			break;
+		if (td->td_compression == COMPRESSION_SGILOG24) {
+			tif->tif_decoderow = LogLuvDecode24;
+			switch (sp->user_datafmt) {
+			case SGILOGDATAFMT_FLOAT:
+				sp->tfunc = Luv24toXYZ;
+				break;
+			case SGILOGDATAFMT_16BIT:
+				sp->tfunc = Luv24toLuv48;
+				break;
+			case SGILOGDATAFMT_8BIT:
+				sp->tfunc = Luv24toRGB;
+				break;
+			}
+		} else {
+			tif->tif_decoderow = LogLuvDecode32;
+			switch (sp->user_datafmt) {
+			case SGILOGDATAFMT_FLOAT:
+				sp->tfunc = Luv32toXYZ;
+				break;
+			case SGILOGDATAFMT_16BIT:
+				sp->tfunc = Luv32toLuv48;
+				break;
+			case SGILOGDATAFMT_8BIT:
+				sp->tfunc = Luv32toRGB;
+				break;
+			}
+		}
+		return (1);
+	case PHOTOMETRIC_LOGL:
+		if (!LogL16InitState(tif))
+			break;
+		tif->tif_decoderow = LogL16Decode;
+		switch (sp->user_datafmt) {
+		case SGILOGDATAFMT_FLOAT:
+			sp->tfunc = L16toY;
+			break;
+		case SGILOGDATAFMT_8BIT:
+			sp->tfunc = L16toGry;
+			break;
+		}
+		return (1);
+	default:
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+    "Inappropriate photometric interpretation %d for SGILog compression; %s",
+		    td->td_photometric, "must be either LogLUV or LogL");
+		break;
+	}
+	return (0);
+}
+
+static int
+LogLuvSetupEncode(TIFF* tif)
+{
+	LogLuvState* sp = EncoderState(tif);
+	TIFFDirectory* td = &tif->tif_dir;
+
+	switch (td->td_photometric) {
+	case PHOTOMETRIC_LOGLUV:
+		if (!LogLuvInitState(tif))
+			break;
+		if (td->td_compression == COMPRESSION_SGILOG24) {
+			tif->tif_encoderow = LogLuvEncode24;
+			switch (sp->user_datafmt) {
+			case SGILOGDATAFMT_FLOAT:
+				sp->tfunc = Luv24fromXYZ;
+				break;
+			case SGILOGDATAFMT_16BIT:
+				sp->tfunc = Luv24fromLuv48;
+				break;
+			case SGILOGDATAFMT_RAW:
+				break;
+			default:
+				goto notsupported;
+			}
+		} else {
+			tif->tif_encoderow = LogLuvEncode32;
+			switch (sp->user_datafmt) {
+			case SGILOGDATAFMT_FLOAT:
+				sp->tfunc = Luv32fromXYZ;
+				break;
+			case SGILOGDATAFMT_16BIT:
+				sp->tfunc = Luv32fromLuv48;
+				break;
+			case SGILOGDATAFMT_RAW:
+				break;
+			default:
+				goto notsupported;
+			}
+		}
+		break;
+	case PHOTOMETRIC_LOGL:
+		if (!LogL16InitState(tif))
+			break;
+		tif->tif_encoderow = LogL16Encode;
+		switch (sp->user_datafmt) {
+		case SGILOGDATAFMT_FLOAT:
+			sp->tfunc = L16fromY;
+			break;
+		case SGILOGDATAFMT_16BIT:
+			break;
+		default:
+			goto notsupported;
+		}
+		break;
+	default:
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+    "Inappropriate photometric interpretation %d for SGILog compression; %s",
+    		    td->td_photometric, "must be either LogLUV or LogL");
+		break;
+	}
+	return (1);
+notsupported:
+	TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+	    "SGILog compression supported only for %s, or raw data",
+	    td->td_photometric == PHOTOMETRIC_LOGL ? "Y, L" : "XYZ, Luv");
+	return (0);
+}
+
+static void
+LogLuvClose(TIFF* tif)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+
+	/*
+	 * For consistency, we always want to write out the same
+	 * bitspersample and sampleformat for our TIFF file,
+	 * regardless of the data format being used by the application.
+	 * Since this routine is called after tags have been set but
+	 * before they have been recorded in the file, we reset them here.
+	 */
+	td->td_samplesperpixel =
+	    (td->td_photometric == PHOTOMETRIC_LOGL) ? 1 : 3;
+	td->td_bitspersample = 16;
+	td->td_sampleformat = SAMPLEFORMAT_INT;
+}
+
+static void
+LogLuvCleanup(TIFF* tif)
+{
+	LogLuvState* sp = (LogLuvState *)tif->tif_data;
+
+	assert(sp != 0);
+
+	tif->tif_tagmethods.vgetfield = sp->vgetparent;
+	tif->tif_tagmethods.vsetfield = sp->vsetparent;
+
+	if (sp->tbuf)
+		_TIFFfree(sp->tbuf);
+	_TIFFfree(sp);
+	tif->tif_data = NULL;
+
+	_TIFFSetDefaultCompressionState(tif);
+}
+
+static int
+LogLuvVSetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+	LogLuvState* sp = DecoderState(tif);
+	int bps, fmt;
+
+	switch (tag) {
+	case TIFFTAG_SGILOGDATAFMT:
+		sp->user_datafmt = va_arg(ap, int);
+		/*
+		 * Tweak the TIFF header so that the rest of libtiff knows what
+		 * size of data will be passed between app and library, and
+		 * assume that the app knows what it is doing and is not
+		 * confused by these header manipulations...
+		 */
+		switch (sp->user_datafmt) {
+		case SGILOGDATAFMT_FLOAT:
+			bps = 32, fmt = SAMPLEFORMAT_IEEEFP;
+			break;
+		case SGILOGDATAFMT_16BIT:
+			bps = 16, fmt = SAMPLEFORMAT_INT;
+			break;
+		case SGILOGDATAFMT_RAW:
+			bps = 32, fmt = SAMPLEFORMAT_UINT;
+			TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
+			break;
+		case SGILOGDATAFMT_8BIT:
+			bps = 8, fmt = SAMPLEFORMAT_UINT;
+			break;
+		default:
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			    "Unknown data format %d for LogLuv compression",
+			    sp->user_datafmt);
+			return (0);
+		}
+		TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps);
+		TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, fmt);
+		/*
+		 * Must recalculate sizes should bits/sample change.
+		 */
+		tif->tif_tilesize = isTiled(tif) ? TIFFTileSize(tif) : (tsize_t) -1;
+		tif->tif_scanlinesize = TIFFScanlineSize(tif);
+		return (1);
+	case TIFFTAG_SGILOGENCODE:
+		sp->encode_meth = va_arg(ap, int);
+		if (sp->encode_meth != SGILOGENCODE_NODITHER &&
+				sp->encode_meth != SGILOGENCODE_RANDITHER) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+				"Unknown encoding %d for LogLuv compression",
+				sp->encode_meth);
+			return (0);
+		}
+		return (1);
+	default:
+		return (*sp->vsetparent)(tif, tag, ap);
+	}
+}
+
+static int
+LogLuvVGetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+	LogLuvState *sp = (LogLuvState *)tif->tif_data;
+
+	switch (tag) {
+	case TIFFTAG_SGILOGDATAFMT:
+		*va_arg(ap, int*) = sp->user_datafmt;
+		return (1);
+	default:
+		return (*sp->vgetparent)(tif, tag, ap);
+	}
+}
+
+static const TIFFFieldInfo LogLuvFieldInfo[] = {
+    { TIFFTAG_SGILOGDATAFMT,	  0, 0,	TIFF_SHORT,	FIELD_PSEUDO,
+      TRUE,	FALSE,	"SGILogDataFmt"},
+    { TIFFTAG_SGILOGENCODE,	  0, 0, TIFF_SHORT,	FIELD_PSEUDO,
+      TRUE,	FALSE,	"SGILogEncode"}
+};
+
+int
+TIFFInitSGILog(TIFF* tif, int scheme)
+{
+	static const char module[] = "TIFFInitSGILog";
+	LogLuvState* sp;
+
+	assert(scheme == COMPRESSION_SGILOG24 || scheme == COMPRESSION_SGILOG);
+
+	/*
+	 * Allocate state block so tag methods have storage to record values.
+	 */
+	tif->tif_data = (tidata_t) _TIFFmalloc(sizeof (LogLuvState));
+	if (tif->tif_data == NULL)
+		goto bad;
+	sp = (LogLuvState*) tif->tif_data;
+	_TIFFmemset((tdata_t)sp, 0, sizeof (*sp));
+	sp->user_datafmt = SGILOGDATAFMT_UNKNOWN;
+	sp->encode_meth = (scheme == COMPRESSION_SGILOG24) ?
+				SGILOGENCODE_RANDITHER : SGILOGENCODE_NODITHER;
+	sp->tfunc = _logLuvNop;
+
+	/*
+	 * Install codec methods.
+	 * NB: tif_decoderow & tif_encoderow are filled
+	 *     in at setup time.
+	 */
+	tif->tif_setupdecode = LogLuvSetupDecode;
+	tif->tif_decodestrip = LogLuvDecodeStrip;
+	tif->tif_decodetile = LogLuvDecodeTile;
+	tif->tif_setupencode = LogLuvSetupEncode;
+	tif->tif_encodestrip = LogLuvEncodeStrip;
+	tif->tif_encodetile = LogLuvEncodeTile;
+	tif->tif_close = LogLuvClose;
+	tif->tif_cleanup = LogLuvCleanup;
+
+	/* override SetField so we can handle our private pseudo-tag */
+	_TIFFMergeFieldInfo(tif, LogLuvFieldInfo,
+			    TIFFArrayCount(LogLuvFieldInfo));
+	sp->vgetparent = tif->tif_tagmethods.vgetfield;
+	tif->tif_tagmethods.vgetfield = LogLuvVGetField;   /* hook for codec tags */
+	sp->vsetparent = tif->tif_tagmethods.vsetfield;
+	tif->tif_tagmethods.vsetfield = LogLuvVSetField;   /* hook for codec tags */
+
+	return (1);
+bad:
+	TIFFErrorExt(tif->tif_clientdata, module,
+		     "%s: No space for LogLuv state block", tif->tif_name);
+	return (0);
+}
+#endif /* LOGLUV_SUPPORT */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_lzw.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_lzw.c
new file mode 100644
index 0000000000..12db5a48da
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_lzw.c
@@ -0,0 +1,1084 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#include "tiffiop.h"
+#ifdef LZW_SUPPORT
+/*
+ * TIFF Library.
+ * Rev 5.0 Lempel-Ziv & Welch Compression Support
+ *
+ * This code is derived from the compress program whose code is
+ * derived from software contributed to Berkeley by James A. Woods,
+ * derived from original work by Spencer Thomas and Joseph Orost.
+ *
+ * The original Berkeley copyright notice appears below in its entirety.
+ */
+#include "tif_predict.h"
+
+#include <stdio.h>
+
+/*
+ * NB: The 5.0 spec describes a different algorithm than Aldus
+ *     implements.  Specifically, Aldus does code length transitions
+ *     one code earlier than should be done (for real LZW).
+ *     Earlier versions of this library implemented the correct
+ *     LZW algorithm, but emitted codes in a bit order opposite
+ *     to the TIFF spec.  Thus, to maintain compatibility w/ Aldus
+ *     we interpret MSB-LSB ordered codes to be images written w/
+ *     old versions of this library, but otherwise adhere to the
+ *     Aldus "off by one" algorithm.
+ *
+ * Future revisions to the TIFF spec are expected to "clarify this issue".
+ */
+#define	LZW_COMPAT		/* include backwards compatibility code */
+/*
+ * Each strip of data is supposed to be terminated by a CODE_EOI.
+ * If the following #define is included, the decoder will also
+ * check for end-of-strip w/o seeing this code.  This makes the
+ * library more robust, but also slower.
+ */
+#define	LZW_CHECKEOS		/* include checks for strips w/o EOI code */
+
+#define MAXCODE(n)	((1L<<(n))-1)
+/*
+ * The TIFF spec specifies that encoded bit
+ * strings range from 9 to 12 bits.
+ */
+#define	BITS_MIN	9		/* start with 9 bits */
+#define	BITS_MAX	12		/* max of 12 bit strings */
+/* predefined codes */
+#define	CODE_CLEAR	256		/* code to clear string table */
+#define	CODE_EOI	257		/* end-of-information code */
+#define CODE_FIRST	258		/* first free code entry */
+#define	CODE_MAX	MAXCODE(BITS_MAX)
+#define	HSIZE		9001L		/* 91% occupancy */
+#define	HSHIFT		(13-8)
+#ifdef LZW_COMPAT
+/* NB: +1024 is for compatibility with old files */
+#define	CSIZE		(MAXCODE(BITS_MAX)+1024L)
+#else
+#define	CSIZE		(MAXCODE(BITS_MAX)+1L)
+#endif
+
+/*
+ * State block for each open TIFF file using LZW
+ * compression/decompression.  Note that the predictor
+ * state block must be first in this data structure.
+ */
+typedef	struct {
+	TIFFPredictorState predict;	/* predictor super class */
+
+	unsigned short	nbits;		/* # of bits/code */
+	unsigned short	maxcode;	/* maximum code for lzw_nbits */
+	unsigned short	free_ent;	/* next free entry in hash table */
+	long		nextdata;	/* next bits of i/o */
+	long		nextbits;	/* # of valid bits in lzw_nextdata */
+
+        int             rw_mode;        /* preserve rw_mode from init */
+} LZWBaseState;
+
+#define	lzw_nbits	base.nbits
+#define	lzw_maxcode	base.maxcode
+#define	lzw_free_ent	base.free_ent
+#define	lzw_nextdata	base.nextdata
+#define	lzw_nextbits	base.nextbits
+
+/*
+ * Encoding-specific state.
+ */
+typedef uint16 hcode_t;			/* codes fit in 16 bits */
+typedef struct {
+	long	hash;
+	hcode_t	code;
+} hash_t;
+
+/*
+ * Decoding-specific state.
+ */
+typedef struct code_ent {
+	struct code_ent *next;
+	unsigned short	length;		/* string len, including this token */
+	unsigned char	value;		/* data value */
+	unsigned char	firstchar;	/* first token of string */
+} code_t;
+
+typedef	int (*decodeFunc)(TIFF*, tidata_t, tsize_t, tsample_t);
+
+typedef struct {
+	LZWBaseState base;
+
+	/* Decoding specific data */
+	long	dec_nbitsmask;		/* lzw_nbits 1 bits, right adjusted */
+	long	dec_restart;		/* restart count */
+#ifdef LZW_CHECKEOS
+	long	dec_bitsleft;		/* available bits in raw data */
+#endif
+	decodeFunc dec_decode;		/* regular or backwards compatible */
+	code_t*	dec_codep;		/* current recognized code */
+	code_t*	dec_oldcodep;		/* previously recognized code */
+	code_t*	dec_free_entp;		/* next free entry */
+	code_t*	dec_maxcodep;		/* max available entry */
+	code_t*	dec_codetab;		/* kept separate for small machines */
+
+	/* Encoding specific data */
+	int	enc_oldcode;		/* last code encountered */
+	long	enc_checkpoint;		/* point at which to clear table */
+#define CHECK_GAP	10000		/* enc_ratio check interval */
+	long	enc_ratio;		/* current compression ratio */
+	long	enc_incount;		/* (input) data bytes encoded */
+	long	enc_outcount;		/* encoded (output) bytes */
+	tidata_t enc_rawlimit;		/* bound on tif_rawdata buffer */
+	hash_t*	enc_hashtab;		/* kept separate for small machines */
+} LZWCodecState;
+
+#define	LZWState(tif)		((LZWBaseState*) (tif)->tif_data)
+#define	DecoderState(tif)	((LZWCodecState*) LZWState(tif))
+#define	EncoderState(tif)	((LZWCodecState*) LZWState(tif))
+
+static	int LZWDecode(TIFF*, tidata_t, tsize_t, tsample_t);
+#ifdef LZW_COMPAT
+static	int LZWDecodeCompat(TIFF*, tidata_t, tsize_t, tsample_t);
+#endif
+static  void cl_hash(LZWCodecState*);
+
+/*
+ * LZW Decoder.
+ */
+
+#ifdef LZW_CHECKEOS
+/*
+ * This check shouldn't be necessary because each
+ * strip is suppose to be terminated with CODE_EOI.
+ */
+#define	NextCode(_tif, _sp, _bp, _code, _get) {				\
+	if ((_sp)->dec_bitsleft < nbits) {				\
+		TIFFWarningExt(_tif->tif_clientdata, _tif->tif_name,				\
+		    "LZWDecode: Strip %d not terminated with EOI code", \
+		    _tif->tif_curstrip);				\
+		_code = CODE_EOI;					\
+	} else {							\
+		_get(_sp,_bp,_code);					\
+		(_sp)->dec_bitsleft -= nbits;				\
+	}								\
+}
+#else
+#define	NextCode(tif, sp, bp, code, get) get(sp, bp, code)
+#endif
+
+static int
+LZWSetupDecode(TIFF* tif)
+{
+	LZWCodecState* sp = DecoderState(tif);
+	static const char module[] = " LZWSetupDecode";
+	int code;
+
+        if( sp == NULL )
+        {
+            /*
+             * Allocate state block so tag methods have storage to record 
+			 * values.
+             */
+            tif->tif_data = (tidata_t) _TIFFmalloc(sizeof(LZWCodecState));
+            if (tif->tif_data == NULL)
+            {
+				TIFFErrorExt(tif->tif_clientdata, "LZWPreDecode", "No space for LZW state block");
+                return (0);
+            }
+
+            DecoderState(tif)->dec_codetab = NULL;
+            DecoderState(tif)->dec_decode = NULL;
+            
+            /*
+             * Setup predictor setup.
+             */
+            (void) TIFFPredictorInit(tif);
+
+            sp = DecoderState(tif);
+        }
+            
+	assert(sp != NULL);
+
+	if (sp->dec_codetab == NULL) {
+		sp->dec_codetab = (code_t*)_TIFFmalloc(CSIZE*sizeof (code_t));
+		if (sp->dec_codetab == NULL) {
+			TIFFErrorExt(tif->tif_clientdata, module, "No space for LZW code table");
+			return (0);
+		}
+		/*
+		 * Pre-load the table.
+		 */
+                code = 255;
+                do {
+                    sp->dec_codetab[code].value = code;
+                    sp->dec_codetab[code].firstchar = code;
+                    sp->dec_codetab[code].length = 1;
+                    sp->dec_codetab[code].next = NULL;
+                } while (code--);
+	}
+	return (1);
+}
+
+/*
+ * Setup state for decoding a strip.
+ */
+static int
+LZWPreDecode(TIFF* tif, tsample_t s)
+{
+	LZWCodecState *sp = DecoderState(tif);
+
+	(void) s;
+	assert(sp != NULL);
+	/*
+	 * Check for old bit-reversed codes.
+	 */
+	if (tif->tif_rawdata[0] == 0 && (tif->tif_rawdata[1] & 0x1)) {
+#ifdef LZW_COMPAT
+		if (!sp->dec_decode) {
+			TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
+			    "Old-style LZW codes, convert file");
+			/*
+			 * Override default decoding methods with
+			 * ones that deal with the old coding.
+			 * Otherwise the predictor versions set
+			 * above will call the compatibility routines
+			 * through the dec_decode method.
+			 */
+			tif->tif_decoderow = LZWDecodeCompat;
+			tif->tif_decodestrip = LZWDecodeCompat;
+			tif->tif_decodetile = LZWDecodeCompat;
+			/*
+			 * If doing horizontal differencing, must
+			 * re-setup the predictor logic since we
+			 * switched the basic decoder methods...
+			 */
+			(*tif->tif_setupdecode)(tif);
+			sp->dec_decode = LZWDecodeCompat;
+		}
+		sp->lzw_maxcode = MAXCODE(BITS_MIN);
+#else /* !LZW_COMPAT */
+		if (!sp->dec_decode) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			    "Old-style LZW codes not supported");
+			sp->dec_decode = LZWDecode;
+		}
+		return (0);
+#endif/* !LZW_COMPAT */
+	} else {
+		sp->lzw_maxcode = MAXCODE(BITS_MIN)-1;
+		sp->dec_decode = LZWDecode;
+	}
+	sp->lzw_nbits = BITS_MIN;
+	sp->lzw_nextbits = 0;
+	sp->lzw_nextdata = 0;
+
+	sp->dec_restart = 0;
+	sp->dec_nbitsmask = MAXCODE(BITS_MIN);
+#ifdef LZW_CHECKEOS
+	sp->dec_bitsleft = tif->tif_rawcc << 3;
+#endif
+	sp->dec_free_entp = sp->dec_codetab + CODE_FIRST;
+	/*
+	 * Zero entries that are not yet filled in.  We do
+	 * this to guard against bogus input data that causes
+	 * us to index into undefined entries.  If you can
+	 * come up with a way to safely bounds-check input codes
+	 * while decoding then you can remove this operation.
+	 */
+	_TIFFmemset(sp->dec_free_entp, 0, (CSIZE-CODE_FIRST)*sizeof (code_t));
+	sp->dec_oldcodep = &sp->dec_codetab[-1];
+	sp->dec_maxcodep = &sp->dec_codetab[sp->dec_nbitsmask-1];
+	return (1);
+}
+
+/*
+ * Decode a "hunk of data".
+ */
+#define	GetNextCode(sp, bp, code) {				\
+	nextdata = (nextdata<<8) | *(bp)++;			\
+	nextbits += 8;						\
+	if (nextbits < nbits) {					\
+		nextdata = (nextdata<<8) | *(bp)++;		\
+		nextbits += 8;					\
+	}							\
+	code = (hcode_t)((nextdata >> (nextbits-nbits)) & nbitsmask);	\
+	nextbits -= nbits;					\
+}
+
+static void
+codeLoop(TIFF* tif)
+{
+	TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+	    "LZWDecode: Bogus encoding, loop in the code table; scanline %d",
+	    tif->tif_row);
+}
+
+static int
+LZWDecode(TIFF* tif, tidata_t op0, tsize_t occ0, tsample_t s)
+{
+	LZWCodecState *sp = DecoderState(tif);
+	char *op = (char*) op0;
+	long occ = (long) occ0;
+	char *tp;
+	unsigned char *bp;
+	hcode_t code;
+	int len;
+	long nbits, nextbits, nextdata, nbitsmask;
+	code_t *codep, *free_entp, *maxcodep, *oldcodep;
+
+	(void) s;
+	assert(sp != NULL);
+	/*
+	 * Restart interrupted output operation.
+	 */
+	if (sp->dec_restart) {
+		long residue;
+
+		codep = sp->dec_codep;
+		residue = codep->length - sp->dec_restart;
+		if (residue > occ) {
+			/*
+			 * Residue from previous decode is sufficient
+			 * to satisfy decode request.  Skip to the
+			 * start of the decoded string, place decoded
+			 * values in the output buffer, and return.
+			 */
+			sp->dec_restart += occ;
+			do {
+				codep = codep->next;
+			} while (--residue > occ && codep);
+			if (codep) {
+				tp = op + occ;
+				do {
+					*--tp = codep->value;
+					codep = codep->next;
+				} while (--occ && codep);
+			}
+			return (1);
+		}
+		/*
+		 * Residue satisfies only part of the decode request.
+		 */
+		op += residue, occ -= residue;
+		tp = op;
+		do {
+			int t;
+			--tp;
+			t = codep->value;
+			codep = codep->next;
+			*tp = t;
+		} while (--residue && codep);
+		sp->dec_restart = 0;
+	}
+
+	bp = (unsigned char *)tif->tif_rawcp;
+	nbits = sp->lzw_nbits;
+	nextdata = sp->lzw_nextdata;
+	nextbits = sp->lzw_nextbits;
+	nbitsmask = sp->dec_nbitsmask;
+	oldcodep = sp->dec_oldcodep;
+	free_entp = sp->dec_free_entp;
+	maxcodep = sp->dec_maxcodep;
+
+	while (occ > 0) {
+		NextCode(tif, sp, bp, code, GetNextCode);
+		if (code == CODE_EOI)
+			break;
+		if (code == CODE_CLEAR) {
+			free_entp = sp->dec_codetab + CODE_FIRST;
+			nbits = BITS_MIN;
+			nbitsmask = MAXCODE(BITS_MIN);
+			maxcodep = sp->dec_codetab + nbitsmask-1;
+			NextCode(tif, sp, bp, code, GetNextCode);
+			if (code == CODE_EOI)
+				break;
+			*op++ = (char)code, occ--;
+			oldcodep = sp->dec_codetab + code;
+			continue;
+		}
+		codep = sp->dec_codetab + code;
+
+		/*
+	 	 * Add the new entry to the code table.
+	 	 */
+		if (free_entp < &sp->dec_codetab[0] ||
+			free_entp >= &sp->dec_codetab[CSIZE]) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			"LZWDecode: Corrupted LZW table at scanline %d",
+			tif->tif_row);
+			return (0);
+		}
+
+		free_entp->next = oldcodep;
+		if (free_entp->next < &sp->dec_codetab[0] ||
+			free_entp->next >= &sp->dec_codetab[CSIZE]) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			"LZWDecode: Corrupted LZW table at scanline %d",
+			tif->tif_row);
+			return (0);
+		}
+		free_entp->firstchar = free_entp->next->firstchar;
+		free_entp->length = free_entp->next->length+1;
+		free_entp->value = (codep < free_entp) ?
+		    codep->firstchar : free_entp->firstchar;
+		if (++free_entp > maxcodep) {
+			if (++nbits > BITS_MAX)		/* should not happen */
+				nbits = BITS_MAX;
+			nbitsmask = MAXCODE(nbits);
+			maxcodep = sp->dec_codetab + nbitsmask-1;
+		}
+		oldcodep = codep;
+		if (code >= 256) {
+			/*
+		 	 * Code maps to a string, copy string
+			 * value to output (written in reverse).
+		 	 */
+			if(codep->length == 0) {
+				TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+	    		    "LZWDecode: Wrong length of decoded string: "
+			    "data probably corrupted at scanline %d",
+			    tif->tif_row);	
+			    return (0);
+			}
+			if (codep->length > occ) {
+				/*
+				 * String is too long for decode buffer,
+				 * locate portion that will fit, copy to
+				 * the decode buffer, and setup restart
+				 * logic for the next decoding call.
+				 */
+				sp->dec_codep = codep;
+				do {
+					codep = codep->next;
+				} while (codep && codep->length > occ);
+				if (codep) {
+					sp->dec_restart = occ;
+					tp = op + occ;
+					do  {
+						*--tp = codep->value;
+						codep = codep->next;
+					}  while (--occ && codep);
+					if (codep)
+						codeLoop(tif);
+				}
+				break;
+			}
+			len = codep->length;
+			tp = op + len;
+			do {
+				int t;
+				--tp;
+				t = codep->value;
+				codep = codep->next;
+				*tp = t;
+			} while (codep && tp > op);
+			if (codep) {
+			    codeLoop(tif);
+			    break;
+			}
+			op += len, occ -= len;
+		} else
+			*op++ = (char)code, occ--;
+	}
+
+	tif->tif_rawcp = (tidata_t) bp;
+	sp->lzw_nbits = (unsigned short) nbits;
+	sp->lzw_nextdata = nextdata;
+	sp->lzw_nextbits = nextbits;
+	sp->dec_nbitsmask = nbitsmask;
+	sp->dec_oldcodep = oldcodep;
+	sp->dec_free_entp = free_entp;
+	sp->dec_maxcodep = maxcodep;
+
+	if (occ > 0) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		"LZWDecode: Not enough data at scanline %d (short %d bytes)",
+		    tif->tif_row, occ);
+		return (0);
+	}
+	return (1);
+}
+
+#ifdef LZW_COMPAT
+/*
+ * Decode a "hunk of data" for old images.
+ */
+#define	GetNextCodeCompat(sp, bp, code) {			\
+	nextdata |= (unsigned long) *(bp)++ << nextbits;	\
+	nextbits += 8;						\
+	if (nextbits < nbits) {					\
+		nextdata |= (unsigned long) *(bp)++ << nextbits;\
+		nextbits += 8;					\
+	}							\
+	code = (hcode_t)(nextdata & nbitsmask);			\
+	nextdata >>= nbits;					\
+	nextbits -= nbits;					\
+}
+
+static int
+LZWDecodeCompat(TIFF* tif, tidata_t op0, tsize_t occ0, tsample_t s)
+{
+	LZWCodecState *sp = DecoderState(tif);
+	char *op = (char*) op0;
+	long occ = (long) occ0;
+	char *tp;
+	unsigned char *bp;
+	int code, nbits;
+	long nextbits, nextdata, nbitsmask;
+	code_t *codep, *free_entp, *maxcodep, *oldcodep;
+
+	(void) s;
+	assert(sp != NULL);
+	/*
+	 * Restart interrupted output operation.
+	 */
+	if (sp->dec_restart) {
+		long residue;
+
+		codep = sp->dec_codep;
+		residue = codep->length - sp->dec_restart;
+		if (residue > occ) {
+			/*
+			 * Residue from previous decode is sufficient
+			 * to satisfy decode request.  Skip to the
+			 * start of the decoded string, place decoded
+			 * values in the output buffer, and return.
+			 */
+			sp->dec_restart += occ;
+			do {
+				codep = codep->next;
+			} while (--residue > occ);
+			tp = op + occ;
+			do {
+				*--tp = codep->value;
+				codep = codep->next;
+			} while (--occ);
+			return (1);
+		}
+		/*
+		 * Residue satisfies only part of the decode request.
+		 */
+		op += residue, occ -= residue;
+		tp = op;
+		do {
+			*--tp = codep->value;
+			codep = codep->next;
+		} while (--residue);
+		sp->dec_restart = 0;
+	}
+
+	bp = (unsigned char *)tif->tif_rawcp;
+	nbits = sp->lzw_nbits;
+	nextdata = sp->lzw_nextdata;
+	nextbits = sp->lzw_nextbits;
+	nbitsmask = sp->dec_nbitsmask;
+	oldcodep = sp->dec_oldcodep;
+	free_entp = sp->dec_free_entp;
+	maxcodep = sp->dec_maxcodep;
+
+	while (occ > 0) {
+		NextCode(tif, sp, bp, code, GetNextCodeCompat);
+		if (code == CODE_EOI)
+			break;
+		if (code == CODE_CLEAR) {
+			free_entp = sp->dec_codetab + CODE_FIRST;
+			nbits = BITS_MIN;
+			nbitsmask = MAXCODE(BITS_MIN);
+			maxcodep = sp->dec_codetab + nbitsmask;
+			NextCode(tif, sp, bp, code, GetNextCodeCompat);
+			if (code == CODE_EOI)
+				break;
+			*op++ = code, occ--;
+			oldcodep = sp->dec_codetab + code;
+			continue;
+		}
+		codep = sp->dec_codetab + code;
+
+		/*
+	 	 * Add the new entry to the code table.
+	 	 */
+		if (free_entp < &sp->dec_codetab[0] ||
+			free_entp >= &sp->dec_codetab[CSIZE]) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			"LZWDecodeCompat: Corrupted LZW table at scanline %d",
+			tif->tif_row);
+			return (0);
+		}
+
+		free_entp->next = oldcodep;
+		if (free_entp->next < &sp->dec_codetab[0] ||
+			free_entp->next >= &sp->dec_codetab[CSIZE]) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			"LZWDecodeCompat: Corrupted LZW table at scanline %d",
+			tif->tif_row);
+			return (0);
+		}
+		free_entp->firstchar = free_entp->next->firstchar;
+		free_entp->length = free_entp->next->length+1;
+		free_entp->value = (codep < free_entp) ?
+		    codep->firstchar : free_entp->firstchar;
+		if (++free_entp > maxcodep) {
+			if (++nbits > BITS_MAX)		/* should not happen */
+				nbits = BITS_MAX;
+			nbitsmask = MAXCODE(nbits);
+			maxcodep = sp->dec_codetab + nbitsmask;
+		}
+		oldcodep = codep;
+		if (code >= 256) {
+			/*
+		 	 * Code maps to a string, copy string
+			 * value to output (written in reverse).
+		 	 */
+			if(codep->length == 0) {
+				TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+	    		    "LZWDecodeCompat: Wrong length of decoded "
+			    "string: data probably corrupted at scanline %d",
+			    tif->tif_row);	
+			    return (0);
+			}
+			if (codep->length > occ) {
+				/*
+				 * String is too long for decode buffer,
+				 * locate portion that will fit, copy to
+				 * the decode buffer, and setup restart
+				 * logic for the next decoding call.
+				 */
+				sp->dec_codep = codep;
+				do {
+					codep = codep->next;
+				} while (codep->length > occ);
+				sp->dec_restart = occ;
+				tp = op + occ;
+				do  {
+					*--tp = codep->value;
+					codep = codep->next;
+				}  while (--occ);
+				break;
+			}
+			op += codep->length, occ -= codep->length;
+			tp = op;
+			do {
+				*--tp = codep->value;
+			} while( (codep = codep->next) != NULL);
+		} else
+			*op++ = code, occ--;
+	}
+
+	tif->tif_rawcp = (tidata_t) bp;
+	sp->lzw_nbits = nbits;
+	sp->lzw_nextdata = nextdata;
+	sp->lzw_nextbits = nextbits;
+	sp->dec_nbitsmask = nbitsmask;
+	sp->dec_oldcodep = oldcodep;
+	sp->dec_free_entp = free_entp;
+	sp->dec_maxcodep = maxcodep;
+
+	if (occ > 0) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+	    "LZWDecodeCompat: Not enough data at scanline %d (short %d bytes)",
+		    tif->tif_row, occ);
+		return (0);
+	}
+	return (1);
+}
+#endif /* LZW_COMPAT */
+
+/*
+ * LZW Encoding.
+ */
+
+static int
+LZWSetupEncode(TIFF* tif)
+{
+	LZWCodecState* sp = EncoderState(tif);
+	static const char module[] = "LZWSetupEncode";
+
+	assert(sp != NULL);
+	sp->enc_hashtab = (hash_t*) _TIFFmalloc(HSIZE*sizeof (hash_t));
+	if (sp->enc_hashtab == NULL) {
+		TIFFErrorExt(tif->tif_clientdata, module, "No space for LZW hash table");
+		return (0);
+	}
+	return (1);
+}
+
+/*
+ * Reset encoding state at the start of a strip.
+ */
+static int
+LZWPreEncode(TIFF* tif, tsample_t s)
+{
+	LZWCodecState *sp = EncoderState(tif);
+
+	(void) s;
+	assert(sp != NULL);
+	sp->lzw_nbits = BITS_MIN;
+	sp->lzw_maxcode = MAXCODE(BITS_MIN);
+	sp->lzw_free_ent = CODE_FIRST;
+	sp->lzw_nextbits = 0;
+	sp->lzw_nextdata = 0;
+	sp->enc_checkpoint = CHECK_GAP;
+	sp->enc_ratio = 0;
+	sp->enc_incount = 0;
+	sp->enc_outcount = 0;
+	/*
+	 * The 4 here insures there is space for 2 max-sized
+	 * codes in LZWEncode and LZWPostDecode.
+	 */
+	sp->enc_rawlimit = tif->tif_rawdata + tif->tif_rawdatasize-1 - 4;
+	cl_hash(sp);		/* clear hash table */
+	sp->enc_oldcode = (hcode_t) -1;	/* generates CODE_CLEAR in LZWEncode */
+	return (1);
+}
+
+#define	CALCRATIO(sp, rat) {					\
+	if (incount > 0x007fffff) { /* NB: shift will overflow */\
+		rat = outcount >> 8;				\
+		rat = (rat == 0 ? 0x7fffffff : incount/rat);	\
+	} else							\
+		rat = (incount<<8) / outcount;			\
+}
+#define	PutNextCode(op, c) {					\
+	nextdata = (nextdata << nbits) | c;			\
+	nextbits += nbits;					\
+	*op++ = (unsigned char)(nextdata >> (nextbits-8));		\
+	nextbits -= 8;						\
+	if (nextbits >= 8) {					\
+		*op++ = (unsigned char)(nextdata >> (nextbits-8));	\
+		nextbits -= 8;					\
+	}							\
+	outcount += nbits;					\
+}
+
+/*
+ * Encode a chunk of pixels.
+ *
+ * Uses an 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 CODE_CLEAR is generated
+ * for the decoder. 
+ */
+static int
+LZWEncode(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s)
+{
+	register LZWCodecState *sp = EncoderState(tif);
+	register long fcode;
+	register hash_t *hp;
+	register int h, c;
+	hcode_t ent;
+	long disp;
+	long incount, outcount, checkpoint;
+	long nextdata, nextbits;
+	int free_ent, maxcode, nbits;
+	tidata_t op, limit;
+
+	(void) s;
+	if (sp == NULL)
+		return (0);
+	/*
+	 * Load local state.
+	 */
+	incount = sp->enc_incount;
+	outcount = sp->enc_outcount;
+	checkpoint = sp->enc_checkpoint;
+	nextdata = sp->lzw_nextdata;
+	nextbits = sp->lzw_nextbits;
+	free_ent = sp->lzw_free_ent;
+	maxcode = sp->lzw_maxcode;
+	nbits = sp->lzw_nbits;
+	op = tif->tif_rawcp;
+	limit = sp->enc_rawlimit;
+	ent = sp->enc_oldcode;
+
+	if (ent == (hcode_t) -1 && cc > 0) {
+		/*
+		 * NB: This is safe because it can only happen
+		 *     at the start of a strip where we know there
+		 *     is space in the data buffer.
+		 */
+		PutNextCode(op, CODE_CLEAR);
+		ent = *bp++; cc--; incount++;
+	}
+	while (cc > 0) {
+		c = *bp++; cc--; incount++;
+		fcode = ((long)c << BITS_MAX) + ent;
+		h = (c << HSHIFT) ^ ent;	/* xor hashing */
+#ifdef _WINDOWS
+		/*
+		 * Check hash index for an overflow.
+		 */
+		if (h >= HSIZE)
+			h -= HSIZE;
+#endif
+		hp = &sp->enc_hashtab[h];
+		if (hp->hash == fcode) {
+			ent = hp->code;
+			continue;
+		}
+		if (hp->hash >= 0) {
+			/*
+			 * Primary hash failed, check secondary hash.
+			 */
+			disp = HSIZE - h;
+			if (h == 0)
+				disp = 1;
+			do {
+				/*
+				 * Avoid pointer arithmetic 'cuz of
+				 * wraparound problems with segments.
+				 */
+				if ((h -= disp) < 0)
+					h += HSIZE;
+				hp = &sp->enc_hashtab[h];
+				if (hp->hash == fcode) {
+					ent = hp->code;
+					goto hit;
+				}
+			} while (hp->hash >= 0);
+		}
+		/*
+		 * New entry, emit code and add to table.
+		 */
+		/*
+		 * Verify there is space in the buffer for the code
+		 * and any potential Clear code that might be emitted
+		 * below.  The value of limit is setup so that there
+		 * are at least 4 bytes free--room for 2 codes.
+		 */
+		if (op > limit) {
+			tif->tif_rawcc = (tsize_t)(op - tif->tif_rawdata);
+			TIFFFlushData1(tif);
+			op = tif->tif_rawdata;
+		}
+		PutNextCode(op, ent);
+		ent = c;
+		hp->code = free_ent++;
+		hp->hash = fcode;
+		if (free_ent == CODE_MAX-1) {
+			/* table is full, emit clear code and reset */
+			cl_hash(sp);
+			sp->enc_ratio = 0;
+			incount = 0;
+			outcount = 0;
+			free_ent = CODE_FIRST;
+			PutNextCode(op, CODE_CLEAR);
+			nbits = BITS_MIN;
+			maxcode = MAXCODE(BITS_MIN);
+		} else {
+			/*
+			 * If the next entry is going to be too big for
+			 * the code size, then increase it, if possible.
+			 */
+			if (free_ent > maxcode) {
+				nbits++;
+				assert(nbits <= BITS_MAX);
+				maxcode = (int) MAXCODE(nbits);
+			} else if (incount >= checkpoint) {
+				long rat;
+				/*
+				 * Check compression ratio and, if things seem
+				 * to be slipping, clear the hash table and
+				 * reset state.  The compression ratio is a
+				 * 24+8-bit fractional number.
+				 */
+				checkpoint = incount+CHECK_GAP;
+				CALCRATIO(sp, rat);
+				if (rat <= sp->enc_ratio) {
+					cl_hash(sp);
+					sp->enc_ratio = 0;
+					incount = 0;
+					outcount = 0;
+					free_ent = CODE_FIRST;
+					PutNextCode(op, CODE_CLEAR);
+					nbits = BITS_MIN;
+					maxcode = MAXCODE(BITS_MIN);
+				} else
+					sp->enc_ratio = rat;
+			}
+		}
+	hit:
+		;
+	}
+
+	/*
+	 * Restore global state.
+	 */
+	sp->enc_incount = incount;
+	sp->enc_outcount = outcount;
+	sp->enc_checkpoint = checkpoint;
+	sp->enc_oldcode = ent;
+	sp->lzw_nextdata = nextdata;
+	sp->lzw_nextbits = nextbits;
+	sp->lzw_free_ent = free_ent;
+	sp->lzw_maxcode = maxcode;
+	sp->lzw_nbits = nbits;
+	tif->tif_rawcp = op;
+	return (1);
+}
+
+/*
+ * Finish off an encoded strip by flushing the last
+ * string and tacking on an End Of Information code.
+ */
+static int
+LZWPostEncode(TIFF* tif)
+{
+	register LZWCodecState *sp = EncoderState(tif);
+	tidata_t op = tif->tif_rawcp;
+	long nextbits = sp->lzw_nextbits;
+	long nextdata = sp->lzw_nextdata;
+	long outcount = sp->enc_outcount;
+	int nbits = sp->lzw_nbits;
+
+	if (op > sp->enc_rawlimit) {
+		tif->tif_rawcc = (tsize_t)(op - tif->tif_rawdata);
+		TIFFFlushData1(tif);
+		op = tif->tif_rawdata;
+	}
+	if (sp->enc_oldcode != (hcode_t) -1) {
+		PutNextCode(op, sp->enc_oldcode);
+		sp->enc_oldcode = (hcode_t) -1;
+	}
+	PutNextCode(op, CODE_EOI);
+	if (nextbits > 0) 
+		*op++ = (unsigned char)(nextdata << (8-nextbits));
+	tif->tif_rawcc = (tsize_t)(op - tif->tif_rawdata);
+	return (1);
+}
+
+/*
+ * Reset encoding hash table.
+ */
+static void
+cl_hash(LZWCodecState* sp)
+{
+	register hash_t *hp = &sp->enc_hashtab[HSIZE-1];
+	register long i = HSIZE-8;
+
+ 	do {
+		i -= 8;
+		hp[-7].hash = -1;
+		hp[-6].hash = -1;
+		hp[-5].hash = -1;
+		hp[-4].hash = -1;
+		hp[-3].hash = -1;
+		hp[-2].hash = -1;
+		hp[-1].hash = -1;
+		hp[ 0].hash = -1;
+		hp -= 8;
+	} while (i >= 0);
+    	for (i += 8; i > 0; i--, hp--)
+		hp->hash = -1;
+}
+
+static void
+LZWCleanup(TIFF* tif)
+{
+	(void)TIFFPredictorCleanup(tif);
+
+	assert(tif->tif_data != 0);
+
+	if (DecoderState(tif)->dec_codetab)
+		_TIFFfree(DecoderState(tif)->dec_codetab);
+
+	if (EncoderState(tif)->enc_hashtab)
+		_TIFFfree(EncoderState(tif)->enc_hashtab);
+
+	_TIFFfree(tif->tif_data);
+	tif->tif_data = NULL;
+
+	_TIFFSetDefaultCompressionState(tif);
+}
+
+int
+TIFFInitLZW(TIFF* tif, int scheme)
+{
+	assert(scheme == COMPRESSION_LZW);
+	/*
+	 * Allocate state block so tag methods have storage to record values.
+	 */
+	tif->tif_data = (tidata_t) _TIFFmalloc(sizeof (LZWCodecState));
+	if (tif->tif_data == NULL)
+		goto bad;
+	DecoderState(tif)->dec_codetab = NULL;
+	DecoderState(tif)->dec_decode = NULL;
+	EncoderState(tif)->enc_hashtab = NULL;
+        LZWState(tif)->rw_mode = tif->tif_mode;
+
+	/*
+	 * Install codec methods.
+	 */
+	tif->tif_setupdecode = LZWSetupDecode;
+	tif->tif_predecode = LZWPreDecode;
+	tif->tif_decoderow = LZWDecode;
+	tif->tif_decodestrip = LZWDecode;
+	tif->tif_decodetile = LZWDecode;
+	tif->tif_setupencode = LZWSetupEncode;
+	tif->tif_preencode = LZWPreEncode;
+	tif->tif_postencode = LZWPostEncode;
+	tif->tif_encoderow = LZWEncode;
+	tif->tif_encodestrip = LZWEncode;
+	tif->tif_encodetile = LZWEncode;
+	tif->tif_cleanup = LZWCleanup;
+	/*
+	 * Setup predictor setup.
+	 */
+	(void) TIFFPredictorInit(tif);
+	return (1);
+bad:
+	TIFFErrorExt(tif->tif_clientdata, "TIFFInitLZW", 
+		     "No space for LZW state block");
+	return (0);
+}
+
+/*
+ * Copyright (c) 1985, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods, derived from original work by Spencer Thomas
+ * and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+#endif /* LZW_SUPPORT */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_next.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_next.c
new file mode 100644
index 0000000000..89df304a72
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_next.c
@@ -0,0 +1,144 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#include "tiffiop.h"
+#ifdef NEXT_SUPPORT
+/*
+ * TIFF Library.
+ *
+ * NeXT 2-bit Grey Scale Compression Algorithm Support
+ */
+
+#define SETPIXEL(op, v) {			\
+	switch (npixels++ & 3) {		\
+	case 0:	op[0]  = (unsigned char) ((v) << 6); break;	\
+	case 1:	op[0] |= (v) << 4; break;	\
+	case 2:	op[0] |= (v) << 2; break;	\
+	case 3:	*op++ |= (v);	   break;	\
+	}					\
+}
+
+#define LITERALROW	0x00
+#define LITERALSPAN	0x40
+#define WHITE   	((1<<2)-1)
+
+static int
+NeXTDecode(TIFF* tif, tidata_t buf, tsize_t occ, tsample_t s)
+{
+	register unsigned char *bp, *op;
+	register tsize_t cc;
+	register int n;
+	tidata_t row;
+	tsize_t scanline;
+
+	(void) s;
+	/*
+	 * Each scanline is assumed to start off as all
+	 * white (we assume a PhotometricInterpretation
+	 * of ``min-is-black'').
+	 */
+	for (op = buf, cc = occ; cc-- > 0;)
+		*op++ = 0xff;
+
+	bp = (unsigned char *)tif->tif_rawcp;
+	cc = tif->tif_rawcc;
+	scanline = tif->tif_scanlinesize;
+	for (row = buf; (long)occ > 0; occ -= scanline, row += scanline) {
+		n = *bp++, cc--;
+		switch (n) {
+		case LITERALROW:
+			/*
+			 * The entire scanline is given as literal values.
+			 */
+			if (cc < scanline)
+				goto bad;
+			_TIFFmemcpy(row, bp, scanline);
+			bp += scanline;
+			cc -= scanline;
+			break;
+		case LITERALSPAN: {
+			int off;
+			/*
+			 * The scanline has a literal span
+			 * that begins at some offset.
+			 */
+			off = (bp[0] * 256) + bp[1];
+			n = (bp[2] * 256) + bp[3];
+			if (cc < 4+n || off+n > scanline)
+				goto bad;
+			_TIFFmemcpy(row+off, bp+4, n);
+			bp += 4+n;
+			cc -= 4+n;
+			break;
+		}
+		default: {
+			register int npixels = 0, grey;
+			unsigned long imagewidth = tif->tif_dir.td_imagewidth;
+
+			/*
+			 * The scanline is composed of a sequence
+			 * of constant color ``runs''.  We shift
+			 * into ``run mode'' and interpret bytes
+			 * as codes of the form <color><npixels>
+			 * until we've filled the scanline.
+			 */
+			op = row;
+			for (;;) {
+				grey = (n>>6) & 0x3;
+				n &= 0x3f;
+				while (n-- > 0)
+					SETPIXEL(op, grey);
+				if (npixels >= (int) imagewidth)
+					break;
+				if (cc == 0)
+					goto bad;
+				n = *bp++, cc--;
+			}
+			break;
+		}
+		}
+	}
+	tif->tif_rawcp = (tidata_t) bp;
+	tif->tif_rawcc = cc;
+	return (1);
+bad:
+	TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "NeXTDecode: Not enough data for scanline %ld",
+	    (long) tif->tif_row);
+	return (0);
+}
+
+int
+TIFFInitNeXT(TIFF* tif, int scheme)
+{
+	(void) scheme;
+	tif->tif_decoderow = NeXTDecode;
+	tif->tif_decodestrip = NeXTDecode;
+	tif->tif_decodetile = NeXTDecode;
+	return (1);
+}
+#endif /* NEXT_SUPPORT */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_open.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_open.c
new file mode 100644
index 0000000000..c307cb12c1
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_open.c
@@ -0,0 +1,683 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ */
+#include "tiffiop.h"
+
+static const long typemask[13] = {
+	(long)0L,		/* TIFF_NOTYPE */
+	(long)0x000000ffL,	/* TIFF_BYTE */
+	(long)0xffffffffL,	/* TIFF_ASCII */
+	(long)0x0000ffffL,	/* TIFF_SHORT */
+	(long)0xffffffffL,	/* TIFF_LONG */
+	(long)0xffffffffL,	/* TIFF_RATIONAL */
+	(long)0x000000ffL,	/* TIFF_SBYTE */
+	(long)0x000000ffL,	/* TIFF_UNDEFINED */
+	(long)0x0000ffffL,	/* TIFF_SSHORT */
+	(long)0xffffffffL,	/* TIFF_SLONG */
+	(long)0xffffffffL,	/* TIFF_SRATIONAL */
+	(long)0xffffffffL,	/* TIFF_FLOAT */
+	(long)0xffffffffL,	/* TIFF_DOUBLE */
+};
+static const int bigTypeshift[13] = {
+	0,		/* TIFF_NOTYPE */
+	24,		/* TIFF_BYTE */
+	0,		/* TIFF_ASCII */
+	16,		/* TIFF_SHORT */
+	0,		/* TIFF_LONG */
+	0,		/* TIFF_RATIONAL */
+	24,		/* TIFF_SBYTE */
+	24,		/* TIFF_UNDEFINED */
+	16,		/* TIFF_SSHORT */
+	0,		/* TIFF_SLONG */
+	0,		/* TIFF_SRATIONAL */
+	0,		/* TIFF_FLOAT */
+	0,		/* TIFF_DOUBLE */
+};
+static const int litTypeshift[13] = {
+	0,		/* TIFF_NOTYPE */
+	0,		/* TIFF_BYTE */
+	0,		/* TIFF_ASCII */
+	0,		/* TIFF_SHORT */
+	0,		/* TIFF_LONG */
+	0,		/* TIFF_RATIONAL */
+	0,		/* TIFF_SBYTE */
+	0,		/* TIFF_UNDEFINED */
+	0,		/* TIFF_SSHORT */
+	0,		/* TIFF_SLONG */
+	0,		/* TIFF_SRATIONAL */
+	0,		/* TIFF_FLOAT */
+	0,		/* TIFF_DOUBLE */
+};
+
+/*
+ * Dummy functions to fill the omitted client procedures.
+ */
+static int
+_tiffDummyMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize)
+{
+	(void) fd; (void) pbase; (void) psize;
+	return (0);
+}
+
+static void
+_tiffDummyUnmapProc(thandle_t fd, tdata_t base, toff_t size)
+{
+	(void) fd; (void) base; (void) size;
+}
+
+/*
+ * Initialize the shift & mask tables, and the
+ * byte swapping state according to the file
+ * contents and the machine architecture.
+ */
+static void
+TIFFInitOrder(TIFF* tif, int magic)
+{
+	tif->tif_typemask = typemask;
+	if (magic == TIFF_BIGENDIAN) {
+		tif->tif_typeshift = bigTypeshift;
+#ifndef WORDS_BIGENDIAN
+		tif->tif_flags |= TIFF_SWAB;
+#endif
+	} else {
+		tif->tif_typeshift = litTypeshift;
+#ifdef WORDS_BIGENDIAN
+		tif->tif_flags |= TIFF_SWAB;
+#endif
+	}
+}
+
+int
+_TIFFgetMode(const char* mode, const char* module)
+{
+	int m = -1;
+
+	switch (mode[0]) {
+	case 'r':
+		m = O_RDONLY;
+		if (mode[1] == '+')
+			m = O_RDWR;
+		break;
+	case 'w':
+	case 'a':
+		m = O_RDWR|O_CREAT;
+		if (mode[0] == 'w')
+			m |= O_TRUNC;
+		break;
+	default:
+		TIFFErrorExt(0, module, "\"%s\": Bad mode", mode);
+		break;
+	}
+	return (m);
+}
+
+TIFF*
+TIFFClientOpen(
+	const char* name, const char* mode,
+	thandle_t clientdata,
+	TIFFReadWriteProc readproc,
+	TIFFReadWriteProc writeproc,
+	TIFFSeekProc seekproc,
+	TIFFCloseProc closeproc,
+	TIFFSizeProc sizeproc,
+	TIFFMapFileProc mapproc,
+	TIFFUnmapFileProc unmapproc
+)
+{
+	static const char module[] = "TIFFClientOpen";
+	TIFF *tif;
+	int m;
+	const char* cp;
+
+	m = _TIFFgetMode(mode, module);
+	if (m == -1)
+		goto bad2;
+	tif = (TIFF *)_TIFFmalloc(sizeof (TIFF) + strlen(name) + 1);
+	if (tif == NULL) {
+		TIFFErrorExt(clientdata, module, "%s: Out of memory (TIFF structure)", name);
+		goto bad2;
+	}
+	_TIFFmemset(tif, 0, sizeof (*tif));
+	tif->tif_name = (char *)tif + sizeof (TIFF);
+	strcpy(tif->tif_name, name);
+	tif->tif_mode = m &~ (O_CREAT|O_TRUNC);
+	tif->tif_curdir = (tdir_t) -1;		/* non-existent directory */
+	tif->tif_curoff = 0;
+	tif->tif_curstrip = (tstrip_t) -1;	/* invalid strip */
+	tif->tif_row = (uint32) -1;		/* read/write pre-increment */
+	tif->tif_clientdata = clientdata;
+	if (!readproc || !writeproc || !seekproc || !closeproc || !sizeproc) {
+		TIFFErrorExt(clientdata, module,
+			  "One of the client procedures is NULL pointer.");
+		goto bad2;
+	}
+	tif->tif_readproc = readproc;
+	tif->tif_writeproc = writeproc;
+	tif->tif_seekproc = seekproc;
+	tif->tif_closeproc = closeproc;
+	tif->tif_sizeproc = sizeproc;
+        if (mapproc)
+		tif->tif_mapproc = mapproc;
+	else
+		tif->tif_mapproc = _tiffDummyMapProc;
+	if (unmapproc)
+		tif->tif_unmapproc = unmapproc;
+	else
+		tif->tif_unmapproc = _tiffDummyUnmapProc;
+	_TIFFSetDefaultCompressionState(tif);	/* setup default state */
+	/*
+	 * Default is to return data MSB2LSB and enable the
+	 * use of memory-mapped files and strip chopping when
+	 * a file is opened read-only.
+	 */
+	tif->tif_flags = FILLORDER_MSB2LSB;
+	if (m == O_RDONLY )
+		tif->tif_flags |= TIFF_MAPPED;
+
+#ifdef STRIPCHOP_DEFAULT
+	if (m == O_RDONLY || m == O_RDWR)
+		tif->tif_flags |= STRIPCHOP_DEFAULT;
+#endif
+
+	/*
+	 * Process library-specific flags in the open mode string.
+	 * The following flags may be used to control intrinsic library
+	 * behaviour that may or may not be desirable (usually for
+	 * compatibility with some application that claims to support
+	 * TIFF but only supports some braindead idea of what the
+	 * vendor thinks TIFF is):
+	 *
+	 * 'l'		use little-endian byte order for creating a file
+	 * 'b'		use big-endian byte order for creating a file
+	 * 'L'		read/write information using LSB2MSB bit order
+	 * 'B'		read/write information using MSB2LSB bit order
+	 * 'H'		read/write information using host bit order
+	 * 'M'		enable use of memory-mapped files when supported
+	 * 'm'		disable use of memory-mapped files
+	 * 'C'		enable strip chopping support when reading
+	 * 'c'		disable strip chopping support
+	 * 'h'		read TIFF header only, do not load the first IFD
+	 *
+	 * The use of the 'l' and 'b' flags is strongly discouraged.
+	 * These flags are provided solely because numerous vendors,
+	 * typically on the PC, do not correctly support TIFF; they
+	 * only support the Intel little-endian byte order.  This
+	 * support is not configured by default because it supports
+	 * the violation of the TIFF spec that says that readers *MUST*
+	 * support both byte orders.  It is strongly recommended that
+	 * you not use this feature except to deal with busted apps
+	 * that write invalid TIFF.  And even in those cases you should
+	 * bang on the vendors to fix their software.
+	 *
+	 * The 'L', 'B', and 'H' flags are intended for applications
+	 * that can optimize operations on data by using a particular
+	 * bit order.  By default the library returns data in MSB2LSB
+	 * bit order for compatibiltiy with older versions of this
+	 * library.  Returning data in the bit order of the native cpu
+	 * makes the most sense but also requires applications to check
+	 * the value of the FillOrder tag; something they probably do
+	 * not do right now.
+	 *
+	 * The 'M' and 'm' flags are provided because some virtual memory
+	 * systems exhibit poor behaviour when large images are mapped.
+	 * These options permit clients to control the use of memory-mapped
+	 * files on a per-file basis.
+	 *
+	 * The 'C' and 'c' flags are provided because the library support
+	 * for chopping up large strips into multiple smaller strips is not
+	 * application-transparent and as such can cause problems.  The 'c'
+	 * option permits applications that only want to look at the tags,
+	 * for example, to get the unadulterated TIFF tag information.
+	 */
+	for (cp = mode; *cp; cp++)
+		switch (*cp) {
+		case 'b':
+#ifndef WORDS_BIGENDIAN
+		    if (m&O_CREAT)
+				tif->tif_flags |= TIFF_SWAB;
+#endif
+			break;
+		case 'l':
+#ifdef WORDS_BIGENDIAN
+			if ((m&O_CREAT))
+				tif->tif_flags |= TIFF_SWAB;
+#endif
+			break;
+		case 'B':
+			tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
+			    FILLORDER_MSB2LSB;
+			break;
+		case 'L':
+			tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
+			    FILLORDER_LSB2MSB;
+			break;
+		case 'H':
+			tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
+			    HOST_FILLORDER;
+			break;
+		case 'M':
+			if (m == O_RDONLY)
+				tif->tif_flags |= TIFF_MAPPED;
+			break;
+		case 'm':
+			if (m == O_RDONLY)
+				tif->tif_flags &= ~TIFF_MAPPED;
+			break;
+		case 'C':
+			if (m == O_RDONLY)
+				tif->tif_flags |= TIFF_STRIPCHOP;
+			break;
+		case 'c':
+			if (m == O_RDONLY)
+				tif->tif_flags &= ~TIFF_STRIPCHOP;
+			break;
+		case 'h':
+			tif->tif_flags |= TIFF_HEADERONLY;
+			break;
+		}
+	/*
+	 * Read in TIFF header.
+	 */
+	if (tif->tif_mode & O_TRUNC ||
+	    !ReadOK(tif, &tif->tif_header, sizeof (TIFFHeader))) {
+		if (tif->tif_mode == O_RDONLY) {
+			TIFFErrorExt(tif->tif_clientdata, name, "Cannot read TIFF header");
+			goto bad;
+		}
+		/*
+		 * Setup header and write.
+		 */
+#ifdef WORDS_BIGENDIAN
+		tif->tif_header.tiff_magic = tif->tif_flags & TIFF_SWAB
+		    ? TIFF_LITTLEENDIAN : TIFF_BIGENDIAN;
+#else
+		tif->tif_header.tiff_magic = tif->tif_flags & TIFF_SWAB
+		    ? TIFF_BIGENDIAN : TIFF_LITTLEENDIAN;
+#endif
+		tif->tif_header.tiff_version = TIFF_VERSION;
+		if (tif->tif_flags & TIFF_SWAB)
+			TIFFSwabShort(&tif->tif_header.tiff_version);
+		tif->tif_header.tiff_diroff = 0;	/* filled in later */
+
+
+                /*
+                 * The doc for "fopen" for some STD_C_LIBs says that if you 
+                 * open a file for modify ("+"), then you must fseek (or 
+                 * fflush?) between any freads and fwrites.  This is not
+                 * necessary on most systems, but has been shown to be needed
+                 * on Solaris. 
+                 */
+                TIFFSeekFile( tif, 0, SEEK_SET );
+               
+		if (!WriteOK(tif, &tif->tif_header, sizeof (TIFFHeader))) {
+			TIFFErrorExt(tif->tif_clientdata, name, "Error writing TIFF header");
+			goto bad;
+		}
+		/*
+		 * Setup the byte order handling.
+		 */
+		TIFFInitOrder(tif, tif->tif_header.tiff_magic);
+		/*
+		 * Setup default directory.
+		 */
+		if (!TIFFDefaultDirectory(tif))
+			goto bad;
+		tif->tif_diroff = 0;
+		tif->tif_dirlist = NULL;
+		tif->tif_dirnumber = 0;
+		return (tif);
+	}
+	/*
+	 * Setup the byte order handling.
+	 */
+	if (tif->tif_header.tiff_magic != TIFF_BIGENDIAN &&
+	    tif->tif_header.tiff_magic != TIFF_LITTLEENDIAN
+#if MDI_SUPPORT
+	    &&
+#if HOST_BIGENDIAN
+	    tif->tif_header.tiff_magic != MDI_BIGENDIAN
+#else
+	    tif->tif_header.tiff_magic != MDI_LITTLEENDIAN
+#endif
+	    ) {
+		TIFFErrorExt(tif->tif_clientdata, name,  "Not a TIFF or MDI file, bad magic number %d (0x%x)",
+#else
+	    ) {
+		TIFFErrorExt(tif->tif_clientdata, name,  "Not a TIFF file, bad magic number %d (0x%x)",
+#endif
+		    tif->tif_header.tiff_magic,
+		    tif->tif_header.tiff_magic);
+		goto bad;
+	}
+	TIFFInitOrder(tif, tif->tif_header.tiff_magic);
+	/*
+	 * Swap header if required.
+	 */
+	if (tif->tif_flags & TIFF_SWAB) {
+		TIFFSwabShort(&tif->tif_header.tiff_version);
+		TIFFSwabLong(&tif->tif_header.tiff_diroff);
+	}
+	/*
+	 * Now check version (if needed, it's been byte-swapped).
+	 * Note that this isn't actually a version number, it's a
+	 * magic number that doesn't change (stupid).
+	 */
+	if (tif->tif_header.tiff_version == TIFF_BIGTIFF_VERSION) {
+		TIFFErrorExt(tif->tif_clientdata, name,
+                          "This is a BigTIFF file.  This format not supported\n"
+                          "by this version of libtiff." );
+		goto bad;
+	}
+	if (tif->tif_header.tiff_version != TIFF_VERSION) {
+		TIFFErrorExt(tif->tif_clientdata, name,
+		    "Not a TIFF file, bad version number %d (0x%x)",
+		    tif->tif_header.tiff_version,
+		    tif->tif_header.tiff_version);
+		goto bad;
+	}
+	tif->tif_flags |= TIFF_MYBUFFER;
+	tif->tif_rawcp = tif->tif_rawdata = 0;
+	tif->tif_rawdatasize = 0;
+
+	/*
+	 * Sometimes we do not want to read the first directory (for example,
+	 * it may be broken) and want to proceed to other directories. I this
+	 * case we use the TIFF_HEADERONLY flag to open file and return
+	 * immediately after reading TIFF header.
+	 */
+	if (tif->tif_flags & TIFF_HEADERONLY)
+		return (tif);
+
+	/*
+	 * Setup initial directory.
+	 */
+	switch (mode[0]) {
+	case 'r':
+		tif->tif_nextdiroff = tif->tif_header.tiff_diroff;
+		/*
+		 * Try to use a memory-mapped file if the client
+		 * has not explicitly suppressed usage with the
+		 * 'm' flag in the open mode (see above).
+		 */
+		if ((tif->tif_flags & TIFF_MAPPED) &&
+	!TIFFMapFileContents(tif, (tdata_t*) &tif->tif_base, &tif->tif_size))
+			tif->tif_flags &= ~TIFF_MAPPED;
+		if (TIFFReadDirectory(tif)) {
+			tif->tif_rawcc = -1;
+			tif->tif_flags |= TIFF_BUFFERSETUP;
+			return (tif);
+		}
+		break;
+	case 'a':
+		/*
+		 * New directories are automatically append
+		 * to the end of the directory chain when they
+		 * are written out (see TIFFWriteDirectory).
+		 */
+		if (!TIFFDefaultDirectory(tif))
+			goto bad;
+		return (tif);
+	}
+bad:
+	tif->tif_mode = O_RDONLY;	/* XXX avoid flush */
+        TIFFCleanup(tif);
+bad2:
+	return ((TIFF*)0);
+}
+
+/*
+ * Query functions to access private data.
+ */
+
+/*
+ * Return open file's name.
+ */
+const char *
+TIFFFileName(TIFF* tif)
+{
+	return (tif->tif_name);
+}
+
+/*
+ * Set the file name.
+ */
+const char *
+TIFFSetFileName(TIFF* tif, const char *name)
+{
+	const char* old_name = tif->tif_name;
+	tif->tif_name = (char *)name;
+	return (old_name);
+}
+
+/*
+ * Return open file's I/O descriptor.
+ */
+int
+TIFFFileno(TIFF* tif)
+{
+	return (tif->tif_fd);
+}
+
+/*
+ * Set open file's I/O descriptor, and return previous value.
+ */
+int
+TIFFSetFileno(TIFF* tif, int fd)
+{
+        int old_fd = tif->tif_fd;
+	tif->tif_fd = fd;
+	return old_fd;
+}
+
+/*
+ * Return open file's clientdata.
+ */
+thandle_t
+TIFFClientdata(TIFF* tif)
+{
+	return (tif->tif_clientdata);
+}
+
+/*
+ * Set open file's clientdata, and return previous value.
+ */
+thandle_t
+TIFFSetClientdata(TIFF* tif, thandle_t newvalue)
+{
+	thandle_t m = tif->tif_clientdata;
+	tif->tif_clientdata = newvalue;
+	return m;
+}
+
+/*
+ * Return read/write mode.
+ */
+int
+TIFFGetMode(TIFF* tif)
+{
+	return (tif->tif_mode);
+}
+
+/*
+ * Return read/write mode.
+ */
+int
+TIFFSetMode(TIFF* tif, int mode)
+{
+	int old_mode = tif->tif_mode;
+	tif->tif_mode = mode;
+	return (old_mode);
+}
+
+/*
+ * Return nonzero if file is organized in
+ * tiles; zero if organized as strips.
+ */
+int
+TIFFIsTiled(TIFF* tif)
+{
+	return (isTiled(tif));
+}
+
+/*
+ * Return current row being read/written.
+ */
+uint32
+TIFFCurrentRow(TIFF* tif)
+{
+	return (tif->tif_row);
+}
+
+/*
+ * Return index of the current directory.
+ */
+tdir_t
+TIFFCurrentDirectory(TIFF* tif)
+{
+	return (tif->tif_curdir);
+}
+
+/*
+ * Return current strip.
+ */
+tstrip_t
+TIFFCurrentStrip(TIFF* tif)
+{
+	return (tif->tif_curstrip);
+}
+
+/*
+ * Return current tile.
+ */
+ttile_t
+TIFFCurrentTile(TIFF* tif)
+{
+	return (tif->tif_curtile);
+}
+
+/*
+ * Return nonzero if the file has byte-swapped data.
+ */
+int
+TIFFIsByteSwapped(TIFF* tif)
+{
+	return ((tif->tif_flags & TIFF_SWAB) != 0);
+}
+
+/*
+ * Return nonzero if the data is returned up-sampled.
+ */
+int
+TIFFIsUpSampled(TIFF* tif)
+{
+	return (isUpSampled(tif));
+}
+
+/*
+ * Return nonzero if the data is returned in MSB-to-LSB bit order.
+ */
+int
+TIFFIsMSB2LSB(TIFF* tif)
+{
+	return (isFillOrder(tif, FILLORDER_MSB2LSB));
+}
+
+/*
+ * Return nonzero if given file was written in big-endian order.
+ */
+int
+TIFFIsBigEndian(TIFF* tif)
+{
+	return (tif->tif_header.tiff_magic == TIFF_BIGENDIAN);
+}
+
+/*
+ * Return pointer to file read method.
+ */
+TIFFReadWriteProc
+TIFFGetReadProc(TIFF* tif)
+{
+	return (tif->tif_readproc);
+}
+
+/*
+ * Return pointer to file write method.
+ */
+TIFFReadWriteProc
+TIFFGetWriteProc(TIFF* tif)
+{
+	return (tif->tif_writeproc);
+}
+
+/*
+ * Return pointer to file seek method.
+ */
+TIFFSeekProc
+TIFFGetSeekProc(TIFF* tif)
+{
+	return (tif->tif_seekproc);
+}
+
+/*
+ * Return pointer to file close method.
+ */
+TIFFCloseProc
+TIFFGetCloseProc(TIFF* tif)
+{
+	return (tif->tif_closeproc);
+}
+
+/*
+ * Return pointer to file size requesting method.
+ */
+TIFFSizeProc
+TIFFGetSizeProc(TIFF* tif)
+{
+	return (tif->tif_sizeproc);
+}
+
+/*
+ * Return pointer to memory mapping method.
+ */
+TIFFMapFileProc
+TIFFGetMapFileProc(TIFF* tif)
+{
+	return (tif->tif_mapproc);
+}
+
+/*
+ * Return pointer to memory unmapping method.
+ */
+TIFFUnmapFileProc
+TIFFGetUnmapFileProc(TIFF* tif)
+{
+	return (tif->tif_unmapproc);
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_packbits.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_packbits.c
new file mode 100644
index 0000000000..7cd4dce9ad
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_packbits.c
@@ -0,0 +1,293 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#include "tiffiop.h"
+#ifdef PACKBITS_SUPPORT
+/*
+ * TIFF Library.
+ *
+ * PackBits Compression Algorithm Support
+ */
+#include <stdio.h>
+
+static int
+PackBitsPreEncode(TIFF* tif, tsample_t s)
+{
+	(void) s;
+
+        if (!(tif->tif_data = (tidata_t)_TIFFmalloc(sizeof(tsize_t))))
+		return (0);
+	/*
+	 * Calculate the scanline/tile-width size in bytes.
+	 */
+	if (isTiled(tif))
+		*(tsize_t*)tif->tif_data = TIFFTileRowSize(tif);
+	else
+		*(tsize_t*)tif->tif_data = TIFFScanlineSize(tif);
+	return (1);
+}
+
+static int
+PackBitsPostEncode(TIFF* tif)
+{
+        if (tif->tif_data)
+            _TIFFfree(tif->tif_data);
+	return (1);
+}
+
+/*
+ * NB: tidata is the type representing *(tidata_t);
+ *     if tidata_t is made signed then this type must
+ *     be adjusted accordingly.
+ */
+typedef unsigned char tidata;
+
+/*
+ * Encode a run of pixels.
+ */
+static int
+PackBitsEncode(TIFF* tif, tidata_t buf, tsize_t cc, tsample_t s)
+{
+	unsigned char* bp = (unsigned char*) buf;
+	tidata_t op, ep, lastliteral;
+	long n, slop;
+	int b;
+	enum { BASE, LITERAL, RUN, LITERAL_RUN } state;
+
+	(void) s;
+	op = tif->tif_rawcp;
+	ep = tif->tif_rawdata + tif->tif_rawdatasize;
+	state = BASE;
+	lastliteral = 0;
+	while (cc > 0) {
+		/*
+		 * Find the longest string of identical bytes.
+		 */
+		b = *bp++, cc--, n = 1;
+		for (; cc > 0 && b == *bp; cc--, bp++)
+			n++;
+	again:
+		if (op + 2 >= ep) {		/* insure space for new data */
+			/*
+			 * Be careful about writing the last
+			 * literal.  Must write up to that point
+			 * and then copy the remainder to the
+			 * front of the buffer.
+			 */
+			if (state == LITERAL || state == LITERAL_RUN) {
+				slop = op - lastliteral;
+				tif->tif_rawcc += lastliteral - tif->tif_rawcp;
+				if (!TIFFFlushData1(tif))
+					return (-1);
+				op = tif->tif_rawcp;
+				while (slop-- > 0)
+					*op++ = *lastliteral++;
+				lastliteral = tif->tif_rawcp;
+			} else {
+				tif->tif_rawcc += op - tif->tif_rawcp;
+				if (!TIFFFlushData1(tif))
+					return (-1);
+				op = tif->tif_rawcp;
+			}
+		}
+		switch (state) {
+		case BASE:		/* initial state, set run/literal */
+			if (n > 1) {
+				state = RUN;
+				if (n > 128) {
+					*op++ = (tidata) -127;
+					*op++ = (tidataval_t) b;
+					n -= 128;
+					goto again;
+				}
+				*op++ = (tidataval_t)(-(n-1));
+				*op++ = (tidataval_t) b;
+			} else {
+				lastliteral = op;
+				*op++ = 0;
+				*op++ = (tidataval_t) b;
+				state = LITERAL;
+			}
+			break;
+		case LITERAL:		/* last object was literal string */
+			if (n > 1) {
+				state = LITERAL_RUN;
+				if (n > 128) {
+					*op++ = (tidata) -127;
+					*op++ = (tidataval_t) b;
+					n -= 128;
+					goto again;
+				}
+				*op++ = (tidataval_t)(-(n-1));	/* encode run */
+				*op++ = (tidataval_t) b;
+			} else {			/* extend literal */
+				if (++(*lastliteral) == 127)
+					state = BASE;
+				*op++ = (tidataval_t) b;
+			}
+			break;
+		case RUN:		/* last object was run */
+			if (n > 1) {
+				if (n > 128) {
+					*op++ = (tidata) -127;
+					*op++ = (tidataval_t) b;
+					n -= 128;
+					goto again;
+				}
+				*op++ = (tidataval_t)(-(n-1));
+				*op++ = (tidataval_t) b;
+			} else {
+				lastliteral = op;
+				*op++ = 0;
+				*op++ = (tidataval_t) b;
+				state = LITERAL;
+			}
+			break;
+		case LITERAL_RUN:	/* literal followed by a run */
+			/*
+			 * Check to see if previous run should
+			 * be converted to a literal, in which
+			 * case we convert literal-run-literal
+			 * to a single literal.
+			 */
+			if (n == 1 && op[-2] == (tidata) -1 &&
+			    *lastliteral < 126) {
+				state = (((*lastliteral) += 2) == 127 ?
+				    BASE : LITERAL);
+				op[-2] = op[-1];	/* replicate */
+			} else
+				state = RUN;
+			goto again;
+		}
+	}
+	tif->tif_rawcc += op - tif->tif_rawcp;
+	tif->tif_rawcp = op;
+	return (1);
+}
+
+/*
+ * Encode a rectangular chunk of pixels.  We break it up
+ * into row-sized pieces to insure that encoded runs do
+ * not span rows.  Otherwise, there can be problems with
+ * the decoder if data is read, for example, by scanlines
+ * when it was encoded by strips.
+ */
+static int
+PackBitsEncodeChunk(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s)
+{
+	tsize_t rowsize = *(tsize_t*)tif->tif_data;
+
+	while ((long)cc > 0) {
+		int	chunk = rowsize;
+		
+		if( cc < chunk )
+		    chunk = cc;
+
+		if (PackBitsEncode(tif, bp, chunk, s) < 0)
+		    return (-1);
+		bp += chunk;
+		cc -= chunk;
+	}
+	return (1);
+}
+
+static int
+PackBitsDecode(TIFF* tif, tidata_t op, tsize_t occ, tsample_t s)
+{
+	char *bp;
+	tsize_t cc;
+	long n;
+	int b;
+
+	(void) s;
+	bp = (char*) tif->tif_rawcp;
+	cc = tif->tif_rawcc;
+	while (cc > 0 && (long)occ > 0) {
+		n = (long) *bp++, cc--;
+		/*
+		 * Watch out for compilers that
+		 * don't sign extend chars...
+		 */
+		if (n >= 128)
+			n -= 256;
+		if (n < 0) {		/* replicate next byte -n+1 times */
+			if (n == -128)	/* nop */
+				continue;
+                        n = -n + 1;
+                        if( occ < n )
+                        {
+							TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
+                                        "PackBitsDecode: discarding %d bytes "
+                                        "to avoid buffer overrun",
+                                        n - occ);
+                            n = occ;
+                        }
+			occ -= n;
+			b = *bp++, cc--;
+			while (n-- > 0)
+				*op++ = (tidataval_t) b;
+		} else {		/* copy next n+1 bytes literally */
+			if (occ < n + 1)
+                        {
+                            TIFFWarningExt(tif->tif_clientdata, tif->tif_name,
+                                        "PackBitsDecode: discarding %d bytes "
+                                        "to avoid buffer overrun",
+                                        n - occ + 1);
+                            n = occ - 1;
+                        }
+                        _TIFFmemcpy(op, bp, ++n);
+			op += n; occ -= n;
+			bp += n; cc -= n;
+		}
+	}
+	tif->tif_rawcp = (tidata_t) bp;
+	tif->tif_rawcc = cc;
+	if (occ > 0) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		    "PackBitsDecode: Not enough data for scanline %ld",
+		    (long) tif->tif_row);
+		return (0);
+	}
+	return (1);
+}
+
+int
+TIFFInitPackBits(TIFF* tif, int scheme)
+{
+	(void) scheme;
+	tif->tif_decoderow = PackBitsDecode;
+	tif->tif_decodestrip = PackBitsDecode;
+	tif->tif_decodetile = PackBitsDecode;
+	tif->tif_preencode = PackBitsPreEncode;
+        tif->tif_postencode = PackBitsPostEncode;
+	tif->tif_encoderow = PackBitsEncode;
+	tif->tif_encodestrip = PackBitsEncodeChunk;
+	tif->tif_encodetile = PackBitsEncodeChunk;
+	return (1);
+}
+#endif /* PACKBITS_SUPPORT */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_pixarlog.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_pixarlog.c
new file mode 100644
index 0000000000..817081d8a9
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_pixarlog.c
@@ -0,0 +1,1342 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1996-1997 Sam Leffler
+ * Copyright (c) 1996 Pixar
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Pixar, Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Pixar, Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL PIXAR, SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#include "tiffiop.h"
+#ifdef PIXARLOG_SUPPORT
+
+/*
+ * TIFF Library.
+ * PixarLog Compression Support
+ *
+ * Contributed by Dan McCoy.
+ *
+ * PixarLog film support uses the TIFF library to store companded
+ * 11 bit values into a tiff file, which are compressed using the 
+ * zip compressor.  
+ *
+ * The codec can take as input and produce as output 32-bit IEEE float values 
+ * as well as 16-bit or 8-bit unsigned integer values.
+ *
+ * On writing any of the above are converted into the internal
+ * 11-bit log format.   In the case of  8 and 16 bit values, the
+ * input is assumed to be unsigned linear color values that represent
+ * the range 0-1.  In the case of IEEE values, the 0-1 range is assumed to
+ * be the normal linear color range, in addition over 1 values are
+ * accepted up to a value of about 25.0 to encode "hot" hightlights and such.
+ * The encoding is lossless for 8-bit values, slightly lossy for the
+ * other bit depths.  The actual color precision should be better
+ * than the human eye can perceive with extra room to allow for
+ * error introduced by further image computation.  As with any quantized
+ * color format, it is possible to perform image calculations which
+ * expose the quantization error. This format should certainly be less 
+ * susceptable to such errors than standard 8-bit encodings, but more
+ * susceptable than straight 16-bit or 32-bit encodings.
+ *
+ * On reading the internal format is converted to the desired output format.
+ * The program can request which format it desires by setting the internal
+ * pseudo tag TIFFTAG_PIXARLOGDATAFMT to one of these possible values:
+ *  PIXARLOGDATAFMT_FLOAT     = provide IEEE float values.
+ *  PIXARLOGDATAFMT_16BIT     = provide unsigned 16-bit integer values
+ *  PIXARLOGDATAFMT_8BIT      = provide unsigned 8-bit integer values
+ *
+ * alternately PIXARLOGDATAFMT_8BITABGR provides unsigned 8-bit integer
+ * values with the difference that if there are exactly three or four channels
+ * (rgb or rgba) it swaps the channel order (bgr or abgr).
+ *
+ * PIXARLOGDATAFMT_11BITLOG provides the internal encoding directly
+ * packed in 16-bit values.   However no tools are supplied for interpreting
+ * these values.
+ *
+ * "hot" (over 1.0) areas written in floating point get clamped to
+ * 1.0 in the integer data types.
+ *
+ * When the file is closed after writing, the bit depth and sample format
+ * are set always to appear as if 8-bit data has been written into it.
+ * That way a naive program unaware of the particulars of the encoding
+ * gets the format it is most likely able to handle.
+ *
+ * The codec does it's own horizontal differencing step on the coded
+ * values so the libraries predictor stuff should be turned off.
+ * The codec also handle byte swapping the encoded values as necessary
+ * since the library does not have the information necessary
+ * to know the bit depth of the raw unencoded buffer.
+ * 
+ */
+
+#include "tif_predict.h"
+#include "zlib.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+/* Tables for converting to/from 11 bit coded values */
+
+#define  TSIZE	 2048		/* decode table size (11-bit tokens) */
+#define  TSIZEP1 2049		/* Plus one for slop */
+#define  ONE	 1250		/* token value of 1.0 exactly */
+#define  RATIO	 1.004		/* nominal ratio for log part */
+
+#define CODE_MASK 0x7ff         /* 11 bits. */
+
+static float  Fltsize;
+static float  LogK1, LogK2;
+
+#define REPEAT(n, op)   { int i; i=n; do { i--; op; } while (i>0); }
+
+static void
+horizontalAccumulateF(uint16 *wp, int n, int stride, float *op, 
+	float *ToLinearF)
+{
+    register unsigned int  cr, cg, cb, ca, mask;
+    register float  t0, t1, t2, t3;
+
+    if (n >= stride) {
+	mask = CODE_MASK;
+	if (stride == 3) {
+	    t0 = ToLinearF[cr = wp[0]];
+	    t1 = ToLinearF[cg = wp[1]];
+	    t2 = ToLinearF[cb = wp[2]];
+	    op[0] = t0;
+	    op[1] = t1;
+	    op[2] = t2;
+	    n -= 3;
+	    while (n > 0) {
+		wp += 3;
+		op += 3;
+		n -= 3;
+		t0 = ToLinearF[(cr += wp[0]) & mask];
+		t1 = ToLinearF[(cg += wp[1]) & mask];
+		t2 = ToLinearF[(cb += wp[2]) & mask];
+		op[0] = t0;
+		op[1] = t1;
+		op[2] = t2;
+	    }
+	} else if (stride == 4) {
+	    t0 = ToLinearF[cr = wp[0]];
+	    t1 = ToLinearF[cg = wp[1]];
+	    t2 = ToLinearF[cb = wp[2]];
+	    t3 = ToLinearF[ca = wp[3]];
+	    op[0] = t0;
+	    op[1] = t1;
+	    op[2] = t2;
+	    op[3] = t3;
+	    n -= 4;
+	    while (n > 0) {
+		wp += 4;
+		op += 4;
+		n -= 4;
+		t0 = ToLinearF[(cr += wp[0]) & mask];
+		t1 = ToLinearF[(cg += wp[1]) & mask];
+		t2 = ToLinearF[(cb += wp[2]) & mask];
+		t3 = ToLinearF[(ca += wp[3]) & mask];
+		op[0] = t0;
+		op[1] = t1;
+		op[2] = t2;
+		op[3] = t3;
+	    }
+	} else {
+	    REPEAT(stride, *op = ToLinearF[*wp&mask]; wp++; op++)
+	    n -= stride;
+	    while (n > 0) {
+		REPEAT(stride,
+		    wp[stride] += *wp; *op = ToLinearF[*wp&mask]; wp++; op++)
+		n -= stride;
+	    }
+	}
+    }
+}
+
+static void
+horizontalAccumulate12(uint16 *wp, int n, int stride, int16 *op,
+	float *ToLinearF)
+{
+    register unsigned int  cr, cg, cb, ca, mask;
+    register float  t0, t1, t2, t3;
+
+#define SCALE12 2048.0F
+#define CLAMP12(t) (((t) < 3071) ? (uint16) (t) : 3071)
+
+    if (n >= stride) {
+	mask = CODE_MASK;
+	if (stride == 3) {
+	    t0 = ToLinearF[cr = wp[0]] * SCALE12;
+	    t1 = ToLinearF[cg = wp[1]] * SCALE12;
+	    t2 = ToLinearF[cb = wp[2]] * SCALE12;
+	    op[0] = CLAMP12(t0);
+	    op[1] = CLAMP12(t1);
+	    op[2] = CLAMP12(t2);
+	    n -= 3;
+	    while (n > 0) {
+		wp += 3;
+		op += 3;
+		n -= 3;
+		t0 = ToLinearF[(cr += wp[0]) & mask] * SCALE12;
+		t1 = ToLinearF[(cg += wp[1]) & mask] * SCALE12;
+		t2 = ToLinearF[(cb += wp[2]) & mask] * SCALE12;
+		op[0] = CLAMP12(t0);
+		op[1] = CLAMP12(t1);
+		op[2] = CLAMP12(t2);
+	    }
+	} else if (stride == 4) {
+	    t0 = ToLinearF[cr = wp[0]] * SCALE12;
+	    t1 = ToLinearF[cg = wp[1]] * SCALE12;
+	    t2 = ToLinearF[cb = wp[2]] * SCALE12;
+	    t3 = ToLinearF[ca = wp[3]] * SCALE12;
+	    op[0] = CLAMP12(t0);
+	    op[1] = CLAMP12(t1);
+	    op[2] = CLAMP12(t2);
+	    op[3] = CLAMP12(t3);
+	    n -= 4;
+	    while (n > 0) {
+		wp += 4;
+		op += 4;
+		n -= 4;
+		t0 = ToLinearF[(cr += wp[0]) & mask] * SCALE12;
+		t1 = ToLinearF[(cg += wp[1]) & mask] * SCALE12;
+		t2 = ToLinearF[(cb += wp[2]) & mask] * SCALE12;
+		t3 = ToLinearF[(ca += wp[3]) & mask] * SCALE12;
+		op[0] = CLAMP12(t0);
+		op[1] = CLAMP12(t1);
+		op[2] = CLAMP12(t2);
+		op[3] = CLAMP12(t3);
+	    }
+	} else {
+	    REPEAT(stride, t0 = ToLinearF[*wp&mask] * SCALE12;
+                           *op = CLAMP12(t0); wp++; op++)
+	    n -= stride;
+	    while (n > 0) {
+		REPEAT(stride,
+		    wp[stride] += *wp; t0 = ToLinearF[wp[stride]&mask]*SCALE12;
+		    *op = CLAMP12(t0);  wp++; op++)
+		n -= stride;
+	    }
+	}
+    }
+}
+
+static void
+horizontalAccumulate16(uint16 *wp, int n, int stride, uint16 *op,
+	uint16 *ToLinear16)
+{
+    register unsigned int  cr, cg, cb, ca, mask;
+
+    if (n >= stride) {
+	mask = CODE_MASK;
+	if (stride == 3) {
+	    op[0] = ToLinear16[cr = wp[0]];
+	    op[1] = ToLinear16[cg = wp[1]];
+	    op[2] = ToLinear16[cb = wp[2]];
+	    n -= 3;
+	    while (n > 0) {
+		wp += 3;
+		op += 3;
+		n -= 3;
+		op[0] = ToLinear16[(cr += wp[0]) & mask];
+		op[1] = ToLinear16[(cg += wp[1]) & mask];
+		op[2] = ToLinear16[(cb += wp[2]) & mask];
+	    }
+	} else if (stride == 4) {
+	    op[0] = ToLinear16[cr = wp[0]];
+	    op[1] = ToLinear16[cg = wp[1]];
+	    op[2] = ToLinear16[cb = wp[2]];
+	    op[3] = ToLinear16[ca = wp[3]];
+	    n -= 4;
+	    while (n > 0) {
+		wp += 4;
+		op += 4;
+		n -= 4;
+		op[0] = ToLinear16[(cr += wp[0]) & mask];
+		op[1] = ToLinear16[(cg += wp[1]) & mask];
+		op[2] = ToLinear16[(cb += wp[2]) & mask];
+		op[3] = ToLinear16[(ca += wp[3]) & mask];
+	    }
+	} else {
+	    REPEAT(stride, *op = ToLinear16[*wp&mask]; wp++; op++)
+	    n -= stride;
+	    while (n > 0) {
+		REPEAT(stride,
+		    wp[stride] += *wp; *op = ToLinear16[*wp&mask]; wp++; op++)
+		n -= stride;
+	    }
+	}
+    }
+}
+
+/* 
+ * Returns the log encoded 11-bit values with the horizontal
+ * differencing undone.
+ */
+static void
+horizontalAccumulate11(uint16 *wp, int n, int stride, uint16 *op)
+{
+    register unsigned int  cr, cg, cb, ca, mask;
+
+    if (n >= stride) {
+	mask = CODE_MASK;
+	if (stride == 3) {
+	    op[0] = cr = wp[0];  op[1] = cg = wp[1];  op[2] = cb = wp[2];
+	    n -= 3;
+	    while (n > 0) {
+		wp += 3;
+		op += 3;
+		n -= 3;
+		op[0] = (cr += wp[0]) & mask;
+		op[1] = (cg += wp[1]) & mask;
+		op[2] = (cb += wp[2]) & mask;
+	    }
+	} else if (stride == 4) {
+	    op[0] = cr = wp[0];  op[1] = cg = wp[1];
+	    op[2] = cb = wp[2];  op[3] = ca = wp[3];
+	    n -= 4;
+	    while (n > 0) {
+		wp += 4;
+		op += 4;
+		n -= 4;
+		op[0] = (cr += wp[0]) & mask;
+		op[1] = (cg += wp[1]) & mask;
+		op[2] = (cb += wp[2]) & mask;
+		op[3] = (ca += wp[3]) & mask;
+	    } 
+	} else {
+	    REPEAT(stride, *op = *wp&mask; wp++; op++)
+	    n -= stride;
+	    while (n > 0) {
+		REPEAT(stride,
+		    wp[stride] += *wp; *op = *wp&mask; wp++; op++)
+	    	n -= stride;
+	    }
+	}
+    }
+}
+
+static void
+horizontalAccumulate8(uint16 *wp, int n, int stride, unsigned char *op,
+	unsigned char *ToLinear8)
+{
+    register unsigned int  cr, cg, cb, ca, mask;
+
+    if (n >= stride) {
+	mask = CODE_MASK;
+	if (stride == 3) {
+	    op[0] = ToLinear8[cr = wp[0]];
+	    op[1] = ToLinear8[cg = wp[1]];
+	    op[2] = ToLinear8[cb = wp[2]];
+	    n -= 3;
+	    while (n > 0) {
+		n -= 3;
+		wp += 3;
+		op += 3;
+		op[0] = ToLinear8[(cr += wp[0]) & mask];
+		op[1] = ToLinear8[(cg += wp[1]) & mask];
+		op[2] = ToLinear8[(cb += wp[2]) & mask];
+	    }
+	} else if (stride == 4) {
+	    op[0] = ToLinear8[cr = wp[0]];
+	    op[1] = ToLinear8[cg = wp[1]];
+	    op[2] = ToLinear8[cb = wp[2]];
+	    op[3] = ToLinear8[ca = wp[3]];
+	    n -= 4;
+	    while (n > 0) {
+		n -= 4;
+		wp += 4;
+		op += 4;
+		op[0] = ToLinear8[(cr += wp[0]) & mask];
+		op[1] = ToLinear8[(cg += wp[1]) & mask];
+		op[2] = ToLinear8[(cb += wp[2]) & mask];
+		op[3] = ToLinear8[(ca += wp[3]) & mask];
+	    }
+	} else {
+	    REPEAT(stride, *op = ToLinear8[*wp&mask]; wp++; op++)
+	    n -= stride;
+	    while (n > 0) {
+		REPEAT(stride,
+		    wp[stride] += *wp; *op = ToLinear8[*wp&mask]; wp++; op++)
+		n -= stride;
+	    }
+	}
+    }
+}
+
+
+static void
+horizontalAccumulate8abgr(uint16 *wp, int n, int stride, unsigned char *op,
+	unsigned char *ToLinear8)
+{
+    register unsigned int  cr, cg, cb, ca, mask;
+    register unsigned char  t0, t1, t2, t3;
+
+    if (n >= stride) {
+	mask = CODE_MASK;
+	if (stride == 3) {
+	    op[0] = 0;
+	    t1 = ToLinear8[cb = wp[2]];
+	    t2 = ToLinear8[cg = wp[1]];
+	    t3 = ToLinear8[cr = wp[0]];
+	    op[1] = t1;
+	    op[2] = t2;
+	    op[3] = t3;
+	    n -= 3;
+	    while (n > 0) {
+		n -= 3;
+		wp += 3;
+		op += 4;
+		op[0] = 0;
+		t1 = ToLinear8[(cb += wp[2]) & mask];
+		t2 = ToLinear8[(cg += wp[1]) & mask];
+		t3 = ToLinear8[(cr += wp[0]) & mask];
+		op[1] = t1;
+		op[2] = t2;
+		op[3] = t3;
+	    }
+	} else if (stride == 4) {
+	    t0 = ToLinear8[ca = wp[3]];
+	    t1 = ToLinear8[cb = wp[2]];
+	    t2 = ToLinear8[cg = wp[1]];
+	    t3 = ToLinear8[cr = wp[0]];
+	    op[0] = t0;
+	    op[1] = t1;
+	    op[2] = t2;
+	    op[3] = t3;
+	    n -= 4;
+	    while (n > 0) {
+		n -= 4;
+		wp += 4;
+		op += 4;
+		t0 = ToLinear8[(ca += wp[3]) & mask];
+		t1 = ToLinear8[(cb += wp[2]) & mask];
+		t2 = ToLinear8[(cg += wp[1]) & mask];
+		t3 = ToLinear8[(cr += wp[0]) & mask];
+		op[0] = t0;
+		op[1] = t1;
+		op[2] = t2;
+		op[3] = t3;
+	    }
+	} else {
+	    REPEAT(stride, *op = ToLinear8[*wp&mask]; wp++; op++)
+	    n -= stride;
+	    while (n > 0) {
+		REPEAT(stride,
+		    wp[stride] += *wp; *op = ToLinear8[*wp&mask]; wp++; op++)
+		n -= stride;
+	    }
+	}
+    }
+}
+
+/*
+ * State block for each open TIFF
+ * file using PixarLog compression/decompression.
+ */
+typedef	struct {
+	TIFFPredictorState	predict;
+	z_stream		stream;
+	uint16			*tbuf; 
+	uint16			stride;
+	int			state;
+	int			user_datafmt;
+	int			quality;
+#define PLSTATE_INIT 1
+
+	TIFFVSetMethod		vgetparent;	/* super-class method */
+	TIFFVSetMethod		vsetparent;	/* super-class method */
+
+	float *ToLinearF;
+	uint16 *ToLinear16;
+	unsigned char *ToLinear8;
+	uint16  *FromLT2;
+	uint16  *From14; /* Really for 16-bit data, but we shift down 2 */
+	uint16  *From8;
+	
+} PixarLogState;
+
+static int
+PixarLogMakeTables(PixarLogState *sp)
+{
+
+/*
+ *    We make several tables here to convert between various external
+ *    representations (float, 16-bit, and 8-bit) and the internal
+ *    11-bit companded representation.  The 11-bit representation has two
+ *    distinct regions.  A linear bottom end up through .018316 in steps
+ *    of about .000073, and a region of constant ratio up to about 25.
+ *    These floating point numbers are stored in the main table ToLinearF. 
+ *    All other tables are derived from this one.  The tables (and the
+ *    ratios) are continuous at the internal seam.
+ */
+
+    int  nlin, lt2size;
+    int  i, j;
+    double  b, c, linstep, v;
+    float *ToLinearF;
+    uint16 *ToLinear16;
+    unsigned char *ToLinear8;
+    uint16  *FromLT2;
+    uint16  *From14; /* Really for 16-bit data, but we shift down 2 */
+    uint16  *From8;
+
+    c = log(RATIO);	
+    nlin = (int)(1./c);	/* nlin must be an integer */
+    c = 1./nlin;
+    b = exp(-c*ONE);	/* multiplicative scale factor [b*exp(c*ONE) = 1] */
+    linstep = b*c*exp(1.);
+
+    LogK1 = (float)(1./c);	/* if (v >= 2)  token = k1*log(v*k2) */
+    LogK2 = (float)(1./b);
+    lt2size = (int)(2./linstep) + 1;
+    FromLT2 = (uint16 *)_TIFFmalloc(lt2size*sizeof(uint16));
+    From14 = (uint16 *)_TIFFmalloc(16384*sizeof(uint16));
+    From8 = (uint16 *)_TIFFmalloc(256*sizeof(uint16));
+    ToLinearF = (float *)_TIFFmalloc(TSIZEP1 * sizeof(float));
+    ToLinear16 = (uint16 *)_TIFFmalloc(TSIZEP1 * sizeof(uint16));
+    ToLinear8 = (unsigned char *)_TIFFmalloc(TSIZEP1 * sizeof(unsigned char));
+    if (FromLT2 == NULL || From14  == NULL || From8   == NULL ||
+	 ToLinearF == NULL || ToLinear16 == NULL || ToLinear8 == NULL) {
+	if (FromLT2) _TIFFfree(FromLT2);
+	if (From14) _TIFFfree(From14);
+	if (From8) _TIFFfree(From8);
+	if (ToLinearF) _TIFFfree(ToLinearF);
+	if (ToLinear16) _TIFFfree(ToLinear16);
+	if (ToLinear8) _TIFFfree(ToLinear8);
+	sp->FromLT2 = NULL;
+	sp->From14 = NULL;
+	sp->From8 = NULL;
+	sp->ToLinearF = NULL;
+	sp->ToLinear16 = NULL;
+	sp->ToLinear8 = NULL;
+	return 0;
+    }
+
+    j = 0;
+
+    for (i = 0; i < nlin; i++)  {
+	v = i * linstep;
+	ToLinearF[j++] = (float)v;
+    }
+
+    for (i = nlin; i < TSIZE; i++)
+	ToLinearF[j++] = (float)(b*exp(c*i));
+
+    ToLinearF[2048] = ToLinearF[2047];
+
+    for (i = 0; i < TSIZEP1; i++)  {
+	v = ToLinearF[i]*65535.0 + 0.5;
+	ToLinear16[i] = (v > 65535.0) ? 65535 : (uint16)v;
+	v = ToLinearF[i]*255.0  + 0.5;
+	ToLinear8[i]  = (v > 255.0) ? 255 : (unsigned char)v;
+    }
+
+    j = 0;
+    for (i = 0; i < lt2size; i++)  {
+	if ((i*linstep)*(i*linstep) > ToLinearF[j]*ToLinearF[j+1])
+	    j++;
+	FromLT2[i] = j;
+    }
+
+    /*
+     * Since we lose info anyway on 16-bit data, we set up a 14-bit
+     * table and shift 16-bit values down two bits on input.
+     * saves a little table space.
+     */
+    j = 0;
+    for (i = 0; i < 16384; i++)  {
+	while ((i/16383.)*(i/16383.) > ToLinearF[j]*ToLinearF[j+1])
+	    j++;
+	From14[i] = j;
+    }
+
+    j = 0;
+    for (i = 0; i < 256; i++)  {
+	while ((i/255.)*(i/255.) > ToLinearF[j]*ToLinearF[j+1])
+	    j++;
+	From8[i] = j;
+    }
+
+    Fltsize = (float)(lt2size/2);
+
+    sp->ToLinearF = ToLinearF;
+    sp->ToLinear16 = ToLinear16;
+    sp->ToLinear8 = ToLinear8;
+    sp->FromLT2 = FromLT2;
+    sp->From14 = From14;
+    sp->From8 = From8;
+
+    return 1;
+}
+
+#define	DecoderState(tif)	((PixarLogState*) (tif)->tif_data)
+#define	EncoderState(tif)	((PixarLogState*) (tif)->tif_data)
+
+static	int PixarLogEncode(TIFF*, tidata_t, tsize_t, tsample_t);
+static	int PixarLogDecode(TIFF*, tidata_t, tsize_t, tsample_t);
+
+#define N(a)   (sizeof(a)/sizeof(a[0]))
+#define PIXARLOGDATAFMT_UNKNOWN	-1
+
+static int
+PixarLogGuessDataFmt(TIFFDirectory *td)
+{
+	int guess = PIXARLOGDATAFMT_UNKNOWN;
+	int format = td->td_sampleformat;
+
+	/* If the user didn't tell us his datafmt,
+	 * take our best guess from the bitspersample.
+	 */
+	switch (td->td_bitspersample) {
+	 case 32:
+		if (format == SAMPLEFORMAT_IEEEFP)
+			guess = PIXARLOGDATAFMT_FLOAT;
+		break;
+	 case 16:
+		if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_UINT)
+			guess = PIXARLOGDATAFMT_16BIT;
+		break;
+	 case 12:
+		if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_INT)
+			guess = PIXARLOGDATAFMT_12BITPICIO;
+		break;
+	 case 11:
+		if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_UINT)
+			guess = PIXARLOGDATAFMT_11BITLOG;
+		break;
+	 case 8:
+		if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_UINT)
+			guess = PIXARLOGDATAFMT_8BIT;
+		break;
+	}
+
+	return guess;
+}
+
+static uint32
+multiply(size_t m1, size_t m2)
+{
+	uint32	bytes = m1 * m2;
+
+	if (m1 && bytes / m1 != m2)
+		bytes = 0;
+
+	return bytes;
+}
+
+static int
+PixarLogSetupDecode(TIFF* tif)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	PixarLogState* sp = DecoderState(tif);
+	tsize_t tbuf_size;
+	static const char module[] = "PixarLogSetupDecode";
+
+	assert(sp != NULL);
+
+	/* Make sure no byte swapping happens on the data
+	 * after decompression. */
+	tif->tif_postdecode = _TIFFNoPostDecode;
+
+	/* for some reason, we can't do this in TIFFInitPixarLog */
+
+	sp->stride = (td->td_planarconfig == PLANARCONFIG_CONTIG ?
+	    td->td_samplesperpixel : 1);
+	tbuf_size = multiply(multiply(multiply(sp->stride, td->td_imagewidth),
+				      td->td_rowsperstrip), sizeof(uint16));
+	if (tbuf_size == 0)
+		return (0);
+	sp->tbuf = (uint16 *) _TIFFmalloc(tbuf_size);
+	if (sp->tbuf == NULL)
+		return (0);
+	if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN)
+		sp->user_datafmt = PixarLogGuessDataFmt(td);
+	if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN) {
+		TIFFErrorExt(tif->tif_clientdata, module,
+			"PixarLog compression can't handle bits depth/data format combination (depth: %d)", 
+			td->td_bitspersample);
+		return (0);
+	}
+
+	if (inflateInit(&sp->stream) != Z_OK) {
+		TIFFErrorExt(tif->tif_clientdata, module, "%s: %s", tif->tif_name, sp->stream.msg);
+		return (0);
+	} else {
+		sp->state |= PLSTATE_INIT;
+		return (1);
+	}
+}
+
+/*
+ * Setup state for decoding a strip.
+ */
+static int
+PixarLogPreDecode(TIFF* tif, tsample_t s)
+{
+	PixarLogState* sp = DecoderState(tif);
+
+	(void) s;
+	assert(sp != NULL);
+	sp->stream.next_in = tif->tif_rawdata;
+	sp->stream.avail_in = tif->tif_rawcc;
+	return (inflateReset(&sp->stream) == Z_OK);
+}
+
+static int
+PixarLogDecode(TIFF* tif, tidata_t op, tsize_t occ, tsample_t s)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	PixarLogState* sp = DecoderState(tif);
+	static const char module[] = "PixarLogDecode";
+	int i, nsamples, llen;
+	uint16 *up;
+
+	switch (sp->user_datafmt) {
+	case PIXARLOGDATAFMT_FLOAT:
+		nsamples = occ / sizeof(float);	/* XXX float == 32 bits */
+		break;
+	case PIXARLOGDATAFMT_16BIT:
+	case PIXARLOGDATAFMT_12BITPICIO:
+	case PIXARLOGDATAFMT_11BITLOG:
+		nsamples = occ / sizeof(uint16); /* XXX uint16 == 16 bits */
+		break;
+	case PIXARLOGDATAFMT_8BIT:
+	case PIXARLOGDATAFMT_8BITABGR:
+		nsamples = occ;
+		break;
+	default:
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			"%d bit input not supported in PixarLog",
+			td->td_bitspersample);
+		return 0;
+	}
+
+	llen = sp->stride * td->td_imagewidth;
+
+	(void) s;
+	assert(sp != NULL);
+	sp->stream.next_out = (unsigned char *) sp->tbuf;
+	sp->stream.avail_out = nsamples * sizeof(uint16);
+	do {
+		int state = inflate(&sp->stream, Z_PARTIAL_FLUSH);
+		if (state == Z_STREAM_END) {
+			break;			/* XXX */
+		}
+		if (state == Z_DATA_ERROR) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+			    "%s: Decoding error at scanline %d, %s",
+			    tif->tif_name, tif->tif_row, sp->stream.msg);
+			if (inflateSync(&sp->stream) != Z_OK)
+				return (0);
+			continue;
+		}
+		if (state != Z_OK) {
+			TIFFErrorExt(tif->tif_clientdata, module, "%s: zlib error: %s",
+			    tif->tif_name, sp->stream.msg);
+			return (0);
+		}
+	} while (sp->stream.avail_out > 0);
+
+	/* hopefully, we got all the bytes we needed */
+	if (sp->stream.avail_out != 0) {
+		TIFFErrorExt(tif->tif_clientdata, module,
+		    "%s: Not enough data at scanline %d (short %d bytes)",
+		    tif->tif_name, tif->tif_row, sp->stream.avail_out);
+		return (0);
+	}
+
+	up = sp->tbuf;
+	/* Swap bytes in the data if from a different endian machine. */
+	if (tif->tif_flags & TIFF_SWAB)
+		TIFFSwabArrayOfShort(up, nsamples);
+
+	for (i = 0; i < nsamples; i += llen, up += llen) {
+		switch (sp->user_datafmt)  {
+		case PIXARLOGDATAFMT_FLOAT:
+			horizontalAccumulateF(up, llen, sp->stride,
+					(float *)op, sp->ToLinearF);
+			op += llen * sizeof(float);
+			break;
+		case PIXARLOGDATAFMT_16BIT:
+			horizontalAccumulate16(up, llen, sp->stride,
+					(uint16 *)op, sp->ToLinear16);
+			op += llen * sizeof(uint16);
+			break;
+		case PIXARLOGDATAFMT_12BITPICIO:
+			horizontalAccumulate12(up, llen, sp->stride,
+					(int16 *)op, sp->ToLinearF);
+			op += llen * sizeof(int16);
+			break;
+		case PIXARLOGDATAFMT_11BITLOG:
+			horizontalAccumulate11(up, llen, sp->stride,
+					(uint16 *)op);
+			op += llen * sizeof(uint16);
+			break;
+		case PIXARLOGDATAFMT_8BIT:
+			horizontalAccumulate8(up, llen, sp->stride,
+					(unsigned char *)op, sp->ToLinear8);
+			op += llen * sizeof(unsigned char);
+			break;
+		case PIXARLOGDATAFMT_8BITABGR:
+			horizontalAccumulate8abgr(up, llen, sp->stride,
+					(unsigned char *)op, sp->ToLinear8);
+			op += llen * sizeof(unsigned char);
+			break;
+		default:
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+				  "PixarLogDecode: unsupported bits/sample: %d", 
+				  td->td_bitspersample);
+			return (0);
+		}
+	}
+
+	return (1);
+}
+
+static int
+PixarLogSetupEncode(TIFF* tif)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	PixarLogState* sp = EncoderState(tif);
+	tsize_t tbuf_size;
+	static const char module[] = "PixarLogSetupEncode";
+
+	assert(sp != NULL);
+
+	/* for some reason, we can't do this in TIFFInitPixarLog */
+
+	sp->stride = (td->td_planarconfig == PLANARCONFIG_CONTIG ?
+	    td->td_samplesperpixel : 1);
+	tbuf_size = multiply(multiply(multiply(sp->stride, td->td_imagewidth),
+				      td->td_rowsperstrip), sizeof(uint16));
+	if (tbuf_size == 0)
+		return (0);
+	sp->tbuf = (uint16 *) _TIFFmalloc(tbuf_size);
+	if (sp->tbuf == NULL)
+		return (0);
+	if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN)
+		sp->user_datafmt = PixarLogGuessDataFmt(td);
+	if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN) {
+		TIFFErrorExt(tif->tif_clientdata, module, "PixarLog compression can't handle %d bit linear encodings", td->td_bitspersample);
+		return (0);
+	}
+
+	if (deflateInit(&sp->stream, sp->quality) != Z_OK) {
+		TIFFErrorExt(tif->tif_clientdata, module, "%s: %s", tif->tif_name, sp->stream.msg);
+		return (0);
+	} else {
+		sp->state |= PLSTATE_INIT;
+		return (1);
+	}
+}
+
+/*
+ * Reset encoding state at the start of a strip.
+ */
+static int
+PixarLogPreEncode(TIFF* tif, tsample_t s)
+{
+	PixarLogState *sp = EncoderState(tif);
+
+	(void) s;
+	assert(sp != NULL);
+	sp->stream.next_out = tif->tif_rawdata;
+	sp->stream.avail_out = tif->tif_rawdatasize;
+	return (deflateReset(&sp->stream) == Z_OK);
+}
+
+static void
+horizontalDifferenceF(float *ip, int n, int stride, uint16 *wp, uint16 *FromLT2)
+{
+
+    int32 r1, g1, b1, a1, r2, g2, b2, a2, mask;
+    float fltsize = Fltsize;
+
+#define  CLAMP(v) ( (v<(float)0.)   ? 0				\
+		  : (v<(float)2.)   ? FromLT2[(int)(v*fltsize)]	\
+		  : (v>(float)24.2) ? 2047			\
+		  : LogK1*log(v*LogK2) + 0.5 )
+
+    mask = CODE_MASK;
+    if (n >= stride) {
+	if (stride == 3) {
+	    r2 = wp[0] = (uint16) CLAMP(ip[0]);
+	    g2 = wp[1] = (uint16) CLAMP(ip[1]);
+	    b2 = wp[2] = (uint16) CLAMP(ip[2]);
+	    n -= 3;
+	    while (n > 0) {
+		n -= 3;
+		wp += 3;
+		ip += 3;
+		r1 = (int32) CLAMP(ip[0]); wp[0] = (r1-r2) & mask; r2 = r1;
+		g1 = (int32) CLAMP(ip[1]); wp[1] = (g1-g2) & mask; g2 = g1;
+		b1 = (int32) CLAMP(ip[2]); wp[2] = (b1-b2) & mask; b2 = b1;
+	    }
+	} else if (stride == 4) {
+	    r2 = wp[0] = (uint16) CLAMP(ip[0]);
+	    g2 = wp[1] = (uint16) CLAMP(ip[1]);
+	    b2 = wp[2] = (uint16) CLAMP(ip[2]);
+	    a2 = wp[3] = (uint16) CLAMP(ip[3]);
+	    n -= 4;
+	    while (n > 0) {
+		n -= 4;
+		wp += 4;
+		ip += 4;
+		r1 = (int32) CLAMP(ip[0]); wp[0] = (r1-r2) & mask; r2 = r1;
+		g1 = (int32) CLAMP(ip[1]); wp[1] = (g1-g2) & mask; g2 = g1;
+		b1 = (int32) CLAMP(ip[2]); wp[2] = (b1-b2) & mask; b2 = b1;
+		a1 = (int32) CLAMP(ip[3]); wp[3] = (a1-a2) & mask; a2 = a1;
+	    }
+	} else {
+	    ip += n - 1;	/* point to last one */
+	    wp += n - 1;	/* point to last one */
+	    n -= stride;
+	    while (n > 0) {
+		REPEAT(stride, wp[0] = (uint16) CLAMP(ip[0]);
+				wp[stride] -= wp[0];
+				wp[stride] &= mask;
+				wp--; ip--)
+		n -= stride;
+	    }
+	    REPEAT(stride, wp[0] = (uint16) CLAMP(ip[0]); wp--; ip--)
+	}
+    }
+}
+
+static void
+horizontalDifference16(unsigned short *ip, int n, int stride, 
+	unsigned short *wp, uint16 *From14)
+{
+    register int  r1, g1, b1, a1, r2, g2, b2, a2, mask;
+
+/* assumption is unsigned pixel values */
+#undef   CLAMP
+#define  CLAMP(v) From14[(v) >> 2]
+
+    mask = CODE_MASK;
+    if (n >= stride) {
+	if (stride == 3) {
+	    r2 = wp[0] = CLAMP(ip[0]);  g2 = wp[1] = CLAMP(ip[1]);
+	    b2 = wp[2] = CLAMP(ip[2]);
+	    n -= 3;
+	    while (n > 0) {
+		n -= 3;
+		wp += 3;
+		ip += 3;
+		r1 = CLAMP(ip[0]); wp[0] = (r1-r2) & mask; r2 = r1;
+		g1 = CLAMP(ip[1]); wp[1] = (g1-g2) & mask; g2 = g1;
+		b1 = CLAMP(ip[2]); wp[2] = (b1-b2) & mask; b2 = b1;
+	    }
+	} else if (stride == 4) {
+	    r2 = wp[0] = CLAMP(ip[0]);  g2 = wp[1] = CLAMP(ip[1]);
+	    b2 = wp[2] = CLAMP(ip[2]);  a2 = wp[3] = CLAMP(ip[3]);
+	    n -= 4;
+	    while (n > 0) {
+		n -= 4;
+		wp += 4;
+		ip += 4;
+		r1 = CLAMP(ip[0]); wp[0] = (r1-r2) & mask; r2 = r1;
+		g1 = CLAMP(ip[1]); wp[1] = (g1-g2) & mask; g2 = g1;
+		b1 = CLAMP(ip[2]); wp[2] = (b1-b2) & mask; b2 = b1;
+		a1 = CLAMP(ip[3]); wp[3] = (a1-a2) & mask; a2 = a1;
+	    }
+	} else {
+	    ip += n - 1;	/* point to last one */
+	    wp += n - 1;	/* point to last one */
+	    n -= stride;
+	    while (n > 0) {
+		REPEAT(stride, wp[0] = CLAMP(ip[0]);
+				wp[stride] -= wp[0];
+				wp[stride] &= mask;
+				wp--; ip--)
+		n -= stride;
+	    }
+	    REPEAT(stride, wp[0] = CLAMP(ip[0]); wp--; ip--)
+	}
+    }
+}
+
+
+static void
+horizontalDifference8(unsigned char *ip, int n, int stride, 
+	unsigned short *wp, uint16 *From8)
+{
+    register int  r1, g1, b1, a1, r2, g2, b2, a2, mask;
+
+#undef	 CLAMP
+#define  CLAMP(v) (From8[(v)])
+
+    mask = CODE_MASK;
+    if (n >= stride) {
+	if (stride == 3) {
+	    r2 = wp[0] = CLAMP(ip[0]);  g2 = wp[1] = CLAMP(ip[1]);
+	    b2 = wp[2] = CLAMP(ip[2]);
+	    n -= 3;
+	    while (n > 0) {
+		n -= 3;
+		r1 = CLAMP(ip[3]); wp[3] = (r1-r2) & mask; r2 = r1;
+		g1 = CLAMP(ip[4]); wp[4] = (g1-g2) & mask; g2 = g1;
+		b1 = CLAMP(ip[5]); wp[5] = (b1-b2) & mask; b2 = b1;
+		wp += 3;
+		ip += 3;
+	    }
+	} else if (stride == 4) {
+	    r2 = wp[0] = CLAMP(ip[0]);  g2 = wp[1] = CLAMP(ip[1]);
+	    b2 = wp[2] = CLAMP(ip[2]);  a2 = wp[3] = CLAMP(ip[3]);
+	    n -= 4;
+	    while (n > 0) {
+		n -= 4;
+		r1 = CLAMP(ip[4]); wp[4] = (r1-r2) & mask; r2 = r1;
+		g1 = CLAMP(ip[5]); wp[5] = (g1-g2) & mask; g2 = g1;
+		b1 = CLAMP(ip[6]); wp[6] = (b1-b2) & mask; b2 = b1;
+		a1 = CLAMP(ip[7]); wp[7] = (a1-a2) & mask; a2 = a1;
+		wp += 4;
+		ip += 4;
+	    }
+	} else {
+	    wp += n + stride - 1;	/* point to last one */
+	    ip += n + stride - 1;	/* point to last one */
+	    n -= stride;
+	    while (n > 0) {
+		REPEAT(stride, wp[0] = CLAMP(ip[0]);
+				wp[stride] -= wp[0];
+				wp[stride] &= mask;
+				wp--; ip--)
+		n -= stride;
+	    }
+	    REPEAT(stride, wp[0] = CLAMP(ip[0]); wp--; ip--)
+	}
+    }
+}
+
+/*
+ * Encode a chunk of pixels.
+ */
+static int
+PixarLogEncode(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	PixarLogState *sp = EncoderState(tif);
+	static const char module[] = "PixarLogEncode";
+	int 	i, n, llen;
+	unsigned short * up;
+
+	(void) s;
+
+	switch (sp->user_datafmt) {
+	case PIXARLOGDATAFMT_FLOAT:
+		n = cc / sizeof(float);		/* XXX float == 32 bits */
+		break;
+	case PIXARLOGDATAFMT_16BIT:
+	case PIXARLOGDATAFMT_12BITPICIO:
+	case PIXARLOGDATAFMT_11BITLOG:
+		n = cc / sizeof(uint16);	/* XXX uint16 == 16 bits */
+		break;
+	case PIXARLOGDATAFMT_8BIT:
+	case PIXARLOGDATAFMT_8BITABGR:
+		n = cc;
+		break;
+	default:
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			"%d bit input not supported in PixarLog",
+			td->td_bitspersample);
+		return 0;
+	}
+
+	llen = sp->stride * td->td_imagewidth;
+
+	for (i = 0, up = sp->tbuf; i < n; i += llen, up += llen) {
+		switch (sp->user_datafmt)  {
+		case PIXARLOGDATAFMT_FLOAT:
+			horizontalDifferenceF((float *)bp, llen, 
+				sp->stride, up, sp->FromLT2);
+			bp += llen * sizeof(float);
+			break;
+		case PIXARLOGDATAFMT_16BIT:
+			horizontalDifference16((uint16 *)bp, llen, 
+				sp->stride, up, sp->From14);
+			bp += llen * sizeof(uint16);
+			break;
+		case PIXARLOGDATAFMT_8BIT:
+			horizontalDifference8((unsigned char *)bp, llen, 
+				sp->stride, up, sp->From8);
+			bp += llen * sizeof(unsigned char);
+			break;
+		default:
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+				"%d bit input not supported in PixarLog",
+				td->td_bitspersample);
+			return 0;
+		}
+	}
+ 
+	sp->stream.next_in = (unsigned char *) sp->tbuf;
+	sp->stream.avail_in = n * sizeof(uint16);
+
+	do {
+		if (deflate(&sp->stream, Z_NO_FLUSH) != Z_OK) {
+			TIFFErrorExt(tif->tif_clientdata, module, "%s: Encoder error: %s",
+			    tif->tif_name, sp->stream.msg);
+			return (0);
+		}
+		if (sp->stream.avail_out == 0) {
+			tif->tif_rawcc = tif->tif_rawdatasize;
+			TIFFFlushData1(tif);
+			sp->stream.next_out = tif->tif_rawdata;
+			sp->stream.avail_out = tif->tif_rawdatasize;
+		}
+	} while (sp->stream.avail_in > 0);
+	return (1);
+}
+
+/*
+ * Finish off an encoded strip by flushing the last
+ * string and tacking on an End Of Information code.
+ */
+
+static int
+PixarLogPostEncode(TIFF* tif)
+{
+	PixarLogState *sp = EncoderState(tif);
+	static const char module[] = "PixarLogPostEncode";
+	int state;
+
+	sp->stream.avail_in = 0;
+
+	do {
+		state = deflate(&sp->stream, Z_FINISH);
+		switch (state) {
+		case Z_STREAM_END:
+		case Z_OK:
+		    if (sp->stream.avail_out != (uint32)tif->tif_rawdatasize) {
+			    tif->tif_rawcc =
+				tif->tif_rawdatasize - sp->stream.avail_out;
+			    TIFFFlushData1(tif);
+			    sp->stream.next_out = tif->tif_rawdata;
+			    sp->stream.avail_out = tif->tif_rawdatasize;
+		    }
+		    break;
+		default:
+			TIFFErrorExt(tif->tif_clientdata, module, "%s: zlib error: %s",
+			tif->tif_name, sp->stream.msg);
+		    return (0);
+		}
+	} while (state != Z_STREAM_END);
+	return (1);
+}
+
+static void
+PixarLogClose(TIFF* tif)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+
+	/* In a really sneaky maneuver, on close, we covertly modify both
+	 * bitspersample and sampleformat in the directory to indicate
+	 * 8-bit linear.  This way, the decode "just works" even for
+	 * readers that don't know about PixarLog, or how to set
+	 * the PIXARLOGDATFMT pseudo-tag.
+	 */
+	td->td_bitspersample = 8;
+	td->td_sampleformat = SAMPLEFORMAT_UINT;
+}
+
+static void
+PixarLogCleanup(TIFF* tif)
+{
+	PixarLogState* sp = (PixarLogState*) tif->tif_data;
+
+	assert(sp != 0);
+
+	(void)TIFFPredictorCleanup(tif);
+
+	tif->tif_tagmethods.vgetfield = sp->vgetparent;
+	tif->tif_tagmethods.vsetfield = sp->vsetparent;
+
+	if (sp->FromLT2) _TIFFfree(sp->FromLT2);
+	if (sp->From14) _TIFFfree(sp->From14);
+	if (sp->From8) _TIFFfree(sp->From8);
+	if (sp->ToLinearF) _TIFFfree(sp->ToLinearF);
+	if (sp->ToLinear16) _TIFFfree(sp->ToLinear16);
+	if (sp->ToLinear8) _TIFFfree(sp->ToLinear8);
+	if (sp->state&PLSTATE_INIT) {
+		if (tif->tif_mode == O_RDONLY)
+			inflateEnd(&sp->stream);
+		else
+			deflateEnd(&sp->stream);
+	}
+	if (sp->tbuf)
+		_TIFFfree(sp->tbuf);
+	_TIFFfree(sp);
+	tif->tif_data = NULL;
+
+	_TIFFSetDefaultCompressionState(tif);
+}
+
+static int
+PixarLogVSetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+    PixarLogState *sp = (PixarLogState *)tif->tif_data;
+    int result;
+    static const char module[] = "PixarLogVSetField";
+
+    switch (tag) {
+     case TIFFTAG_PIXARLOGQUALITY:
+		sp->quality = va_arg(ap, int);
+		if (tif->tif_mode != O_RDONLY && (sp->state&PLSTATE_INIT)) {
+			if (deflateParams(&sp->stream,
+			    sp->quality, Z_DEFAULT_STRATEGY) != Z_OK) {
+				TIFFErrorExt(tif->tif_clientdata, module, "%s: zlib error: %s",
+					tif->tif_name, sp->stream.msg);
+				return (0);
+			}
+		}
+		return (1);
+     case TIFFTAG_PIXARLOGDATAFMT:
+	sp->user_datafmt = va_arg(ap, int);
+	/* Tweak the TIFF header so that the rest of libtiff knows what
+	 * size of data will be passed between app and library, and
+	 * assume that the app knows what it is doing and is not
+	 * confused by these header manipulations...
+	 */
+	switch (sp->user_datafmt) {
+	 case PIXARLOGDATAFMT_8BIT:
+	 case PIXARLOGDATAFMT_8BITABGR:
+	    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
+	    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
+	    break;
+	 case PIXARLOGDATAFMT_11BITLOG:
+	    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16);
+	    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
+	    break;
+	 case PIXARLOGDATAFMT_12BITPICIO:
+	    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16);
+	    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT);
+	    break;
+	 case PIXARLOGDATAFMT_16BIT:
+	    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16);
+	    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
+	    break;
+	 case PIXARLOGDATAFMT_FLOAT:
+	    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32);
+	    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);
+	    break;
+	}
+	/*
+	 * Must recalculate sizes should bits/sample change.
+	 */
+	tif->tif_tilesize = isTiled(tif) ? TIFFTileSize(tif) : (tsize_t) -1;
+	tif->tif_scanlinesize = TIFFScanlineSize(tif);
+	result = 1;		/* NB: pseudo tag */
+	break;
+     default:
+	result = (*sp->vsetparent)(tif, tag, ap);
+    }
+    return (result);
+}
+
+static int
+PixarLogVGetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+    PixarLogState *sp = (PixarLogState *)tif->tif_data;
+
+    switch (tag) {
+     case TIFFTAG_PIXARLOGQUALITY:
+	*va_arg(ap, int*) = sp->quality;
+	break;
+     case TIFFTAG_PIXARLOGDATAFMT:
+	*va_arg(ap, int*) = sp->user_datafmt;
+	break;
+     default:
+	return (*sp->vgetparent)(tif, tag, ap);
+    }
+    return (1);
+}
+
+static const TIFFFieldInfo pixarlogFieldInfo[] = {
+    {TIFFTAG_PIXARLOGDATAFMT,0,0,TIFF_ANY,  FIELD_PSEUDO,FALSE,FALSE,""},
+    {TIFFTAG_PIXARLOGQUALITY,0,0,TIFF_ANY,  FIELD_PSEUDO,FALSE,FALSE,""}
+};
+
+int
+TIFFInitPixarLog(TIFF* tif, int scheme)
+{
+	PixarLogState* sp;
+
+	assert(scheme == COMPRESSION_PIXARLOG);
+
+	/*
+	 * Allocate state block so tag methods have storage to record values.
+	 */
+	tif->tif_data = (tidata_t) _TIFFmalloc(sizeof (PixarLogState));
+	if (tif->tif_data == NULL)
+		goto bad;
+	sp = (PixarLogState*) tif->tif_data;
+	_TIFFmemset(sp, 0, sizeof (*sp));
+	sp->stream.data_type = Z_BINARY;
+	sp->user_datafmt = PIXARLOGDATAFMT_UNKNOWN;
+
+	/*
+	 * Install codec methods.
+	 */
+	tif->tif_setupdecode = PixarLogSetupDecode;
+	tif->tif_predecode = PixarLogPreDecode;
+	tif->tif_decoderow = PixarLogDecode;
+	tif->tif_decodestrip = PixarLogDecode;
+	tif->tif_decodetile = PixarLogDecode;
+	tif->tif_setupencode = PixarLogSetupEncode;
+	tif->tif_preencode = PixarLogPreEncode;
+	tif->tif_postencode = PixarLogPostEncode;
+	tif->tif_encoderow = PixarLogEncode;
+	tif->tif_encodestrip = PixarLogEncode;
+	tif->tif_encodetile = PixarLogEncode;
+	tif->tif_close = PixarLogClose;
+	tif->tif_cleanup = PixarLogCleanup;
+
+	/* Override SetField so we can handle our private pseudo-tag */
+	_TIFFMergeFieldInfo(tif, pixarlogFieldInfo, N(pixarlogFieldInfo));
+	sp->vgetparent = tif->tif_tagmethods.vgetfield;
+	tif->tif_tagmethods.vgetfield = PixarLogVGetField;   /* hook for codec tags */
+	sp->vsetparent = tif->tif_tagmethods.vsetfield;
+	tif->tif_tagmethods.vsetfield = PixarLogVSetField;   /* hook for codec tags */
+
+	/* Default values for codec-specific fields */
+	sp->quality = Z_DEFAULT_COMPRESSION; /* default comp. level */
+	sp->state = 0;
+
+	/* we don't wish to use the predictor, 
+	 * the default is none, which predictor value 1
+	 */
+	(void) TIFFPredictorInit(tif);
+
+	/*
+	 * build the companding tables 
+	 */
+	PixarLogMakeTables(sp);
+
+	return (1);
+bad:
+	TIFFErrorExt(tif->tif_clientdata, "TIFFInitPixarLog",
+		     "No space for PixarLog state block");
+	return (0);
+}
+#endif /* PIXARLOG_SUPPORT */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_predict.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_predict.c
new file mode 100644
index 0000000000..e487d5b87b
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_predict.c
@@ -0,0 +1,626 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ *
+ * Predictor Tag Support (used by multiple codecs).
+ */
+#include "tiffiop.h"
+#include "tif_predict.h"
+
+#define	PredictorState(tif)	((TIFFPredictorState*) (tif)->tif_data)
+
+static	void horAcc8(TIFF*, tidata_t, tsize_t);
+static	void horAcc16(TIFF*, tidata_t, tsize_t);
+static	void swabHorAcc16(TIFF*, tidata_t, tsize_t);
+static	void horDiff8(TIFF*, tidata_t, tsize_t);
+static	void horDiff16(TIFF*, tidata_t, tsize_t);
+static	void fpAcc(TIFF*, tidata_t, tsize_t);
+static	void fpDiff(TIFF*, tidata_t, tsize_t);
+static	int PredictorDecodeRow(TIFF*, tidata_t, tsize_t, tsample_t);
+static	int PredictorDecodeTile(TIFF*, tidata_t, tsize_t, tsample_t);
+static	int PredictorEncodeRow(TIFF*, tidata_t, tsize_t, tsample_t);
+static	int PredictorEncodeTile(TIFF*, tidata_t, tsize_t, tsample_t);
+
+static int
+PredictorSetup(TIFF* tif)
+{
+	static const char module[] = "PredictorSetup";
+
+	TIFFPredictorState* sp = PredictorState(tif);
+	TIFFDirectory* td = &tif->tif_dir;
+
+	switch (sp->predictor)		/* no differencing */
+	{
+		case PREDICTOR_NONE:
+			return 1;
+		case PREDICTOR_HORIZONTAL:
+			if (td->td_bitspersample != 8
+			    && td->td_bitspersample != 16) {
+				TIFFErrorExt(tif->tif_clientdata, module,
+    "Horizontal differencing \"Predictor\" not supported with %d-bit samples",
+					  td->td_bitspersample);
+				return 0;
+			}
+			break;
+		case PREDICTOR_FLOATINGPOINT:
+			if (td->td_sampleformat != SAMPLEFORMAT_IEEEFP) {
+				TIFFErrorExt(tif->tif_clientdata, module,
+	"Floating point \"Predictor\" not supported with %d data format",
+					  td->td_sampleformat);
+				return 0;
+			}
+			break;
+		default:
+			TIFFErrorExt(tif->tif_clientdata, module,
+				  "\"Predictor\" value %d not supported",
+				  sp->predictor);
+			return 0;
+	}
+	sp->stride = (td->td_planarconfig == PLANARCONFIG_CONTIG ?
+	    td->td_samplesperpixel : 1);
+	/*
+	 * Calculate the scanline/tile-width size in bytes.
+	 */
+	if (isTiled(tif))
+		sp->rowsize = TIFFTileRowSize(tif);
+	else
+		sp->rowsize = TIFFScanlineSize(tif);
+
+	return 1;
+}
+
+static int
+PredictorSetupDecode(TIFF* tif)
+{
+	TIFFPredictorState* sp = PredictorState(tif);
+	TIFFDirectory* td = &tif->tif_dir;
+
+	if (!(*sp->setupdecode)(tif) || !PredictorSetup(tif))
+		return 0;
+
+	if (sp->predictor == 2) {
+		switch (td->td_bitspersample) {
+			case 8:  sp->pfunc = horAcc8; break;
+			case 16: sp->pfunc = horAcc16; break;
+		}
+		/*
+		 * Override default decoding method with one that does the
+		 * predictor stuff.
+		 */
+		sp->coderow = tif->tif_decoderow;
+		tif->tif_decoderow = PredictorDecodeRow;
+		sp->codestrip = tif->tif_decodestrip;
+		tif->tif_decodestrip = PredictorDecodeTile;
+		sp->codetile = tif->tif_decodetile;
+		tif->tif_decodetile = PredictorDecodeTile;
+		/*
+		 * If the data is horizontally differenced 16-bit data that
+		 * requires byte-swapping, then it must be byte swapped before
+		 * the accumulation step.  We do this with a special-purpose
+		 * routine and override the normal post decoding logic that
+		 * the library setup when the directory was read.
+		 */
+		if (tif->tif_flags & TIFF_SWAB) {
+			if (sp->pfunc == horAcc16) {
+				sp->pfunc = swabHorAcc16;
+				tif->tif_postdecode = _TIFFNoPostDecode;
+			} /* else handle 32-bit case... */
+		}
+	}
+
+	else if (sp->predictor == 3) {
+		sp->pfunc = fpAcc;
+		/*
+		 * Override default decoding method with one that does the
+		 * predictor stuff.
+		 */
+		sp->coderow = tif->tif_decoderow;
+		tif->tif_decoderow = PredictorDecodeRow;
+		sp->codestrip = tif->tif_decodestrip;
+		tif->tif_decodestrip = PredictorDecodeTile;
+		sp->codetile = tif->tif_decodetile;
+		tif->tif_decodetile = PredictorDecodeTile;
+		/*
+		 * The data should not be swapped outside of the floating
+		 * point predictor, the accumulation routine should return
+		 * byres in the native order.
+		 */
+		if (tif->tif_flags & TIFF_SWAB) {
+			tif->tif_postdecode = _TIFFNoPostDecode;
+		}
+		/*
+		 * Allocate buffer to keep the decoded bytes before
+		 * rearranging in the ight order
+		 */
+	}
+
+	return 1;
+}
+
+static int
+PredictorSetupEncode(TIFF* tif)
+{
+	TIFFPredictorState* sp = PredictorState(tif);
+	TIFFDirectory* td = &tif->tif_dir;
+
+	if (!(*sp->setupencode)(tif) || !PredictorSetup(tif))
+		return 0;
+
+	if (sp->predictor == 2) {
+		switch (td->td_bitspersample) {
+			case 8:  sp->pfunc = horDiff8; break;
+			case 16: sp->pfunc = horDiff16; break;
+		}
+		/*
+		 * Override default encoding method with one that does the
+		 * predictor stuff.
+		 */
+		sp->coderow = tif->tif_encoderow;
+		tif->tif_encoderow = PredictorEncodeRow;
+		sp->codestrip = tif->tif_encodestrip;
+		tif->tif_encodestrip = PredictorEncodeTile;
+		sp->codetile = tif->tif_encodetile;
+		tif->tif_encodetile = PredictorEncodeTile;
+	}
+	
+	else if (sp->predictor == 3) {
+		sp->pfunc = fpDiff;
+		/*
+		 * Override default encoding method with one that does the
+		 * predictor stuff.
+		 */
+		sp->coderow = tif->tif_encoderow;
+		tif->tif_encoderow = PredictorEncodeRow;
+		sp->codestrip = tif->tif_encodestrip;
+		tif->tif_encodestrip = PredictorEncodeTile;
+		sp->codetile = tif->tif_encodetile;
+		tif->tif_encodetile = PredictorEncodeTile;
+	}
+
+	return 1;
+}
+
+#define REPEAT4(n, op)		\
+    switch (n) {		\
+    default: { int i; for (i = n-4; i > 0; i--) { op; } } \
+    case 4:  op;		\
+    case 3:  op;		\
+    case 2:  op;		\
+    case 1:  op;		\
+    case 0:  ;			\
+    }
+
+static void
+horAcc8(TIFF* tif, tidata_t cp0, tsize_t cc)
+{
+	tsize_t stride = PredictorState(tif)->stride;
+
+	char* cp = (char*) cp0;
+	if (cc > stride) {
+		cc -= stride;
+		/*
+		 * Pipeline the most common cases.
+		 */
+		if (stride == 3)  {
+			unsigned int cr = cp[0];
+			unsigned int cg = cp[1];
+			unsigned int cb = cp[2];
+			do {
+				cc -= 3, cp += 3;
+				cp[0] = (char) (cr += cp[0]);
+				cp[1] = (char) (cg += cp[1]);
+				cp[2] = (char) (cb += cp[2]);
+			} while ((int32) cc > 0);
+		} else if (stride == 4)  {
+			unsigned int cr = cp[0];
+			unsigned int cg = cp[1];
+			unsigned int cb = cp[2];
+			unsigned int ca = cp[3];
+			do {
+				cc -= 4, cp += 4;
+				cp[0] = (char) (cr += cp[0]);
+				cp[1] = (char) (cg += cp[1]);
+				cp[2] = (char) (cb += cp[2]);
+				cp[3] = (char) (ca += cp[3]);
+			} while ((int32) cc > 0);
+		} else  {
+			do {
+				REPEAT4(stride, cp[stride] =
+					(char) (cp[stride] + *cp); cp++)
+				cc -= stride;
+			} while ((int32) cc > 0);
+		}
+	}
+}
+
+static void
+swabHorAcc16(TIFF* tif, tidata_t cp0, tsize_t cc)
+{
+	tsize_t stride = PredictorState(tif)->stride;
+	uint16* wp = (uint16*) cp0;
+	tsize_t wc = cc / 2;
+
+	if (wc > stride) {
+		TIFFSwabArrayOfShort(wp, wc);
+		wc -= stride;
+		do {
+			REPEAT4(stride, wp[stride] += wp[0]; wp++)
+			wc -= stride;
+		} while ((int32) wc > 0);
+	}
+}
+
+static void
+horAcc16(TIFF* tif, tidata_t cp0, tsize_t cc)
+{
+	tsize_t stride = PredictorState(tif)->stride;
+	uint16* wp = (uint16*) cp0;
+	tsize_t wc = cc / 2;
+
+	if (wc > stride) {
+		wc -= stride;
+		do {
+			REPEAT4(stride, wp[stride] += wp[0]; wp++)
+			wc -= stride;
+		} while ((int32) wc > 0);
+	}
+}
+
+/*
+ * Floating point predictor accumulation routine.
+ */
+static void
+fpAcc(TIFF* tif, tidata_t cp0, tsize_t cc)
+{
+	tsize_t stride = PredictorState(tif)->stride;
+	uint32 bps = tif->tif_dir.td_bitspersample / 8;
+	tsize_t wc = cc / bps;
+	tsize_t count = cc;
+	uint8 *cp = (uint8 *) cp0;
+	uint8 *tmp = (uint8 *)_TIFFmalloc(cc);
+
+	if (!tmp)
+		return;
+
+	while (count > stride) {
+		REPEAT4(stride, cp[stride] += cp[0]; cp++)
+		count -= stride;
+	}
+
+	_TIFFmemcpy(tmp, cp0, cc);
+	cp = (uint8 *) cp0;
+	for (count = 0; count < wc; count++) {
+		uint32 byte;
+		for (byte = 0; byte < bps; byte++) {
+#if WORDS_BIGENDIAN
+			cp[bps * count + byte] = tmp[byte * wc + count];
+#else
+			cp[bps * count + byte] =
+				tmp[(bps - byte - 1) * wc + count];
+#endif
+		}
+	}
+	_TIFFfree(tmp);
+}
+
+/*
+ * Decode a scanline and apply the predictor routine.
+ */
+static int
+PredictorDecodeRow(TIFF* tif, tidata_t op0, tsize_t occ0, tsample_t s)
+{
+	TIFFPredictorState *sp = PredictorState(tif);
+
+	assert(sp != NULL);
+	assert(sp->coderow != NULL);
+	assert(sp->pfunc != NULL);
+
+	if ((*sp->coderow)(tif, op0, occ0, s)) {
+		(*sp->pfunc)(tif, op0, occ0);
+		return 1;
+	} else
+		return 0;
+}
+
+/*
+ * Decode a tile/strip and apply the predictor routine.
+ * Note that horizontal differencing must be done on a
+ * row-by-row basis.  The width of a "row" has already
+ * been calculated at pre-decode time according to the
+ * strip/tile dimensions.
+ */
+static int
+PredictorDecodeTile(TIFF* tif, tidata_t op0, tsize_t occ0, tsample_t s)
+{
+	TIFFPredictorState *sp = PredictorState(tif);
+
+	assert(sp != NULL);
+	assert(sp->codetile != NULL);
+
+	if ((*sp->codetile)(tif, op0, occ0, s)) {
+		tsize_t rowsize = sp->rowsize;
+		assert(rowsize > 0);
+		assert(sp->pfunc != NULL);
+		while ((long)occ0 > 0) {
+			(*sp->pfunc)(tif, op0, (tsize_t) rowsize);
+			occ0 -= rowsize;
+			op0 += rowsize;
+		}
+		return 1;
+	} else
+		return 0;
+}
+
+static void
+horDiff8(TIFF* tif, tidata_t cp0, tsize_t cc)
+{
+	TIFFPredictorState* sp = PredictorState(tif);
+	tsize_t stride = sp->stride;
+	char* cp = (char*) cp0;
+
+	if (cc > stride) {
+		cc -= stride;
+		/*
+		 * Pipeline the most common cases.
+		 */
+		if (stride == 3) {
+			int r1, g1, b1;
+			int r2 = cp[0];
+			int g2 = cp[1];
+			int b2 = cp[2];
+			do {
+				r1 = cp[3]; cp[3] = r1-r2; r2 = r1;
+				g1 = cp[4]; cp[4] = g1-g2; g2 = g1;
+				b1 = cp[5]; cp[5] = b1-b2; b2 = b1;
+				cp += 3;
+			} while ((int32)(cc -= 3) > 0);
+		} else if (stride == 4) {
+			int r1, g1, b1, a1;
+			int r2 = cp[0];
+			int g2 = cp[1];
+			int b2 = cp[2];
+			int a2 = cp[3];
+			do {
+				r1 = cp[4]; cp[4] = r1-r2; r2 = r1;
+				g1 = cp[5]; cp[5] = g1-g2; g2 = g1;
+				b1 = cp[6]; cp[6] = b1-b2; b2 = b1;
+				a1 = cp[7]; cp[7] = a1-a2; a2 = a1;
+				cp += 4;
+			} while ((int32)(cc -= 4) > 0);
+		} else {
+			cp += cc - 1;
+			do {
+				REPEAT4(stride, cp[stride] -= cp[0]; cp--)
+			} while ((int32)(cc -= stride) > 0);
+		}
+	}
+}
+
+static void
+horDiff16(TIFF* tif, tidata_t cp0, tsize_t cc)
+{
+	TIFFPredictorState* sp = PredictorState(tif);
+	tsize_t stride = sp->stride;
+	int16 *wp = (int16*) cp0;
+	tsize_t wc = cc/2;
+
+	if (wc > stride) {
+		wc -= stride;
+		wp += wc - 1;
+		do {
+			REPEAT4(stride, wp[stride] -= wp[0]; wp--)
+			wc -= stride;
+		} while ((int32) wc > 0);
+	}
+}
+
+/*
+ * Floating point predictor differencing routine.
+ */
+static void
+fpDiff(TIFF* tif, tidata_t cp0, tsize_t cc)
+{
+	tsize_t stride = PredictorState(tif)->stride;
+	uint32 bps = tif->tif_dir.td_bitspersample / 8;
+	tsize_t wc = cc / bps;
+	tsize_t count;
+	uint8 *cp = (uint8 *) cp0;
+	uint8 *tmp = (uint8 *)_TIFFmalloc(cc);
+
+	if (!tmp)
+		return;
+
+	_TIFFmemcpy(tmp, cp0, cc);
+	for (count = 0; count < wc; count++) {
+		uint32 byte;
+		for (byte = 0; byte < bps; byte++) {
+#if WORDS_BIGENDIAN
+			cp[byte * wc + count] =	tmp[bps * count + byte];
+#else
+			cp[(bps - byte - 1) * wc + count] =
+				tmp[bps * count + byte];
+#endif
+		}
+	}
+	_TIFFfree(tmp);
+
+	cp = (uint8 *) cp0;
+	cp += cc - stride - 1;
+	for (count = cc; count > stride; count -= stride)
+		REPEAT4(stride, cp[stride] -= cp[0]; cp--)
+}
+
+static int
+PredictorEncodeRow(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s)
+{
+	TIFFPredictorState *sp = PredictorState(tif);
+
+	assert(sp != NULL);
+	assert(sp->pfunc != NULL);
+	assert(sp->coderow != NULL);
+
+	/* XXX horizontal differencing alters user's data XXX */
+	(*sp->pfunc)(tif, bp, cc);
+	return (*sp->coderow)(tif, bp, cc, s);
+}
+
+static int
+PredictorEncodeTile(TIFF* tif, tidata_t bp0, tsize_t cc0, tsample_t s)
+{
+	TIFFPredictorState *sp = PredictorState(tif);
+	tsize_t cc = cc0, rowsize;
+	unsigned char* bp = bp0;
+
+	assert(sp != NULL);
+	assert(sp->pfunc != NULL);
+	assert(sp->codetile != NULL);
+
+	rowsize = sp->rowsize;
+	assert(rowsize > 0);
+	while ((long)cc > 0) {
+		(*sp->pfunc)(tif, bp, (tsize_t) rowsize);
+		cc -= rowsize;
+		bp += rowsize;
+	}
+	return (*sp->codetile)(tif, bp0, cc0, s);
+}
+
+#define	FIELD_PREDICTOR	(FIELD_CODEC+0)		/* XXX */
+
+static const TIFFFieldInfo predictFieldInfo[] = {
+    { TIFFTAG_PREDICTOR,	 1, 1, TIFF_SHORT,	FIELD_PREDICTOR,
+      FALSE,	FALSE,	"Predictor" },
+};
+#define	N(a)	(sizeof (a) / sizeof (a[0]))
+
+static int
+PredictorVSetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+	TIFFPredictorState *sp = PredictorState(tif);
+
+	assert(sp != NULL);
+	assert(sp->vsetparent != NULL);
+
+	switch (tag) {
+	case TIFFTAG_PREDICTOR:
+		sp->predictor = (uint16) va_arg(ap, int);
+		TIFFSetFieldBit(tif, FIELD_PREDICTOR);
+		break;
+	default:
+		return (*sp->vsetparent)(tif, tag, ap);
+	}
+	tif->tif_flags |= TIFF_DIRTYDIRECT;
+	return 1;
+}
+
+static int
+PredictorVGetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+	TIFFPredictorState *sp = PredictorState(tif);
+
+	assert(sp != NULL);
+	assert(sp->vgetparent != NULL);
+
+	switch (tag) {
+	case TIFFTAG_PREDICTOR:
+		*va_arg(ap, uint16*) = sp->predictor;
+		break;
+	default:
+		return (*sp->vgetparent)(tif, tag, ap);
+	}
+	return 1;
+}
+
+static void
+PredictorPrintDir(TIFF* tif, FILE* fd, long flags)
+{
+	TIFFPredictorState* sp = PredictorState(tif);
+
+	(void) flags;
+	if (TIFFFieldSet(tif,FIELD_PREDICTOR)) {
+		fprintf(fd, "  Predictor: ");
+		switch (sp->predictor) {
+		case 1: fprintf(fd, "none "); break;
+		case 2: fprintf(fd, "horizontal differencing "); break;
+		case 3: fprintf(fd, "floating point predictor "); break;
+		}
+		fprintf(fd, "%u (0x%x)\n", sp->predictor, sp->predictor);
+	}
+	if (sp->printdir)
+		(*sp->printdir)(tif, fd, flags);
+}
+
+int
+TIFFPredictorInit(TIFF* tif)
+{
+	TIFFPredictorState* sp = PredictorState(tif);
+
+	assert(sp != 0);
+
+	/*
+	 * Merge codec-specific tag information and
+	 * override parent get/set field methods.
+	 */
+	_TIFFMergeFieldInfo(tif, predictFieldInfo, N(predictFieldInfo));
+	sp->vgetparent = tif->tif_tagmethods.vgetfield;
+	tif->tif_tagmethods.vgetfield =
+            PredictorVGetField;/* hook for predictor tag */
+	sp->vsetparent = tif->tif_tagmethods.vsetfield;
+	tif->tif_tagmethods.vsetfield =
+            PredictorVSetField;/* hook for predictor tag */
+	sp->printdir = tif->tif_tagmethods.printdir;
+	tif->tif_tagmethods.printdir =
+            PredictorPrintDir;	/* hook for predictor tag */
+
+	sp->setupdecode = tif->tif_setupdecode;
+	tif->tif_setupdecode = PredictorSetupDecode;
+	sp->setupencode = tif->tif_setupencode;
+	tif->tif_setupencode = PredictorSetupEncode;
+
+	sp->predictor = 1;			/* default value */
+	sp->pfunc = NULL;			/* no predictor routine */
+	return 1;
+}
+
+int
+TIFFPredictorCleanup(TIFF* tif)
+{
+	TIFFPredictorState* sp = PredictorState(tif);
+
+	assert(sp != 0);
+
+	tif->tif_tagmethods.vgetfield = sp->vgetparent;
+	tif->tif_tagmethods.vsetfield = sp->vsetparent;
+	tif->tif_tagmethods.printdir = sp->printdir;
+	tif->tif_setupdecode = sp->setupdecode;
+	tif->tif_setupencode = sp->setupencode;
+
+	return 1;
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_predict.h b/Utilities/GDAL/frmts/gtiff/libtiff/tif_predict.h
new file mode 100644
index 0000000000..8563c83c8e
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_predict.h
@@ -0,0 +1,64 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1995-1997 Sam Leffler
+ * Copyright (c) 1995-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _TIFFPREDICT_
+#define	_TIFFPREDICT_
+/*
+ * ``Library-private'' Support for the Predictor Tag
+ */
+
+/*
+ * Codecs that want to support the Predictor tag must place
+ * this structure first in their private state block so that
+ * the predictor code can cast tif_data to find its state.
+ */
+typedef struct {
+	int		predictor;	/* predictor tag value */
+	int		stride;		/* sample stride over data */
+	tsize_t		rowsize;	/* tile/strip row size */
+
+	TIFFPostMethod	pfunc;		/* horizontal differencer/accumulator */
+	TIFFCodeMethod	coderow;	/* parent codec encode/decode row */
+	TIFFCodeMethod	codestrip;	/* parent codec encode/decode strip */
+	TIFFCodeMethod	codetile;	/* parent codec encode/decode tile */
+	TIFFVGetMethod	vgetparent;	/* super-class method */
+	TIFFVSetMethod	vsetparent;	/* super-class method */
+	TIFFPrintMethod	printdir;	/* super-class method */
+	TIFFBoolMethod	setupdecode;	/* super-class method */
+	TIFFBoolMethod	setupencode;	/* super-class method */
+} TIFFPredictorState;
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+extern	int TIFFPredictorInit(TIFF*);
+extern	int TIFFPredictorCleanup(TIFF*);
+#if defined(__cplusplus)
+}
+#endif
+#endif /* _TIFFPREDICT_ */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_print.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_print.c
new file mode 100644
index 0000000000..e716296034
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_print.c
@@ -0,0 +1,639 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ *
+ * Directory Printing Support
+ */
+#include "tiffiop.h"
+#include <stdio.h>
+
+#include <ctype.h>
+
+static const char *photoNames[] = {
+    "min-is-white",				/* PHOTOMETRIC_MINISWHITE */
+    "min-is-black",				/* PHOTOMETRIC_MINISBLACK */
+    "RGB color",				/* PHOTOMETRIC_RGB */
+    "palette color (RGB from colormap)",	/* PHOTOMETRIC_PALETTE */
+    "transparency mask",			/* PHOTOMETRIC_MASK */
+    "separated",				/* PHOTOMETRIC_SEPARATED */
+    "YCbCr",					/* PHOTOMETRIC_YCBCR */
+    "7 (0x7)",
+    "CIE L*a*b*",				/* PHOTOMETRIC_CIELAB */
+};
+#define	NPHOTONAMES	(sizeof (photoNames) / sizeof (photoNames[0]))
+
+static const char *orientNames[] = {
+    "0 (0x0)",
+    "row 0 top, col 0 lhs",			/* ORIENTATION_TOPLEFT */
+    "row 0 top, col 0 rhs",			/* ORIENTATION_TOPRIGHT */
+    "row 0 bottom, col 0 rhs",			/* ORIENTATION_BOTRIGHT */
+    "row 0 bottom, col 0 lhs",			/* ORIENTATION_BOTLEFT */
+    "row 0 lhs, col 0 top",			/* ORIENTATION_LEFTTOP */
+    "row 0 rhs, col 0 top",			/* ORIENTATION_RIGHTTOP */
+    "row 0 rhs, col 0 bottom",			/* ORIENTATION_RIGHTBOT */
+    "row 0 lhs, col 0 bottom",			/* ORIENTATION_LEFTBOT */
+};
+#define	NORIENTNAMES	(sizeof (orientNames) / sizeof (orientNames[0]))
+
+static void
+_TIFFPrintField(FILE* fd, const TIFFFieldInfo *fip,
+		uint32 value_count, void *raw_data)
+{
+	uint32 j;
+		
+	fprintf(fd, "  %s: ", fip->field_name);
+
+	for(j = 0; j < value_count; j++) {
+		if(fip->field_type == TIFF_BYTE)
+			fprintf(fd, "%u", ((uint8 *) raw_data)[j]);
+		else if(fip->field_type == TIFF_UNDEFINED)
+			fprintf(fd, "0x%x",
+				(unsigned int) ((unsigned char *) raw_data)[j]);
+		else if(fip->field_type == TIFF_SBYTE)
+			fprintf(fd, "%d", ((int8 *) raw_data)[j]);
+		else if(fip->field_type == TIFF_SHORT)
+			fprintf(fd, "%u", ((uint16 *) raw_data)[j]);
+		else if(fip->field_type == TIFF_SSHORT)
+			fprintf(fd, "%d", ((int16 *) raw_data)[j]);
+		else if(fip->field_type == TIFF_LONG)
+			fprintf(fd, "%lu",
+				(unsigned long)((uint32 *) raw_data)[j]);
+		else if(fip->field_type == TIFF_SLONG)
+			fprintf(fd, "%ld", (long)((int32 *) raw_data)[j]);
+		else if(fip->field_type == TIFF_RATIONAL
+			|| fip->field_type == TIFF_SRATIONAL
+			|| fip->field_type == TIFF_FLOAT)
+			fprintf(fd, "%f", ((float *) raw_data)[j]);
+		else if(fip->field_type == TIFF_IFD)
+			fprintf(fd, "0x%ulx", ((uint32 *) raw_data)[j]);
+		else if(fip->field_type == TIFF_ASCII) {
+			fprintf(fd, "%s", (char *) raw_data);
+			break;
+		}
+		else if(fip->field_type == TIFF_DOUBLE)
+			fprintf(fd, "%f", ((double *) raw_data)[j]);
+		else if(fip->field_type == TIFF_FLOAT)
+			fprintf(fd, "%f", ((float *)raw_data)[j]);
+		else {
+			fprintf(fd, "<unsupported data type in TIFFPrint>");
+			break;
+		}
+
+		if(j < value_count - 1)
+			fprintf(fd, ",");
+	}
+
+	fprintf(fd, "\n");
+}
+
+static int
+_TIFFPrettyPrintField(TIFF* tif, FILE* fd, ttag_t tag,
+		      uint32 value_count, void *raw_data)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+
+	switch (tag)
+	{
+		case TIFFTAG_INKSET:
+			fprintf(fd, "  Ink Set: ");
+			switch (*((uint16*)raw_data)) {
+				case INKSET_CMYK:
+					fprintf(fd, "CMYK\n");
+					break;
+				default:
+					fprintf(fd, "%u (0x%x)\n",
+						*((uint16*)raw_data),
+						*((uint16*)raw_data));
+					break;
+			}
+			return 1;
+		case TIFFTAG_DOTRANGE:
+			fprintf(fd, "  Dot Range: %u-%u\n",
+				((uint16*)raw_data)[0], ((uint16*)raw_data)[1]);
+			return 1;
+		case TIFFTAG_WHITEPOINT:
+			fprintf(fd, "  White Point: %g-%g\n",
+				((float *)raw_data)[0], ((float *)raw_data)[1]);			return 1;
+		case TIFFTAG_REFERENCEBLACKWHITE:
+		{
+			uint16 i;
+
+			fprintf(fd, "  Reference Black/White:\n");
+			for (i = 0; i < td->td_samplesperpixel; i++)
+			fprintf(fd, "    %2d: %5g %5g\n", i,
+				((float *)raw_data)[2*i+0],
+				((float *)raw_data)[2*i+1]);
+			return 1;
+		}
+		case TIFFTAG_XMLPACKET:
+		{
+			uint32 i;
+			
+			fprintf(fd, "  XMLPacket (XMP Metadata):\n" );
+			for(i = 0; i < value_count; i++)
+				fputc(((char *)raw_data)[i], fd);
+			fprintf( fd, "\n" );
+			return 1;
+		}
+		case TIFFTAG_RICHTIFFIPTC:
+			/*
+			 * XXX: for some weird reason RichTIFFIPTC tag
+			 * defined as array of LONG values.
+			 */
+			fprintf(fd,
+				"  RichTIFFIPTC Data: <present>, %lu bytes\n",
+				(unsigned long) value_count * 4);
+			return 1;
+		case TIFFTAG_PHOTOSHOP:
+			fprintf(fd, "  Photoshop Data: <present>, %lu bytes\n",
+				(unsigned long) value_count);
+			return 1;
+		case TIFFTAG_ICCPROFILE:
+			fprintf(fd, "  ICC Profile: <present>, %lu bytes\n",
+				(unsigned long) value_count);
+			return 1;
+		case TIFFTAG_STONITS:
+			fprintf(fd,
+				"  Sample to Nits conversion factor: %.4e\n",
+				*((double*)raw_data));
+			return 1;
+        }
+
+	return 0;
+}
+
+/*
+ * Print the contents of the current directory
+ * to the specified stdio file stream.
+ */
+void
+TIFFPrintDirectory(TIFF* tif, FILE* fd, long flags)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	char *sep;
+	uint16 i;
+	long l, n;
+
+	fprintf(fd, "TIFF Directory at offset 0x%lx (%lu)\n",
+		(unsigned long)tif->tif_diroff, (unsigned long)tif->tif_diroff);
+	if (TIFFFieldSet(tif,FIELD_SUBFILETYPE)) {
+		fprintf(fd, "  Subfile Type:");
+		sep = " ";
+		if (td->td_subfiletype & FILETYPE_REDUCEDIMAGE) {
+			fprintf(fd, "%sreduced-resolution image", sep);
+			sep = "/";
+		}
+		if (td->td_subfiletype & FILETYPE_PAGE) {
+			fprintf(fd, "%smulti-page document", sep);
+			sep = "/";
+		}
+		if (td->td_subfiletype & FILETYPE_MASK)
+			fprintf(fd, "%stransparency mask", sep);
+		fprintf(fd, " (%lu = 0x%lx)\n",
+		    (long) td->td_subfiletype, (long) td->td_subfiletype);
+	}
+	if (TIFFFieldSet(tif,FIELD_IMAGEDIMENSIONS)) {
+		fprintf(fd, "  Image Width: %lu Image Length: %lu",
+		    (unsigned long) td->td_imagewidth, (unsigned long) td->td_imagelength);
+		if (TIFFFieldSet(tif,FIELD_IMAGEDEPTH))
+			fprintf(fd, " Image Depth: %lu",
+			    (unsigned long) td->td_imagedepth);
+		fprintf(fd, "\n");
+	}
+	if (TIFFFieldSet(tif,FIELD_TILEDIMENSIONS)) {
+		fprintf(fd, "  Tile Width: %lu Tile Length: %lu",
+		    (unsigned long) td->td_tilewidth, (unsigned long) td->td_tilelength);
+		if (TIFFFieldSet(tif,FIELD_TILEDEPTH))
+			fprintf(fd, " Tile Depth: %lu",
+			    (unsigned long) td->td_tiledepth);
+		fprintf(fd, "\n");
+	}
+	if (TIFFFieldSet(tif,FIELD_RESOLUTION)) {
+		fprintf(fd, "  Resolution: %g, %g",
+		    td->td_xresolution, td->td_yresolution);
+		if (TIFFFieldSet(tif,FIELD_RESOLUTIONUNIT)) {
+			switch (td->td_resolutionunit) {
+			case RESUNIT_NONE:
+				fprintf(fd, " (unitless)");
+				break;
+			case RESUNIT_INCH:
+				fprintf(fd, " pixels/inch");
+				break;
+			case RESUNIT_CENTIMETER:
+				fprintf(fd, " pixels/cm");
+				break;
+			default:
+				fprintf(fd, " (unit %u = 0x%x)",
+				    td->td_resolutionunit,
+				    td->td_resolutionunit);
+				break;
+			}
+		}
+		fprintf(fd, "\n");
+	}
+	if (TIFFFieldSet(tif,FIELD_POSITION))
+		fprintf(fd, "  Position: %g, %g\n",
+		    td->td_xposition, td->td_yposition);
+	if (TIFFFieldSet(tif,FIELD_BITSPERSAMPLE))
+		fprintf(fd, "  Bits/Sample: %u\n", td->td_bitspersample);
+	if (TIFFFieldSet(tif,FIELD_SAMPLEFORMAT)) {
+		fprintf(fd, "  Sample Format: ");
+		switch (td->td_sampleformat) {
+		case SAMPLEFORMAT_VOID:
+			fprintf(fd, "void\n");
+			break;
+		case SAMPLEFORMAT_INT:
+			fprintf(fd, "signed integer\n");
+			break;
+		case SAMPLEFORMAT_UINT:
+			fprintf(fd, "unsigned integer\n");
+			break;
+		case SAMPLEFORMAT_IEEEFP:
+			fprintf(fd, "IEEE floating point\n");
+			break;
+		case SAMPLEFORMAT_COMPLEXINT:
+			fprintf(fd, "complex signed integer\n");
+			break;
+		case SAMPLEFORMAT_COMPLEXIEEEFP:
+			fprintf(fd, "complex IEEE floating point\n");
+			break;
+		default:
+			fprintf(fd, "%u (0x%x)\n",
+			    td->td_sampleformat, td->td_sampleformat);
+			break;
+		}
+	}
+	if (TIFFFieldSet(tif,FIELD_COMPRESSION)) {
+		const TIFFCodec* c = TIFFFindCODEC(td->td_compression);
+		fprintf(fd, "  Compression Scheme: ");
+		if (c)
+			fprintf(fd, "%s\n", c->name);
+		else
+			fprintf(fd, "%u (0x%x)\n",
+			    td->td_compression, td->td_compression);
+	}
+	if (TIFFFieldSet(tif,FIELD_PHOTOMETRIC)) {
+		fprintf(fd, "  Photometric Interpretation: ");
+		if (td->td_photometric < NPHOTONAMES)
+			fprintf(fd, "%s\n", photoNames[td->td_photometric]);
+		else {
+			switch (td->td_photometric) {
+			case PHOTOMETRIC_LOGL:
+				fprintf(fd, "CIE Log2(L)\n");
+				break;
+			case PHOTOMETRIC_LOGLUV:
+				fprintf(fd, "CIE Log2(L) (u',v')\n");
+				break;
+			default:
+				fprintf(fd, "%u (0x%x)\n",
+				    td->td_photometric, td->td_photometric);
+				break;
+			}
+		}
+	}
+	if (TIFFFieldSet(tif,FIELD_EXTRASAMPLES) && td->td_extrasamples) {
+		fprintf(fd, "  Extra Samples: %u<", td->td_extrasamples);
+		sep = "";
+		for (i = 0; i < td->td_extrasamples; i++) {
+			switch (td->td_sampleinfo[i]) {
+			case EXTRASAMPLE_UNSPECIFIED:
+				fprintf(fd, "%sunspecified", sep);
+				break;
+			case EXTRASAMPLE_ASSOCALPHA:
+				fprintf(fd, "%sassoc-alpha", sep);
+				break;
+			case EXTRASAMPLE_UNASSALPHA:
+				fprintf(fd, "%sunassoc-alpha", sep);
+				break;
+			default:
+				fprintf(fd, "%s%u (0x%x)", sep,
+				    td->td_sampleinfo[i], td->td_sampleinfo[i]);
+				break;
+			}
+			sep = ", ";
+		}
+		fprintf(fd, ">\n");
+	}
+	if (TIFFFieldSet(tif,FIELD_INKNAMES)) {
+		char* cp;
+		fprintf(fd, "  Ink Names: ");
+		i = td->td_samplesperpixel;
+		sep = "";
+		for (cp = td->td_inknames; i > 0; cp = strchr(cp,'\0')+1, i--) {
+			fputs(sep, fd);
+			_TIFFprintAscii(fd, cp);
+			sep = ", ";
+		}
+                fputs("\n", fd);
+	}
+	if (TIFFFieldSet(tif,FIELD_THRESHHOLDING)) {
+		fprintf(fd, "  Thresholding: ");
+		switch (td->td_threshholding) {
+		case THRESHHOLD_BILEVEL:
+			fprintf(fd, "bilevel art scan\n");
+			break;
+		case THRESHHOLD_HALFTONE:
+			fprintf(fd, "halftone or dithered scan\n");
+			break;
+		case THRESHHOLD_ERRORDIFFUSE:
+			fprintf(fd, "error diffused\n");
+			break;
+		default:
+			fprintf(fd, "%u (0x%x)\n",
+			    td->td_threshholding, td->td_threshholding);
+			break;
+		}
+	}
+	if (TIFFFieldSet(tif,FIELD_FILLORDER)) {
+		fprintf(fd, "  FillOrder: ");
+		switch (td->td_fillorder) {
+		case FILLORDER_MSB2LSB:
+			fprintf(fd, "msb-to-lsb\n");
+			break;
+		case FILLORDER_LSB2MSB:
+			fprintf(fd, "lsb-to-msb\n");
+			break;
+		default:
+			fprintf(fd, "%u (0x%x)\n",
+			    td->td_fillorder, td->td_fillorder);
+			break;
+		}
+	}
+	if (TIFFFieldSet(tif,FIELD_YCBCRSUBSAMPLING))
+        {
+            /*
+             * For hacky reasons (see tif_jpeg.c - JPEGFixupTestSubsampling),
+             * we need to fetch this rather than trust what is in our
+             * structures.
+             */
+            uint16 subsampling[2];
+
+            TIFFGetField( tif, TIFFTAG_YCBCRSUBSAMPLING, 
+                          subsampling + 0, subsampling + 1 );
+		fprintf(fd, "  YCbCr Subsampling: %u, %u\n",
+                        subsampling[0], subsampling[1] );
+        }
+	if (TIFFFieldSet(tif,FIELD_YCBCRPOSITIONING)) {
+		fprintf(fd, "  YCbCr Positioning: ");
+		switch (td->td_ycbcrpositioning) {
+		case YCBCRPOSITION_CENTERED:
+			fprintf(fd, "centered\n");
+			break;
+		case YCBCRPOSITION_COSITED:
+			fprintf(fd, "cosited\n");
+			break;
+		default:
+			fprintf(fd, "%u (0x%x)\n",
+			    td->td_ycbcrpositioning, td->td_ycbcrpositioning);
+			break;
+		}
+	}
+	if (TIFFFieldSet(tif,FIELD_HALFTONEHINTS))
+		fprintf(fd, "  Halftone Hints: light %u dark %u\n",
+		    td->td_halftonehints[0], td->td_halftonehints[1]);
+	if (TIFFFieldSet(tif,FIELD_ORIENTATION)) {
+		fprintf(fd, "  Orientation: ");
+		if (td->td_orientation < NORIENTNAMES)
+			fprintf(fd, "%s\n", orientNames[td->td_orientation]);
+		else
+			fprintf(fd, "%u (0x%x)\n",
+			    td->td_orientation, td->td_orientation);
+	}
+	if (TIFFFieldSet(tif,FIELD_SAMPLESPERPIXEL))
+		fprintf(fd, "  Samples/Pixel: %u\n", td->td_samplesperpixel);
+	if (TIFFFieldSet(tif,FIELD_ROWSPERSTRIP)) {
+		fprintf(fd, "  Rows/Strip: ");
+		if (td->td_rowsperstrip == (uint32) -1)
+			fprintf(fd, "(infinite)\n");
+		else
+			fprintf(fd, "%lu\n", (unsigned long) td->td_rowsperstrip);
+	}
+	if (TIFFFieldSet(tif,FIELD_MINSAMPLEVALUE))
+		fprintf(fd, "  Min Sample Value: %u\n", td->td_minsamplevalue);
+	if (TIFFFieldSet(tif,FIELD_MAXSAMPLEVALUE))
+		fprintf(fd, "  Max Sample Value: %u\n", td->td_maxsamplevalue);
+	if (TIFFFieldSet(tif,FIELD_SMINSAMPLEVALUE))
+		fprintf(fd, "  SMin Sample Value: %g\n",
+		    td->td_sminsamplevalue);
+	if (TIFFFieldSet(tif,FIELD_SMAXSAMPLEVALUE))
+		fprintf(fd, "  SMax Sample Value: %g\n",
+		    td->td_smaxsamplevalue);
+	if (TIFFFieldSet(tif,FIELD_PLANARCONFIG)) {
+		fprintf(fd, "  Planar Configuration: ");
+		switch (td->td_planarconfig) {
+		case PLANARCONFIG_CONTIG:
+			fprintf(fd, "single image plane\n");
+			break;
+		case PLANARCONFIG_SEPARATE:
+			fprintf(fd, "separate image planes\n");
+			break;
+		default:
+			fprintf(fd, "%u (0x%x)\n",
+			    td->td_planarconfig, td->td_planarconfig);
+			break;
+		}
+	}
+	if (TIFFFieldSet(tif,FIELD_PAGENUMBER))
+		fprintf(fd, "  Page Number: %u-%u\n",
+		    td->td_pagenumber[0], td->td_pagenumber[1]);
+	if (TIFFFieldSet(tif,FIELD_COLORMAP)) {
+		fprintf(fd, "  Color Map: ");
+		if (flags & TIFFPRINT_COLORMAP) {
+			fprintf(fd, "\n");
+			n = 1L<<td->td_bitspersample;
+			for (l = 0; l < n; l++)
+				fprintf(fd, "   %5lu: %5u %5u %5u\n",
+				    l,
+				    td->td_colormap[0][l],
+				    td->td_colormap[1][l],
+				    td->td_colormap[2][l]);
+		} else
+			fprintf(fd, "(present)\n");
+	}
+	if (TIFFFieldSet(tif,FIELD_TRANSFERFUNCTION)) {
+		fprintf(fd, "  Transfer Function: ");
+		if (flags & TIFFPRINT_CURVES) {
+			fprintf(fd, "\n");
+			n = 1L<<td->td_bitspersample;
+			for (l = 0; l < n; l++) {
+				fprintf(fd, "    %2lu: %5u",
+				    l, td->td_transferfunction[0][l]);
+				for (i = 1; i < td->td_samplesperpixel; i++)
+					fprintf(fd, " %5u",
+					    td->td_transferfunction[i][l]);
+				fputc('\n', fd);
+			}
+		} else
+			fprintf(fd, "(present)\n");
+	}
+	if (TIFFFieldSet(tif, FIELD_SUBIFD)) {
+		fprintf(fd, "  SubIFD Offsets:");
+		for (i = 0; i < td->td_nsubifd; i++)
+			fprintf(fd, " %5lu", (long) td->td_subifd[i]);
+		fputc('\n', fd);
+	}
+
+        /*
+        ** Custom tag support.
+        */
+        {
+            int  i;
+            short count;
+
+            count = (short) TIFFGetTagListCount(tif);
+            for(i = 0; i < count; i++) {
+                ttag_t  tag = TIFFGetTagListEntry(tif, i);
+                const TIFFFieldInfo *fip;
+                uint16 value_count;
+                int mem_alloc = 0;
+                void *raw_data;
+
+                fip = TIFFFieldWithTag(tif, tag);
+                if(fip == NULL)
+			continue;
+
+		if(fip->field_passcount) {
+			if(TIFFGetField(tif, tag, &value_count, &raw_data) != 1)
+				continue;
+		} else {
+			if (fip->field_readcount == TIFF_VARIABLE
+			    || fip->field_readcount == TIFF_VARIABLE2)
+				value_count = 1;
+			else if (fip->field_readcount == TIFF_SPP)
+				value_count = td->td_samplesperpixel;
+			else
+				value_count = fip->field_readcount;
+			if ((fip->field_type == TIFF_ASCII
+			     || fip->field_readcount == TIFF_VARIABLE
+			     || fip->field_readcount == TIFF_VARIABLE2
+			     || fip->field_readcount == TIFF_SPP
+			     || value_count > 1)
+			    && fip->field_tag != TIFFTAG_PAGENUMBER
+			    && fip->field_tag != TIFFTAG_HALFTONEHINTS
+			    && fip->field_tag != TIFFTAG_YCBCRSUBSAMPLING
+			    && fip->field_tag != TIFFTAG_DOTRANGE) {
+				if(TIFFGetField(tif, tag, &raw_data) != 1)
+					continue;
+			} else if (fip->field_tag != TIFFTAG_PAGENUMBER
+				   && fip->field_tag != TIFFTAG_HALFTONEHINTS
+				   && fip->field_tag != TIFFTAG_YCBCRSUBSAMPLING
+				   && fip->field_tag != TIFFTAG_DOTRANGE) {
+				raw_data = _TIFFmalloc(
+					_TIFFDataSize(fip->field_type)
+					* value_count);
+				mem_alloc = 1;
+				if(TIFFGetField(tif, tag, raw_data) != 1) {
+					_TIFFfree(raw_data);
+					continue;
+				}
+			} else {
+				/* 
+				 * XXX: Should be fixed and removed, see the
+				 * notes related to TIFFTAG_PAGENUMBER,
+				 * TIFFTAG_HALFTONEHINTS,
+				 * TIFFTAG_YCBCRSUBSAMPLING and
+				 * TIFFTAG_DOTRANGE tags in tif_dir.c. */
+				char *tmp;
+				raw_data = _TIFFmalloc(
+					_TIFFDataSize(fip->field_type)
+					* value_count);
+				tmp = raw_data;
+				mem_alloc = 1;
+				if(TIFFGetField(tif, tag, tmp,
+				tmp + _TIFFDataSize(fip->field_type)) != 1) {
+					_TIFFfree(raw_data);
+					continue;
+				}
+			}
+		}
+
+		/*
+		 * Catch the tags which needs to be specially handled and
+		 * pretty print them. If tag not handled in
+		 * _TIFFPrettyPrintField() fall down and print it as any other
+		 * tag.
+		 */
+		if (_TIFFPrettyPrintField(tif, fd, tag, value_count, raw_data)) {
+			if(mem_alloc)
+				_TIFFfree(raw_data);
+			continue;
+		}
+		else
+			_TIFFPrintField(fd, fip, value_count, raw_data);
+
+		if(mem_alloc)
+			_TIFFfree(raw_data);
+            }
+        }
+        
+	if (tif->tif_tagmethods.printdir)
+		(*tif->tif_tagmethods.printdir)(tif, fd, flags);
+	if ((flags & TIFFPRINT_STRIPS) &&
+	    TIFFFieldSet(tif,FIELD_STRIPOFFSETS)) {
+		tstrip_t s;
+
+		fprintf(fd, "  %lu %s:\n",
+		    (long) td->td_nstrips,
+		    isTiled(tif) ? "Tiles" : "Strips");
+		for (s = 0; s < td->td_nstrips; s++)
+			fprintf(fd, "    %3lu: [%8lu, %8lu]\n",
+			    (unsigned long) s,
+			    (unsigned long) td->td_stripoffset[s],
+			    (unsigned long) td->td_stripbytecount[s]);
+	}
+}
+
+void
+_TIFFprintAscii(FILE* fd, const char* cp)
+{
+	for (; *cp != '\0'; cp++) {
+		const char* tp;
+
+		if (isprint((int)*cp)) {
+			fputc(*cp, fd);
+			continue;
+		}
+		for (tp = "\tt\bb\rr\nn\vv"; *tp; tp++)
+			if (*tp++ == *cp)
+				break;
+		if (*tp)
+			fprintf(fd, "\\%c", *tp);
+		else
+			fprintf(fd, "\\%03o", *cp & 0xff);
+	}
+}
+
+void
+_TIFFprintAsciiTag(FILE* fd, const char* name, const char* value)
+{
+	fprintf(fd, "  %s: \"", name);
+	_TIFFprintAscii(fd, value);
+	fprintf(fd, "\"\n");
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_read.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_read.c
new file mode 100644
index 0000000000..30645207ae
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_read.c
@@ -0,0 +1,685 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ * Scanline-oriented Read Support
+ */
+#include "tiffiop.h"
+#include <stdio.h>
+
+	int TIFFFillStrip(TIFF*, tstrip_t);
+	int TIFFFillTile(TIFF*, ttile_t);
+static	int TIFFStartStrip(TIFF*, tstrip_t);
+static	int TIFFStartTile(TIFF*, ttile_t);
+static	int TIFFCheckRead(TIFF*, int);
+
+#define	NOSTRIP	((tstrip_t) -1)			/* undefined state */
+#define	NOTILE	((ttile_t) -1)			/* undefined state */
+
+/*
+ * Seek to a random row+sample in a file.
+ */
+static int
+TIFFSeek(TIFF* tif, uint32 row, tsample_t sample)
+{
+	register TIFFDirectory *td = &tif->tif_dir;
+	tstrip_t strip;
+
+	if (row >= td->td_imagelength) {	/* out of range */
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "%lu: Row out of range, max %lu",
+		    (unsigned long) row, (unsigned long) td->td_imagelength);
+		return (0);
+	}
+	if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
+		if (sample >= td->td_samplesperpixel) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			    "%lu: Sample out of range, max %lu",
+			    (unsigned long) sample, (unsigned long) td->td_samplesperpixel);
+			return (0);
+		}
+		strip = sample*td->td_stripsperimage + row/td->td_rowsperstrip;
+	} else
+		strip = row / td->td_rowsperstrip;
+	if (strip != tif->tif_curstrip) { 	/* different strip, refill */
+		if (!TIFFFillStrip(tif, strip))
+			return (0);
+	} else if (row < tif->tif_row) {
+		/*
+		 * Moving backwards within the same strip: backup
+		 * to the start and then decode forward (below).
+		 *
+		 * NB: If you're planning on lots of random access within a
+		 * strip, it's better to just read and decode the entire
+		 * strip, and then access the decoded data in a random fashion.
+		 */
+		if (!TIFFStartStrip(tif, strip))
+			return (0);
+	}
+	if (row != tif->tif_row) {
+		/*
+		 * Seek forward to the desired row.
+		 */
+		if (!(*tif->tif_seek)(tif, row - tif->tif_row))
+			return (0);
+		tif->tif_row = row;
+	}
+	return (1);
+}
+
+int
+TIFFReadScanline(TIFF* tif, tdata_t buf, uint32 row, tsample_t sample)
+{
+	int e;
+
+	if (!TIFFCheckRead(tif, 0))
+		return (-1);
+	if( (e = TIFFSeek(tif, row, sample)) != 0) {
+		/*
+		 * Decompress desired row into user buffer.
+		 */
+		e = (*tif->tif_decoderow)
+		    (tif, (tidata_t) buf, tif->tif_scanlinesize, sample);
+
+		/* we are now poised at the beginning of the next row */
+		tif->tif_row = row + 1;
+
+		if (e)
+			(*tif->tif_postdecode)(tif, (tidata_t) buf,
+			    tif->tif_scanlinesize);
+	}
+	return (e > 0 ? 1 : -1);
+}
+
+/*
+ * Read a strip of data and decompress the specified
+ * amount into the user-supplied buffer.
+ */
+tsize_t
+TIFFReadEncodedStrip(TIFF* tif, tstrip_t strip, tdata_t buf, tsize_t size)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	uint32 nrows;
+	tsize_t stripsize;
+        tstrip_t sep_strip, strips_per_sep;
+
+	if (!TIFFCheckRead(tif, 0))
+		return (-1);
+	if (strip >= td->td_nstrips) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "%ld: Strip out of range, max %ld",
+		    (long) strip, (long) td->td_nstrips);
+		return (-1);
+	}
+	/*
+	 * Calculate the strip size according to the number of
+	 * rows in the strip (check for truncated last strip on any
+	 * of the separations).
+	 */
+	if( td->td_rowsperstrip >= td->td_imagelength )
+		strips_per_sep = 1;
+	else
+		strips_per_sep = (td->td_imagelength+td->td_rowsperstrip-1)
+		    / td->td_rowsperstrip;
+
+	sep_strip = strip % strips_per_sep;
+
+	if (sep_strip != strips_per_sep-1 ||
+	    (nrows = td->td_imagelength % td->td_rowsperstrip) == 0)
+		nrows = td->td_rowsperstrip;
+
+	stripsize = TIFFVStripSize(tif, nrows);
+	if (size == (tsize_t) -1)
+		size = stripsize;
+	else if (size > stripsize)
+		size = stripsize;
+	if (TIFFFillStrip(tif, strip)
+	    && (*tif->tif_decodestrip)(tif, (tidata_t) buf, size,   
+	    (tsample_t)(strip / td->td_stripsperimage)) > 0 ) {
+		(*tif->tif_postdecode)(tif, (tidata_t) buf, size);
+		return (size);
+	} else
+		return ((tsize_t) -1);
+}
+
+static tsize_t
+TIFFReadRawStrip1(TIFF* tif,
+    tstrip_t strip, tdata_t buf, tsize_t size, const char* module)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+
+	assert((tif->tif_flags&TIFF_NOREADRAW)==0);
+	if (!isMapped(tif)) {
+		tsize_t cc;
+
+		if (!SeekOK(tif, td->td_stripoffset[strip])) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+			    "%s: Seek error at scanline %lu, strip %lu",
+			    tif->tif_name,
+			    (unsigned long) tif->tif_row, (unsigned long) strip);
+			return (-1);
+		}
+		cc = TIFFReadFile(tif, buf, size);
+		if (cc != size) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+		"%s: Read error at scanline %lu; got %lu bytes, expected %lu",
+			    tif->tif_name,
+			    (unsigned long) tif->tif_row,
+			    (unsigned long) cc,
+			    (unsigned long) size);
+			return (-1);
+		}
+	} else {
+		if (td->td_stripoffset[strip] + size > tif->tif_size) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+    "%s: Read error at scanline %lu, strip %lu; got %lu bytes, expected %lu",
+			    tif->tif_name,
+			    (unsigned long) tif->tif_row,
+			    (unsigned long) strip,
+			    (unsigned long) tif->tif_size - td->td_stripoffset[strip],
+			    (unsigned long) size);
+			return (-1);
+		}
+		_TIFFmemcpy(buf, tif->tif_base + td->td_stripoffset[strip],
+                            size);
+	}
+	return (size);
+}
+
+/*
+ * Read a strip of data from the file.
+ */
+tsize_t
+TIFFReadRawStrip(TIFF* tif, tstrip_t strip, tdata_t buf, tsize_t size)
+{
+	static const char module[] = "TIFFReadRawStrip";
+	TIFFDirectory *td = &tif->tif_dir;
+	tsize_t bytecount;
+
+	if (!TIFFCheckRead(tif, 0))
+		return ((tsize_t) -1);
+	if (strip >= td->td_nstrips) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "%lu: Strip out of range, max %lu",
+		    (unsigned long) strip, (unsigned long) td->td_nstrips);
+		return ((tsize_t) -1);
+	}
+	if (tif->tif_flags&TIFF_NOREADRAW)
+	{
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Compression scheme does not support access to raw uncompressed data");
+		return ((tsize_t) -1);
+	}
+	bytecount = td->td_stripbytecount[strip];
+	if (bytecount <= 0) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		    "%lu: Invalid strip byte count, strip %lu",
+		    (unsigned long) bytecount, (unsigned long) strip);
+		return ((tsize_t) -1);
+	}
+	if (size != (tsize_t)-1 && size < bytecount)
+		bytecount = size;
+	return (TIFFReadRawStrip1(tif, strip, buf, bytecount, module));
+}
+
+/*
+ * Read the specified strip and setup for decoding.
+ * The data buffer is expanded, as necessary, to
+ * hold the strip's data.
+ */
+int
+TIFFFillStrip(TIFF* tif, tstrip_t strip)
+{
+	static const char module[] = "TIFFFillStrip";
+	TIFFDirectory *td = &tif->tif_dir;
+	tsize_t bytecount;
+
+	if ((tif->tif_flags&TIFF_NOREADRAW)==0)
+	{
+		bytecount = td->td_stripbytecount[strip];
+		if (bytecount <= 0) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			    "%lu: Invalid strip byte count, strip %lu",
+			    (unsigned long) bytecount, (unsigned long) strip);
+			return (0);
+		}
+		if (isMapped(tif) &&
+		    (isFillOrder(tif, td->td_fillorder)
+		    || (tif->tif_flags & TIFF_NOBITREV))) {
+			/*
+			 * The image is mapped into memory and we either don't
+			 * need to flip bits or the compression routine is going
+			 * to handle this operation itself.  In this case, avoid
+			 * copying the raw data and instead just reference the
+			 * data from the memory mapped file image.  This assumes
+			 * that the decompression routines do not modify the
+			 * contents of the raw data buffer (if they try to,
+			 * the application will get a fault since the file is
+			 * mapped read-only).
+			 */
+			if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata)
+				_TIFFfree(tif->tif_rawdata);
+			tif->tif_flags &= ~TIFF_MYBUFFER;
+			if ( td->td_stripoffset[strip] + bytecount > tif->tif_size) {
+				/*
+				 * This error message might seem strange, but it's
+				 * what would happen if a read were done instead.
+				 */
+				TIFFErrorExt(tif->tif_clientdata, module,
+			    "%s: Read error on strip %lu; got %lu bytes, expected %lu",
+				    tif->tif_name,
+				    (unsigned long) strip,
+				    (unsigned long) tif->tif_size - td->td_stripoffset[strip],
+				    (unsigned long) bytecount);
+				tif->tif_curstrip = NOSTRIP;
+				return (0);
+			}
+			tif->tif_rawdatasize = bytecount;
+			tif->tif_rawdata = tif->tif_base + td->td_stripoffset[strip];
+		} else {
+			/*
+			 * Expand raw data buffer, if needed, to
+			 * hold data strip coming from file
+			 * (perhaps should set upper bound on
+			 *  the size of a buffer we'll use?).
+			 */
+			if (bytecount > tif->tif_rawdatasize) {
+				tif->tif_curstrip = NOSTRIP;
+				if ((tif->tif_flags & TIFF_MYBUFFER) == 0) {
+					TIFFErrorExt(tif->tif_clientdata, module,
+					"%s: Data buffer too small to hold strip %lu",
+					    tif->tif_name, (unsigned long) strip);
+					return (0);
+				}
+				if (!TIFFReadBufferSetup(tif, 0,
+				    TIFFroundup(bytecount, 1024)))
+					return (0);
+			}
+			if (TIFFReadRawStrip1(tif, strip, (unsigned char *)tif->tif_rawdata,
+			    bytecount, module) != bytecount)
+				return (0);
+			if (!isFillOrder(tif, td->td_fillorder) &&
+			    (tif->tif_flags & TIFF_NOBITREV) == 0)
+				TIFFReverseBits(tif->tif_rawdata, bytecount);
+		}
+	}
+	return (TIFFStartStrip(tif, strip));
+}
+
+/*
+ * Tile-oriented Read Support
+ * Contributed by Nancy Cam (Silicon Graphics).
+ */
+
+/*
+ * Read and decompress a tile of data.  The
+ * tile is selected by the (x,y,z,s) coordinates.
+ */
+tsize_t
+TIFFReadTile(TIFF* tif,
+    tdata_t buf, uint32 x, uint32 y, uint32 z, tsample_t s)
+{
+	if (!TIFFCheckRead(tif, 1) || !TIFFCheckTile(tif, x, y, z, s))
+		return (-1);
+	return (TIFFReadEncodedTile(tif,
+	    TIFFComputeTile(tif, x, y, z, s), buf, (tsize_t) -1));
+}
+
+/*
+ * Read a tile of data and decompress the specified
+ * amount into the user-supplied buffer.
+ */
+tsize_t
+TIFFReadEncodedTile(TIFF* tif, ttile_t tile, tdata_t buf, tsize_t size)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	tsize_t tilesize = tif->tif_tilesize;
+
+	if (!TIFFCheckRead(tif, 1))
+		return (-1);
+	if (tile >= td->td_nstrips) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "%ld: Tile out of range, max %ld",
+		    (long) tile, (unsigned long) td->td_nstrips);
+		return (-1);
+	}
+	if (size == (tsize_t) -1)
+		size = tilesize;
+	else if (size > tilesize)
+		size = tilesize;
+	if (TIFFFillTile(tif, tile) && (*tif->tif_decodetile)(tif,
+	    (tidata_t) buf, size, (tsample_t)(tile/td->td_stripsperimage))) {
+		(*tif->tif_postdecode)(tif, (tidata_t) buf, size);
+		return (size);
+	} else
+		return (-1);
+}
+
+static tsize_t
+TIFFReadRawTile1(TIFF* tif,
+    ttile_t tile, tdata_t buf, tsize_t size, const char* module)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+
+	assert((tif->tif_flags&TIFF_NOREADRAW)==0);
+	if (!isMapped(tif)) {
+		tsize_t cc;
+
+		if (!SeekOK(tif, td->td_stripoffset[tile])) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+			    "%s: Seek error at row %ld, col %ld, tile %ld",
+			    tif->tif_name,
+			    (long) tif->tif_row,
+			    (long) tif->tif_col,
+			    (long) tile);
+			return ((tsize_t) -1);
+		}
+		cc = TIFFReadFile(tif, buf, size);
+		if (cc != size) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+	    "%s: Read error at row %ld, col %ld; got %lu bytes, expected %lu",
+			    tif->tif_name,
+			    (long) tif->tif_row,
+			    (long) tif->tif_col,
+			    (unsigned long) cc,
+			    (unsigned long) size);
+			return ((tsize_t) -1);
+		}
+	} else {
+		if (td->td_stripoffset[tile] + size > tif->tif_size) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+    "%s: Read error at row %ld, col %ld, tile %ld; got %lu bytes, expected %lu",
+			    tif->tif_name,
+			    (long) tif->tif_row,
+			    (long) tif->tif_col,
+			    (long) tile,
+			    (unsigned long) tif->tif_size - td->td_stripoffset[tile],
+			    (unsigned long) size);
+			return ((tsize_t) -1);
+		}
+		_TIFFmemcpy(buf, tif->tif_base + td->td_stripoffset[tile], size);
+	}
+	return (size);
+}
+
+/*
+ * Read a tile of data from the file.
+ */
+tsize_t
+TIFFReadRawTile(TIFF* tif, ttile_t tile, tdata_t buf, tsize_t size)
+{
+	static const char module[] = "TIFFReadRawTile";
+	TIFFDirectory *td = &tif->tif_dir;
+	tsize_t bytecount;
+
+	if (!TIFFCheckRead(tif, 1))
+		return ((tsize_t) -1);
+	if (tile >= td->td_nstrips) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "%lu: Tile out of range, max %lu",
+		    (unsigned long) tile, (unsigned long) td->td_nstrips);
+		return ((tsize_t) -1);
+	}
+	if (tif->tif_flags&TIFF_NOREADRAW)
+	{
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Compression scheme does not support access to raw uncompressed data");
+		return ((tsize_t) -1);
+	}
+	bytecount = td->td_stripbytecount[tile];
+	if (size != (tsize_t) -1 && size < bytecount)
+		bytecount = size;
+	return (TIFFReadRawTile1(tif, tile, buf, bytecount, module));
+}
+
+/*
+ * Read the specified tile and setup for decoding. 
+ * The data buffer is expanded, as necessary, to
+ * hold the tile's data.
+ */
+int
+TIFFFillTile(TIFF* tif, ttile_t tile)
+{
+	static const char module[] = "TIFFFillTile";
+	TIFFDirectory *td = &tif->tif_dir;
+	tsize_t bytecount;
+
+	if ((tif->tif_flags&TIFF_NOREADRAW)==0)
+	{
+		bytecount = td->td_stripbytecount[tile];
+		if (bytecount <= 0) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			    "%lu: Invalid tile byte count, tile %lu",
+			    (unsigned long) bytecount, (unsigned long) tile);
+			return (0);
+		}
+		if (isMapped(tif) &&
+		    (isFillOrder(tif, td->td_fillorder)
+		     || (tif->tif_flags & TIFF_NOBITREV))) {
+			/*
+			 * The image is mapped into memory and we either don't
+			 * need to flip bits or the compression routine is going
+			 * to handle this operation itself.  In this case, avoid
+			 * copying the raw data and instead just reference the
+			 * data from the memory mapped file image.  This assumes
+			 * that the decompression routines do not modify the
+			 * contents of the raw data buffer (if they try to,
+			 * the application will get a fault since the file is
+			 * mapped read-only).
+			 */
+			if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata)
+				_TIFFfree(tif->tif_rawdata);
+			tif->tif_flags &= ~TIFF_MYBUFFER;
+			if ( td->td_stripoffset[tile] + bytecount > tif->tif_size) {
+				tif->tif_curtile = NOTILE;
+				return (0);
+			}
+			tif->tif_rawdatasize = bytecount;
+			tif->tif_rawdata = tif->tif_base + td->td_stripoffset[tile];
+		} else {
+			/*
+			 * Expand raw data buffer, if needed, to
+			 * hold data tile coming from file
+			 * (perhaps should set upper bound on
+			 *  the size of a buffer we'll use?).
+			 */
+			if (bytecount > tif->tif_rawdatasize) {
+				tif->tif_curtile = NOTILE;
+				if ((tif->tif_flags & TIFF_MYBUFFER) == 0) {
+					TIFFErrorExt(tif->tif_clientdata, module,
+					"%s: Data buffer too small to hold tile %ld",
+					    tif->tif_name, (long) tile);
+					return (0);
+				}
+				if (!TIFFReadBufferSetup(tif, 0,
+				    TIFFroundup(bytecount, 1024)))
+					return (0);
+			}
+			if (TIFFReadRawTile1(tif, tile,
+			    (unsigned char *)tif->tif_rawdata,
+			    bytecount, module) != bytecount)
+				return (0);
+			if (!isFillOrder(tif, td->td_fillorder) &&
+			    (tif->tif_flags & TIFF_NOBITREV) == 0)
+				TIFFReverseBits(tif->tif_rawdata, bytecount);
+		}
+	}
+	return (TIFFStartTile(tif, tile));
+}
+
+/*
+ * Setup the raw data buffer in preparation for
+ * reading a strip of raw data.  If the buffer
+ * is specified as zero, then a buffer of appropriate
+ * size is allocated by the library.  Otherwise,
+ * the client must guarantee that the buffer is
+ * large enough to hold any individual strip of
+ * raw data.
+ */
+int
+TIFFReadBufferSetup(TIFF* tif, tdata_t bp, tsize_t size)
+{
+	static const char module[] = "TIFFReadBufferSetup";
+
+	assert((tif->tif_flags&TIFF_NOREADRAW)==0);
+	if (tif->tif_rawdata) {
+		if (tif->tif_flags & TIFF_MYBUFFER)
+			_TIFFfree(tif->tif_rawdata);
+		tif->tif_rawdata = NULL;
+	}
+	if (bp) {
+		tif->tif_rawdatasize = size;
+		tif->tif_rawdata = (tidata_t) bp;
+		tif->tif_flags &= ~TIFF_MYBUFFER;
+	} else {
+		tif->tif_rawdatasize = TIFFroundup(size, 1024);
+		tif->tif_rawdata = (tidata_t) _TIFFmalloc(tif->tif_rawdatasize);
+		tif->tif_flags |= TIFF_MYBUFFER;
+	}
+	if (tif->tif_rawdata == NULL) {
+		TIFFErrorExt(tif->tif_clientdata, module,
+		    "%s: No space for data buffer at scanline %ld",
+		    tif->tif_name, (long) tif->tif_row);
+		tif->tif_rawdatasize = 0;
+		return (0);
+	}
+	return (1);
+}
+
+/*
+ * Set state to appear as if a
+ * strip has just been read in.
+ */
+static int
+TIFFStartStrip(TIFF* tif, tstrip_t strip)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+
+	if ((tif->tif_flags & TIFF_CODERSETUP) == 0) {
+		if (!(*tif->tif_setupdecode)(tif))
+			return (0);
+		tif->tif_flags |= TIFF_CODERSETUP;
+	}
+	tif->tif_curstrip = strip;
+	tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip;
+	if (tif->tif_flags&TIFF_NOREADRAW)
+	{
+		tif->tif_rawcp = NULL;
+		tif->tif_rawcc = 0;
+	}
+	else
+	{
+		tif->tif_rawcp = tif->tif_rawdata;
+		tif->tif_rawcc = td->td_stripbytecount[strip];
+	}
+	return ((*tif->tif_predecode)(tif,
+			(tsample_t)(strip / td->td_stripsperimage)));
+}
+
+/*
+ * Set state to appear as if a
+ * tile has just been read in.
+ */
+static int
+TIFFStartTile(TIFF* tif, ttile_t tile)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+
+	if ((tif->tif_flags & TIFF_CODERSETUP) == 0) {
+		if (!(*tif->tif_setupdecode)(tif))
+			return (0);
+		tif->tif_flags |= TIFF_CODERSETUP;
+	}
+	tif->tif_curtile = tile;
+	tif->tif_row =
+	    (tile % TIFFhowmany(td->td_imagewidth, td->td_tilewidth)) *
+		td->td_tilelength;
+	tif->tif_col =
+	    (tile % TIFFhowmany(td->td_imagelength, td->td_tilelength)) *
+		td->td_tilewidth;
+	if (tif->tif_flags&TIFF_NOREADRAW)
+	{
+		tif->tif_rawcp = NULL;
+		tif->tif_rawcc = 0;
+	}
+	else
+	{
+		tif->tif_rawcp = tif->tif_rawdata;
+		tif->tif_rawcc = td->td_stripbytecount[tile];
+	}
+	return ((*tif->tif_predecode)(tif,
+			(tsample_t)(tile/td->td_stripsperimage)));
+}
+
+static int
+TIFFCheckRead(TIFF* tif, int tiles)
+{
+	if (tif->tif_mode == O_WRONLY) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "File not open for reading");
+		return (0);
+	}
+	if (tiles ^ isTiled(tif)) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, tiles ?
+		    "Can not read tiles from a stripped image" :
+		    "Can not read scanlines from a tiled image");
+		return (0);
+	}
+	return (1);
+}
+
+void
+_TIFFNoPostDecode(TIFF* tif, tidata_t buf, tsize_t cc)
+{
+    (void) tif; (void) buf; (void) cc;
+}
+
+void
+_TIFFSwab16BitData(TIFF* tif, tidata_t buf, tsize_t cc)
+{
+    (void) tif;
+    assert((cc & 1) == 0);
+    TIFFSwabArrayOfShort((uint16*) buf, cc/2);
+}
+
+void
+_TIFFSwab24BitData(TIFF* tif, tidata_t buf, tsize_t cc)
+{
+    (void) tif;
+    assert((cc % 3) == 0);
+    TIFFSwabArrayOfTriples((uint8*) buf, cc/3);
+}
+
+void
+_TIFFSwab32BitData(TIFF* tif, tidata_t buf, tsize_t cc)
+{
+    (void) tif;
+    assert((cc & 3) == 0);
+    TIFFSwabArrayOfLong((uint32*) buf, cc/4);
+}
+
+void
+_TIFFSwab64BitData(TIFF* tif, tidata_t buf, tsize_t cc)
+{
+    (void) tif;
+    assert((cc & 7) == 0);
+    TIFFSwabArrayOfDouble((double*) buf, cc/8);
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_strip.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_strip.c
new file mode 100644
index 0000000000..75fe0b69e5
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_strip.c
@@ -0,0 +1,363 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1991-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ *
+ * Strip-organized Image Support Routines.
+ */
+#include "tiffiop.h"
+
+static uint32
+summarize(TIFF* tif, size_t summand1, size_t summand2, const char* where)
+{
+	/*
+	 * XXX: We are using casting to uint32 here, bacause sizeof(size_t)
+	 * may be larger than sizeof(uint32) on 64-bit architectures.
+	 */
+	uint32	bytes = summand1 + summand2;
+
+	if (bytes - summand1 != summand2) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Integer overflow in %s", where);
+		bytes = 0;
+	}
+
+	return (bytes);
+}
+
+static uint32
+multiply(TIFF* tif, size_t nmemb, size_t elem_size, const char* where)
+{
+	uint32	bytes = nmemb * elem_size;
+
+	if (elem_size && bytes / elem_size != nmemb) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Integer overflow in %s", where);
+		bytes = 0;
+	}
+
+	return (bytes);
+}
+
+/*
+ * Compute which strip a (row,sample) value is in.
+ */
+tstrip_t
+TIFFComputeStrip(TIFF* tif, uint32 row, tsample_t sample)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	tstrip_t strip;
+
+	strip = row / td->td_rowsperstrip;
+	if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
+		if (sample >= td->td_samplesperpixel) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			    "%lu: Sample out of range, max %lu",
+			    (unsigned long) sample, (unsigned long) td->td_samplesperpixel);
+			return ((tstrip_t) 0);
+		}
+		strip += sample*td->td_stripsperimage;
+	}
+	return (strip);
+}
+
+/*
+ * Compute how many strips are in an image.
+ */
+tstrip_t
+TIFFNumberOfStrips(TIFF* tif)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	tstrip_t nstrips;
+
+	nstrips = (td->td_rowsperstrip == (uint32) -1 ? 1 :
+	     TIFFhowmany(td->td_imagelength, td->td_rowsperstrip));
+	if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+		nstrips = multiply(tif, nstrips, td->td_samplesperpixel,
+				   "TIFFNumberOfStrips");
+	return (nstrips);
+}
+
+/*
+ * Compute the # bytes in a variable height, row-aligned strip.
+ */
+tsize_t
+TIFFVStripSize(TIFF* tif, uint32 nrows)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+
+	if (nrows == (uint32) -1)
+		nrows = td->td_imagelength;
+	if (td->td_planarconfig == PLANARCONFIG_CONTIG &&
+	    td->td_photometric == PHOTOMETRIC_YCBCR &&
+	    !isUpSampled(tif)) {
+		/*
+		 * Packed YCbCr data contain one Cb+Cr for every
+		 * HorizontalSampling*VerticalSampling Y values.
+		 * Must also roundup width and height when calculating
+		 * since images that are not a multiple of the
+		 * horizontal/vertical subsampling area include
+		 * YCbCr data for the extended image.
+		 */
+		uint16 ycbcrsubsampling[2];
+		tsize_t w, scanline, samplingarea;
+
+		TIFFGetField( tif, TIFFTAG_YCBCRSUBSAMPLING,
+			      ycbcrsubsampling + 0,
+			      ycbcrsubsampling + 1 );
+
+		samplingarea = ycbcrsubsampling[0]*ycbcrsubsampling[1];
+		if (samplingarea == 0) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+				     "Invalid YCbCr subsampling");
+			return 0;
+		}
+
+		w = TIFFroundup(td->td_imagewidth, ycbcrsubsampling[0]);
+		scanline = TIFFhowmany8(multiply(tif, w, td->td_bitspersample,
+						 "TIFFVStripSize"));
+		nrows = TIFFroundup(nrows, ycbcrsubsampling[1]);
+		/* NB: don't need TIFFhowmany here 'cuz everything is rounded */
+		scanline = multiply(tif, nrows, scanline, "TIFFVStripSize");
+		return ((tsize_t)
+		    summarize(tif, scanline,
+			      multiply(tif, 2, scanline / samplingarea,
+				       "TIFFVStripSize"), "TIFFVStripSize"));
+	} else
+		return ((tsize_t) multiply(tif, nrows, TIFFScanlineSize(tif),
+					   "TIFFVStripSize"));
+}
+
+
+/*
+ * Compute the # bytes in a raw strip.
+ */
+tsize_t
+TIFFRawStripSize(TIFF* tif, tstrip_t strip)
+{
+	TIFFDirectory* td = &tif->tif_dir;
+	tsize_t bytecount = td->td_stripbytecount[strip];
+
+	if (bytecount <= 0) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			  "%lu: Invalid strip byte count, strip %lu",
+			  (unsigned long) bytecount, (unsigned long) strip);
+		bytecount = (tsize_t) -1;
+	}
+
+	return bytecount;
+}
+
+/*
+ * Compute the # bytes in a (row-aligned) strip.
+ *
+ * Note that if RowsPerStrip is larger than the
+ * recorded ImageLength, then the strip size is
+ * truncated to reflect the actual space required
+ * to hold the strip.
+ */
+tsize_t
+TIFFStripSize(TIFF* tif)
+{
+	TIFFDirectory* td = &tif->tif_dir;
+	uint32 rps = td->td_rowsperstrip;
+	if (rps > td->td_imagelength)
+		rps = td->td_imagelength;
+	return (TIFFVStripSize(tif, rps));
+}
+
+/*
+ * Compute a default strip size based on the image
+ * characteristics and a requested value.  If the
+ * request is <1 then we choose a strip size according
+ * to certain heuristics.
+ */
+uint32
+TIFFDefaultStripSize(TIFF* tif, uint32 request)
+{
+	return (*tif->tif_defstripsize)(tif, request);
+}
+
+uint32
+_TIFFDefaultStripSize(TIFF* tif, uint32 s)
+{
+	if ((int32) s < 1) {
+		/*
+		 * If RowsPerStrip is unspecified, try to break the
+		 * image up into strips that are approximately
+		 * STRIP_SIZE_DEFAULT bytes long.
+		 */
+		tsize_t scanline = TIFFScanlineSize(tif);
+		s = (uint32)STRIP_SIZE_DEFAULT / (scanline == 0 ? 1 : scanline);
+		if (s == 0)		/* very wide images */
+			s = 1;
+	}
+	return (s);
+}
+
+/*
+ * Return the number of bytes to read/write in a call to
+ * one of the scanline-oriented i/o routines.  Note that
+ * this number may be 1/samples-per-pixel if data is
+ * stored as separate planes.
+ */
+tsize_t
+TIFFScanlineSize(TIFF* tif)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	tsize_t scanline;
+
+	if (td->td_planarconfig == PLANARCONFIG_CONTIG) {
+		if (td->td_photometric == PHOTOMETRIC_YCBCR
+		    && !isUpSampled(tif)) {
+			uint16 ycbcrsubsampling[2];
+
+			TIFFGetField(tif, TIFFTAG_YCBCRSUBSAMPLING,
+				     ycbcrsubsampling + 0,
+				     ycbcrsubsampling + 1);
+
+			if (ycbcrsubsampling[0] == 0) {
+				TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+					     "Invalid YCbCr subsampling");
+				return 0;
+			}
+
+			scanline = TIFFroundup(td->td_imagewidth,
+					       ycbcrsubsampling[0]);
+			scanline = TIFFhowmany8(multiply(tif, scanline,
+							 td->td_bitspersample,
+							 "TIFFScanlineSize"));
+			return ((tsize_t)
+				summarize(tif, scanline,
+					  multiply(tif, 2,
+						scanline / ycbcrsubsampling[0],
+						"TIFFVStripSize"),
+					  "TIFFVStripSize"));
+		} else {
+			scanline = multiply(tif, td->td_imagewidth,
+					    td->td_samplesperpixel,
+					    "TIFFScanlineSize");
+		}
+	} else
+		scanline = td->td_imagewidth;
+	return ((tsize_t) TIFFhowmany8(multiply(tif, scanline,
+						td->td_bitspersample,
+						"TIFFScanlineSize")));
+}
+
+/*
+ * Some stuff depends on this older version of TIFFScanlineSize
+ * TODO: resolve this
+ */
+tsize_t
+TIFFOldScanlineSize(TIFF* tif)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	tsize_t scanline;
+
+	scanline = multiply (tif, td->td_bitspersample, td->td_imagewidth,
+			     "TIFFScanlineSize");
+	if (td->td_planarconfig == PLANARCONFIG_CONTIG)
+		scanline = multiply (tif, scanline, td->td_samplesperpixel,
+				     "TIFFScanlineSize");
+	return ((tsize_t) TIFFhowmany8(scanline));
+}
+
+/*
+ * Return the number of bytes to read/write in a call to
+ * one of the scanline-oriented i/o routines.  Note that
+ * this number may be 1/samples-per-pixel if data is
+ * stored as separate planes.
+ * The ScanlineSize in case of YCbCrSubsampling is defined as the
+ * strip size divided by the strip height, i.e. the size of a pack of vertical
+ * subsampling lines divided by vertical subsampling. It should thus make
+ * sense when multiplied by a multiple of vertical subsampling.
+ * Some stuff depends on this newer version of TIFFScanlineSize
+ * TODO: resolve this
+ */
+tsize_t
+TIFFNewScanlineSize(TIFF* tif)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	tsize_t scanline;
+
+	if (td->td_planarconfig == PLANARCONFIG_CONTIG) {
+		if (td->td_photometric == PHOTOMETRIC_YCBCR
+		    && !isUpSampled(tif)) {
+			uint16 ycbcrsubsampling[2];
+
+			TIFFGetField(tif, TIFFTAG_YCBCRSUBSAMPLING,
+				     ycbcrsubsampling + 0,
+				     ycbcrsubsampling + 1);
+
+			if (ycbcrsubsampling[0]*ycbcrsubsampling[1] == 0) {
+				TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+					     "Invalid YCbCr subsampling");
+				return 0;
+			}
+
+			return((tsize_t) ((((td->td_imagewidth+ycbcrsubsampling[0]-1)
+					    /ycbcrsubsampling[0])
+					   *(ycbcrsubsampling[0]*ycbcrsubsampling[1]+2)
+					   *td->td_bitspersample+7)
+					  /8)/ycbcrsubsampling[1]);
+
+		} else {
+			scanline = multiply(tif, td->td_imagewidth,
+					    td->td_samplesperpixel,
+					    "TIFFScanlineSize");
+		}
+	} else
+		scanline = td->td_imagewidth;
+	return ((tsize_t) TIFFhowmany8(multiply(tif, scanline,
+						td->td_bitspersample,
+						"TIFFScanlineSize")));
+}
+
+/*
+ * Return the number of bytes required to store a complete
+ * decoded and packed raster scanline (as opposed to the
+ * I/O size returned by TIFFScanlineSize which may be less
+ * if data is store as separate planes).
+ */
+tsize_t
+TIFFRasterScanlineSize(TIFF* tif)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	tsize_t scanline;
+	
+	scanline = multiply (tif, td->td_bitspersample, td->td_imagewidth,
+			     "TIFFRasterScanlineSize");
+	if (td->td_planarconfig == PLANARCONFIG_CONTIG) {
+		scanline = multiply (tif, scanline, td->td_samplesperpixel,
+				     "TIFFRasterScanlineSize");
+		return ((tsize_t) TIFFhowmany8(scanline));
+	} else
+		return ((tsize_t) multiply (tif, TIFFhowmany8(scanline),
+					    td->td_samplesperpixel,
+					    "TIFFRasterScanlineSize"));
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_swab.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_swab.c
new file mode 100644
index 0000000000..aad43df97c
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_swab.c
@@ -0,0 +1,235 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library Bit & Byte Swapping Support.
+ *
+ * XXX We assume short = 16-bits and long = 32-bits XXX
+ */
+#include "tiffiop.h"
+
+#ifndef TIFFSwabShort
+void
+TIFFSwabShort(uint16* wp)
+{
+	register unsigned char* cp = (unsigned char*) wp;
+	unsigned char t;
+
+	t = cp[1]; cp[1] = cp[0]; cp[0] = t;
+}
+#endif
+
+#ifndef TIFFSwabLong
+void
+TIFFSwabLong(uint32* lp)
+{
+	register unsigned char* cp = (unsigned char*) lp;
+	unsigned char t;
+
+	t = cp[3]; cp[3] = cp[0]; cp[0] = t;
+	t = cp[2]; cp[2] = cp[1]; cp[1] = t;
+}
+#endif
+
+#ifndef TIFFSwabArrayOfShort
+void
+TIFFSwabArrayOfShort(uint16* wp, register unsigned long n)
+{
+	register unsigned char* cp;
+	register unsigned char t;
+
+	/* XXX unroll loop some */
+	while (n-- > 0) {
+		cp = (unsigned char*) wp;
+		t = cp[1]; cp[1] = cp[0]; cp[0] = t;
+		wp++;
+	}
+}
+#endif
+
+#ifndef TIFFSwabArrayOfTriples
+void
+TIFFSwabArrayOfTriples(uint8* tp, unsigned long n)
+{
+	unsigned char* cp;
+	unsigned char t;
+
+	/* XXX unroll loop some */
+	while (n-- > 0) {
+		cp = (unsigned char*) tp;
+		t = cp[2]; cp[2] = cp[0]; cp[0] = t;
+		tp += 3;
+	}
+}
+#endif
+
+#ifndef TIFFSwabArrayOfLong
+void
+TIFFSwabArrayOfLong(register uint32* lp, register unsigned long n)
+{
+	register unsigned char *cp;
+	register unsigned char t;
+
+	/* XXX unroll loop some */
+	while (n-- > 0) {
+		cp = (unsigned char *)lp;
+		t = cp[3]; cp[3] = cp[0]; cp[0] = t;
+		t = cp[2]; cp[2] = cp[1]; cp[1] = t;
+		lp++;
+	}
+}
+#endif
+
+#ifndef TIFFSwabDouble
+void
+TIFFSwabDouble(double *dp)
+{
+        register uint32* lp = (uint32*) dp;
+        uint32 t;
+
+	TIFFSwabArrayOfLong(lp, 2);
+	t = lp[0]; lp[0] = lp[1]; lp[1] = t;
+}
+#endif
+
+#ifndef TIFFSwabArrayOfDouble
+void
+TIFFSwabArrayOfDouble(double* dp, register unsigned long n)
+{
+	register uint32* lp = (uint32*) dp;
+        register uint32 t;
+
+	TIFFSwabArrayOfLong(lp, n + n);
+        while (n-- > 0) {
+		t = lp[0]; lp[0] = lp[1]; lp[1] = t;
+                lp += 2;
+        }
+}
+#endif
+
+/*
+ * Bit reversal tables.  TIFFBitRevTable[<byte>] gives
+ * the bit reversed value of <byte>.  Used in various
+ * places in the library when the FillOrder requires
+ * bit reversal of byte values (e.g. CCITT Fax 3
+ * encoding/decoding).  TIFFNoBitRevTable is provided
+ * for algorithms that want an equivalent table that
+ * do not reverse bit values.
+ */
+static const unsigned char TIFFBitRevTable[256] = {
+    0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+    0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+    0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+    0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+    0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+    0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+    0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+    0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+    0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+    0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+    0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+    0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+    0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+    0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+    0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+    0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+    0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+    0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+    0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+    0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+    0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+    0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+    0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+    0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+    0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+    0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+    0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+    0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+    0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+    0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+    0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+    0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+};
+static const unsigned char TIFFNoBitRevTable[256] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 
+    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 
+    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 
+    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 
+    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 
+};
+
+const unsigned char*
+TIFFGetBitRevTable(int reversed)
+{
+	return (reversed ? TIFFBitRevTable : TIFFNoBitRevTable);
+}
+
+void
+TIFFReverseBits(register unsigned char* cp, register unsigned long n)
+{
+	for (; n > 8; n -= 8) {
+		cp[0] = TIFFBitRevTable[cp[0]];
+		cp[1] = TIFFBitRevTable[cp[1]];
+		cp[2] = TIFFBitRevTable[cp[2]];
+		cp[3] = TIFFBitRevTable[cp[3]];
+		cp[4] = TIFFBitRevTable[cp[4]];
+		cp[5] = TIFFBitRevTable[cp[5]];
+		cp[6] = TIFFBitRevTable[cp[6]];
+		cp[7] = TIFFBitRevTable[cp[7]];
+		cp += 8;
+	}
+	while (n-- > 0)
+		*cp = TIFFBitRevTable[*cp], cp++;
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_thunder.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_thunder.c
new file mode 100644
index 0000000000..bbd8fe2052
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_thunder.c
@@ -0,0 +1,158 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#include "tiffiop.h"
+#ifdef THUNDER_SUPPORT
+/*
+ * TIFF Library.
+ *
+ * ThunderScan 4-bit Compression Algorithm Support
+ */
+
+/*
+ * ThunderScan uses an encoding scheme designed for
+ * 4-bit pixel values.  Data is encoded in bytes, with
+ * each byte split into a 2-bit code word and a 6-bit
+ * data value.  The encoding gives raw data, runs of
+ * pixels, or pixel values encoded as a delta from the
+ * previous pixel value.  For the latter, either 2-bit
+ * or 3-bit delta values are used, with the deltas packed
+ * into a single byte.
+ */
+#define	THUNDER_DATA		0x3f	/* mask for 6-bit data */
+#define	THUNDER_CODE		0xc0	/* mask for 2-bit code word */
+/* code values */
+#define	THUNDER_RUN		0x00	/* run of pixels w/ encoded count */
+#define	THUNDER_2BITDELTAS	0x40	/* 3 pixels w/ encoded 2-bit deltas */
+#define	    DELTA2_SKIP		2	/* skip code for 2-bit deltas */
+#define	THUNDER_3BITDELTAS	0x80	/* 2 pixels w/ encoded 3-bit deltas */
+#define	    DELTA3_SKIP		4	/* skip code for 3-bit deltas */
+#define	THUNDER_RAW		0xc0	/* raw data encoded */
+
+static const int twobitdeltas[4] = { 0, 1, 0, -1 };
+static const int threebitdeltas[8] = { 0, 1, 2, 3, 0, -3, -2, -1 };
+
+#define	SETPIXEL(op, v) { \
+	lastpixel = (v) & 0xf; \
+	if (npixels++ & 1) \
+	    *op++ |= lastpixel; \
+	else \
+	    op[0] = (tidataval_t) (lastpixel << 4); \
+}
+
+static int
+ThunderDecode(TIFF* tif, tidata_t op, tsize_t maxpixels)
+{
+	register unsigned char *bp;
+	register tsize_t cc;
+	unsigned int lastpixel;
+	tsize_t npixels;
+
+	bp = (unsigned char *)tif->tif_rawcp;
+	cc = tif->tif_rawcc;
+	lastpixel = 0;
+	npixels = 0;
+	while (cc > 0 && npixels < maxpixels) {
+		int n, delta;
+
+		n = *bp++, cc--;
+		switch (n & THUNDER_CODE) {
+		case THUNDER_RUN:		/* pixel run */
+			/*
+			 * Replicate the last pixel n times,
+			 * where n is the lower-order 6 bits.
+			 */
+			if (npixels & 1) {
+				op[0] |= lastpixel;
+				lastpixel = *op++; npixels++; n--;
+			} else
+				lastpixel |= lastpixel << 4;
+			npixels += n;
+			if (npixels < maxpixels) {
+				for (; n > 0; n -= 2)
+					*op++ = (tidataval_t) lastpixel;
+			}
+			if (n == -1)
+				*--op &= 0xf0;
+			lastpixel &= 0xf;
+			break;
+		case THUNDER_2BITDELTAS:	/* 2-bit deltas */
+			if ((delta = ((n >> 4) & 3)) != DELTA2_SKIP)
+				SETPIXEL(op, lastpixel + twobitdeltas[delta]);
+			if ((delta = ((n >> 2) & 3)) != DELTA2_SKIP)
+				SETPIXEL(op, lastpixel + twobitdeltas[delta]);
+			if ((delta = (n & 3)) != DELTA2_SKIP)
+				SETPIXEL(op, lastpixel + twobitdeltas[delta]);
+			break;
+		case THUNDER_3BITDELTAS:	/* 3-bit deltas */
+			if ((delta = ((n >> 3) & 7)) != DELTA3_SKIP)
+				SETPIXEL(op, lastpixel + threebitdeltas[delta]);
+			if ((delta = (n & 7)) != DELTA3_SKIP)
+				SETPIXEL(op, lastpixel + threebitdeltas[delta]);
+			break;
+		case THUNDER_RAW:		/* raw data */
+			SETPIXEL(op, n);
+			break;
+		}
+	}
+	tif->tif_rawcp = (tidata_t) bp;
+	tif->tif_rawcc = cc;
+	if (npixels != maxpixels) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		    "ThunderDecode: %s data at scanline %ld (%lu != %lu)",
+		    npixels < maxpixels ? "Not enough" : "Too much",
+		    (long) tif->tif_row, (long) npixels, (long) maxpixels);
+		return (0);
+	}
+	return (1);
+}
+
+static int
+ThunderDecodeRow(TIFF* tif, tidata_t buf, tsize_t occ, tsample_t s)
+{
+	tidata_t row = buf;
+	
+	(void) s;
+	while ((long)occ > 0) {
+		if (!ThunderDecode(tif, row, tif->tif_dir.td_imagewidth))
+			return (0);
+		occ -= tif->tif_scanlinesize;
+		row += tif->tif_scanlinesize;
+	}
+	return (1);
+}
+
+int
+TIFFInitThunderScan(TIFF* tif, int scheme)
+{
+	(void) scheme;
+	tif->tif_decoderow = ThunderDecodeRow;
+	tif->tif_decodestrip = ThunderDecodeRow;
+	return (1);
+}
+#endif /* THUNDER_SUPPORT */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_tile.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_tile.c
new file mode 100644
index 0000000000..22ad35cd8d
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_tile.c
@@ -0,0 +1,273 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1991-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ *
+ * Tiled Image Support Routines.
+ */
+#include "tiffiop.h"
+
+static uint32
+summarize(TIFF* tif, size_t summand1, size_t summand2, const char* where)
+{
+	/*
+	 * XXX: We are using casting to uint32 here, because sizeof(size_t)
+	 * may be larger than sizeof(uint32) on 64-bit architectures.
+	 */
+	uint32	bytes = summand1 + summand2;
+
+	if (bytes - summand1 != summand2) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Integer overflow in %s", where);
+		bytes = 0;
+	}
+
+	return (bytes);
+}
+
+static uint32
+multiply(TIFF* tif, size_t nmemb, size_t elem_size, const char* where)
+{
+	uint32	bytes = nmemb * elem_size;
+
+	if (elem_size && bytes / elem_size != nmemb) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Integer overflow in %s", where);
+		bytes = 0;
+	}
+
+	return (bytes);
+}
+
+/*
+ * Compute which tile an (x,y,z,s) value is in.
+ */
+ttile_t
+TIFFComputeTile(TIFF* tif, uint32 x, uint32 y, uint32 z, tsample_t s)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	uint32 dx = td->td_tilewidth;
+	uint32 dy = td->td_tilelength;
+	uint32 dz = td->td_tiledepth;
+	ttile_t tile = 1;
+
+	if (td->td_imagedepth == 1)
+		z = 0;
+	if (dx == (uint32) -1)
+		dx = td->td_imagewidth;
+	if (dy == (uint32) -1)
+		dy = td->td_imagelength;
+	if (dz == (uint32) -1)
+		dz = td->td_imagedepth;
+	if (dx != 0 && dy != 0 && dz != 0) {
+		uint32 xpt = TIFFhowmany(td->td_imagewidth, dx); 
+		uint32 ypt = TIFFhowmany(td->td_imagelength, dy); 
+		uint32 zpt = TIFFhowmany(td->td_imagedepth, dz); 
+
+		if (td->td_planarconfig == PLANARCONFIG_SEPARATE) 
+			tile = (xpt*ypt*zpt)*s +
+			     (xpt*ypt)*(z/dz) +
+			     xpt*(y/dy) +
+			     x/dx;
+		else
+			tile = (xpt*ypt)*(z/dz) + xpt*(y/dy) + x/dx;
+	}
+	return (tile);
+}
+
+/*
+ * Check an (x,y,z,s) coordinate
+ * against the image bounds.
+ */
+int
+TIFFCheckTile(TIFF* tif, uint32 x, uint32 y, uint32 z, tsample_t s)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+
+	if (x >= td->td_imagewidth) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			     "%lu: Col out of range, max %lu",
+			     (unsigned long) x,
+			     (unsigned long) (td->td_imagewidth - 1));
+		return (0);
+	}
+	if (y >= td->td_imagelength) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			     "%lu: Row out of range, max %lu",
+			     (unsigned long) y,
+			     (unsigned long) (td->td_imagelength - 1));
+		return (0);
+	}
+	if (z >= td->td_imagedepth) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			     "%lu: Depth out of range, max %lu",
+			     (unsigned long) z,
+			     (unsigned long) (td->td_imagedepth - 1));
+		return (0);
+	}
+	if (td->td_planarconfig == PLANARCONFIG_SEPARATE &&
+	    s >= td->td_samplesperpixel) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			     "%lu: Sample out of range, max %lu",
+			     (unsigned long) s,
+			     (unsigned long) (td->td_samplesperpixel - 1));
+		return (0);
+	}
+	return (1);
+}
+
+/*
+ * Compute how many tiles are in an image.
+ */
+ttile_t
+TIFFNumberOfTiles(TIFF* tif)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	uint32 dx = td->td_tilewidth;
+	uint32 dy = td->td_tilelength;
+	uint32 dz = td->td_tiledepth;
+	ttile_t ntiles;
+
+	if (dx == (uint32) -1)
+		dx = td->td_imagewidth;
+	if (dy == (uint32) -1)
+		dy = td->td_imagelength;
+	if (dz == (uint32) -1)
+		dz = td->td_imagedepth;
+	ntiles = (dx == 0 || dy == 0 || dz == 0) ? 0 :
+	    multiply(tif, multiply(tif, TIFFhowmany(td->td_imagewidth, dx),
+				   TIFFhowmany(td->td_imagelength, dy),
+				   "TIFFNumberOfTiles"),
+		     TIFFhowmany(td->td_imagedepth, dz), "TIFFNumberOfTiles");
+	if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+		ntiles = multiply(tif, ntiles, td->td_samplesperpixel,
+				  "TIFFNumberOfTiles");
+	return (ntiles);
+}
+
+/*
+ * Compute the # bytes in each row of a tile.
+ */
+tsize_t
+TIFFTileRowSize(TIFF* tif)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	tsize_t rowsize;
+	
+	if (td->td_tilelength == 0 || td->td_tilewidth == 0)
+		return ((tsize_t) 0);
+	rowsize = multiply(tif, td->td_bitspersample, td->td_tilewidth,
+			   "TIFFTileRowSize");
+	if (td->td_planarconfig == PLANARCONFIG_CONTIG)
+		rowsize = multiply(tif, rowsize, td->td_samplesperpixel,
+				   "TIFFTileRowSize");
+	return ((tsize_t) TIFFhowmany8(rowsize));
+}
+
+/*
+ * Compute the # bytes in a variable length, row-aligned tile.
+ */
+tsize_t
+TIFFVTileSize(TIFF* tif, uint32 nrows)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	tsize_t tilesize;
+
+	if (td->td_tilelength == 0 || td->td_tilewidth == 0 ||
+	    td->td_tiledepth == 0)
+		return ((tsize_t) 0);
+	if (td->td_planarconfig == PLANARCONFIG_CONTIG &&
+	    td->td_photometric == PHOTOMETRIC_YCBCR &&
+	    !isUpSampled(tif)) {
+		/*
+		 * Packed YCbCr data contain one Cb+Cr for every
+		 * HorizontalSampling*VerticalSampling Y values.
+		 * Must also roundup width and height when calculating
+		 * since images that are not a multiple of the
+		 * horizontal/vertical subsampling area include
+		 * YCbCr data for the extended image.
+		 */
+		tsize_t w =
+		    TIFFroundup(td->td_tilewidth, td->td_ycbcrsubsampling[0]);
+		tsize_t rowsize =
+		    TIFFhowmany8(multiply(tif, w, td->td_bitspersample,
+					  "TIFFVTileSize"));
+		tsize_t samplingarea =
+		    td->td_ycbcrsubsampling[0]*td->td_ycbcrsubsampling[1];
+		if (samplingarea == 0) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Invalid YCbCr subsampling");
+			return 0;
+		}
+		nrows = TIFFroundup(nrows, td->td_ycbcrsubsampling[1]);
+		/* NB: don't need TIFFhowmany here 'cuz everything is rounded */
+		tilesize = multiply(tif, nrows, rowsize, "TIFFVTileSize");
+		tilesize = summarize(tif, tilesize,
+				     multiply(tif, 2, tilesize / samplingarea,
+					      "TIFFVTileSize"),
+				     "TIFFVTileSize");
+	} else
+		tilesize = multiply(tif, nrows, TIFFTileRowSize(tif),
+				    "TIFFVTileSize");
+	return ((tsize_t)
+	    multiply(tif, tilesize, td->td_tiledepth, "TIFFVTileSize"));
+}
+
+/*
+ * Compute the # bytes in a row-aligned tile.
+ */
+tsize_t
+TIFFTileSize(TIFF* tif)
+{
+	return (TIFFVTileSize(tif, tif->tif_dir.td_tilelength));
+}
+
+/*
+ * Compute a default tile size based on the image
+ * characteristics and a requested value.  If a
+ * request is <1 then we choose a size according
+ * to certain heuristics.
+ */
+void
+TIFFDefaultTileSize(TIFF* tif, uint32* tw, uint32* th)
+{
+	(*tif->tif_deftilesize)(tif, tw, th);
+}
+
+void
+_TIFFDefaultTileSize(TIFF* tif, uint32* tw, uint32* th)
+{
+	(void) tif;
+	if (*(int32*) tw < 1)
+		*tw = 256;
+	if (*(int32*) th < 1)
+		*th = 256;
+	/* roundup to a multiple of 16 per the spec */
+	if (*tw & 0xf)
+		*tw = TIFFroundup(*tw, 16);
+	if (*th & 0xf)
+		*th = TIFFroundup(*th, 16);
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_version.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_version.c
new file mode 100644
index 0000000000..1264b71591
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_version.c
@@ -0,0 +1,33 @@
+/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_version.c,v 1.2 2000/11/13 14:42:38 warmerda Exp $ */
+/*
+ * Copyright (c) 1992-1997 Sam Leffler
+ * Copyright (c) 1992-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+#include "tiffiop.h"
+
+static const char TIFFVersion[] = TIFFLIB_VERSION_STR;
+
+const char*
+TIFFGetVersion(void)
+{
+	return (TIFFVersion);
+}
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_vsi.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_vsi.c
new file mode 100644
index 0000000000..02c075ed0f
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_vsi.c
@@ -0,0 +1,235 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  GeoTIFF Driver
+ * Purpose:  Implement system hook functions for libtiff on top of CPL/VSI,
+ *           including > 2GB support.  Based on tif_unix.c from libtiff
+ *           distribution.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam, warmerdam@pobox.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: tif_vsi.c,v $
+ * Revision 1.8  2005/10/13 18:38:36  fwarmerdam
+ * Try to report system level errors on open in TIFFOpen.
+ *
+ * Revision 1.7  2005/03/18 20:30:57  fwarmerdam
+ * ensure we close the underlying file if the open fails.
+ *
+ * Revision 1.6  2003/07/17 22:45:58  warmerda
+ * avoid casting warning
+ *
+ * Revision 1.5  2003/07/08 19:49:30  warmerda
+ * avoid warning
+ *
+ * Revision 1.4  2001/09/24 15:54:41  warmerda
+ * implement SizeProc properly
+ *
+ * Revision 1.3  2001/07/20 03:29:45  warmerda
+ * updated
+ *
+ * Revision 1.2  2001/02/15 15:33:18  warmerda
+ * don't access through tif pointer if open fails
+ *
+ * Revision 1.1  2001/01/03 17:03:41  warmerda
+ * *** empty log message ***
+ *
+ */
+
+/*
+ * TIFF Library UNIX-specific Routines.
+ */
+#include "tiffiop.h"
+#include "cpl_vsi.h"
+
+static tsize_t
+_tiffReadProc(thandle_t fd, tdata_t buf, tsize_t size)
+{
+    return VSIFReadL( buf, 1, size, (FILE *) fd );
+}
+
+static tsize_t
+_tiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size)
+{
+    return VSIFWriteL( buf, 1, size, (FILE *) fd );
+}
+
+static toff_t
+_tiffSeekProc(thandle_t fd, toff_t off, int whence)
+{
+    if( VSIFSeekL( (FILE *) fd, off, whence ) == 0 )
+        return (toff_t) VSIFTellL( (FILE *) fd );
+    else
+        return (toff_t) -1;
+}
+
+static int
+_tiffCloseProc(thandle_t fd)
+{
+    return VSIFCloseL( (FILE *) fd );
+}
+
+static toff_t
+_tiffSizeProc(thandle_t fd)
+{
+    vsi_l_offset  old_off;
+    toff_t        file_size;
+
+    old_off = VSIFTellL( (FILE *) fd );
+    VSIFSeekL( (FILE *) fd, 0, SEEK_END );
+    
+    file_size = (toff_t) VSIFTellL( (FILE *) fd );
+    VSIFSeekL( (FILE *) fd, old_off, SEEK_SET );
+
+    return file_size;
+}
+
+static int
+_tiffMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize)
+{
+	(void) fd; (void) pbase; (void) psize;
+	return (0);
+}
+
+static void
+_tiffUnmapProc(thandle_t fd, tdata_t base, toff_t size)
+{
+	(void) fd; (void) base; (void) size;
+}
+
+/*
+ * Open a TIFF file descriptor for read/writing.
+ */
+TIFF*
+TIFFFdOpen(int fd, const char* name, const char* mode)
+{
+	return NULL;
+}
+
+/*
+ * Open a TIFF file for read/writing.
+ */
+TIFF*
+TIFFOpen(const char* name, const char* mode)
+{
+	static const char module[] = "TIFFOpen";
+	int           i, a_out;
+        char          access[32];
+        FILE          *fp;
+        TIFF          *tif;
+
+        a_out = 0;
+        access[0] = '\0';
+        for( i = 0; mode[i] != '\0'; i++ )
+        {
+            if( mode[i] == 'r'
+                || mode[i] == 'w'
+                || mode[i] == '+'
+                || mode[i] == 'a' )
+            {
+                access[a_out++] = mode[i];
+                access[a_out] = '\0';
+            }
+        }
+
+        strcat( access, "b" );
+                    
+        fp = VSIFOpenL( name, access );
+	if (fp == NULL) {
+            if( errno >= 0 )
+                TIFFError(module,"%s: %s", name, VSIStrerror( errno ) );
+            else
+		TIFFError(module, "%s: Cannot open", name);
+            return ((TIFF *)0);
+	}
+
+	tif = TIFFClientOpen(name, mode,
+	    (thandle_t) fp,
+	    _tiffReadProc, _tiffWriteProc,
+	    _tiffSeekProc, _tiffCloseProc, _tiffSizeProc,
+	    _tiffMapProc, _tiffUnmapProc);
+
+        if( tif != NULL )
+            tif->tif_fd = 0;
+        else
+            VSIFCloseL( fp );
+        
+	return tif;
+}
+
+void*
+_TIFFmalloc(tsize_t s)
+{
+    return VSIMalloc((size_t) s);
+}
+
+void
+_TIFFfree(tdata_t p)
+{
+    VSIFree( p );
+}
+
+void*
+_TIFFrealloc(tdata_t p, tsize_t s)
+{
+    return VSIRealloc( p, s );
+}
+
+void
+_TIFFmemset(tdata_t p, int v, tsize_t c)
+{
+	memset(p, v, (size_t) c);
+}
+
+void
+_TIFFmemcpy(tdata_t d, const tdata_t s, tsize_t c)
+{
+	memcpy(d, s, (size_t) c);
+}
+
+int
+_TIFFmemcmp(const tdata_t p1, const tdata_t p2, tsize_t c)
+{
+	return (memcmp(p1, p2, (size_t) c));
+}
+
+static void
+unixWarningHandler(const char* module, const char* fmt, va_list ap)
+{
+	if (module != NULL)
+		fprintf(stderr, "%s: ", module);
+	fprintf(stderr, "Warning, ");
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, ".\n");
+}
+TIFFErrorHandler _TIFFwarningHandler = unixWarningHandler;
+
+static void
+unixErrorHandler(const char* module, const char* fmt, va_list ap)
+{
+	if (module != NULL)
+		fprintf(stderr, "%s: ", module);
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, ".\n");
+}
+TIFFErrorHandler _TIFFerrorHandler = unixErrorHandler;
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_warning.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_warning.c
new file mode 100644
index 0000000000..d593469d2d
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_warning.c
@@ -0,0 +1,74 @@
+/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_warning.c,v 1.2 2005/12/23 01:18:59 joris Exp $ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ */
+#include "tiffiop.h"
+
+TIFFErrorHandlerExt _TIFFwarningHandlerExt = NULL;
+
+TIFFErrorHandler
+TIFFSetWarningHandler(TIFFErrorHandler handler)
+{
+	TIFFErrorHandler prev = _TIFFwarningHandler;
+	_TIFFwarningHandler = handler;
+	return (prev);
+}
+
+TIFFErrorHandlerExt
+TIFFSetWarningHandlerExt(TIFFErrorHandlerExt handler)
+{
+	TIFFErrorHandlerExt prev = _TIFFwarningHandlerExt;
+	_TIFFwarningHandlerExt = handler;
+	return (prev);
+}
+
+void
+TIFFWarning(const char* module, const char* fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	if (_TIFFwarningHandler)
+		(*_TIFFwarningHandler)(module, fmt, ap);
+	if (_TIFFwarningHandlerExt)
+		(*_TIFFwarningHandlerExt)(0, module, fmt, ap);
+	va_end(ap);
+}
+
+void
+TIFFWarningExt(thandle_t fd, const char* module, const char* fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	if (_TIFFwarningHandler)
+		(*_TIFFwarningHandler)(module, fmt, ap);
+	if (_TIFFwarningHandlerExt)
+		(*_TIFFwarningHandlerExt)(fd, module, fmt, ap);
+	va_end(ap);
+}
+
+
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_write.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_write.c
new file mode 100644
index 0000000000..aef022a951
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_write.c
@@ -0,0 +1,725 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ *
+ * Scanline-oriented Write Support
+ */
+#include "tiffiop.h"
+#include <stdio.h>
+
+#define	STRIPINCR	20		/* expansion factor on strip array */
+
+#define	WRITECHECKSTRIPS(tif, module)				\
+	(((tif)->tif_flags&TIFF_BEENWRITING) || TIFFWriteCheck((tif),0,module))
+#define	WRITECHECKTILES(tif, module)				\
+	(((tif)->tif_flags&TIFF_BEENWRITING) || TIFFWriteCheck((tif),1,module))
+#define	BUFFERCHECK(tif)					\
+	((((tif)->tif_flags & TIFF_BUFFERSETUP) && tif->tif_rawdata) ||	\
+	    TIFFWriteBufferSetup((tif), NULL, (tsize_t) -1))
+
+static	int TIFFGrowStrips(TIFF*, int, const char*);
+static	int TIFFAppendToStrip(TIFF*, tstrip_t, tidata_t, tsize_t);
+
+int
+TIFFWriteScanline(TIFF* tif, tdata_t buf, uint32 row, tsample_t sample)
+{
+	static const char module[] = "TIFFWriteScanline";
+	register TIFFDirectory *td;
+	int status, imagegrew = 0;
+	tstrip_t strip;
+
+	if (!WRITECHECKSTRIPS(tif, module))
+		return (-1);
+	/*
+	 * Handle delayed allocation of data buffer.  This
+	 * permits it to be sized more intelligently (using
+	 * directory information).
+	 */
+	if (!BUFFERCHECK(tif))
+		return (-1);
+	td = &tif->tif_dir;
+	/*
+	 * Extend image length if needed
+	 * (but only for PlanarConfig=1).
+	 */
+	if (row >= td->td_imagelength) {	/* extend image */
+		if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		"Can not change \"ImageLength\" when using separate planes");
+			return (-1);
+		}
+		td->td_imagelength = row+1;
+		imagegrew = 1;
+	}
+	/*
+	 * Calculate strip and check for crossings.
+	 */
+	if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
+		if (sample >= td->td_samplesperpixel) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+			    "%d: Sample out of range, max %d",
+			    sample, td->td_samplesperpixel);
+			return (-1);
+		}
+		strip = sample*td->td_stripsperimage + row/td->td_rowsperstrip;
+	} else
+		strip = row / td->td_rowsperstrip;
+	/*
+	 * Check strip array to make sure there's space. We don't support
+	 * dynamically growing files that have data organized in separate
+	 * bitplanes because it's too painful.  In that case we require that
+	 * the imagelength be set properly before the first write (so that the
+	 * strips array will be fully allocated above).
+	 */
+	if (strip >= td->td_nstrips && !TIFFGrowStrips(tif, 1, module))
+		return (-1);
+	if (strip != tif->tif_curstrip) {
+		/*
+		 * Changing strips -- flush any data present.
+		 */
+		if (!TIFFFlushData(tif))
+			return (-1);
+		tif->tif_curstrip = strip;
+		/*
+		 * Watch out for a growing image.  The value of strips/image
+		 * will initially be 1 (since it can't be deduced until the
+		 * imagelength is known).
+		 */
+		if (strip >= td->td_stripsperimage && imagegrew)
+			td->td_stripsperimage =
+			    TIFFhowmany(td->td_imagelength,td->td_rowsperstrip);
+		tif->tif_row =
+		    (strip % td->td_stripsperimage) * td->td_rowsperstrip;
+		if ((tif->tif_flags & TIFF_CODERSETUP) == 0) {
+			if (!(*tif->tif_setupencode)(tif))
+				return (-1);
+			tif->tif_flags |= TIFF_CODERSETUP;
+		}
+        
+		tif->tif_rawcc = 0;
+		tif->tif_rawcp = tif->tif_rawdata;
+
+		if( td->td_stripbytecount[strip] > 0 )
+		{
+			/* if we are writing over existing tiles, zero length */
+			td->td_stripbytecount[strip] = 0;
+
+			/* this forces TIFFAppendToStrip() to do a seek */
+			tif->tif_curoff = 0;
+		}
+
+		if (!(*tif->tif_preencode)(tif, sample))
+			return (-1);
+		tif->tif_flags |= TIFF_POSTENCODE;
+	}
+	/*
+	 * Ensure the write is either sequential or at the
+	 * beginning of a strip (or that we can randomly
+	 * access the data -- i.e. no encoding).
+	 */
+	if (row != tif->tif_row) {
+		if (row < tif->tif_row) {
+			/*
+			 * Moving backwards within the same strip:
+			 * backup to the start and then decode
+			 * forward (below).
+			 */
+			tif->tif_row = (strip % td->td_stripsperimage) *
+			    td->td_rowsperstrip;
+			tif->tif_rawcp = tif->tif_rawdata;
+		}
+		/*
+		 * Seek forward to the desired row.
+		 */
+		if (!(*tif->tif_seek)(tif, row - tif->tif_row))
+			return (-1);
+		tif->tif_row = row;
+	}
+
+        /* swab if needed - note that source buffer will be altered */
+        tif->tif_postdecode( tif, (tidata_t) buf, tif->tif_scanlinesize );
+
+	status = (*tif->tif_encoderow)(tif, (tidata_t) buf,
+	    tif->tif_scanlinesize, sample);
+
+        /* we are now poised at the beginning of the next row */
+	tif->tif_row = row + 1;
+	return (status);
+}
+
+/*
+ * Encode the supplied data and write it to the
+ * specified strip.
+ *
+ * NB: Image length must be setup before writing.
+ */
+tsize_t
+TIFFWriteEncodedStrip(TIFF* tif, tstrip_t strip, tdata_t data, tsize_t cc)
+{
+	static const char module[] = "TIFFWriteEncodedStrip";
+	TIFFDirectory *td = &tif->tif_dir;
+	tsample_t sample;
+
+	if (!WRITECHECKSTRIPS(tif, module))
+		return ((tsize_t) -1);
+	/*
+	 * Check strip array to make sure there's space.
+	 * We don't support dynamically growing files that
+	 * have data organized in separate bitplanes because
+	 * it's too painful.  In that case we require that
+	 * the imagelength be set properly before the first
+	 * write (so that the strips array will be fully
+	 * allocated above).
+	 */
+	if (strip >= td->td_nstrips) {
+		if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		"Can not grow image by strips when using separate planes");
+			return ((tsize_t) -1);
+		}
+		if (!TIFFGrowStrips(tif, 1, module))
+			return ((tsize_t) -1);
+		td->td_stripsperimage =
+		    TIFFhowmany(td->td_imagelength, td->td_rowsperstrip);
+	}
+	/*
+	 * Handle delayed allocation of data buffer.  This
+	 * permits it to be sized according to the directory
+	 * info.
+	 */
+	if (!BUFFERCHECK(tif))
+		return ((tsize_t) -1);
+	tif->tif_curstrip = strip;
+	tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip;
+	if ((tif->tif_flags & TIFF_CODERSETUP) == 0) {
+		if (!(*tif->tif_setupencode)(tif))
+			return ((tsize_t) -1);
+		tif->tif_flags |= TIFF_CODERSETUP;
+	}
+        
+	tif->tif_rawcc = 0;
+	tif->tif_rawcp = tif->tif_rawdata;
+
+        if( td->td_stripbytecount[strip] > 0 )
+        {
+            /* if we are writing over existing tiles, zero length. */
+            td->td_stripbytecount[strip] = 0;
+
+            /* this forces TIFFAppendToStrip() to do a seek */
+            tif->tif_curoff = 0;
+        }
+        
+	tif->tif_flags &= ~TIFF_POSTENCODE;
+	sample = (tsample_t)(strip / td->td_stripsperimage);
+	if (!(*tif->tif_preencode)(tif, sample))
+		return ((tsize_t) -1);
+
+        /* swab if needed - note that source buffer will be altered */
+        tif->tif_postdecode( tif, (tidata_t) data, cc );
+
+	if (!(*tif->tif_encodestrip)(tif, (tidata_t) data, cc, sample))
+		return ((tsize_t) 0);
+	if (!(*tif->tif_postencode)(tif))
+		return ((tsize_t) -1);
+	if (!isFillOrder(tif, td->td_fillorder) &&
+	    (tif->tif_flags & TIFF_NOBITREV) == 0)
+		TIFFReverseBits(tif->tif_rawdata, tif->tif_rawcc);
+	if (tif->tif_rawcc > 0 &&
+	    !TIFFAppendToStrip(tif, strip, tif->tif_rawdata, tif->tif_rawcc))
+		return ((tsize_t) -1);
+	tif->tif_rawcc = 0;
+	tif->tif_rawcp = tif->tif_rawdata;
+	return (cc);
+}
+
+/*
+ * Write the supplied data to the specified strip.
+ *
+ * NB: Image length must be setup before writing.
+ */
+tsize_t
+TIFFWriteRawStrip(TIFF* tif, tstrip_t strip, tdata_t data, tsize_t cc)
+{
+	static const char module[] = "TIFFWriteRawStrip";
+	TIFFDirectory *td = &tif->tif_dir;
+
+	if (!WRITECHECKSTRIPS(tif, module))
+		return ((tsize_t) -1);
+	/*
+	 * Check strip array to make sure there's space.
+	 * We don't support dynamically growing files that
+	 * have data organized in separate bitplanes because
+	 * it's too painful.  In that case we require that
+	 * the imagelength be set properly before the first
+	 * write (so that the strips array will be fully
+	 * allocated above).
+	 */
+	if (strip >= td->td_nstrips) {
+		if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
+			TIFFErrorExt(tif->tif_clientdata, tif->tif_name,
+		"Can not grow image by strips when using separate planes");
+			return ((tsize_t) -1);
+		}
+		/*
+		 * Watch out for a growing image.  The value of
+		 * strips/image will initially be 1 (since it
+		 * can't be deduced until the imagelength is known).
+		 */
+		if (strip >= td->td_stripsperimage)
+			td->td_stripsperimage =
+			    TIFFhowmany(td->td_imagelength,td->td_rowsperstrip);
+		if (!TIFFGrowStrips(tif, 1, module))
+			return ((tsize_t) -1);
+	}
+	tif->tif_curstrip = strip;
+	tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip;
+	return (TIFFAppendToStrip(tif, strip, (tidata_t) data, cc) ?
+	    cc : (tsize_t) -1);
+}
+
+/*
+ * Write and compress a tile of data.  The
+ * tile is selected by the (x,y,z,s) coordinates.
+ */
+tsize_t
+TIFFWriteTile(TIFF* tif,
+    tdata_t buf, uint32 x, uint32 y, uint32 z, tsample_t s)
+{
+	if (!TIFFCheckTile(tif, x, y, z, s))
+		return (-1);
+	/*
+	 * NB: A tile size of -1 is used instead of tif_tilesize knowing
+	 *     that TIFFWriteEncodedTile will clamp this to the tile size.
+	 *     This is done because the tile size may not be defined until
+	 *     after the output buffer is setup in TIFFWriteBufferSetup.
+	 */
+	return (TIFFWriteEncodedTile(tif,
+	    TIFFComputeTile(tif, x, y, z, s), buf, (tsize_t) -1));
+}
+
+/*
+ * Encode the supplied data and write it to the
+ * specified tile.  There must be space for the
+ * data.  The function clamps individual writes
+ * to a tile to the tile size, but does not (and
+ * can not) check that multiple writes to the same
+ * tile do not write more than tile size data.
+ *
+ * NB: Image length must be setup before writing; this
+ *     interface does not support automatically growing
+ *     the image on each write (as TIFFWriteScanline does).
+ */
+tsize_t
+TIFFWriteEncodedTile(TIFF* tif, ttile_t tile, tdata_t data, tsize_t cc)
+{
+	static const char module[] = "TIFFWriteEncodedTile";
+	TIFFDirectory *td;
+	tsample_t sample;
+
+	if (!WRITECHECKTILES(tif, module))
+		return ((tsize_t) -1);
+	td = &tif->tif_dir;
+	if (tile >= td->td_nstrips) {
+		TIFFErrorExt(tif->tif_clientdata, module, "%s: Tile %lu out of range, max %lu",
+		    tif->tif_name, (unsigned long) tile, (unsigned long) td->td_nstrips);
+		return ((tsize_t) -1);
+	}
+	/*
+	 * Handle delayed allocation of data buffer.  This
+	 * permits it to be sized more intelligently (using
+	 * directory information).
+	 */
+	if (!BUFFERCHECK(tif))
+		return ((tsize_t) -1);
+	tif->tif_curtile = tile;
+
+	tif->tif_rawcc = 0;
+	tif->tif_rawcp = tif->tif_rawdata;
+
+        if( td->td_stripbytecount[tile] > 0 )
+        {
+            /* if we are writing over existing tiles, zero length. */
+            td->td_stripbytecount[tile] = 0;
+
+            /* this forces TIFFAppendToStrip() to do a seek */
+            tif->tif_curoff = 0;
+        }
+        
+	/* 
+	 * Compute tiles per row & per column to compute
+	 * current row and column
+	 */
+	tif->tif_row = (tile % TIFFhowmany(td->td_imagelength, td->td_tilelength))
+		* td->td_tilelength;
+	tif->tif_col = (tile % TIFFhowmany(td->td_imagewidth, td->td_tilewidth))
+		* td->td_tilewidth;
+
+	if ((tif->tif_flags & TIFF_CODERSETUP) == 0) {
+		if (!(*tif->tif_setupencode)(tif))
+			return ((tsize_t) -1);
+		tif->tif_flags |= TIFF_CODERSETUP;
+	}
+	tif->tif_flags &= ~TIFF_POSTENCODE;
+	sample = (tsample_t)(tile/td->td_stripsperimage);
+	if (!(*tif->tif_preencode)(tif, sample))
+		return ((tsize_t) -1);
+	/*
+	 * Clamp write amount to the tile size.  This is mostly
+	 * done so that callers can pass in some large number
+	 * (e.g. -1) and have the tile size used instead.
+	 */
+	if ( cc < 1 || cc > tif->tif_tilesize)
+		cc = tif->tif_tilesize;
+
+        /* swab if needed - note that source buffer will be altered */
+        tif->tif_postdecode( tif, (tidata_t) data, cc );
+
+	if (!(*tif->tif_encodetile)(tif, (tidata_t) data, cc, sample))
+		return ((tsize_t) 0);
+	if (!(*tif->tif_postencode)(tif))
+		return ((tsize_t) -1);
+	if (!isFillOrder(tif, td->td_fillorder) &&
+	    (tif->tif_flags & TIFF_NOBITREV) == 0)
+		TIFFReverseBits((unsigned char *)tif->tif_rawdata, tif->tif_rawcc);
+	if (tif->tif_rawcc > 0 && !TIFFAppendToStrip(tif, tile,
+	    tif->tif_rawdata, tif->tif_rawcc))
+		return ((tsize_t) -1);
+	tif->tif_rawcc = 0;
+	tif->tif_rawcp = tif->tif_rawdata;
+	return (cc);
+}
+
+/*
+ * Write the supplied data to the specified strip.
+ * There must be space for the data; we don't check
+ * if strips overlap!
+ *
+ * NB: Image length must be setup before writing; this
+ *     interface does not support automatically growing
+ *     the image on each write (as TIFFWriteScanline does).
+ */
+tsize_t
+TIFFWriteRawTile(TIFF* tif, ttile_t tile, tdata_t data, tsize_t cc)
+{
+	static const char module[] = "TIFFWriteRawTile";
+
+	if (!WRITECHECKTILES(tif, module))
+		return ((tsize_t) -1);
+	if (tile >= tif->tif_dir.td_nstrips) {
+		TIFFErrorExt(tif->tif_clientdata, module, "%s: Tile %lu out of range, max %lu",
+		    tif->tif_name, (unsigned long) tile,
+		    (unsigned long) tif->tif_dir.td_nstrips);
+		return ((tsize_t) -1);
+	}
+	return (TIFFAppendToStrip(tif, tile, (tidata_t) data, cc) ?
+	    cc : (tsize_t) -1);
+}
+
+#define	isUnspecified(tif, f) \
+    (TIFFFieldSet(tif,f) && (tif)->tif_dir.td_imagelength == 0)
+
+int
+TIFFSetupStrips(TIFF* tif)
+{
+	TIFFDirectory* td = &tif->tif_dir;
+
+	if (isTiled(tif))
+		td->td_stripsperimage =
+		    isUnspecified(tif, FIELD_TILEDIMENSIONS) ?
+			td->td_samplesperpixel : TIFFNumberOfTiles(tif);
+	else
+		td->td_stripsperimage =
+		    isUnspecified(tif, FIELD_ROWSPERSTRIP) ?
+			td->td_samplesperpixel : TIFFNumberOfStrips(tif);
+	td->td_nstrips = td->td_stripsperimage;
+	if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
+		td->td_stripsperimage /= td->td_samplesperpixel;
+	td->td_stripoffset = (uint32 *)
+	    _TIFFmalloc(td->td_nstrips * sizeof (uint32));
+	td->td_stripbytecount = (uint32 *)
+	    _TIFFmalloc(td->td_nstrips * sizeof (uint32));
+	if (td->td_stripoffset == NULL || td->td_stripbytecount == NULL)
+		return (0);
+	/*
+	 * Place data at the end-of-file
+	 * (by setting offsets to zero).
+	 */
+	_TIFFmemset(td->td_stripoffset, 0, td->td_nstrips*sizeof (uint32));
+	_TIFFmemset(td->td_stripbytecount, 0, td->td_nstrips*sizeof (uint32));
+	TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS);
+	TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS);
+	return (1);
+}
+#undef isUnspecified
+
+/*
+ * Verify file is writable and that the directory
+ * information is setup properly.  In doing the latter
+ * we also "freeze" the state of the directory so
+ * that important information is not changed.
+ */
+int
+TIFFWriteCheck(TIFF* tif, int tiles, const char* module)
+{
+	if (tif->tif_mode == O_RDONLY) {
+		TIFFErrorExt(tif->tif_clientdata, module, "%s: File not open for writing",
+		    tif->tif_name);
+		return (0);
+	}
+	if (tiles ^ isTiled(tif)) {
+		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, tiles ?
+		    "Can not write tiles to a stripped image" :
+		    "Can not write scanlines to a tiled image");
+		return (0);
+	}
+        
+	/*
+	 * On the first write verify all the required information
+	 * has been setup and initialize any data structures that
+	 * had to wait until directory information was set.
+	 * Note that a lot of our work is assumed to remain valid
+	 * because we disallow any of the important parameters
+	 * from changing after we start writing (i.e. once
+	 * TIFF_BEENWRITING is set, TIFFSetField will only allow
+	 * the image's length to be changed).
+	 */
+	if (!TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS)) {
+		TIFFErrorExt(tif->tif_clientdata, module,
+		    "%s: Must set \"ImageWidth\" before writing data",
+		    tif->tif_name);
+		return (0);
+	}
+	if (tif->tif_dir.td_samplesperpixel == 1) {
+		/* 
+		 * Planarconfiguration is irrelevant in case of single band
+		 * images and need not be included. We will set it anyway,
+		 * because this field is used in other parts of library even
+		 * in the single band case.
+		 */
+		tif->tif_dir.td_planarconfig = PLANARCONFIG_CONTIG;
+	} else {
+		if (!TIFFFieldSet(tif, FIELD_PLANARCONFIG)) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+		    "%s: Must set \"PlanarConfiguration\" before writing data",
+			    tif->tif_name);
+			return (0);
+		}
+	}
+	if (tif->tif_dir.td_stripoffset == NULL && !TIFFSetupStrips(tif)) {
+		tif->tif_dir.td_nstrips = 0;
+		TIFFErrorExt(tif->tif_clientdata, module, "%s: No space for %s arrays",
+		    tif->tif_name, isTiled(tif) ? "tile" : "strip");
+		return (0);
+	}
+	tif->tif_tilesize = isTiled(tif) ? TIFFTileSize(tif) : (tsize_t) -1;
+	tif->tif_scanlinesize = TIFFScanlineSize(tif);
+	tif->tif_flags |= TIFF_BEENWRITING;
+	return (1);
+}
+
+/*
+ * Setup the raw data buffer used for encoding.
+ */
+int
+TIFFWriteBufferSetup(TIFF* tif, tdata_t bp, tsize_t size)
+{
+	static const char module[] = "TIFFWriteBufferSetup";
+
+	if (tif->tif_rawdata) {
+		if (tif->tif_flags & TIFF_MYBUFFER) {
+			_TIFFfree(tif->tif_rawdata);
+			tif->tif_flags &= ~TIFF_MYBUFFER;
+		}
+		tif->tif_rawdata = NULL;
+	}
+	if (size == (tsize_t) -1) {
+		size = (isTiled(tif) ?
+		    tif->tif_tilesize : TIFFStripSize(tif));
+		/*
+		 * Make raw data buffer at least 8K
+		 */
+		if (size < 8*1024)
+			size = 8*1024;
+		bp = NULL;			/* NB: force malloc */
+	}
+	if (bp == NULL) {
+		bp = _TIFFmalloc(size);
+		if (bp == NULL) {
+			TIFFErrorExt(tif->tif_clientdata, module, "%s: No space for output buffer",
+			    tif->tif_name);
+			return (0);
+		}
+		tif->tif_flags |= TIFF_MYBUFFER;
+	} else
+		tif->tif_flags &= ~TIFF_MYBUFFER;
+	tif->tif_rawdata = (tidata_t) bp;
+	tif->tif_rawdatasize = size;
+	tif->tif_rawcc = 0;
+	tif->tif_rawcp = tif->tif_rawdata;
+	tif->tif_flags |= TIFF_BUFFERSETUP;
+	return (1);
+}
+
+/*
+ * Grow the strip data structures by delta strips.
+ */
+static int
+TIFFGrowStrips(TIFF* tif, int delta, const char* module)
+{
+	TIFFDirectory	*td = &tif->tif_dir;
+	uint32		*new_stripoffset, *new_stripbytecount;
+
+	assert(td->td_planarconfig == PLANARCONFIG_CONTIG);
+	new_stripoffset = (uint32*)_TIFFrealloc(td->td_stripoffset,
+		(td->td_nstrips + delta) * sizeof (uint32));
+	new_stripbytecount = (uint32*)_TIFFrealloc(td->td_stripbytecount,
+		(td->td_nstrips + delta) * sizeof (uint32));
+	if (new_stripoffset == NULL || new_stripbytecount == NULL) {
+		if (new_stripoffset)
+			_TIFFfree(new_stripoffset);
+		if (new_stripbytecount)
+			_TIFFfree(new_stripbytecount);
+		td->td_nstrips = 0;
+		TIFFErrorExt(tif->tif_clientdata, module, "%s: No space to expand strip arrays",
+			  tif->tif_name);
+		return (0);
+	}
+	td->td_stripoffset = new_stripoffset;
+	td->td_stripbytecount = new_stripbytecount;
+	_TIFFmemset(td->td_stripoffset + td->td_nstrips,
+		    0, delta*sizeof (uint32));
+	_TIFFmemset(td->td_stripbytecount + td->td_nstrips,
+		    0, delta*sizeof (uint32));
+	td->td_nstrips += delta;
+	return (1);
+}
+
+/*
+ * Append the data to the specified strip.
+ */
+static int
+TIFFAppendToStrip(TIFF* tif, tstrip_t strip, tidata_t data, tsize_t cc)
+{
+	TIFFDirectory *td = &tif->tif_dir;
+	static const char module[] = "TIFFAppendToStrip";
+
+	if (td->td_stripoffset[strip] == 0 || tif->tif_curoff == 0) {
+		/*
+		 * No current offset, set the current strip.
+		 */
+		assert(td->td_nstrips > 0);
+		if (td->td_stripoffset[strip] != 0) {
+			/*
+			 * Prevent overlapping of the data chunks. We need
+                         * this to enable in place updating of the compressed
+                         * images. Larger blocks will be moved at the end of
+                         * the file without any optimization of the spare
+                         * space, so such scheme is not too much effective.
+			 */
+			if (td->td_stripbytecountsorted) {
+				if (strip == td->td_nstrips - 1
+				    || td->td_stripoffset[strip + 1] <
+					td->td_stripoffset[strip] + cc) {
+					td->td_stripoffset[strip] =
+						TIFFSeekFile(tif, (toff_t)0,
+							     SEEK_END);
+				}
+			} else {
+				tstrip_t i;
+				for (i = 0; i < td->td_nstrips; i++) {
+					if (td->td_stripoffset[i] > 
+						td->td_stripoffset[strip]
+					    && td->td_stripoffset[i] <
+						td->td_stripoffset[strip] + cc) {
+						td->td_stripoffset[strip] =
+							TIFFSeekFile(tif,
+								     (toff_t)0,
+								     SEEK_END);
+					}
+				}
+			}
+
+			if (!SeekOK(tif, td->td_stripoffset[strip])) {
+				TIFFErrorExt(tif->tif_clientdata, module,
+					  "%s: Seek error at scanline %lu",
+					  tif->tif_name,
+					  (unsigned long)tif->tif_row);
+				return (0);
+			}
+		} else
+			td->td_stripoffset[strip] =
+			    TIFFSeekFile(tif, (toff_t) 0, SEEK_END);
+		tif->tif_curoff = td->td_stripoffset[strip];
+	}
+
+	if (!WriteOK(tif, data, cc)) {
+		TIFFErrorExt(tif->tif_clientdata, module, "%s: Write error at scanline %lu",
+		    tif->tif_name, (unsigned long) tif->tif_row);
+		return (0);
+	}
+	tif->tif_curoff += cc;
+	td->td_stripbytecount[strip] += cc;
+	return (1);
+}
+
+/*
+ * Internal version of TIFFFlushData that can be
+ * called by ``encodestrip routines'' w/o concern
+ * for infinite recursion.
+ */
+int
+TIFFFlushData1(TIFF* tif)
+{
+	if (tif->tif_rawcc > 0) {
+		if (!isFillOrder(tif, tif->tif_dir.td_fillorder) &&
+		    (tif->tif_flags & TIFF_NOBITREV) == 0)
+			TIFFReverseBits((unsigned char *)tif->tif_rawdata,
+			    tif->tif_rawcc);
+		if (!TIFFAppendToStrip(tif,
+		    isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip,
+		    tif->tif_rawdata, tif->tif_rawcc))
+			return (0);
+		tif->tif_rawcc = 0;
+		tif->tif_rawcp = tif->tif_rawdata;
+	}
+	return (1);
+}
+
+/*
+ * Set the current write offset.  This should only be
+ * used to set the offset to a known previous location
+ * (very carefully), or to 0 so that the next write gets
+ * appended to the end of the file.
+ */
+void
+TIFFSetWriteOffset(TIFF* tif, toff_t off)
+{
+	tif->tif_curoff = off;
+}
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tif_zip.c b/Utilities/GDAL/frmts/gtiff/libtiff/tif_zip.c
new file mode 100644
index 0000000000..0a19921e77
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tif_zip.c
@@ -0,0 +1,378 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1995-1997 Sam Leffler
+ * Copyright (c) 1995-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#include "tiffiop.h"
+#ifdef ZIP_SUPPORT
+/*
+ * TIFF Library.
+ *
+ * ZIP (aka Deflate) Compression Support
+ *
+ * This file is simply an interface to the zlib library written by
+ * Jean-loup Gailly and Mark Adler.  You must use version 1.0 or later
+ * of the library: this code assumes the 1.0 API and also depends on
+ * the ability to write the zlib header multiple times (one per strip)
+ * which was not possible with versions prior to 0.95.  Note also that
+ * older versions of this codec avoided this bug by supressing the header
+ * entirely.  This means that files written with the old library cannot
+ * be read; they should be converted to a different compression scheme
+ * and then reconverted.
+ *
+ * The data format used by the zlib library is described in the files
+ * zlib-3.1.doc, deflate-1.1.doc and gzip-4.1.doc, available in the
+ * directory ftp://ftp.uu.net/pub/archiving/zip/doc.  The library was
+ * last found at ftp://ftp.uu.net/pub/archiving/zip/zlib/zlib-0.99.tar.gz.
+ */
+#include "tif_predict.h"
+#include "zlib.h"
+
+#include <stdio.h>
+
+/*
+ * Sigh, ZLIB_VERSION is defined as a string so there's no
+ * way to do a proper check here.  Instead we guess based
+ * on the presence of #defines that were added between the
+ * 0.95 and 1.0 distributions.
+ */
+#if !defined(Z_NO_COMPRESSION) || !defined(Z_DEFLATED)
+#error "Antiquated ZLIB software; you must use version 1.0 or later"
+#endif
+
+/*
+ * State block for each open TIFF
+ * file using ZIP compression/decompression.
+ */
+typedef	struct {
+	TIFFPredictorState predict;
+	z_stream	stream;
+	int		zipquality;		/* compression level */
+	int		state;			/* state flags */
+#define	ZSTATE_INIT	0x1		/* zlib setup successfully */
+
+	TIFFVGetMethod	vgetparent;		/* super-class method */
+	TIFFVSetMethod	vsetparent;		/* super-class method */
+} ZIPState;
+
+#define	ZState(tif)		((ZIPState*) (tif)->tif_data)
+#define	DecoderState(tif)	ZState(tif)
+#define	EncoderState(tif)	ZState(tif)
+
+static	int ZIPEncode(TIFF*, tidata_t, tsize_t, tsample_t);
+static	int ZIPDecode(TIFF*, tidata_t, tsize_t, tsample_t);
+
+static int
+ZIPSetupDecode(TIFF* tif)
+{
+	ZIPState* sp = DecoderState(tif);
+	static const char module[] = "ZIPSetupDecode";
+
+	assert(sp != NULL);
+	if (inflateInit(&sp->stream) != Z_OK) {
+		TIFFErrorExt(tif->tif_clientdata, module, "%s: %s", tif->tif_name, sp->stream.msg);
+		return (0);
+	} else {
+		sp->state |= ZSTATE_INIT;
+		return (1);
+	}
+}
+
+/*
+ * Setup state for decoding a strip.
+ */
+static int
+ZIPPreDecode(TIFF* tif, tsample_t s)
+{
+	ZIPState* sp = DecoderState(tif);
+
+	(void) s;
+	assert(sp != NULL);
+	sp->stream.next_in = tif->tif_rawdata;
+	sp->stream.avail_in = tif->tif_rawcc;
+	return (inflateReset(&sp->stream) == Z_OK);
+}
+
+static int
+ZIPDecode(TIFF* tif, tidata_t op, tsize_t occ, tsample_t s)
+{
+	ZIPState* sp = DecoderState(tif);
+	static const char module[] = "ZIPDecode";
+
+	(void) s;
+	assert(sp != NULL);
+	sp->stream.next_out = op;
+	sp->stream.avail_out = occ;
+	do {
+		int state = inflate(&sp->stream, Z_PARTIAL_FLUSH);
+		if (state == Z_STREAM_END)
+			break;
+		if (state == Z_DATA_ERROR) {
+			TIFFErrorExt(tif->tif_clientdata, module,
+			    "%s: Decoding error at scanline %d, %s",
+			    tif->tif_name, tif->tif_row, sp->stream.msg);
+			if (inflateSync(&sp->stream) != Z_OK)
+				return (0);
+			continue;
+		}
+		if (state != Z_OK) {
+			TIFFErrorExt(tif->tif_clientdata, module, "%s: zlib error: %s",
+			    tif->tif_name, sp->stream.msg);
+			return (0);
+		}
+	} while (sp->stream.avail_out > 0);
+	if (sp->stream.avail_out != 0) {
+		TIFFErrorExt(tif->tif_clientdata, module,
+		    "%s: Not enough data at scanline %d (short %d bytes)",
+		    tif->tif_name, tif->tif_row, sp->stream.avail_out);
+		return (0);
+	}
+	return (1);
+}
+
+static int
+ZIPSetupEncode(TIFF* tif)
+{
+	ZIPState* sp = EncoderState(tif);
+	static const char module[] = "ZIPSetupEncode";
+
+	assert(sp != NULL);
+	if (deflateInit(&sp->stream, sp->zipquality) != Z_OK) {
+		TIFFErrorExt(tif->tif_clientdata, module, "%s: %s", tif->tif_name, sp->stream.msg);
+		return (0);
+	} else {
+		sp->state |= ZSTATE_INIT;
+		return (1);
+	}
+}
+
+/*
+ * Reset encoding state at the start of a strip.
+ */
+static int
+ZIPPreEncode(TIFF* tif, tsample_t s)
+{
+	ZIPState *sp = EncoderState(tif);
+
+	(void) s;
+	assert(sp != NULL);
+	sp->stream.next_out = tif->tif_rawdata;
+	sp->stream.avail_out = tif->tif_rawdatasize;
+	return (deflateReset(&sp->stream) == Z_OK);
+}
+
+/*
+ * Encode a chunk of pixels.
+ */
+static int
+ZIPEncode(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s)
+{
+	ZIPState *sp = EncoderState(tif);
+	static const char module[] = "ZIPEncode";
+
+	(void) s;
+	sp->stream.next_in = bp;
+	sp->stream.avail_in = cc;
+	do {
+		if (deflate(&sp->stream, Z_NO_FLUSH) != Z_OK) {
+			TIFFErrorExt(tif->tif_clientdata, module, "%s: Encoder error: %s",
+			    tif->tif_name, sp->stream.msg);
+			return (0);
+		}
+		if (sp->stream.avail_out == 0) {
+			tif->tif_rawcc = tif->tif_rawdatasize;
+			TIFFFlushData1(tif);
+			sp->stream.next_out = tif->tif_rawdata;
+			sp->stream.avail_out = tif->tif_rawdatasize;
+		}
+	} while (sp->stream.avail_in > 0);
+	return (1);
+}
+
+/*
+ * Finish off an encoded strip by flushing the last
+ * string and tacking on an End Of Information code.
+ */
+static int
+ZIPPostEncode(TIFF* tif)
+{
+	ZIPState *sp = EncoderState(tif);
+	static const char module[] = "ZIPPostEncode";
+	int state;
+
+	sp->stream.avail_in = 0;
+	do {
+		state = deflate(&sp->stream, Z_FINISH);
+		switch (state) {
+		case Z_STREAM_END:
+		case Z_OK:
+		    if ((int)sp->stream.avail_out != (int)tif->tif_rawdatasize)
+                    {
+			    tif->tif_rawcc =
+				tif->tif_rawdatasize - sp->stream.avail_out;
+			    TIFFFlushData1(tif);
+			    sp->stream.next_out = tif->tif_rawdata;
+			    sp->stream.avail_out = tif->tif_rawdatasize;
+		    }
+		    break;
+		default:
+			TIFFErrorExt(tif->tif_clientdata, module, "%s: zlib error: %s",
+			tif->tif_name, sp->stream.msg);
+		    return (0);
+		}
+	} while (state != Z_STREAM_END);
+	return (1);
+}
+
+static void
+ZIPCleanup(TIFF* tif)
+{
+	ZIPState* sp = ZState(tif);
+
+	assert(sp != 0);
+
+	(void)TIFFPredictorCleanup(tif);
+
+	tif->tif_tagmethods.vgetfield = sp->vgetparent;
+	tif->tif_tagmethods.vsetfield = sp->vsetparent;
+
+	if (sp->state&ZSTATE_INIT) {
+		/* NB: avoid problems in the library */
+		if (tif->tif_mode == O_RDONLY)
+			inflateEnd(&sp->stream);
+		else
+			deflateEnd(&sp->stream);
+	}
+	_TIFFfree(sp);
+	tif->tif_data = NULL;
+
+	_TIFFSetDefaultCompressionState(tif);
+}
+
+static int
+ZIPVSetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+	ZIPState* sp = ZState(tif);
+	static const char module[] = "ZIPVSetField";
+
+	switch (tag) {
+	case TIFFTAG_ZIPQUALITY:
+		sp->zipquality = va_arg(ap, int);
+		if (tif->tif_mode != O_RDONLY && (sp->state&ZSTATE_INIT)) {
+			if (deflateParams(&sp->stream,
+			    sp->zipquality, Z_DEFAULT_STRATEGY) != Z_OK) {
+				TIFFErrorExt(tif->tif_clientdata, module, "%s: zlib error: %s",
+				    tif->tif_name, sp->stream.msg);
+				return (0);
+			}
+		}
+		return (1);
+	default:
+		return (*sp->vsetparent)(tif, tag, ap);
+	}
+	/*NOTREACHED*/
+}
+
+static int
+ZIPVGetField(TIFF* tif, ttag_t tag, va_list ap)
+{
+	ZIPState* sp = ZState(tif);
+
+	switch (tag) {
+	case TIFFTAG_ZIPQUALITY:
+		*va_arg(ap, int*) = sp->zipquality;
+		break;
+	default:
+		return (*sp->vgetparent)(tif, tag, ap);
+	}
+	return (1);
+}
+
+static const TIFFFieldInfo zipFieldInfo[] = {
+    { TIFFTAG_ZIPQUALITY,	 0, 0,	TIFF_ANY,	FIELD_PSEUDO,
+      TRUE,	FALSE,	"" },
+};
+
+int
+TIFFInitZIP(TIFF* tif, int scheme)
+{
+	ZIPState* sp;
+
+	assert( (scheme == COMPRESSION_DEFLATE)
+		|| (scheme == COMPRESSION_ADOBE_DEFLATE));
+
+	/*
+	 * Allocate state block so tag methods have storage to record values.
+	 */
+	tif->tif_data = (tidata_t) _TIFFmalloc(sizeof (ZIPState));
+	if (tif->tif_data == NULL)
+		goto bad;
+	sp = ZState(tif);
+	sp->stream.zalloc = NULL;
+	sp->stream.zfree = NULL;
+	sp->stream.opaque = NULL;
+	sp->stream.data_type = Z_BINARY;
+
+	/*
+	 * Merge codec-specific tag information and
+	 * override parent get/set field methods.
+	 */
+	_TIFFMergeFieldInfo(tif, zipFieldInfo, TIFFArrayCount(zipFieldInfo));
+	sp->vgetparent = tif->tif_tagmethods.vgetfield;
+	tif->tif_tagmethods.vgetfield = ZIPVGetField; /* hook for codec tags */
+	sp->vsetparent = tif->tif_tagmethods.vsetfield;
+	tif->tif_tagmethods.vsetfield = ZIPVSetField; /* hook for codec tags */
+
+	/* Default values for codec-specific fields */
+	sp->zipquality = Z_DEFAULT_COMPRESSION;	/* default comp. level */
+	sp->state = 0;
+
+	/*
+	 * Install codec methods.
+	 */
+	tif->tif_setupdecode = ZIPSetupDecode;
+	tif->tif_predecode = ZIPPreDecode;
+	tif->tif_decoderow = ZIPDecode;
+	tif->tif_decodestrip = ZIPDecode;
+	tif->tif_decodetile = ZIPDecode;
+	tif->tif_setupencode = ZIPSetupEncode;
+	tif->tif_preencode = ZIPPreEncode;
+	tif->tif_postencode = ZIPPostEncode;
+	tif->tif_encoderow = ZIPEncode;
+	tif->tif_encodestrip = ZIPEncode;
+	tif->tif_encodetile = ZIPEncode;
+	tif->tif_cleanup = ZIPCleanup;
+	/*
+	 * Setup predictor setup.
+	 */
+	(void) TIFFPredictorInit(tif);
+	return (1);
+bad:
+	TIFFErrorExt(tif->tif_clientdata, "TIFFInitZIP",
+		     "No space for ZIP state block");
+	return (0);
+}
+#endif /* ZIP_SUPORT */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tiff.h b/Utilities/GDAL/frmts/gtiff/libtiff/tiff.h
new file mode 100644
index 0000000000..c7a058a9a3
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tiff.h
@@ -0,0 +1,647 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _TIFF_
+#define	_TIFF_
+
+#include "tiffconf.h"
+
+/*
+ * Tag Image File Format (TIFF)
+ *
+ * Based on Rev 6.0 from:
+ *    Developer's Desk
+ *    Aldus Corporation
+ *    411 First Ave. South
+ *    Suite 200
+ *    Seattle, WA  98104
+ *    206-622-5500
+ *    
+ *    (http://partners.adobe.com/asn/developer/PDFS/TN/TIFF6.pdf)
+ *
+ * For Big TIFF design notes see the following link
+ *    http://gdal.maptools.org/twiki/bin/view/libtiff/BigTIFFDesign
+ */
+#define	TIFF_VERSION	        42
+#define TIFF_BIGTIFF_VERSION    43
+
+#define	TIFF_BIGENDIAN		0x4d4d
+#define	TIFF_LITTLEENDIAN	0x4949
+#define	MDI_LITTLEENDIAN        0x5045
+#define	MDI_BIGENDIAN           0x4550
+/*
+ * Intrinsic data types required by the file format:
+ *
+ * 8-bit quantities	int8/uint8
+ * 16-bit quantities	int16/uint16
+ * 32-bit quantities	int32/uint32
+ * strings		unsigned char*
+ */
+
+#ifndef HAVE_INT8
+typedef	signed char int8;	/* NB: non-ANSI compilers may not grok */
+#endif
+typedef	unsigned char uint8;
+#ifndef HAVE_INT16
+typedef	short int16;
+#endif
+typedef	unsigned short uint16;	/* sizeof (uint16) must == 2 */
+#if SIZEOF_INT == 4
+#ifndef HAVE_INT32
+typedef	int int32;
+#endif
+typedef	unsigned int uint32;	/* sizeof (uint32) must == 4 */
+#elif SIZEOF_LONG == 4
+#ifndef HAVE_INT32
+typedef	long int32;
+#endif
+typedef	unsigned long uint32;	/* sizeof (uint32) must == 4 */
+#endif
+
+/* For TIFFReassignTagToIgnore */
+enum TIFFIgnoreSense /* IGNORE tag table */
+{
+	TIS_STORE,
+	TIS_EXTRACT,
+	TIS_EMPTY
+};
+
+/*
+ * TIFF header.
+ */
+typedef	struct {
+	uint16	tiff_magic;	/* magic number (defines byte order) */
+#define TIFF_MAGIC_SIZE		2
+	uint16	tiff_version;	/* TIFF version number */
+#define TIFF_VERSION_SIZE	2
+	uint32	tiff_diroff;	/* byte offset to first directory */
+#define TIFF_DIROFFSET_SIZE	4
+} TIFFHeader;
+
+
+/*
+ * TIFF Image File Directories are comprised of a table of field
+ * descriptors of the form shown below.  The table is sorted in
+ * ascending order by tag.  The values associated with each entry are
+ * disjoint and may appear anywhere in the file (so long as they are
+ * placed on a word boundary).
+ *
+ * If the value is 4 bytes or less, then it is placed in the offset
+ * field to save space.  If the value is less than 4 bytes, it is
+ * left-justified in the offset field.
+ */
+typedef	struct {
+	uint16		tdir_tag;	/* see below */
+	uint16		tdir_type;	/* data type; see below */
+	uint32		tdir_count;	/* number of items; length in spec */
+	uint32		tdir_offset;	/* byte offset to field data */
+} TIFFDirEntry;
+
+/*
+ * NB: In the comments below,
+ *  - items marked with a + are obsoleted by revision 5.0,
+ *  - items marked with a ! are introduced in revision 6.0.
+ *  - items marked with a % are introduced post revision 6.0.
+ *  - items marked with a $ are obsoleted by revision 6.0.
+ *  - items marked with a & are introduced by Adobe DNG specification.
+ */
+
+/*
+ * Tag data type information.
+ *
+ * Note: RATIONALs are the ratio of two 32-bit integer values.
+ */
+typedef	enum {
+	TIFF_NOTYPE	= 0,	/* placeholder */
+	TIFF_BYTE	= 1,	/* 8-bit unsigned integer */
+	TIFF_ASCII	= 2,	/* 8-bit bytes w/ last byte null */
+	TIFF_SHORT	= 3,	/* 16-bit unsigned integer */
+	TIFF_LONG	= 4,	/* 32-bit unsigned integer */
+	TIFF_RATIONAL	= 5,	/* 64-bit unsigned fraction */
+	TIFF_SBYTE	= 6,	/* !8-bit signed integer */
+	TIFF_UNDEFINED	= 7,	/* !8-bit untyped data */
+	TIFF_SSHORT	= 8,	/* !16-bit signed integer */
+	TIFF_SLONG	= 9,	/* !32-bit signed integer */
+	TIFF_SRATIONAL	= 10,	/* !64-bit signed fraction */
+	TIFF_FLOAT	= 11,	/* !32-bit IEEE floating point */
+	TIFF_DOUBLE	= 12,	/* !64-bit IEEE floating point */
+	TIFF_IFD	= 13	/* %32-bit unsigned integer (offset) */
+} TIFFDataType;
+
+/*
+ * TIFF Tag Definitions.
+ */
+#define	TIFFTAG_SUBFILETYPE		254	/* subfile data descriptor */
+#define	    FILETYPE_REDUCEDIMAGE	0x1	/* reduced resolution version */
+#define	    FILETYPE_PAGE		0x2	/* one page of many */
+#define	    FILETYPE_MASK		0x4	/* transparency mask */
+#define	TIFFTAG_OSUBFILETYPE		255	/* +kind of data in subfile */
+#define	    OFILETYPE_IMAGE		1	/* full resolution image data */
+#define	    OFILETYPE_REDUCEDIMAGE	2	/* reduced size image data */
+#define	    OFILETYPE_PAGE		3	/* one page of many */
+#define	TIFFTAG_IMAGEWIDTH		256	/* image width in pixels */
+#define	TIFFTAG_IMAGELENGTH		257	/* image height in pixels */
+#define	TIFFTAG_BITSPERSAMPLE		258	/* bits per channel (sample) */
+#define	TIFFTAG_COMPRESSION		259	/* data compression technique */
+#define	    COMPRESSION_NONE		1	/* dump mode */
+#define	    COMPRESSION_CCITTRLE	2	/* CCITT modified Huffman RLE */
+#define	    COMPRESSION_CCITTFAX3	3	/* CCITT Group 3 fax encoding */
+#define     COMPRESSION_CCITT_T4        3       /* CCITT T.4 (TIFF 6 name) */
+#define	    COMPRESSION_CCITTFAX4	4	/* CCITT Group 4 fax encoding */
+#define     COMPRESSION_CCITT_T6        4       /* CCITT T.6 (TIFF 6 name) */
+#define	    COMPRESSION_LZW		5       /* Lempel-Ziv  & Welch */
+#define	    COMPRESSION_OJPEG		6	/* !6.0 JPEG */
+#define	    COMPRESSION_JPEG		7	/* %JPEG DCT compression */
+#define	    COMPRESSION_NEXT		32766	/* NeXT 2-bit RLE */
+#define	    COMPRESSION_CCITTRLEW	32771	/* #1 w/ word alignment */
+#define	    COMPRESSION_PACKBITS	32773	/* Macintosh RLE */
+#define	    COMPRESSION_THUNDERSCAN	32809	/* ThunderScan RLE */
+/* codes 32895-32898 are reserved for ANSI IT8 TIFF/IT <dkelly@apago.com) */
+#define	    COMPRESSION_IT8CTPAD	32895   /* IT8 CT w/padding */
+#define	    COMPRESSION_IT8LW		32896   /* IT8 Linework RLE */
+#define	    COMPRESSION_IT8MP		32897   /* IT8 Monochrome picture */
+#define	    COMPRESSION_IT8BL		32898   /* IT8 Binary line art */
+/* compression codes 32908-32911 are reserved for Pixar */
+#define     COMPRESSION_PIXARFILM	32908   /* Pixar companded 10bit LZW */
+#define	    COMPRESSION_PIXARLOG	32909   /* Pixar companded 11bit ZIP */
+#define	    COMPRESSION_DEFLATE		32946	/* Deflate compression */
+#define     COMPRESSION_ADOBE_DEFLATE   8       /* Deflate compression,
+						   as recognized by Adobe */
+/* compression code 32947 is reserved for Oceana Matrix <dev@oceana.com> */
+#define     COMPRESSION_DCS             32947   /* Kodak DCS encoding */
+#define	    COMPRESSION_JBIG		34661	/* ISO JBIG */
+#define     COMPRESSION_SGILOG		34676	/* SGI Log Luminance RLE */
+#define     COMPRESSION_SGILOG24	34677	/* SGI Log 24-bit packed */
+#define     COMPRESSION_JP2000          34712   /* Leadtools JPEG2000 */
+#define	TIFFTAG_PHOTOMETRIC		262	/* photometric interpretation */
+#define	    PHOTOMETRIC_MINISWHITE	0	/* min value is white */
+#define	    PHOTOMETRIC_MINISBLACK	1	/* min value is black */
+#define	    PHOTOMETRIC_RGB		2	/* RGB color model */
+#define	    PHOTOMETRIC_PALETTE		3	/* color map indexed */
+#define	    PHOTOMETRIC_MASK		4	/* $holdout mask */
+#define	    PHOTOMETRIC_SEPARATED	5	/* !color separations */
+#define	    PHOTOMETRIC_YCBCR		6	/* !CCIR 601 */
+#define	    PHOTOMETRIC_CIELAB		8	/* !1976 CIE L*a*b* */
+#define	    PHOTOMETRIC_ICCLAB		9	/* ICC L*a*b* [Adobe TIFF Technote 4] */
+#define	    PHOTOMETRIC_ITULAB		10	/* ITU L*a*b* */
+#define     PHOTOMETRIC_LOGL		32844	/* CIE Log2(L) */
+#define     PHOTOMETRIC_LOGLUV		32845	/* CIE Log2(L) (u',v') */
+#define	TIFFTAG_THRESHHOLDING		263	/* +thresholding used on data */
+#define	    THRESHHOLD_BILEVEL		1	/* b&w art scan */
+#define	    THRESHHOLD_HALFTONE		2	/* or dithered scan */
+#define	    THRESHHOLD_ERRORDIFFUSE	3	/* usually floyd-steinberg */
+#define	TIFFTAG_CELLWIDTH		264	/* +dithering matrix width */
+#define	TIFFTAG_CELLLENGTH		265	/* +dithering matrix height */
+#define	TIFFTAG_FILLORDER		266	/* data order within a byte */
+#define	    FILLORDER_MSB2LSB		1	/* most significant -> least */
+#define	    FILLORDER_LSB2MSB		2	/* least significant -> most */
+#define	TIFFTAG_DOCUMENTNAME		269	/* name of doc. image is from */
+#define	TIFFTAG_IMAGEDESCRIPTION	270	/* info about image */
+#define	TIFFTAG_MAKE			271	/* scanner manufacturer name */
+#define	TIFFTAG_MODEL			272	/* scanner model name/number */
+#define	TIFFTAG_STRIPOFFSETS		273	/* offsets to data strips */
+#define	TIFFTAG_ORIENTATION		274	/* +image orientation */
+#define	    ORIENTATION_TOPLEFT		1	/* row 0 top, col 0 lhs */
+#define	    ORIENTATION_TOPRIGHT	2	/* row 0 top, col 0 rhs */
+#define	    ORIENTATION_BOTRIGHT	3	/* row 0 bottom, col 0 rhs */
+#define	    ORIENTATION_BOTLEFT		4	/* row 0 bottom, col 0 lhs */
+#define	    ORIENTATION_LEFTTOP		5	/* row 0 lhs, col 0 top */
+#define	    ORIENTATION_RIGHTTOP	6	/* row 0 rhs, col 0 top */
+#define	    ORIENTATION_RIGHTBOT	7	/* row 0 rhs, col 0 bottom */
+#define	    ORIENTATION_LEFTBOT		8	/* row 0 lhs, col 0 bottom */
+#define	TIFFTAG_SAMPLESPERPIXEL		277	/* samples per pixel */
+#define	TIFFTAG_ROWSPERSTRIP		278	/* rows per strip of data */
+#define	TIFFTAG_STRIPBYTECOUNTS		279	/* bytes counts for strips */
+#define	TIFFTAG_MINSAMPLEVALUE		280	/* +minimum sample value */
+#define	TIFFTAG_MAXSAMPLEVALUE		281	/* +maximum sample value */
+#define	TIFFTAG_XRESOLUTION		282	/* pixels/resolution in x */
+#define	TIFFTAG_YRESOLUTION		283	/* pixels/resolution in y */
+#define	TIFFTAG_PLANARCONFIG		284	/* storage organization */
+#define	    PLANARCONFIG_CONTIG		1	/* single image plane */
+#define	    PLANARCONFIG_SEPARATE	2	/* separate planes of data */
+#define	TIFFTAG_PAGENAME		285	/* page name image is from */
+#define	TIFFTAG_XPOSITION		286	/* x page offset of image lhs */
+#define	TIFFTAG_YPOSITION		287	/* y page offset of image lhs */
+#define	TIFFTAG_FREEOFFSETS		288	/* +byte offset to free block */
+#define	TIFFTAG_FREEBYTECOUNTS		289	/* +sizes of free blocks */
+#define	TIFFTAG_GRAYRESPONSEUNIT	290	/* $gray scale curve accuracy */
+#define	    GRAYRESPONSEUNIT_10S	1	/* tenths of a unit */
+#define	    GRAYRESPONSEUNIT_100S	2	/* hundredths of a unit */
+#define	    GRAYRESPONSEUNIT_1000S	3	/* thousandths of a unit */
+#define	    GRAYRESPONSEUNIT_10000S	4	/* ten-thousandths of a unit */
+#define	    GRAYRESPONSEUNIT_100000S	5	/* hundred-thousandths */
+#define	TIFFTAG_GRAYRESPONSECURVE	291	/* $gray scale response curve */
+#define	TIFFTAG_GROUP3OPTIONS		292	/* 32 flag bits */
+#define	TIFFTAG_T4OPTIONS		292	/* TIFF 6.0 proper name alias */
+#define	    GROUP3OPT_2DENCODING	0x1	/* 2-dimensional coding */
+#define	    GROUP3OPT_UNCOMPRESSED	0x2	/* data not compressed */
+#define	    GROUP3OPT_FILLBITS		0x4	/* fill to byte boundary */
+#define	TIFFTAG_GROUP4OPTIONS		293	/* 32 flag bits */
+#define TIFFTAG_T6OPTIONS               293     /* TIFF 6.0 proper name */
+#define	    GROUP4OPT_UNCOMPRESSED	0x2	/* data not compressed */
+#define	TIFFTAG_RESOLUTIONUNIT		296	/* units of resolutions */
+#define	    RESUNIT_NONE		1	/* no meaningful units */
+#define	    RESUNIT_INCH		2	/* english */
+#define	    RESUNIT_CENTIMETER		3	/* metric */
+#define	TIFFTAG_PAGENUMBER		297	/* page numbers of multi-page */
+#define	TIFFTAG_COLORRESPONSEUNIT	300	/* $color curve accuracy */
+#define	    COLORRESPONSEUNIT_10S	1	/* tenths of a unit */
+#define	    COLORRESPONSEUNIT_100S	2	/* hundredths of a unit */
+#define	    COLORRESPONSEUNIT_1000S	3	/* thousandths of a unit */
+#define	    COLORRESPONSEUNIT_10000S	4	/* ten-thousandths of a unit */
+#define	    COLORRESPONSEUNIT_100000S	5	/* hundred-thousandths */
+#define	TIFFTAG_TRANSFERFUNCTION	301	/* !colorimetry info */
+#define	TIFFTAG_SOFTWARE		305	/* name & release */
+#define	TIFFTAG_DATETIME		306	/* creation date and time */
+#define	TIFFTAG_ARTIST			315	/* creator of image */
+#define	TIFFTAG_HOSTCOMPUTER		316	/* machine where created */
+#define	TIFFTAG_PREDICTOR		317	/* prediction scheme w/ LZW */
+#define     PREDICTOR_NONE		1	/* no prediction scheme used */
+#define     PREDICTOR_HORIZONTAL	2	/* horizontal differencing */
+#define     PREDICTOR_FLOATINGPOINT	3	/* floating point predictor */
+#define	TIFFTAG_WHITEPOINT		318	/* image white point */
+#define	TIFFTAG_PRIMARYCHROMATICITIES	319	/* !primary chromaticities */
+#define	TIFFTAG_COLORMAP		320	/* RGB map for pallette image */
+#define	TIFFTAG_HALFTONEHINTS		321	/* !highlight+shadow info */
+#define	TIFFTAG_TILEWIDTH		322	/* !tile width in pixels */
+#define	TIFFTAG_TILELENGTH		323	/* !tile height in pixels */
+#define TIFFTAG_TILEOFFSETS		324	/* !offsets to data tiles */
+#define TIFFTAG_TILEBYTECOUNTS		325	/* !byte counts for tiles */
+#define	TIFFTAG_BADFAXLINES		326	/* lines w/ wrong pixel count */
+#define	TIFFTAG_CLEANFAXDATA		327	/* regenerated line info */
+#define	    CLEANFAXDATA_CLEAN		0	/* no errors detected */
+#define	    CLEANFAXDATA_REGENERATED	1	/* receiver regenerated lines */
+#define	    CLEANFAXDATA_UNCLEAN	2	/* uncorrected errors exist */
+#define	TIFFTAG_CONSECUTIVEBADFAXLINES	328	/* max consecutive bad lines */
+#define	TIFFTAG_SUBIFD			330	/* subimage descriptors */
+#define	TIFFTAG_INKSET			332	/* !inks in separated image */
+#define	    INKSET_CMYK			1	/* !cyan-magenta-yellow-black color */
+#define	    INKSET_MULTIINK		2	/* !multi-ink or hi-fi color */
+#define	TIFFTAG_INKNAMES		333	/* !ascii names of inks */
+#define	TIFFTAG_NUMBEROFINKS		334	/* !number of inks */
+#define	TIFFTAG_DOTRANGE		336	/* !0% and 100% dot codes */
+#define	TIFFTAG_TARGETPRINTER		337	/* !separation target */
+#define	TIFFTAG_EXTRASAMPLES		338	/* !info about extra samples */
+#define	    EXTRASAMPLE_UNSPECIFIED	0	/* !unspecified data */
+#define	    EXTRASAMPLE_ASSOCALPHA	1	/* !associated alpha data */
+#define	    EXTRASAMPLE_UNASSALPHA	2	/* !unassociated alpha data */
+#define	TIFFTAG_SAMPLEFORMAT		339	/* !data sample format */
+#define	    SAMPLEFORMAT_UINT		1	/* !unsigned integer data */
+#define	    SAMPLEFORMAT_INT		2	/* !signed integer data */
+#define	    SAMPLEFORMAT_IEEEFP		3	/* !IEEE floating point data */
+#define	    SAMPLEFORMAT_VOID		4	/* !untyped data */
+#define	    SAMPLEFORMAT_COMPLEXINT	5	/* !complex signed int */
+#define	    SAMPLEFORMAT_COMPLEXIEEEFP	6	/* !complex ieee floating */
+#define	TIFFTAG_SMINSAMPLEVALUE		340	/* !variable MinSampleValue */
+#define	TIFFTAG_SMAXSAMPLEVALUE		341	/* !variable MaxSampleValue */
+#define	TIFFTAG_CLIPPATH		343	/* %ClipPath
+						   [Adobe TIFF technote 2] */
+#define	TIFFTAG_XCLIPPATHUNITS		344	/* %XClipPathUnits
+						   [Adobe TIFF technote 2] */
+#define	TIFFTAG_YCLIPPATHUNITS		345	/* %YClipPathUnits
+						   [Adobe TIFF technote 2] */
+#define	TIFFTAG_INDEXED			346	/* %Indexed
+						   [Adobe TIFF Technote 3] */
+#define	TIFFTAG_JPEGTABLES		347	/* %JPEG table stream */
+#define	TIFFTAG_OPIPROXY		351	/* %OPI Proxy [Adobe TIFF technote] */
+/*
+ * Tags 512-521 are obsoleted by Technical Note #2 which specifies a
+ * revised JPEG-in-TIFF scheme.
+ */
+#define	TIFFTAG_JPEGPROC		512	/* !JPEG processing algorithm */
+#define	    JPEGPROC_BASELINE		1	/* !baseline sequential */
+#define	    JPEGPROC_LOSSLESS		14	/* !Huffman coded lossless */
+#define	TIFFTAG_JPEGIFOFFSET		513	/* !pointer to SOI marker */
+#define	TIFFTAG_JPEGIFBYTECOUNT		514	/* !JFIF stream length */
+#define	TIFFTAG_JPEGRESTARTINTERVAL	515	/* !restart interval length */
+#define	TIFFTAG_JPEGLOSSLESSPREDICTORS	517	/* !lossless proc predictor */
+#define	TIFFTAG_JPEGPOINTTRANSFORM	518	/* !lossless point transform */
+#define	TIFFTAG_JPEGQTABLES		519	/* !Q matrice offsets */
+#define	TIFFTAG_JPEGDCTABLES		520	/* !DCT table offsets */
+#define	TIFFTAG_JPEGACTABLES		521	/* !AC coefficient offsets */
+#define	TIFFTAG_YCBCRCOEFFICIENTS	529	/* !RGB -> YCbCr transform */
+#define	TIFFTAG_YCBCRSUBSAMPLING	530	/* !YCbCr subsampling factors */
+#define	TIFFTAG_YCBCRPOSITIONING	531	/* !subsample positioning */
+#define	    YCBCRPOSITION_CENTERED	1	/* !as in PostScript Level 2 */
+#define	    YCBCRPOSITION_COSITED	2	/* !as in CCIR 601-1 */
+#define	TIFFTAG_REFERENCEBLACKWHITE	532	/* !colorimetry info */
+#define	TIFFTAG_XMLPACKET		700	/* %XML packet
+						   [Adobe XMP Specification,
+						   January 2004 */
+#define TIFFTAG_OPIIMAGEID		32781	/* %OPI ImageID
+						   [Adobe TIFF technote] */
+/* tags 32952-32956 are private tags registered to Island Graphics */
+#define TIFFTAG_REFPTS			32953	/* image reference points */
+#define TIFFTAG_REGIONTACKPOINT		32954	/* region-xform tack point */
+#define TIFFTAG_REGIONWARPCORNERS	32955	/* warp quadrilateral */
+#define TIFFTAG_REGIONAFFINE		32956	/* affine transformation mat */
+/* tags 32995-32999 are private tags registered to SGI */
+#define	TIFFTAG_MATTEING		32995	/* $use ExtraSamples */
+#define	TIFFTAG_DATATYPE		32996	/* $use SampleFormat */
+#define	TIFFTAG_IMAGEDEPTH		32997	/* z depth of image */
+#define	TIFFTAG_TILEDEPTH		32998	/* z depth/data tile */
+/* tags 33300-33309 are private tags registered to Pixar */
+/*
+ * TIFFTAG_PIXAR_IMAGEFULLWIDTH and TIFFTAG_PIXAR_IMAGEFULLLENGTH
+ * are set when an image has been cropped out of a larger image.  
+ * They reflect the size of the original uncropped image.
+ * The TIFFTAG_XPOSITION and TIFFTAG_YPOSITION can be used
+ * to determine the position of the smaller image in the larger one.
+ */
+#define TIFFTAG_PIXAR_IMAGEFULLWIDTH    33300   /* full image size in x */
+#define TIFFTAG_PIXAR_IMAGEFULLLENGTH   33301   /* full image size in y */
+ /* Tags 33302-33306 are used to identify special image modes and data
+  * used by Pixar's texture formats.
+  */
+#define TIFFTAG_PIXAR_TEXTUREFORMAT	33302	/* texture map format */
+#define TIFFTAG_PIXAR_WRAPMODES		33303	/* s & t wrap modes */
+#define TIFFTAG_PIXAR_FOVCOT		33304	/* cotan(fov) for env. maps */
+#define TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN 33305
+#define TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA 33306
+/* tag 33405 is a private tag registered to Eastman Kodak */
+#define TIFFTAG_WRITERSERIALNUMBER      33405   /* device serial number */
+/* tag 33432 is listed in the 6.0 spec w/ unknown ownership */
+#define	TIFFTAG_COPYRIGHT		33432	/* copyright string */
+/* IPTC TAG from RichTIFF specifications */
+#define TIFFTAG_RICHTIFFIPTC		33723
+/* 34016-34029 are reserved for ANSI IT8 TIFF/IT <dkelly@apago.com) */
+#define TIFFTAG_IT8SITE			34016	/* site name */
+#define TIFFTAG_IT8COLORSEQUENCE	34017	/* color seq. [RGB,CMYK,etc] */
+#define TIFFTAG_IT8HEADER		34018	/* DDES Header */
+#define TIFFTAG_IT8RASTERPADDING	34019	/* raster scanline padding */
+#define TIFFTAG_IT8BITSPERRUNLENGTH	34020	/* # of bits in short run */
+#define TIFFTAG_IT8BITSPEREXTENDEDRUNLENGTH 34021/* # of bits in long run */
+#define TIFFTAG_IT8COLORTABLE		34022	/* LW colortable */
+#define TIFFTAG_IT8IMAGECOLORINDICATOR	34023	/* BP/BL image color switch */
+#define TIFFTAG_IT8BKGCOLORINDICATOR	34024	/* BP/BL bg color switch */
+#define TIFFTAG_IT8IMAGECOLORVALUE	34025	/* BP/BL image color value */
+#define TIFFTAG_IT8BKGCOLORVALUE	34026	/* BP/BL bg color value */
+#define TIFFTAG_IT8PIXELINTENSITYRANGE	34027	/* MP pixel intensity value */
+#define TIFFTAG_IT8TRANSPARENCYINDICATOR 34028	/* HC transparency switch */
+#define TIFFTAG_IT8COLORCHARACTERIZATION 34029	/* color character. table */
+#define TIFFTAG_IT8HCUSAGE		34030	/* HC usage indicator */
+#define TIFFTAG_IT8TRAPINDICATOR	34031	/* Trapping indicator
+						   (untrapped=0, trapped=1) */
+#define TIFFTAG_IT8CMYKEQUIVALENT	34032	/* CMYK color equivalents */
+/* tags 34232-34236 are private tags registered to Texas Instruments */
+#define TIFFTAG_FRAMECOUNT              34232   /* Sequence Frame Count */
+/* tag 34377 is private tag registered to Adobe for PhotoShop */
+#define TIFFTAG_PHOTOSHOP		34377 
+/* tags 34665, 34853 and 40965 are documented in EXIF specification */
+#define TIFFTAG_EXIFIFD			34665	/* Pointer to EXIF private directory */
+/* tag 34750 is a private tag registered to Adobe? */
+#define TIFFTAG_ICCPROFILE		34675	/* ICC profile data */
+/* tag 34750 is a private tag registered to Pixel Magic */
+#define	TIFFTAG_JBIGOPTIONS		34750	/* JBIG options */
+#define TIFFTAG_GPSIFD			34853	/* Pointer to GPS private directory */
+/* tags 34908-34914 are private tags registered to SGI */
+#define	TIFFTAG_FAXRECVPARAMS		34908	/* encoded Class 2 ses. parms */
+#define	TIFFTAG_FAXSUBADDRESS		34909	/* received SubAddr string */
+#define	TIFFTAG_FAXRECVTIME		34910	/* receive time (secs) */
+#define	TIFFTAG_FAXDCS			34911	/* encoded fax ses. params, Table 2/T.30 */
+/* tags 37439-37443 are registered to SGI <gregl@sgi.com> */
+#define TIFFTAG_STONITS			37439	/* Sample value to Nits */
+/* tag 34929 is a private tag registered to FedEx */
+#define	TIFFTAG_FEDEX_EDR		34929	/* unknown use */
+#define TIFFTAG_INTEROPERABILITYIFD	40965	/* Pointer to Interoperability private directory */
+/* Adobe Digital Negative (DNG) format tags */
+#define TIFFTAG_DNGVERSION		50706	/* &DNG version number */
+#define TIFFTAG_DNGBACKWARDVERSION	50707	/* &DNG compatibility version */
+#define TIFFTAG_UNIQUECAMERAMODEL	50708	/* &name for the camera model */
+#define TIFFTAG_LOCALIZEDCAMERAMODEL	50709	/* &localized camera model
+						   name */
+#define TIFFTAG_CFAPLANECOLOR		50710	/* &CFAPattern->LinearRaw space
+						   mapping */
+#define TIFFTAG_CFALAYOUT		50711	/* &spatial layout of the CFA */
+#define TIFFTAG_LINEARIZATIONTABLE	50712	/* &lookup table description */
+#define TIFFTAG_BLACKLEVELREPEATDIM	50713	/* &repeat pattern size for
+						   the BlackLevel tag */
+#define TIFFTAG_BLACKLEVEL		50714	/* &zero light encoding level */
+#define TIFFTAG_BLACKLEVELDELTAH	50715	/* &zero light encoding level
+						   differences (columns) */
+#define TIFFTAG_BLACKLEVELDELTAV	50716	/* &zero light encoding level
+						   differences (rows) */
+#define TIFFTAG_WHITELEVEL		50717	/* &fully saturated encoding
+						   level */
+#define TIFFTAG_DEFAULTSCALE		50718	/* &default scale factors */
+#define TIFFTAG_DEFAULTCROPORIGIN	50719	/* &origin of the final image
+						   area */
+#define TIFFTAG_DEFAULTCROPSIZE		50720	/* &size of the final image 
+						   area */
+#define TIFFTAG_COLORMATRIX1		50721	/* &XYZ->reference color space
+						   transformation matrix 1 */
+#define TIFFTAG_COLORMATRIX2		50722	/* &XYZ->reference color space
+						   transformation matrix 2 */
+#define TIFFTAG_CAMERACALIBRATION1	50723	/* &calibration matrix 1 */
+#define TIFFTAG_CAMERACALIBRATION2	50724	/* &calibration matrix 2 */
+#define TIFFTAG_REDUCTIONMATRIX1	50725	/* &dimensionality reduction
+						   matrix 1 */
+#define TIFFTAG_REDUCTIONMATRIX2	50726	/* &dimensionality reduction
+						   matrix 2 */
+#define TIFFTAG_ANALOGBALANCE		50727	/* &gain applied the stored raw
+						   values*/
+#define TIFFTAG_ASSHOTNEUTRAL		50728	/* &selected white balance in
+						   linear reference space */
+#define TIFFTAG_ASSHOTWHITEXY		50729	/* &selected white balance in
+						   x-y chromaticity
+						   coordinates */
+#define TIFFTAG_BASELINEEXPOSURE	50730	/* &how much to move the zero
+						   point */
+#define TIFFTAG_BASELINENOISE		50731	/* &relative noise level */
+#define TIFFTAG_BASELINESHARPNESS	50732	/* &relative amount of
+						   sharpening */
+#define TIFFTAG_BAYERGREENSPLIT		50733	/* &how closely the values of
+						   the green pixels in the
+						   blue/green rows track the
+						   values of the green pixels
+						   in the red/green rows */
+#define TIFFTAG_LINEARRESPONSELIMIT	50734	/* &non-linear encoding range */
+#define TIFFTAG_CAMERASERIALNUMBER	50735	/* &camera's serial number */
+#define TIFFTAG_LENSINFO		50736	/* info about the lens */
+#define TIFFTAG_CHROMABLURRADIUS	50737	/* &chroma blur radius */
+#define TIFFTAG_ANTIALIASSTRENGTH	50738	/* &relative strength of the
+						   camera's anti-alias filter */
+#define TIFFTAG_SHADOWSCALE		50739	/* &used by Adobe Camera Raw */
+#define TIFFTAG_DNGPRIVATEDATA		50740	/* &manufacturer's private data */
+#define TIFFTAG_MAKERNOTESAFETY		50741	/* &whether the EXIF MakerNote
+						   tag is safe to preserve
+						   along with the rest of the
+						   EXIF data */
+#define	TIFFTAG_CALIBRATIONILLUMINANT1	50778	/* &illuminant 1 */
+#define TIFFTAG_CALIBRATIONILLUMINANT2	50779	/* &illuminant 2 */
+#define TIFFTAG_BESTQUALITYSCALE	50780	/* &best quality multiplier */
+#define TIFFTAG_RAWDATAUNIQUEID		50781	/* &unique identifier for
+						   the raw image data */
+#define TIFFTAG_ORIGINALRAWFILENAME	50827	/* &file name of the original
+						   raw file */
+#define TIFFTAG_ORIGINALRAWFILEDATA	50828	/* &contents of the original
+						   raw file */
+#define TIFFTAG_ACTIVEAREA		50829	/* &active (non-masked) pixels
+						   of the sensor */
+#define TIFFTAG_MASKEDAREAS		50830	/* &list of coordinates
+						   of fully masked pixels */
+#define TIFFTAG_ASSHOTICCPROFILE	50831	/* &these two tags used to */
+#define TIFFTAG_ASSHOTPREPROFILEMATRIX	50832	/* map cameras's color space
+						   into ICC profile space */
+#define TIFFTAG_CURRENTICCPROFILE	50833	/* & */
+#define TIFFTAG_CURRENTPREPROFILEMATRIX	50834	/* & */
+/* tag 65535 is an undefined tag used by Eastman Kodak */
+#define TIFFTAG_DCSHUESHIFTVALUES       65535   /* hue shift correction data */
+
+/*
+ * The following are ``pseudo tags'' that can be used to control
+ * codec-specific functionality.  These tags are not written to file.
+ * Note that these values start at 0xffff+1 so that they'll never
+ * collide with Aldus-assigned tags.
+ *
+ * If you want your private pseudo tags ``registered'' (i.e. added to
+ * this file), please post a bug report via the tracking system at
+ * http://www.remotesensing.org/libtiff/bugs.html with the appropriate
+ * C definitions to add.
+ */
+#define	TIFFTAG_FAXMODE			65536	/* Group 3/4 format control */
+#define	    FAXMODE_CLASSIC	0x0000		/* default, include RTC */
+#define	    FAXMODE_NORTC	0x0001		/* no RTC at end of data */
+#define	    FAXMODE_NOEOL	0x0002		/* no EOL code at end of row */
+#define	    FAXMODE_BYTEALIGN	0x0004		/* byte align row */
+#define	    FAXMODE_WORDALIGN	0x0008		/* word align row */
+#define	    FAXMODE_CLASSF	FAXMODE_NORTC	/* TIFF Class F */
+#define	TIFFTAG_JPEGQUALITY		65537	/* Compression quality level */
+/* Note: quality level is on the IJG 0-100 scale.  Default value is 75 */
+#define	TIFFTAG_JPEGCOLORMODE		65538	/* Auto RGB<=>YCbCr convert? */
+#define	    JPEGCOLORMODE_RAW	0x0000		/* no conversion (default) */
+#define	    JPEGCOLORMODE_RGB	0x0001		/* do auto conversion */
+#define	TIFFTAG_JPEGTABLESMODE		65539	/* What to put in JPEGTables */
+#define	    JPEGTABLESMODE_QUANT 0x0001		/* include quantization tbls */
+#define	    JPEGTABLESMODE_HUFF	0x0002		/* include Huffman tbls */
+/* Note: default is JPEGTABLESMODE_QUANT | JPEGTABLESMODE_HUFF */
+#define	TIFFTAG_FAXFILLFUNC		65540	/* G3/G4 fill function */
+#define	TIFFTAG_PIXARLOGDATAFMT		65549	/* PixarLogCodec I/O data sz */
+#define	    PIXARLOGDATAFMT_8BIT	0	/* regular u_char samples */
+#define	    PIXARLOGDATAFMT_8BITABGR	1	/* ABGR-order u_chars */
+#define	    PIXARLOGDATAFMT_11BITLOG	2	/* 11-bit log-encoded (raw) */
+#define	    PIXARLOGDATAFMT_12BITPICIO	3	/* as per PICIO (1.0==2048) */
+#define	    PIXARLOGDATAFMT_16BIT	4	/* signed short samples */
+#define	    PIXARLOGDATAFMT_FLOAT	5	/* IEEE float samples */
+/* 65550-65556 are allocated to Oceana Matrix <dev@oceana.com> */
+#define TIFFTAG_DCSIMAGERTYPE           65550   /* imager model & filter */
+#define     DCSIMAGERMODEL_M3           0       /* M3 chip (1280 x 1024) */
+#define     DCSIMAGERMODEL_M5           1       /* M5 chip (1536 x 1024) */
+#define     DCSIMAGERMODEL_M6           2       /* M6 chip (3072 x 2048) */
+#define     DCSIMAGERFILTER_IR          0       /* infrared filter */
+#define     DCSIMAGERFILTER_MONO        1       /* monochrome filter */
+#define     DCSIMAGERFILTER_CFA         2       /* color filter array */
+#define     DCSIMAGERFILTER_OTHER       3       /* other filter */
+#define TIFFTAG_DCSINTERPMODE           65551   /* interpolation mode */
+#define     DCSINTERPMODE_NORMAL        0x0     /* whole image, default */
+#define     DCSINTERPMODE_PREVIEW       0x1     /* preview of image (384x256) */
+#define TIFFTAG_DCSBALANCEARRAY         65552   /* color balance values */
+#define TIFFTAG_DCSCORRECTMATRIX        65553   /* color correction values */
+#define TIFFTAG_DCSGAMMA                65554   /* gamma value */
+#define TIFFTAG_DCSTOESHOULDERPTS       65555   /* toe & shoulder points */
+#define TIFFTAG_DCSCALIBRATIONFD        65556   /* calibration file desc */
+/* Note: quality level is on the ZLIB 1-9 scale. Default value is -1 */
+#define	TIFFTAG_ZIPQUALITY		65557	/* compression quality level */
+#define	TIFFTAG_PIXARLOGQUALITY		65558	/* PixarLog uses same scale */
+/* 65559 is allocated to Oceana Matrix <dev@oceana.com> */
+#define TIFFTAG_DCSCLIPRECTANGLE	65559	/* area of image to acquire */
+#define TIFFTAG_SGILOGDATAFMT		65560	/* SGILog user data format */
+#define     SGILOGDATAFMT_FLOAT		0	/* IEEE float samples */
+#define     SGILOGDATAFMT_16BIT		1	/* 16-bit samples */
+#define     SGILOGDATAFMT_RAW		2	/* uninterpreted data */
+#define     SGILOGDATAFMT_8BIT		3	/* 8-bit RGB monitor values */
+#define TIFFTAG_SGILOGENCODE		65561 /* SGILog data encoding control*/
+#define     SGILOGENCODE_NODITHER	0     /* do not dither encoded values*/
+#define     SGILOGENCODE_RANDITHER	1     /* randomly dither encd values */
+
+/*
+ * EXIF tags
+ */
+#define EXIFTAG_EXPOSURETIME		33434	/* Exposure time */
+#define EXIFTAG_FNUMBER			33437	/* F number */
+#define EXIFTAG_EXPOSUREPROGRAM		34850	/* Exposure program */
+#define EXIFTAG_SPECTRALSENSITIVITY	34852	/* Spectral sensitivity */
+#define EXIFTAG_ISOSPEEDRATINGS		34855	/* ISO speed rating */
+#define EXIFTAG_OECF			34856	/* Optoelectric conversion
+						   factor */
+#define EXIFTAG_EXIFVERSION		36864	/* Exif version */
+#define EXIFTAG_DATETIMEORIGINAL	36867	/* Date and time of original
+						   data generation */
+#define EXIFTAG_DATETIMEDIGITIZED	36868	/* Date and time of digital
+						   data generation */
+#define EXIFTAG_COMPONENTSCONFIGURATION	37121	/* Meaning of each component */
+#define EXIFTAG_COMPRESSEDBITSPERPIXEL	37122	/* Image compression mode */
+#define EXIFTAG_SHUTTERSPEEDVALUE	37377	/* Shutter speed */
+#define EXIFTAG_APERTUREVALUE		37378	/* Aperture */
+#define EXIFTAG_BRIGHTNESSVALUE		37379	/* Brightness */
+#define EXIFTAG_EXPOSUREBIASVALUE	37380	/* Exposure bias */
+#define EXIFTAG_MAXAPERTUREVALUE	37381	/* Maximum lens aperture */
+#define EXIFTAG_SUBJECTDISTANCE		37382	/* Subject distance */
+#define EXIFTAG_METERINGMODE		37383	/* Metering mode */
+#define EXIFTAG_LIGHTSOURCE		37384	/* Light source */
+#define EXIFTAG_FLASH			37385	/* Flash */
+#define EXIFTAG_FOCALLENGTH		37386	/* Lens focal length */
+#define EXIFTAG_SUBJECTAREA		37396	/* Subject area */
+#define EXIFTAG_MAKERNOTE		37500	/* Manufacturer notes */
+#define EXIFTAG_USERCOMMENT		37510	/* User comments */
+#define EXIFTAG_SUBSECTIME		37520	/* DateTime subseconds */
+#define EXIFTAG_SUBSECTIMEORIGINAL	37521	/* DateTimeOriginal subseconds */
+#define EXIFTAG_SUBSECTIMEDIGITIZED	37522	/* DateTimeDigitized subseconds */
+#define EXIFTAG_FLASHPIXVERSION		40960	/* Supported Flashpix version */
+#define EXIFTAG_COLORSPACE		40961	/* Color space information */
+#define EXIFTAG_PIXELXDIMENSION		40962	/* Valid image width */
+#define EXIFTAG_PIXELYDIMENSION		40963	/* Valid image height */
+#define EXIFTAG_RELATEDSOUNDFILE	40964	/* Related audio file */
+#define EXIFTAG_FLASHENERGY		41483	/* Flash energy */
+#define EXIFTAG_SPATIALFREQUENCYRESPONSE 41484	/* Spatial frequency response */
+#define EXIFTAG_FOCALPLANEXRESOLUTION	41486	/* Focal plane X resolution */
+#define EXIFTAG_FOCALPLANEYRESOLUTION	41487	/* Focal plane Y resolution */
+#define EXIFTAG_FOCALPLANERESOLUTIONUNIT 41488	/* Focal plane resolution unit */
+#define EXIFTAG_SUBJECTLOCATION		41492	/* Subject location */
+#define EXIFTAG_EXPOSUREINDEX		41493	/* Exposure index */
+#define EXIFTAG_SENSINGMETHOD		41495	/* Sensing method */
+#define EXIFTAG_FILESOURCE		41728	/* File source */
+#define EXIFTAG_SCENETYPE		41729	/* Scene type */
+#define EXIFTAG_CFAPATTERN		41730	/* CFA pattern */
+#define EXIFTAG_CUSTOMRENDERED		41985	/* Custom image processing */
+#define EXIFTAG_EXPOSUREMODE		41986	/* Exposure mode */
+#define EXIFTAG_WHITEBALANCE		41987	/* White balance */
+#define EXIFTAG_DIGITALZOOMRATIO	41988	/* Digital zoom ratio */
+#define EXIFTAG_FOCALLENGTHIN35MMFILM	41989	/* Focal length in 35 mm film */
+#define EXIFTAG_SCENECAPTURETYPE	41990	/* Scene capture type */
+#define EXIFTAG_GAINCONTROL		41991	/* Gain control */
+#define EXIFTAG_CONTRAST		41992	/* Contrast */
+#define EXIFTAG_SATURATION		41993	/* Saturation */
+#define EXIFTAG_SHARPNESS		41994	/* Sharpness */
+#define EXIFTAG_DEVICESETTINGDESCRIPTION 41995	/* Device settings description */
+#define EXIFTAG_SUBJECTDISTANCERANGE	41996	/* Subject distance range */
+#define EXIFTAG_GAINCONTROL		41991	/* Gain control */
+#define EXIFTAG_GAINCONTROL		41991	/* Gain control */
+#define EXIFTAG_IMAGEUNIQUEID		42016	/* Unique image ID */
+
+#endif /* _TIFF_ */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tiffconf.h b/Utilities/GDAL/frmts/gtiff/libtiff/tiffconf.h
new file mode 100644
index 0000000000..f56582ee70
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tiffconf.h
@@ -0,0 +1,3 @@
+/* I'm not sure why tiff.h includes this instead of tif_config.h */
+
+#include "tif_config.h"
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tiffio.h b/Utilities/GDAL/frmts/gtiff/libtiff/tiffio.h
new file mode 100644
index 0000000000..5b2489dadd
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tiffio.h
@@ -0,0 +1,520 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _TIFFIO_
+#define	_TIFFIO_
+
+/*
+ * TIFF I/O Library Definitions.
+ */
+#include "tiff.h"
+#include "tiffvers.h"
+
+/*
+ * TIFF is defined as an incomplete type to hide the
+ * library's internal data structures from clients.
+ */
+typedef	struct tiff TIFF;
+
+/*
+ * The following typedefs define the intrinsic size of
+ * data types used in the *exported* interfaces.  These
+ * definitions depend on the proper definition of types
+ * in tiff.h.  Note also that the varargs interface used
+ * to pass tag types and values uses the types defined in
+ * tiff.h directly.
+ *
+ * NB: ttag_t is unsigned int and not unsigned short because
+ *     ANSI C requires that the type before the ellipsis be a
+ *     promoted type (i.e. one of int, unsigned int, pointer,
+ *     or double) and because we defined pseudo-tags that are
+ *     outside the range of legal Aldus-assigned tags.
+ * NB: tsize_t is int32 and not uint32 because some functions
+ *     return -1.
+ * NB: toff_t is not off_t for many reasons; TIFFs max out at
+ *     32-bit file offsets being the most important, and to ensure
+ *     that it is unsigned, rather than signed.
+ */
+typedef uint32 ttag_t;          /* directory tag */
+typedef uint16 tdir_t;          /* directory index */
+typedef uint16 tsample_t;       /* sample number */
+typedef uint32 tstrile_t;       /* strip or tile number */
+typedef tstrile_t tstrip_t;     /* strip number */
+typedef tstrile_t ttile_t;      /* tile number */
+typedef int32 tsize_t;          /* i/o size in bytes */
+typedef void* tdata_t;          /* image data ref */
+typedef uint32 toff_t;          /* file offset */
+
+#if !defined(__WIN32__) && (defined(_WIN32) || defined(WIN32))
+#define __WIN32__
+#endif
+
+/*
+ * On windows you should define USE_WIN32_FILEIO if you are using tif_win32.c
+ * or AVOID_WIN32_FILEIO if you are using something else (like tif_unix.c).
+ *
+ * By default tif_unix.c is assumed.
+ */
+
+#if defined(_WINDOWS) || defined(__WIN32__) || defined(_Windows)
+#  if !defined(__CYGWIN) && !defined(AVOID_WIN32_FILEIO) && !defined(USE_WIN32_FILEIO)
+#    define AVOID_WIN32_FILEIO
+#  endif
+#endif
+
+#if defined(USE_WIN32_FILEIO)
+# define VC_EXTRALEAN
+# include <windows.h>
+# ifdef __WIN32__
+DECLARE_HANDLE(thandle_t);	/* Win32 file handle */
+# else
+typedef	HFILE thandle_t;	/* client data handle */
+# endif /* __WIN32__ */
+#else
+typedef	void* thandle_t;	/* client data handle */
+#endif /* USE_WIN32_FILEIO */
+
+#ifndef NULL
+# define NULL	0
+#endif
+
+/*
+ * Flags to pass to TIFFPrintDirectory to control
+ * printing of data structures that are potentially
+ * very large.   Bit-or these flags to enable printing
+ * multiple items.
+ */
+#define	TIFFPRINT_NONE		0x0		/* no extra info */
+#define	TIFFPRINT_STRIPS	0x1		/* strips/tiles info */
+#define	TIFFPRINT_CURVES	0x2		/* color/gray response curves */
+#define	TIFFPRINT_COLORMAP	0x4		/* colormap */
+#define	TIFFPRINT_JPEGQTABLES	0x100		/* JPEG Q matrices */
+#define	TIFFPRINT_JPEGACTABLES	0x200		/* JPEG AC tables */
+#define	TIFFPRINT_JPEGDCTABLES	0x200		/* JPEG DC tables */
+
+/* 
+ * Colour conversion stuff
+ */
+
+/* reference white */
+#define D65_X0 (95.0470F)
+#define D65_Y0 (100.0F)
+#define D65_Z0 (108.8827F)
+
+#define D50_X0 (96.4250F)
+#define D50_Y0 (100.0F)
+#define D50_Z0 (82.4680F)
+
+/* Structure for holding information about a display device. */
+
+typedef	unsigned char TIFFRGBValue;		/* 8-bit samples */
+
+typedef struct {
+	float d_mat[3][3]; 		/* XYZ -> luminance matrix */
+	float d_YCR;			/* Light o/p for reference white */
+	float d_YCG;
+	float d_YCB;
+	uint32 d_Vrwr;			/* Pixel values for ref. white */
+	uint32 d_Vrwg;
+	uint32 d_Vrwb;
+	float d_Y0R;			/* Residual light for black pixel */
+	float d_Y0G;
+	float d_Y0B;
+	float d_gammaR;			/* Gamma values for the three guns */
+	float d_gammaG;
+	float d_gammaB;
+} TIFFDisplay;
+
+typedef struct {				/* YCbCr->RGB support */
+	TIFFRGBValue* clamptab;			/* range clamping table */
+	int*	Cr_r_tab;
+	int*	Cb_b_tab;
+	int32*	Cr_g_tab;
+	int32*	Cb_g_tab;
+        int32*  Y_tab;
+} TIFFYCbCrToRGB;
+
+typedef struct {				/* CIE Lab 1976->RGB support */
+	int	range;				/* Size of conversion table */
+#define CIELABTORGB_TABLE_RANGE 1500
+	float	rstep, gstep, bstep;
+	float	X0, Y0, Z0;			/* Reference white point */
+	TIFFDisplay display;
+	float	Yr2r[CIELABTORGB_TABLE_RANGE + 1];  /* Conversion of Yr to r */
+	float	Yg2g[CIELABTORGB_TABLE_RANGE + 1];  /* Conversion of Yg to g */
+	float	Yb2b[CIELABTORGB_TABLE_RANGE + 1];  /* Conversion of Yb to b */
+} TIFFCIELabToRGB;
+
+/*
+ * RGBA-style image support.
+ */
+typedef struct _TIFFRGBAImage TIFFRGBAImage;
+/*
+ * The image reading and conversion routines invoke
+ * ``put routines'' to copy/image/whatever tiles of
+ * raw image data.  A default set of routines are 
+ * provided to convert/copy raw image data to 8-bit
+ * packed ABGR format rasters.  Applications can supply
+ * alternate routines that unpack the data into a
+ * different format or, for example, unpack the data
+ * and draw the unpacked raster on the display.
+ */
+typedef void (*tileContigRoutine)
+    (TIFFRGBAImage*, uint32*, uint32, uint32, uint32, uint32, int32, int32,
+	unsigned char*);
+typedef void (*tileSeparateRoutine)
+    (TIFFRGBAImage*, uint32*, uint32, uint32, uint32, uint32, int32, int32,
+	unsigned char*, unsigned char*, unsigned char*, unsigned char*);
+/*
+ * RGBA-reader state.
+ */
+struct _TIFFRGBAImage {
+	TIFF*	tif;				/* image handle */
+	int	stoponerr;			/* stop on read error */
+	int	isContig;			/* data is packed/separate */
+	int	alpha;				/* type of alpha data present */
+	uint32	width;				/* image width */
+	uint32	height;				/* image height */
+	uint16	bitspersample;			/* image bits/sample */
+	uint16	samplesperpixel;		/* image samples/pixel */
+	uint16	orientation;			/* image orientation */
+	uint16	req_orientation;		/* requested orientation */
+	uint16	photometric;			/* image photometric interp */
+	uint16*	redcmap;			/* colormap pallete */
+	uint16*	greencmap;
+	uint16*	bluecmap;
+						/* get image data routine */
+	int	(*get)(TIFFRGBAImage*, uint32*, uint32, uint32);
+	union {
+	    void (*any)(TIFFRGBAImage*);
+	    tileContigRoutine	contig;
+	    tileSeparateRoutine	separate;
+	} put;					/* put decoded strip/tile */
+	TIFFRGBValue* Map;			/* sample mapping array */
+	uint32** BWmap;				/* black&white map */
+	uint32** PALmap;			/* palette image map */
+	TIFFYCbCrToRGB* ycbcr;			/* YCbCr conversion state */
+	TIFFCIELabToRGB* cielab;		/* CIE L*a*b conversion state */
+
+	uint8* UaToAa;                          /* Unassociated alpha to associated alpha convertion LUT */
+	uint8* Bitdepth16To8;                   /* LUT for conversion from 16bit to 8bit values */
+
+        int	row_offset;
+        int     col_offset;
+};
+
+/*
+ * Macros for extracting components from the
+ * packed ABGR form returned by TIFFReadRGBAImage.
+ */
+#define	TIFFGetR(abgr)	((abgr) & 0xff)
+#define	TIFFGetG(abgr)	(((abgr) >> 8) & 0xff)
+#define	TIFFGetB(abgr)	(((abgr) >> 16) & 0xff)
+#define	TIFFGetA(abgr)	(((abgr) >> 24) & 0xff)
+
+/*
+ * A CODEC is a software package that implements decoding,
+ * encoding, or decoding+encoding of a compression algorithm.
+ * The library provides a collection of builtin codecs.
+ * More codecs may be registered through calls to the library
+ * and/or the builtin implementations may be overridden.
+ */
+typedef	int (*TIFFInitMethod)(TIFF*, int);
+typedef struct {
+	char*		name;
+	uint16		scheme;
+	TIFFInitMethod	init;
+} TIFFCodec;
+
+#include <stdio.h>
+#include <stdarg.h>
+
+/* share internal LogLuv conversion routines? */
+#ifndef LOGLUV_PUBLIC
+#define LOGLUV_PUBLIC		1
+#endif
+
+#if defined(c_plusplus) || defined(__cplusplus)
+extern "C" {
+#endif
+typedef	void (*TIFFErrorHandler)(const char*, const char*, va_list);
+typedef	void (*TIFFErrorHandlerExt)(thandle_t, const char*, const char*, va_list);
+typedef	tsize_t (*TIFFReadWriteProc)(thandle_t, tdata_t, tsize_t);
+typedef	toff_t (*TIFFSeekProc)(thandle_t, toff_t, int);
+typedef	int (*TIFFCloseProc)(thandle_t);
+typedef	toff_t (*TIFFSizeProc)(thandle_t);
+typedef	int (*TIFFMapFileProc)(thandle_t, tdata_t*, toff_t*);
+typedef	void (*TIFFUnmapFileProc)(thandle_t, tdata_t, toff_t);
+typedef	void (*TIFFExtendProc)(TIFF*); 
+
+extern	const char* TIFFGetVersion(void);
+
+extern	const TIFFCodec* TIFFFindCODEC(uint16);
+extern	TIFFCodec* TIFFRegisterCODEC(uint16, const char*, TIFFInitMethod);
+extern	void TIFFUnRegisterCODEC(TIFFCodec*);
+extern  int TIFFIsCODECConfigured(uint16);
+extern	TIFFCodec* TIFFGetConfiguredCODECs(void);
+
+/*
+ * Auxiliary functions.
+ */
+
+extern	tdata_t _TIFFmalloc(tsize_t);
+extern	tdata_t _TIFFrealloc(tdata_t, tsize_t);
+extern	void _TIFFmemset(tdata_t, int, tsize_t);
+extern	void _TIFFmemcpy(tdata_t, const tdata_t, tsize_t);
+extern	int _TIFFmemcmp(const tdata_t, const tdata_t, tsize_t);
+extern	void _TIFFfree(tdata_t);
+
+/*
+** Stuff, related to tag handling and creating custom tags.
+*/
+extern  int  TIFFGetTagListCount( TIFF * );
+extern  ttag_t TIFFGetTagListEntry( TIFF *, int tag_index );
+    
+#define	TIFF_ANY	TIFF_NOTYPE	/* for field descriptor searching */
+#define	TIFF_VARIABLE	-1		/* marker for variable length tags */
+#define	TIFF_SPP	-2		/* marker for SamplesPerPixel tags */
+#define	TIFF_VARIABLE2	-3		/* marker for uint32 var-length tags */
+
+#define FIELD_CUSTOM    65    
+
+typedef	struct {
+	ttag_t	field_tag;		/* field's tag */
+	short	field_readcount;	/* read count/TIFF_VARIABLE/TIFF_SPP */
+	short	field_writecount;	/* write count/TIFF_VARIABLE */
+	TIFFDataType field_type;	/* type of associated data */
+        unsigned short field_bit;	/* bit in fieldsset bit vector */
+	unsigned char field_oktochange;	/* if true, can change while writing */
+	unsigned char field_passcount;	/* if true, pass dir count on set */
+	char	*field_name;		/* ASCII name */
+} TIFFFieldInfo;
+
+typedef struct _TIFFTagValue {
+    const TIFFFieldInfo  *info;
+    int             count;
+    void           *value;
+} TIFFTagValue;
+
+extern	void TIFFMergeFieldInfo(TIFF*, const TIFFFieldInfo[], int);
+extern	const TIFFFieldInfo* TIFFFindFieldInfo(TIFF*, ttag_t, TIFFDataType);
+extern  const TIFFFieldInfo* TIFFFindFieldInfoByName(TIFF* , const char *,
+						     TIFFDataType);
+extern	const TIFFFieldInfo* TIFFFieldWithTag(TIFF*, ttag_t);
+extern	const TIFFFieldInfo* TIFFFieldWithName(TIFF*, const char *);
+
+typedef	int (*TIFFVSetMethod)(TIFF*, ttag_t, va_list);
+typedef	int (*TIFFVGetMethod)(TIFF*, ttag_t, va_list);
+typedef	void (*TIFFPrintMethod)(TIFF*, FILE*, long);
+    
+typedef struct {
+    TIFFVSetMethod	vsetfield;	/* tag set routine */
+    TIFFVGetMethod	vgetfield;	/* tag get routine */
+    TIFFPrintMethod	printdir;	/* directory print routine */
+} TIFFTagMethods;
+        
+extern  TIFFTagMethods *TIFFAccessTagMethods( TIFF * );
+extern  void *TIFFGetClientInfo( TIFF *, const char * );
+extern  void TIFFSetClientInfo( TIFF *, void *, const char * );
+
+extern	void TIFFCleanup(TIFF*);
+extern	void TIFFClose(TIFF*);
+extern	int TIFFFlush(TIFF*);
+extern	int TIFFFlushData(TIFF*);
+extern	int TIFFGetField(TIFF*, ttag_t, ...);
+extern	int TIFFVGetField(TIFF*, ttag_t, va_list);
+extern	int TIFFGetFieldDefaulted(TIFF*, ttag_t, ...);
+extern	int TIFFVGetFieldDefaulted(TIFF*, ttag_t, va_list);
+extern	int TIFFReadDirectory(TIFF*);
+extern	int TIFFReadCustomDirectory(TIFF*, toff_t, const TIFFFieldInfo[],
+				    size_t);
+extern	int TIFFReadEXIFDirectory(TIFF*, toff_t);
+extern	tsize_t TIFFScanlineSize(TIFF*);
+extern	tsize_t TIFFOldScanlineSize(TIFF*);
+extern	tsize_t TIFFRasterScanlineSize(TIFF*);
+extern	tsize_t TIFFStripSize(TIFF*);
+extern	tsize_t TIFFRawStripSize(TIFF*, tstrip_t);
+extern	tsize_t TIFFVStripSize(TIFF*, uint32);
+extern	tsize_t TIFFTileRowSize(TIFF*);
+extern	tsize_t TIFFTileSize(TIFF*);
+extern	tsize_t TIFFVTileSize(TIFF*, uint32);
+extern	uint32 TIFFDefaultStripSize(TIFF*, uint32);
+extern	void TIFFDefaultTileSize(TIFF*, uint32*, uint32*);
+extern	int TIFFFileno(TIFF*);
+extern  int TIFFSetFileno(TIFF*, int);
+extern  thandle_t TIFFClientdata(TIFF*);
+extern  thandle_t TIFFSetClientdata(TIFF*, thandle_t);
+extern	int TIFFGetMode(TIFF*);
+extern	int TIFFSetMode(TIFF*, int);
+extern	int TIFFIsTiled(TIFF*);
+extern	int TIFFIsByteSwapped(TIFF*);
+extern	int TIFFIsUpSampled(TIFF*);
+extern	int TIFFIsMSB2LSB(TIFF*);
+extern	int TIFFIsBigEndian(TIFF*);
+extern	TIFFReadWriteProc TIFFGetReadProc(TIFF*);
+extern	TIFFReadWriteProc TIFFGetWriteProc(TIFF*);
+extern	TIFFSeekProc TIFFGetSeekProc(TIFF*);
+extern	TIFFCloseProc TIFFGetCloseProc(TIFF*);
+extern	TIFFSizeProc TIFFGetSizeProc(TIFF*);
+extern	TIFFMapFileProc TIFFGetMapFileProc(TIFF*);
+extern	TIFFUnmapFileProc TIFFGetUnmapFileProc(TIFF*);
+extern	uint32 TIFFCurrentRow(TIFF*);
+extern	tdir_t TIFFCurrentDirectory(TIFF*);
+extern	tdir_t TIFFNumberOfDirectories(TIFF*);
+extern	uint32 TIFFCurrentDirOffset(TIFF*);
+extern	tstrip_t TIFFCurrentStrip(TIFF*);
+extern	ttile_t TIFFCurrentTile(TIFF*);
+extern	int TIFFReadBufferSetup(TIFF*, tdata_t, tsize_t);
+extern	int TIFFWriteBufferSetup(TIFF*, tdata_t, tsize_t);
+extern	int TIFFSetupStrips(TIFF *);
+extern  int TIFFWriteCheck(TIFF*, int, const char *);
+extern	void TIFFFreeDirectory(TIFF*);
+extern  int TIFFCreateDirectory(TIFF*);
+extern	int TIFFLastDirectory(TIFF*);
+extern	int TIFFSetDirectory(TIFF*, tdir_t);
+extern	int TIFFSetSubDirectory(TIFF*, uint32);
+extern	int TIFFUnlinkDirectory(TIFF*, tdir_t);
+extern	int TIFFSetField(TIFF*, ttag_t, ...);
+extern	int TIFFVSetField(TIFF*, ttag_t, va_list);
+extern	int TIFFWriteDirectory(TIFF *);
+extern	int TIFFCheckpointDirectory(TIFF *);
+extern	int TIFFRewriteDirectory(TIFF *);
+extern	int TIFFReassignTagToIgnore(enum TIFFIgnoreSense, int);
+
+#if defined(c_plusplus) || defined(__cplusplus)
+extern	void TIFFPrintDirectory(TIFF*, FILE*, long = 0);
+extern	int TIFFReadScanline(TIFF*, tdata_t, uint32, tsample_t = 0);
+extern	int TIFFWriteScanline(TIFF*, tdata_t, uint32, tsample_t = 0);
+extern	int TIFFReadRGBAImage(TIFF*, uint32, uint32, uint32*, int = 0);
+extern	int TIFFReadRGBAImageOriented(TIFF*, uint32, uint32, uint32*,
+				      int = ORIENTATION_BOTLEFT, int = 0);
+#else
+extern	void TIFFPrintDirectory(TIFF*, FILE*, long);
+extern	int TIFFReadScanline(TIFF*, tdata_t, uint32, tsample_t);
+extern	int TIFFWriteScanline(TIFF*, tdata_t, uint32, tsample_t);
+extern	int TIFFReadRGBAImage(TIFF*, uint32, uint32, uint32*, int);
+extern	int TIFFReadRGBAImageOriented(TIFF*, uint32, uint32, uint32*, int, int);
+#endif
+
+extern	int TIFFReadRGBAStrip(TIFF*, tstrip_t, uint32 * );
+extern	int TIFFReadRGBATile(TIFF*, uint32, uint32, uint32 * );
+extern	int TIFFRGBAImageOK(TIFF*, char [1024]);
+extern	int TIFFRGBAImageBegin(TIFFRGBAImage*, TIFF*, int, char [1024]);
+extern	int TIFFRGBAImageGet(TIFFRGBAImage*, uint32*, uint32, uint32);
+extern	void TIFFRGBAImageEnd(TIFFRGBAImage*);
+extern	TIFF* TIFFOpen(const char*, const char*);
+# ifdef __WIN32__
+extern	TIFF* TIFFOpenW(const wchar_t*, const char*);
+# endif /* __WIN32__ */
+extern	TIFF* TIFFFdOpen(int, const char*, const char*);
+extern	TIFF* TIFFClientOpen(const char*, const char*,
+	    thandle_t,
+	    TIFFReadWriteProc, TIFFReadWriteProc,
+	    TIFFSeekProc, TIFFCloseProc,
+	    TIFFSizeProc,
+	    TIFFMapFileProc, TIFFUnmapFileProc);
+extern	const char* TIFFFileName(TIFF*);
+extern	const char* TIFFSetFileName(TIFF*, const char *);
+extern	void TIFFError(const char*, const char*, ...);
+extern	void TIFFErrorExt(thandle_t, const char*, const char*, ...);
+extern	void TIFFWarning(const char*, const char*, ...);
+extern	void TIFFWarningExt(thandle_t, const char*, const char*, ...);
+extern	TIFFErrorHandler TIFFSetErrorHandler(TIFFErrorHandler);
+extern	TIFFErrorHandlerExt TIFFSetErrorHandlerExt(TIFFErrorHandlerExt);
+extern	TIFFErrorHandler TIFFSetWarningHandler(TIFFErrorHandler);
+extern	TIFFErrorHandlerExt TIFFSetWarningHandlerExt(TIFFErrorHandlerExt);
+extern	TIFFExtendProc TIFFSetTagExtender(TIFFExtendProc);
+extern	ttile_t TIFFComputeTile(TIFF*, uint32, uint32, uint32, tsample_t);
+extern	int TIFFCheckTile(TIFF*, uint32, uint32, uint32, tsample_t);
+extern	ttile_t TIFFNumberOfTiles(TIFF*);
+extern	tsize_t TIFFReadTile(TIFF*,
+	    tdata_t, uint32, uint32, uint32, tsample_t);
+extern	tsize_t TIFFWriteTile(TIFF*,
+	    tdata_t, uint32, uint32, uint32, tsample_t);
+extern	tstrip_t TIFFComputeStrip(TIFF*, uint32, tsample_t);
+extern	tstrip_t TIFFNumberOfStrips(TIFF*);
+extern	tsize_t TIFFReadEncodedStrip(TIFF*, tstrip_t, tdata_t, tsize_t);
+extern	tsize_t TIFFReadRawStrip(TIFF*, tstrip_t, tdata_t, tsize_t);
+extern	tsize_t TIFFReadEncodedTile(TIFF*, ttile_t, tdata_t, tsize_t);
+extern	tsize_t TIFFReadRawTile(TIFF*, ttile_t, tdata_t, tsize_t);
+extern	tsize_t TIFFWriteEncodedStrip(TIFF*, tstrip_t, tdata_t, tsize_t);
+extern	tsize_t TIFFWriteRawStrip(TIFF*, tstrip_t, tdata_t, tsize_t);
+extern	tsize_t TIFFWriteEncodedTile(TIFF*, ttile_t, tdata_t, tsize_t);
+extern	tsize_t TIFFWriteRawTile(TIFF*, ttile_t, tdata_t, tsize_t);
+extern	int TIFFDataWidth(TIFFDataType);    /* table of tag datatype widths */
+extern	void TIFFSetWriteOffset(TIFF*, toff_t);
+extern	void TIFFSwabShort(uint16*);
+extern	void TIFFSwabLong(uint32*);
+extern	void TIFFSwabDouble(double*);
+extern	void TIFFSwabArrayOfShort(uint16*, unsigned long);
+extern	void TIFFSwabArrayOfTriples(uint8*, unsigned long);
+extern	void TIFFSwabArrayOfLong(uint32*, unsigned long);
+extern	void TIFFSwabArrayOfDouble(double*, unsigned long);
+extern	void TIFFReverseBits(unsigned char *, unsigned long);
+extern	const unsigned char* TIFFGetBitRevTable(int);
+
+#ifdef LOGLUV_PUBLIC
+#define U_NEU		0.210526316
+#define V_NEU		0.473684211
+#define UVSCALE		410.
+extern	double LogL16toY(int);
+extern	double LogL10toY(int);
+extern	void XYZtoRGB24(float*, uint8*);
+extern	int uv_decode(double*, double*, int);
+extern	void LogLuv24toXYZ(uint32, float*);
+extern	void LogLuv32toXYZ(uint32, float*);
+#if defined(c_plusplus) || defined(__cplusplus)
+extern	int LogL16fromY(double, int = SGILOGENCODE_NODITHER);
+extern	int LogL10fromY(double, int = SGILOGENCODE_NODITHER);
+extern	int uv_encode(double, double, int = SGILOGENCODE_NODITHER);
+extern	uint32 LogLuv24fromXYZ(float*, int = SGILOGENCODE_NODITHER);
+extern	uint32 LogLuv32fromXYZ(float*, int = SGILOGENCODE_NODITHER);
+#else
+extern	int LogL16fromY(double, int);
+extern	int LogL10fromY(double, int);
+extern	int uv_encode(double, double, int);
+extern	uint32 LogLuv24fromXYZ(float*, int);
+extern	uint32 LogLuv32fromXYZ(float*, int);
+#endif
+#endif /* LOGLUV_PUBLIC */
+    
+extern int TIFFCIELabToRGBInit(TIFFCIELabToRGB*, TIFFDisplay *, float*);
+extern void TIFFCIELabToXYZ(TIFFCIELabToRGB *, uint32, int32, int32,
+			    float *, float *, float *);
+extern void TIFFXYZToRGB(TIFFCIELabToRGB *, float, float, float,
+			 uint32 *, uint32 *, uint32 *);
+
+extern int TIFFYCbCrToRGBInit(TIFFYCbCrToRGB*, float*, float*);
+extern void TIFFYCbCrtoRGB(TIFFYCbCrToRGB *, uint32, int32, int32,
+			   uint32 *, uint32 *, uint32 *);
+
+#if defined(c_plusplus) || defined(__cplusplus)
+}
+#endif
+
+#endif /* _TIFFIO_ */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tiffiop.h b/Utilities/GDAL/frmts/gtiff/libtiff/tiffiop.h
new file mode 100644
index 0000000000..3cded735fa
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tiffiop.h
@@ -0,0 +1,325 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and 
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
+ * 
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _TIFFIOP_
+#define	_TIFFIOP_
+/*
+ * ``Library-private'' definitions.
+ */
+
+#include "tif_config.h"
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+
+#ifdef HAVE_ASSERT_H
+# include <assert.h>
+#else
+# define assert(x) 
+#endif
+
+#ifdef HAVE_SEARCH_H
+# include <search.h>
+#else
+extern void *lfind(const void *, const void *, size_t *, size_t,
+		   int (*)(const void *, const void *));
+#endif
+
+#include "tiffio.h"
+#include "tif_dir.h"
+
+#ifndef STRIP_SIZE_DEFAULT
+# define STRIP_SIZE_DEFAULT 8192
+#endif
+
+#define    streq(a,b)      (strcmp(a,b) == 0)
+
+#ifndef TRUE
+#define	TRUE	1
+#define	FALSE	0
+#endif
+
+typedef struct client_info {
+    struct client_info *next;
+    void      *data;
+    char      *name;
+} TIFFClientInfoLink;
+
+/*
+ * Typedefs for ``method pointers'' used internally.
+ */
+typedef	unsigned char tidataval_t;	/* internal image data value type */
+typedef	tidataval_t* tidata_t;		/* reference to internal image data */
+
+typedef	void (*TIFFVoidMethod)(TIFF*);
+typedef	int (*TIFFBoolMethod)(TIFF*);
+typedef	int (*TIFFPreMethod)(TIFF*, tsample_t);
+typedef	int (*TIFFCodeMethod)(TIFF*, tidata_t, tsize_t, tsample_t);
+typedef	int (*TIFFSeekMethod)(TIFF*, uint32);
+typedef	void (*TIFFPostMethod)(TIFF*, tidata_t, tsize_t);
+typedef	uint32 (*TIFFStripMethod)(TIFF*, uint32);
+typedef	void (*TIFFTileMethod)(TIFF*, uint32*, uint32*);
+
+struct tiff {
+	char*		tif_name;	/* name of open file */
+	int		tif_fd;		/* open file descriptor */
+	int		tif_mode;	/* open mode (O_*) */
+	uint32		tif_flags;
+#define	TIFF_FILLORDER		0x0003	/* natural bit fill order for machine */
+#define	TIFF_DIRTYHEADER	0x0004	/* header must be written on close */
+#define	TIFF_DIRTYDIRECT	0x0008	/* current directory must be written */
+#define	TIFF_BUFFERSETUP	0x0010	/* data buffers setup */
+#define	TIFF_CODERSETUP		0x0020	/* encoder/decoder setup done */
+#define	TIFF_BEENWRITING	0x0040	/* written 1+ scanlines to file */
+#define	TIFF_SWAB		0x0080	/* byte swap file information */
+#define	TIFF_NOBITREV		0x0100	/* inhibit bit reversal logic */
+#define	TIFF_MYBUFFER		0x0200	/* my raw data buffer; free on close */
+#define	TIFF_ISTILED		0x0400	/* file is tile, not strip- based */
+#define	TIFF_MAPPED		0x0800	/* file is mapped into memory */
+#define	TIFF_POSTENCODE		0x1000	/* need call to postencode routine */
+#define	TIFF_INSUBIFD		0x2000	/* currently writing a subifd */
+#define	TIFF_UPSAMPLED		0x4000	/* library is doing data up-sampling */ 
+#define	TIFF_STRIPCHOP		0x8000	/* enable strip chopping support */
+#define	TIFF_HEADERONLY		0x10000	/* read header only, do not process */
+					/* the first directory */
+#define TIFF_NOREADRAW		0x20000 /* skip reading of raw uncompressed */
+					/* image data */
+	toff_t		tif_diroff;	/* file offset of current directory */
+	toff_t		tif_nextdiroff;	/* file offset of following directory */
+	toff_t*		tif_dirlist;	/* list of offsets to already seen */
+					/* directories to prevent IFD looping */
+	uint16		tif_dirnumber;  /* number of already seen directories */
+	TIFFDirectory	tif_dir;	/* internal rep of current directory */
+	TIFFHeader	tif_header;	/* file's header block */
+	const int*	tif_typeshift;	/* data type shift counts */
+	const long*	tif_typemask;	/* data type masks */
+	uint32		tif_row;	/* current scanline */
+	tdir_t		tif_curdir;	/* current directory (index) */
+	tstrip_t	tif_curstrip;	/* current strip for read/write */
+	toff_t		tif_curoff;	/* current offset for read/write */
+	toff_t		tif_dataoff;	/* current offset for writing dir */
+/* SubIFD support */
+	uint16		tif_nsubifd;	/* remaining subifds to write */
+	toff_t		tif_subifdoff;	/* offset for patching SubIFD link */
+/* tiling support */
+	uint32 		tif_col;	/* current column (offset by row too) */
+	ttile_t		tif_curtile;	/* current tile for read/write */
+	tsize_t		tif_tilesize;	/* # of bytes in a tile */
+/* compression scheme hooks */
+	int		tif_decodestatus;
+	TIFFBoolMethod	tif_setupdecode;/* called once before predecode */
+	TIFFPreMethod	tif_predecode;	/* pre- row/strip/tile decoding */
+	TIFFBoolMethod	tif_setupencode;/* called once before preencode */
+	int		tif_encodestatus;
+	TIFFPreMethod	tif_preencode;	/* pre- row/strip/tile encoding */
+	TIFFBoolMethod	tif_postencode;	/* post- row/strip/tile encoding */
+	TIFFCodeMethod	tif_decoderow;	/* scanline decoding routine */
+	TIFFCodeMethod	tif_encoderow;	/* scanline encoding routine */
+	TIFFCodeMethod	tif_decodestrip;/* strip decoding routine */
+	TIFFCodeMethod	tif_encodestrip;/* strip encoding routine */
+	TIFFCodeMethod	tif_decodetile;	/* tile decoding routine */
+	TIFFCodeMethod	tif_encodetile;	/* tile encoding routine */
+	TIFFVoidMethod	tif_close;	/* cleanup-on-close routine */
+	TIFFSeekMethod	tif_seek;	/* position within a strip routine */
+	TIFFVoidMethod	tif_cleanup;	/* cleanup state routine */
+	TIFFStripMethod	tif_defstripsize;/* calculate/constrain strip size */
+	TIFFTileMethod	tif_deftilesize;/* calculate/constrain tile size */
+	tidata_t	tif_data;	/* compression scheme private data */
+/* input/output buffering */
+	tsize_t		tif_scanlinesize;/* # of bytes in a scanline */
+	tsize_t		tif_scanlineskew;/* scanline skew for reading strips */
+	tidata_t	tif_rawdata;	/* raw data buffer */
+	tsize_t		tif_rawdatasize;/* # of bytes in raw data buffer */
+	tidata_t	tif_rawcp;	/* current spot in raw buffer */
+	tsize_t		tif_rawcc;	/* bytes unread from raw buffer */
+/* memory-mapped file support */
+	tidata_t	tif_base;	/* base of mapped file */
+	toff_t		tif_size;	/* size of mapped file region (bytes) */
+	TIFFMapFileProc	tif_mapproc;	/* map file method */
+	TIFFUnmapFileProc tif_unmapproc;/* unmap file method */
+/* input/output callback methods */
+	thandle_t	tif_clientdata;	/* callback parameter */
+	TIFFReadWriteProc tif_readproc;	/* read method */
+	TIFFReadWriteProc tif_writeproc;/* write method */
+	TIFFSeekProc	tif_seekproc;	/* lseek method */
+	TIFFCloseProc	tif_closeproc;	/* close method */
+	TIFFSizeProc	tif_sizeproc;	/* filesize method */
+/* post-decoding support */
+	TIFFPostMethod	tif_postdecode;	/* post decoding routine */
+/* tag support */
+	TIFFFieldInfo**	tif_fieldinfo;	/* sorted table of registered tags */
+	size_t		tif_nfields;	/* # entries in registered tag table */
+	const TIFFFieldInfo *tif_foundfield;/* cached pointer to already found tag */
+        TIFFTagMethods  tif_tagmethods; /* tag get/set/print routines */
+        TIFFClientInfoLink *tif_clientinfo; /* extra client information. */
+};
+
+#define	isPseudoTag(t)	(t > 0xffff)	/* is tag value normal or pseudo */
+
+#define	isTiled(tif)	(((tif)->tif_flags & TIFF_ISTILED) != 0)
+#define	isMapped(tif)	(((tif)->tif_flags & TIFF_MAPPED) != 0)
+#define	isFillOrder(tif, o)	(((tif)->tif_flags & (o)) != 0)
+#define	isUpSampled(tif)	(((tif)->tif_flags & TIFF_UPSAMPLED) != 0)
+#define	TIFFReadFile(tif, buf, size) \
+	((*(tif)->tif_readproc)((tif)->tif_clientdata,buf,size))
+#define	TIFFWriteFile(tif, buf, size) \
+	((*(tif)->tif_writeproc)((tif)->tif_clientdata,buf,size))
+#define	TIFFSeekFile(tif, off, whence) \
+	((*(tif)->tif_seekproc)((tif)->tif_clientdata,(toff_t)(off),whence))
+#define	TIFFCloseFile(tif) \
+	((*(tif)->tif_closeproc)((tif)->tif_clientdata))
+#define	TIFFGetFileSize(tif) \
+	((*(tif)->tif_sizeproc)((tif)->tif_clientdata))
+#define	TIFFMapFileContents(tif, paddr, psize) \
+	((*(tif)->tif_mapproc)((tif)->tif_clientdata,paddr,psize))
+#define	TIFFUnmapFileContents(tif, addr, size) \
+	((*(tif)->tif_unmapproc)((tif)->tif_clientdata,addr,size))
+
+/*
+ * Default Read/Seek/Write definitions.
+ */
+#ifndef ReadOK
+#define	ReadOK(tif, buf, size) \
+	(TIFFReadFile(tif, (tdata_t) buf, (tsize_t)(size)) == (tsize_t)(size))
+#endif
+#ifndef SeekOK
+#define	SeekOK(tif, off) \
+	(TIFFSeekFile(tif, (toff_t) off, SEEK_SET) == (toff_t) off)
+#endif
+#ifndef WriteOK
+#define	WriteOK(tif, buf, size) \
+	(TIFFWriteFile(tif, (tdata_t) buf, (tsize_t) size) == (tsize_t) size)
+#endif
+
+/* NB: the uint32 casts are to silence certain ANSI-C compilers */
+#define TIFFhowmany(x, y) ((((uint32)(x))+(((uint32)(y))-1))/((uint32)(y)))
+#define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
+#define	TIFFroundup(x, y) (TIFFhowmany(x,y)*(y))
+
+#define TIFFmax(A,B) ((A)>(B)?(A):(B))
+#define TIFFmin(A,B) ((A)<(B)?(A):(B))
+
+#define TIFFArrayCount(a) (sizeof (a) / sizeof ((a)[0]))
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+extern	int _TIFFgetMode(const char*, const char*);
+extern	int _TIFFNoRowEncode(TIFF*, tidata_t, tsize_t, tsample_t);
+extern	int _TIFFNoStripEncode(TIFF*, tidata_t, tsize_t, tsample_t);
+extern	int _TIFFNoTileEncode(TIFF*, tidata_t, tsize_t, tsample_t);
+extern	int _TIFFNoRowDecode(TIFF*, tidata_t, tsize_t, tsample_t);
+extern	int _TIFFNoStripDecode(TIFF*, tidata_t, tsize_t, tsample_t);
+extern	int _TIFFNoTileDecode(TIFF*, tidata_t, tsize_t, tsample_t);
+extern	void _TIFFNoPostDecode(TIFF*, tidata_t, tsize_t);
+extern  int  _TIFFNoPreCode (TIFF*, tsample_t); 
+extern	int _TIFFNoSeek(TIFF*, uint32);
+extern	void _TIFFSwab16BitData(TIFF*, tidata_t, tsize_t);
+extern	void _TIFFSwab24BitData(TIFF*, tidata_t, tsize_t);
+extern	void _TIFFSwab32BitData(TIFF*, tidata_t, tsize_t);
+extern	void _TIFFSwab64BitData(TIFF*, tidata_t, tsize_t);
+extern	int TIFFFlushData1(TIFF*);
+extern	int TIFFDefaultDirectory(TIFF*);
+extern	void _TIFFSetDefaultCompressionState(TIFF*);
+extern	int TIFFSetCompressionScheme(TIFF*, int);
+extern	int TIFFSetDefaultCompressionState(TIFF*);
+extern	uint32 _TIFFDefaultStripSize(TIFF*, uint32);
+extern	void _TIFFDefaultTileSize(TIFF*, uint32*, uint32*);
+extern	int _TIFFDataSize(TIFFDataType);
+
+extern	void _TIFFsetByteArray(void**, void*, uint32);
+extern	void _TIFFsetString(char**, char*);
+extern	void _TIFFsetShortArray(uint16**, uint16*, uint32);
+extern	void _TIFFsetLongArray(uint32**, uint32*, uint32);
+extern	void _TIFFsetFloatArray(float**, float*, uint32);
+extern	void _TIFFsetDoubleArray(double**, double*, uint32);
+
+extern	void _TIFFprintAscii(FILE*, const char*);
+extern	void _TIFFprintAsciiTag(FILE*, const char*, const char*);
+
+extern	TIFFErrorHandler _TIFFwarningHandler;
+extern	TIFFErrorHandler _TIFFerrorHandler;
+extern	TIFFErrorHandlerExt _TIFFwarningHandlerExt;
+extern	TIFFErrorHandlerExt _TIFFerrorHandlerExt;
+
+extern	tdata_t _TIFFCheckMalloc(TIFF*, size_t, size_t, const char*);
+
+extern	int TIFFInitDumpMode(TIFF*, int);
+#ifdef PACKBITS_SUPPORT
+extern	int TIFFInitPackBits(TIFF*, int);
+#endif
+#ifdef CCITT_SUPPORT
+extern	int TIFFInitCCITTRLE(TIFF*, int), TIFFInitCCITTRLEW(TIFF*, int);
+extern	int TIFFInitCCITTFax3(TIFF*, int), TIFFInitCCITTFax4(TIFF*, int);
+#endif
+#ifdef THUNDER_SUPPORT
+extern	int TIFFInitThunderScan(TIFF*, int);
+#endif
+#ifdef NEXT_SUPPORT
+extern	int TIFFInitNeXT(TIFF*, int);
+#endif
+#ifdef LZW_SUPPORT
+extern	int TIFFInitLZW(TIFF*, int);
+#endif
+#ifdef OJPEG_SUPPORT
+extern	int TIFFInitOJPEG(TIFF*, int);
+#endif
+#ifdef JPEG_SUPPORT
+extern	int TIFFInitJPEG(TIFF*, int);
+#endif
+#ifdef JBIG_SUPPORT
+extern	int TIFFInitJBIG(TIFF*, int);
+#endif
+#ifdef ZIP_SUPPORT
+extern	int TIFFInitZIP(TIFF*, int);
+#endif
+#ifdef PIXARLOG_SUPPORT
+extern	int TIFFInitPixarLog(TIFF*, int);
+#endif
+#ifdef LOGLUV_SUPPORT
+extern	int TIFFInitSGILog(TIFF*, int);
+#endif
+#ifdef VMS
+extern	const TIFFCodec _TIFFBuiltinCODECS[];
+#else
+extern	TIFFCodec _TIFFBuiltinCODECS[];
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+#endif /* _TIFFIOP_ */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/tiffvers.h b/Utilities/GDAL/frmts/gtiff/libtiff/tiffvers.h
new file mode 100644
index 0000000000..9744f8d308
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/tiffvers.h
@@ -0,0 +1,9 @@
+#define TIFFLIB_VERSION_STR "LIBTIFF, Version 3.8.2\nCopyright (c) 1988-1996 Sam Leffler\nCopyright (c) 1991-1996 Silicon Graphics, Inc."
+/*
+ * This define can be used in code that requires
+ * compilation-related definitions specific to a
+ * version or versions of the library.  Runtime
+ * version checking should be done based on the
+ * string returned by TIFFGetVersion.
+ */
+#define TIFFLIB_VERSION 20060323
diff --git a/Utilities/GDAL/frmts/gtiff/libtiff/uvcode.h b/Utilities/GDAL/frmts/gtiff/libtiff/uvcode.h
new file mode 100644
index 0000000000..5b2d7d71fa
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/libtiff/uvcode.h
@@ -0,0 +1,173 @@
+/* Version 1.0 generated April 7, 1997 by Greg Ward Larson, SGI */
+#define UV_SQSIZ	(float)0.003500
+#define UV_NDIVS	16289
+#define UV_VSTART	(float)0.016940
+#define UV_NVS		163
+static struct {
+	float	ustart;
+	short	nus, ncum;
+}	uv_row[UV_NVS] = {
+	{ (float)0.247663,	4,	0 },
+	{ (float)0.243779,	6,	4 },
+	{ (float)0.241684,	7,	10 },
+	{ (float)0.237874,	9,	17 },
+	{ (float)0.235906,	10,	26 },
+	{ (float)0.232153,	12,	36 },
+	{ (float)0.228352,	14,	48 },
+	{ (float)0.226259,	15,	62 },
+	{ (float)0.222371,	17,	77 },
+	{ (float)0.220410,	18,	94 },
+	{ (float)0.214710,	21,	112 },
+	{ (float)0.212714,	22,	133 },
+	{ (float)0.210721,	23,	155 },
+	{ (float)0.204976,	26,	178 },
+	{ (float)0.202986,	27,	204 },
+	{ (float)0.199245,	29,	231 },
+	{ (float)0.195525,	31,	260 },
+	{ (float)0.193560,	32,	291 },
+	{ (float)0.189878,	34,	323 },
+	{ (float)0.186216,	36,	357 },
+	{ (float)0.186216,	36,	393 },
+	{ (float)0.182592,	38,	429 },
+	{ (float)0.179003,	40,	467 },
+	{ (float)0.175466,	42,	507 },
+	{ (float)0.172001,	44,	549 },
+	{ (float)0.172001,	44,	593 },
+	{ (float)0.168612,	46,	637 },
+	{ (float)0.168612,	46,	683 },
+	{ (float)0.163575,	49,	729 },
+	{ (float)0.158642,	52,	778 },
+	{ (float)0.158642,	52,	830 },
+	{ (float)0.158642,	52,	882 },
+	{ (float)0.153815,	55,	934 },
+	{ (float)0.153815,	55,	989 },
+	{ (float)0.149097,	58,	1044 },
+	{ (float)0.149097,	58,	1102 },
+	{ (float)0.142746,	62,	1160 },
+	{ (float)0.142746,	62,	1222 },
+	{ (float)0.142746,	62,	1284 },
+	{ (float)0.138270,	65,	1346 },
+	{ (float)0.138270,	65,	1411 },
+	{ (float)0.138270,	65,	1476 },
+	{ (float)0.132166,	69,	1541 },
+	{ (float)0.132166,	69,	1610 },
+	{ (float)0.126204,	73,	1679 },
+	{ (float)0.126204,	73,	1752 },
+	{ (float)0.126204,	73,	1825 },
+	{ (float)0.120381,	77,	1898 },
+	{ (float)0.120381,	77,	1975 },
+	{ (float)0.120381,	77,	2052 },
+	{ (float)0.120381,	77,	2129 },
+	{ (float)0.112962,	82,	2206 },
+	{ (float)0.112962,	82,	2288 },
+	{ (float)0.112962,	82,	2370 },
+	{ (float)0.107450,	86,	2452 },
+	{ (float)0.107450,	86,	2538 },
+	{ (float)0.107450,	86,	2624 },
+	{ (float)0.107450,	86,	2710 },
+	{ (float)0.100343,	91,	2796 },
+	{ (float)0.100343,	91,	2887 },
+	{ (float)0.100343,	91,	2978 },
+	{ (float)0.095126,	95,	3069 },
+	{ (float)0.095126,	95,	3164 },
+	{ (float)0.095126,	95,	3259 },
+	{ (float)0.095126,	95,	3354 },
+	{ (float)0.088276,	100,	3449 },
+	{ (float)0.088276,	100,	3549 },
+	{ (float)0.088276,	100,	3649 },
+	{ (float)0.088276,	100,	3749 },
+	{ (float)0.081523,	105,	3849 },
+	{ (float)0.081523,	105,	3954 },
+	{ (float)0.081523,	105,	4059 },
+	{ (float)0.081523,	105,	4164 },
+	{ (float)0.074861,	110,	4269 },
+	{ (float)0.074861,	110,	4379 },
+	{ (float)0.074861,	110,	4489 },
+	{ (float)0.074861,	110,	4599 },
+	{ (float)0.068290,	115,	4709 },
+	{ (float)0.068290,	115,	4824 },
+	{ (float)0.068290,	115,	4939 },
+	{ (float)0.068290,	115,	5054 },
+	{ (float)0.063573,	119,	5169 },
+	{ (float)0.063573,	119,	5288 },
+	{ (float)0.063573,	119,	5407 },
+	{ (float)0.063573,	119,	5526 },
+	{ (float)0.057219,	124,	5645 },
+	{ (float)0.057219,	124,	5769 },
+	{ (float)0.057219,	124,	5893 },
+	{ (float)0.057219,	124,	6017 },
+	{ (float)0.050985,	129,	6141 },
+	{ (float)0.050985,	129,	6270 },
+	{ (float)0.050985,	129,	6399 },
+	{ (float)0.050985,	129,	6528 },
+	{ (float)0.050985,	129,	6657 },
+	{ (float)0.044859,	134,	6786 },
+	{ (float)0.044859,	134,	6920 },
+	{ (float)0.044859,	134,	7054 },
+	{ (float)0.044859,	134,	7188 },
+	{ (float)0.040571,	138,	7322 },
+	{ (float)0.040571,	138,	7460 },
+	{ (float)0.040571,	138,	7598 },
+	{ (float)0.040571,	138,	7736 },
+	{ (float)0.036339,	142,	7874 },
+	{ (float)0.036339,	142,	8016 },
+	{ (float)0.036339,	142,	8158 },
+	{ (float)0.036339,	142,	8300 },
+	{ (float)0.032139,	146,	8442 },
+	{ (float)0.032139,	146,	8588 },
+	{ (float)0.032139,	146,	8734 },
+	{ (float)0.032139,	146,	8880 },
+	{ (float)0.027947,	150,	9026 },
+	{ (float)0.027947,	150,	9176 },
+	{ (float)0.027947,	150,	9326 },
+	{ (float)0.023739,	154,	9476 },
+	{ (float)0.023739,	154,	9630 },
+	{ (float)0.023739,	154,	9784 },
+	{ (float)0.023739,	154,	9938 },
+	{ (float)0.019504,	158,	10092 },
+	{ (float)0.019504,	158,	10250 },
+	{ (float)0.019504,	158,	10408 },
+	{ (float)0.016976,	161,	10566 },
+	{ (float)0.016976,	161,	10727 },
+	{ (float)0.016976,	161,	10888 },
+	{ (float)0.016976,	161,	11049 },
+	{ (float)0.012639,	165,	11210 },
+	{ (float)0.012639,	165,	11375 },
+	{ (float)0.012639,	165,	11540 },
+	{ (float)0.009991,	168,	11705 },
+	{ (float)0.009991,	168,	11873 },
+	{ (float)0.009991,	168,	12041 },
+	{ (float)0.009016,	170,	12209 },
+	{ (float)0.009016,	170,	12379 },
+	{ (float)0.009016,	170,	12549 },
+	{ (float)0.006217,	173,	12719 },
+	{ (float)0.006217,	173,	12892 },
+	{ (float)0.005097,	175,	13065 },
+	{ (float)0.005097,	175,	13240 },
+	{ (float)0.005097,	175,	13415 },
+	{ (float)0.003909,	177,	13590 },
+	{ (float)0.003909,	177,	13767 },
+	{ (float)0.002340,	177,	13944 },
+	{ (float)0.002389,	170,	14121 },
+	{ (float)0.001068,	164,	14291 },
+	{ (float)0.001653,	157,	14455 },
+	{ (float)0.000717,	150,	14612 },
+	{ (float)0.001614,	143,	14762 },
+	{ (float)0.000270,	136,	14905 },
+	{ (float)0.000484,	129,	15041 },
+	{ (float)0.001103,	123,	15170 },
+	{ (float)0.001242,	115,	15293 },
+	{ (float)0.001188,	109,	15408 },
+	{ (float)0.001011,	103,	15517 },
+	{ (float)0.000709,	97,	15620 },
+	{ (float)0.000301,	89,	15717 },
+	{ (float)0.002416,	82,	15806 },
+	{ (float)0.003251,	76,	15888 },
+	{ (float)0.003246,	69,	15964 },
+	{ (float)0.004141,	62,	16033 },
+	{ (float)0.005963,	55,	16095 },
+	{ (float)0.008839,	47,	16150 },
+	{ (float)0.010490,	40,	16197 },
+	{ (float)0.016994,	31,	16237 },
+	{ (float)0.023659,	21,	16268 },
+};
diff --git a/Utilities/GDAL/frmts/gtiff/makefile.vc b/Utilities/GDAL/frmts/gtiff/makefile.vc
new file mode 100644
index 0000000000..6ce7daac73
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/makefile.vc
@@ -0,0 +1,43 @@
+
+OBJ	=	geotiff.obj gt_wkt_srs.obj gt_overview.obj \
+		tif_overview.obj tif_ovrcache.obj tifvsi.obj tif_float.obj
+
+EXTRAFLAGS = 	$(TIFF_INC) $(GEOTIFF_INC)
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+!IFDEF TIFF_INC
+SUB_TIFF_TARGET =
+!ELSE
+TIFF_INC   =	-Ilibtiff
+SUB_TIFF_TARGET =	tiff
+!ENDIF
+
+!IFDEF GEOTIFF_INC
+SUB_GEOTIFF_TARGET =	
+!ELSE
+GEOTIFF_INC   =	-Ilibgeotiff
+SUB_GEOTIFF_TARGET =	geotiff
+!ENDIF
+
+
+default:	$(OBJ) $(SUB_TIFF_TARGET) $(SUB_GEOTIFF_TARGET)
+	copy *.obj ..\o
+
+tiff:
+	cd libtiff
+	$(MAKE) /f makefile.vc
+	cd ..
+
+geotiff:
+	cd libgeotiff
+	$(MAKE) /f makefile.vc
+	cd ..
+
+clean:
+	-del *.obj libtiff\*.obj libgeotiff\*.obj
+	
+
+
diff --git a/Utilities/GDAL/frmts/gtiff/tif_float.c b/Utilities/GDAL/frmts/gtiff/tif_float.c
new file mode 100644
index 0000000000..f5bed7c716
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/tif_float.c
@@ -0,0 +1,199 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  GeoTIFF Driver
+ * Purpose:  Floating point conversion functions. Convert 16- and 24-bit
+ *           floating point numbers into the 32-bit IEEE 754 compliant ones.
+ * Author:   Andrey Kiselev, dron@remotesensing.org
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Andrey Kiselev <dron@remotesensing.org>
+ *
+ * This code is based on the code from OpenEXR project with the following
+ * copyright:
+ * 
+ * Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
+ * Digital Ltd. LLC
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * *       Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * *       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.
+ * *       Neither the name of Industrial Light & Magic nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission. 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ *
+ ******************************************************************************
+ *
+ * $Log: tif_float.c,v $
+ * Revision 1.1  2005/04/13 15:32:20  dron
+ * Added support for 16- and 24-bit floating point TIFFs (as per TechNote 3).
+ *
+ */
+
+#include "cpl_port.h"
+
+/************************************************************************/
+/*                           HalfToFloat()                              */
+/*                                                                      */
+/*  16-bit floating point number to 32-bit one.                         */
+/************************************************************************/
+
+GUInt32 HalfToFloat( GUInt16 iHalf )
+{
+
+    GUInt32 iSign =     (iHalf >> 15) & 0x00000001;
+    GUInt32 iExponent = (iHalf >> 10) & 0x0000001f;
+    GUInt32 iMantissa = iHalf         & 0x000003ff;
+
+    if (iExponent == 0)
+    {
+	if (iMantissa == 0)
+	{
+/* -------------------------------------------------------------------- */
+/*	Plus or minus zero.			                        */
+/* -------------------------------------------------------------------- */
+
+            return iSign << 31;
+	}
+	else
+	{
+/* -------------------------------------------------------------------- */
+/*	Denormalized number -- renormalize it.			        */
+/* -------------------------------------------------------------------- */
+
+	    while (!(iMantissa & 0x00000400))
+	    {
+		iMantissa <<= 1;
+		iExponent -=  1;
+	    }
+
+	    iExponent += 1;
+	    iMantissa &= ~0x00000400;
+	}
+    }
+    else if (iExponent == 31)
+    {
+	if (iMantissa == 0)
+	{
+/* -------------------------------------------------------------------- */
+/*	 Positive or negative infinity.			                */
+/* -------------------------------------------------------------------- */
+
+	    return (iSign << 31) | 0x7f800000;
+	}
+	else
+	{
+/* -------------------------------------------------------------------- */
+/*	 NaN -- preserve sign and significand bits.	                */
+/* -------------------------------------------------------------------- */
+
+	    return (iSign << 31) | 0x7f800000 | (iMantissa << 13);
+	}
+    }
+
+/* -------------------------------------------------------------------- */
+/*	 Normalized number.			                        */
+/* -------------------------------------------------------------------- */
+
+    iExponent = iExponent + (127 - 15);
+    iMantissa = iMantissa << 13;
+
+/* -------------------------------------------------------------------- */
+/*	 Assemble sign, exponent and mantissa.			        */
+/* -------------------------------------------------------------------- */
+
+    return (iSign << 31) | (iExponent << 23) | iMantissa;
+}
+
+/************************************************************************/
+/*                           TripleToFloat()                            */
+/*                                                                      */
+/*  24-bit floating point number to 32-bit one.                         */
+/************************************************************************/
+
+GUInt32 TripleToFloat( GUInt32 iTriple )
+{
+
+    GUInt32 iSign       = (iTriple >> 23) & 0x00000001;
+    GUInt32 iExponent   = (iTriple >> 16) & 0x0000007f;
+    GUInt32 iMantissa   = iTriple         & 0x0000ffff;
+
+    if (iExponent == 0)
+    {
+	if (iMantissa == 0)
+	{
+/* -------------------------------------------------------------------- */
+/*	Plus or minus zero.			                        */
+/* -------------------------------------------------------------------- */
+
+	    return iSign << 31;
+	}
+	else
+	{
+/* -------------------------------------------------------------------- */
+/*	Denormalized number -- renormalize it.			        */
+/* -------------------------------------------------------------------- */
+
+	    while (!(iMantissa & 0x00002000))
+	    {
+		iMantissa <<= 1;
+		iExponent -=  1;
+	    }
+
+	    iExponent += 1;
+	    iMantissa &= ~0x00002000;
+	}
+    }
+    else if (iExponent == 127)
+    {
+	if (iMantissa == 0)
+	{
+/* -------------------------------------------------------------------- */
+/*	 Positive or negative infinity.			                */
+/* -------------------------------------------------------------------- */
+
+	    return (iSign << 31) | 0x7f800000;
+	}
+	else
+	{
+/* -------------------------------------------------------------------- */
+/*	 NaN -- preserve sign and significand bits.	                */
+/* -------------------------------------------------------------------- */
+
+	    return (iSign << 31) | 0x7f800000 | (iMantissa << 7);
+	}
+    }
+
+/* -------------------------------------------------------------------- */
+/*	 Normalized number.			                        */
+/* -------------------------------------------------------------------- */
+
+    iExponent = iExponent + (127 - 63);
+    iMantissa = iMantissa << 7;
+
+/* -------------------------------------------------------------------- */
+/*	 Assemble sign, exponent and mantissa.			        */
+/* -------------------------------------------------------------------- */
+
+    return (iSign << 31) | (iExponent << 23) | iMantissa;
+}
diff --git a/Utilities/GDAL/frmts/gtiff/tif_memio.c b/Utilities/GDAL/frmts/gtiff/tif_memio.c
new file mode 100644
index 0000000000..396f5fc0fc
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/tif_memio.c
@@ -0,0 +1,255 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  libtiff
+ * Purpose:  Sample implementation of "memory" io functions that can be
+ *           passed to TIFFClientOpen() or XTIFFClientOpen() to manage 
+ *           an in-memory TIFF file. 
+ * Author:   Mike Johnson - Banctec AB
+ *           Frank Warmerdam, warmerdam@pobox.com
+ *
+ * While this file is currently GDAL-only, it is intended to be easily
+ * migrated back into the libtiff core, or libtiff/contrib tree. 
+ *
+ ******************************************************************************
+ * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
+ * Copyright (c) 1996 Mike Johnson
+ * Copyright (c) 1996 BancTec AB
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Mike Johnson and BancTec may not be used in any advertising or
+ * publicity relating to the software.
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: tif_memio.c,v $
+ * Revision 1.3  2003/07/08 15:49:24  warmerda
+ * avoid warnings
+ *
+ * Revision 1.2  2003/05/12 13:26:43  warmerda
+ * Added string.h.
+ *
+ * Revision 1.1  2002/10/08 23:00:27  warmerda
+ * New
+ *
+ */
+
+#include <string.h>
+#include "tif_memio.h"
+
+static void MemIO_ExtendFile( MemIOBuf *miobuf, tsize_t size );
+
+/************************************************************************/
+/*                           MemIO_InitBuf()                            */
+/*                                                                      */
+/*      Initialize a passed in MemIOBuf structure.                      */
+/************************************************************************/
+
+void MemIO_InitBuf( MemIOBuf * miobuf, int size, unsigned char *data )
+
+{
+    miobuf->data = NULL;
+    miobuf->size = 0;
+    miobuf->offset = 0;
+    miobuf->data_buf_size = 0;
+    miobuf->own_buffer = 1;
+
+    if( size > 0 )
+    {
+        miobuf->data = data;
+        miobuf->size = size;
+        miobuf->data_buf_size = size;
+        miobuf->own_buffer = 0;
+    }
+}
+
+/************************************************************************/
+/*                          MemIO_DeinitBuf()                           */
+/*                                                                      */
+/*      Clear and free memory buffer.                                   */
+/************************************************************************/
+
+void MemIO_DeinitBuf( MemIOBuf *miobuf )
+
+{
+    if( miobuf->own_buffer && miobuf->data != NULL )
+        _TIFFfree( miobuf->data );
+    memset( miobuf, 0, sizeof(MemIOBuf) );
+}
+
+/************************************************************************/
+/*                           MemIO_SeekProc()                           */
+/************************************************************************/
+
+toff_t MemIO_SeekProc( thandle_t fd, toff_t off, int whence )
+
+{
+    MemIOBuf *miobuf = (MemIOBuf *) fd;
+    toff_t new_off;
+
+    if( whence == SEEK_SET )
+        new_off = off;
+    else if( whence == SEEK_CUR )
+        new_off = miobuf->offset + off;
+    else if( whence == SEEK_END )
+        new_off = miobuf->size + off;
+    else
+        return (toff_t) -1;
+
+    if( new_off < 0 )
+        return (toff_t) -1;
+
+    if( new_off > (toff_t) miobuf->size )
+    {
+        MemIO_ExtendFile( miobuf, new_off );
+        if( new_off > (toff_t) miobuf->size )
+            return (toff_t) -1;
+    }
+    
+    miobuf->offset = new_off;
+    
+    return miobuf->offset;
+}
+
+/************************************************************************/
+/*                           MemIO_ReadProc()                           */
+/************************************************************************/
+
+tsize_t MemIO_ReadProc( thandle_t fd, tdata_t buf, tsize_t size )
+
+{
+    MemIOBuf *miobuf = (MemIOBuf *) fd;
+    int      result = 0;
+
+    if( miobuf->offset + size > (toff_t) miobuf->size )
+        result = miobuf->size - miobuf->offset;
+    else
+        result = size;
+
+    memcpy( buf, miobuf->data + miobuf->offset, result );
+    miobuf->offset += result;
+
+    return result;
+}
+
+/************************************************************************/
+/*                          MemIO_WriteProc()                           */
+/************************************************************************/
+
+tsize_t MemIO_WriteProc( thandle_t fd, tdata_t buf, tsize_t size )
+
+{
+    MemIOBuf *miobuf = (MemIOBuf *) fd;
+    int      result = 0;
+
+    if( miobuf->offset + size > (toff_t) miobuf->size )
+        MemIO_ExtendFile( miobuf, miobuf->offset + size );
+
+    if( miobuf->offset + size > (toff_t) miobuf->size )
+        result = miobuf->size - miobuf->offset;
+    else
+        result = size;
+
+    memcpy( miobuf->data + miobuf->offset, buf, result );
+    miobuf->offset += result;
+
+    return result;
+}
+
+/************************************************************************/
+/*                           MemIO_SizeProc()                           */
+/************************************************************************/
+
+toff_t MemIO_SizeProc( thandle_t fd )
+
+{
+    MemIOBuf *miobuf = (MemIOBuf *) fd;
+
+    return miobuf->size;
+}
+
+/************************************************************************/
+/*                          MemIO_CloseProc()                           */
+/************************************************************************/
+
+int MemIO_CloseProc( thandle_t fd )
+
+{
+    return 0;
+}
+
+/************************************************************************/
+/*                           MemIO_MapProc()                            */
+/************************************************************************/
+
+int MemIO_MapProc( thandle_t fd, tdata_t *pbase, toff_t *psize )
+
+{
+    MemIOBuf *miobuf = (MemIOBuf *) fd;
+
+    *pbase = miobuf->data;
+    *psize = miobuf->size;
+
+    return 0;
+}
+
+/************************************************************************/
+/*                          MemIO_UnmapProc()                           */
+/************************************************************************/
+
+void MemIO_UnmapProc( thandle_t fd, tdata_t base, toff_t size )
+
+{
+}
+
+/************************************************************************/
+/*                          MemIO_ExtendFile()                          */
+/************************************************************************/
+
+static void MemIO_ExtendFile( MemIOBuf *miobuf, tsize_t size )
+
+{
+    tsize_t new_buf_size;
+    tdata_t new_buffer;
+
+    if( size < miobuf->size )
+        return;
+
+    if( size < miobuf->data_buf_size )
+    {
+        miobuf->size = size;
+        return;
+    }
+
+    new_buf_size = (int) (size * 1.25);
+    
+    if( miobuf->own_buffer )
+        new_buffer = _TIFFrealloc( miobuf->data, new_buf_size );
+    else
+    {
+        new_buffer = _TIFFmalloc( new_buf_size );
+        if( new_buffer != NULL )
+            memcpy( new_buffer, miobuf->data, miobuf->size );
+    }
+    
+    if( new_buffer == NULL )
+        return;
+
+    miobuf->data = new_buffer;
+    miobuf->data_buf_size = new_buf_size;
+    miobuf->size = size;
+}
+
diff --git a/Utilities/GDAL/frmts/gtiff/tif_memio.h b/Utilities/GDAL/frmts/gtiff/tif_memio.h
new file mode 100644
index 0000000000..b4d76e85bc
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/tif_memio.h
@@ -0,0 +1,73 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  libtiff
+ * Purpose:  Sample declarations of "memory" io functions that can be
+ *           passed to TIFFClientOpen() or XTIFFClientOpen() to manage 
+ *           an in-memory TIFF file. 
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ * While this file is currently GDAL-only, it is intended to be easily
+ * migrated back into the libtiff core, or libtiff/contrib tree. 
+ *
+ ******************************************************************************
+ * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: tif_memio.h,v $
+ * Revision 1.1  2002/10/08 23:00:27  warmerda
+ * New
+ *
+ */
+
+#ifndef TIF_MEMIO_H_INCLUDED
+#define TIF_MEMIO_H_INCLUDED
+
+#include "tiffio.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct {
+    unsigned char *data;  /* allocated with _TIFFmalloc() */
+    tsize_t        size;  /* "file" size in bytes */
+    tsize_t        data_buf_size; /* in bytes - may be larger than used */
+    toff_t         offset; /* current file offset from start of file. */
+    int            own_buffer; /* non-zero if 'data' is owned by MemIOBuf. */
+} MemIOBuf;
+
+void  MemIO_InitBuf( MemIOBuf *, int size, unsigned char *data );
+void  MemIO_DeinitBuf( MemIOBuf * );
+
+tsize_t MemIO_ReadProc( thandle_t fd, tdata_t buf, tsize_t size );
+tsize_t MemIO_WriteProc( thandle_t fd, tdata_t buf, tsize_t size );
+toff_t  MemIO_SeekProc( thandle_t fd, toff_t off, int whence );
+int     MemIO_CloseProc( thandle_t fd );
+toff_t  MemIO_SizeProc( thandle_t fd );
+int     MemIO_MapProc( thandle_t fd, tdata_t *pbase, toff_t *psize );
+void    MemIO_UnmapProc( thandle_t fd, tdata_t base, toff_t size );
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* ndef TIF_MEMIO_H_INCLUDED */    
diff --git a/Utilities/GDAL/frmts/gtiff/tif_overview.c b/Utilities/GDAL/frmts/gtiff/tif_overview.c
new file mode 100644
index 0000000000..b37f2ecaf0
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/tif_overview.c
@@ -0,0 +1,644 @@
+/******************************************************************************
+ * tif_overview.c,v 1.9 2005/05/25 09:03:16 dron Exp
+ *
+ * Project:  TIFF Overview Builder
+ * Purpose:  Library function for building overviews in a TIFF file.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ * Notes:
+ *  o Currently only images with bits_per_sample of a multiple of eight
+ *    will work.
+ *
+ *  o The downsampler currently just takes the top left pixel from the
+ *    source rectangle.  Eventually sampling options of averaging, mode, and
+ *    ``center pixel'' should be offered.
+ *
+ *  o The code will attempt to use the same kind of compression,
+ *    photometric interpretation, and organization as the source image, but
+ *    it doesn't copy geotiff tags to the reduced resolution images.
+ *
+ *  o Reduced resolution overviews for multi-sample files will currently
+ *    always be generated as PLANARCONFIG_SEPARATE.  This could be fixed
+ *    reasonable easily if needed to improve compatibility with other
+ *    packages.  Many don't properly support PLANARCONFIG_SEPARATE. 
+ * 
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tiffio.h"
+#include "tif_ovrcache.h"
+
+#include "cpl_port.h"
+
+CPL_CVSID("tif_overview.c,v 1.2 2001/07/18 04:51:56 warmerda Exp");
+
+#ifndef FALSE
+#  define FALSE 0
+#  define TRUE 1
+#endif
+
+#ifndef MAX
+#  define MIN(a,b)      ((a<b) ? a : b)
+#  define MAX(a,b)      ((a>b) ? a : b)
+#endif
+
+void TIFFBuildOverviews( TIFF *, int, int *, int, const char *,
+                         int (*)(double,void*), void * );
+
+/************************************************************************/
+/*                         TIFF_WriteOverview()                         */
+/*                                                                      */
+/*      Create a new directory, without any image data for an overview. */
+/*      Returns offset of newly created overview directory, but the     */
+/*      current directory is reset to be the one in used when this      */
+/*      function is called.                                             */
+/************************************************************************/
+
+uint32 TIFF_WriteOverview( TIFF *hTIFF, int nXSize, int nYSize,
+                           int nBitsPerPixel, int nPlanarConfig, int nSamples, 
+                           int nBlockXSize, int nBlockYSize,
+                           int bTiled, int nCompressFlag, int nPhotometric,
+                           int nSampleFormat,
+                           unsigned short *panRed,
+                           unsigned short *panGreen,
+                           unsigned short *panBlue,
+                           int bUseSubIFDs )
+
+{
+    uint32	nBaseDirOffset;
+    uint32	nOffset;
+
+    nBaseDirOffset = TIFFCurrentDirOffset( hTIFF );
+
+    TIFFFreeDirectory( hTIFF );
+    TIFFCreateDirectory( hTIFF );
+    
+/* -------------------------------------------------------------------- */
+/*      Setup TIFF fields.                                              */
+/* -------------------------------------------------------------------- */
+    TIFFSetField( hTIFF, TIFFTAG_IMAGEWIDTH, nXSize );
+    TIFFSetField( hTIFF, TIFFTAG_IMAGELENGTH, nYSize );
+    if( nSamples == 1 )
+        TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG );
+    else
+        TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, nPlanarConfig );
+
+    TIFFSetField( hTIFF, TIFFTAG_BITSPERSAMPLE, nBitsPerPixel );
+    TIFFSetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, nSamples );
+    TIFFSetField( hTIFF, TIFFTAG_COMPRESSION, nCompressFlag );
+    TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, nPhotometric );
+    TIFFSetField( hTIFF, TIFFTAG_SAMPLEFORMAT, nSampleFormat );
+
+    if( bTiled )
+    {
+        TIFFSetField( hTIFF, TIFFTAG_TILEWIDTH, nBlockXSize );
+        TIFFSetField( hTIFF, TIFFTAG_TILELENGTH, nBlockYSize );
+    }
+    else
+        TIFFSetField( hTIFF, TIFFTAG_ROWSPERSTRIP, nBlockYSize );
+
+    TIFFSetField( hTIFF, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE );
+    
+/* -------------------------------------------------------------------- */
+/*	Write color table if one is present.				*/
+/* -------------------------------------------------------------------- */
+    if( panRed != NULL )
+    {
+        TIFFSetField( hTIFF, TIFFTAG_COLORMAP, panRed, panGreen, panBlue );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Write directory, and return byte offset.                        */
+/* -------------------------------------------------------------------- */
+    if( TIFFWriteCheck( hTIFF, bTiled, "TIFFBuildOverviews" ) == 0 )
+        return 0;
+
+    TIFFWriteDirectory( hTIFF );
+    TIFFSetDirectory( hTIFF, (tdir_t) (TIFFNumberOfDirectories(hTIFF)-1) );
+
+    nOffset = TIFFCurrentDirOffset( hTIFF );
+
+    TIFFSetSubDirectory( hTIFF, nBaseDirOffset );
+
+    return nOffset;
+}
+
+/************************************************************************/
+/*                       TIFF_GetSourceSamples()                        */
+/************************************************************************/
+
+static void 
+TIFF_GetSourceSamples( double * padfSamples, unsigned char *pabySrc, 
+                       int nPixelBytes, int nSampleFormat, 
+                       int nXSize, int nYSize, 
+                       int nPixelOffset, int nLineOffset )
+{
+    int  iXOff, iYOff, iSample;
+
+    iSample = 0;
+
+    for( iYOff = 0; iYOff < nYSize; iYOff++ )
+    {
+        for( iXOff = 0; iXOff < nXSize; iXOff++ )
+        {
+            unsigned char *pabyData;
+
+            pabyData = pabySrc + iYOff * nLineOffset + iXOff * nPixelOffset;
+
+            if( nSampleFormat == SAMPLEFORMAT_UINT && nPixelBytes == 1 )
+            {
+                padfSamples[iSample++] = *pabyData;
+            }
+            else if( nSampleFormat == SAMPLEFORMAT_UINT && nPixelBytes == 2 )
+            {
+                padfSamples[iSample++] = ((uint16 *) pabyData)[0];
+            }
+            else if( nSampleFormat == SAMPLEFORMAT_UINT && nPixelBytes == 4 )
+            {
+                padfSamples[iSample++] = ((uint32 *) pabyData)[0];
+            }
+            else if( nSampleFormat == SAMPLEFORMAT_INT && nPixelBytes == 2 )
+            {
+                padfSamples[iSample++] = ((int16 *) pabyData)[0];
+            }
+            else if( nSampleFormat == SAMPLEFORMAT_INT && nPixelBytes == 32 )
+            {
+                padfSamples[iSample++] = ((int32 *) pabyData)[0];
+            }
+            else if( nSampleFormat == SAMPLEFORMAT_IEEEFP && nPixelBytes == 4 )
+            {
+                padfSamples[iSample++] = ((float *) pabyData)[0];
+            }
+            else if( nSampleFormat == SAMPLEFORMAT_IEEEFP && nPixelBytes == 8 )
+            {
+                padfSamples[iSample++] = ((double *) pabyData)[0];
+            }
+        }
+    }
+} 
+
+/************************************************************************/
+/*                           TIFF_SetSample()                           */
+/************************************************************************/
+
+static void 
+TIFF_SetSample( unsigned char * pabyData, int nPixelBytes, int nSampleFormat, 
+                double dfValue )
+
+{
+    if( nSampleFormat == SAMPLEFORMAT_UINT && nPixelBytes == 1 )
+    {
+        *pabyData = (unsigned char) MAX(0,MIN(255,dfValue));
+    }
+    else if( nSampleFormat == SAMPLEFORMAT_UINT && nPixelBytes == 2 )
+    {
+        *((uint16 *)pabyData) = (uint16) MAX(0,MIN(65535,dfValue));
+    }
+    else if( nSampleFormat == SAMPLEFORMAT_UINT && nPixelBytes == 4 )
+    {
+        *((uint32 *)pabyData) = (uint32) dfValue;
+    }
+    else if( nSampleFormat == SAMPLEFORMAT_INT && nPixelBytes == 2 )
+    {
+        *((int16 *)pabyData) = (int16) MAX(-32768,MIN(32767,dfValue));
+    }
+    else if( nSampleFormat == SAMPLEFORMAT_INT && nPixelBytes == 32 )
+    {
+        *((int32 *)pabyData) = (int32) dfValue;
+    }
+    else if( nSampleFormat == SAMPLEFORMAT_IEEEFP && nPixelBytes == 4 )
+    {
+        *((float *)pabyData) = (float) dfValue;
+    }
+    else if( nSampleFormat == SAMPLEFORMAT_IEEEFP && nPixelBytes == 8 )
+    {
+        *((double *)pabyData) = dfValue;
+    }
+}
+
+/************************************************************************/
+/*                          TIFF_DownSample()                           */
+/*                                                                      */
+/*      Down sample a tile of full res data into a window of a tile     */
+/*      of downsampled data.                                            */
+/************************************************************************/
+
+static
+void TIFF_DownSample( unsigned char *pabySrcTile,
+                      int nBlockXSize, int nBlockYSize,
+                      int nPixelSkewBits, int nBitsPerPixel,
+                      unsigned char * pabyOTile,
+                      int nOBlockXSize, int nOBlockYSize,
+                      int nTXOff, int nTYOff, int nOMult,
+                      int nSampleFormat, const char * pszResampling )
+
+{
+    int		i, j, k, nPixelBytes = (nBitsPerPixel) / 8;
+    int		nPixelGroupBytes = (nBitsPerPixel+nPixelSkewBits)/8;
+    unsigned char *pabySrc, *pabyDst;
+    double      *padfSamples;
+
+    assert( nBitsPerPixel >= 8 );
+
+    padfSamples = (double *) malloc(sizeof(double) * nOMult * nOMult);
+
+/* ==================================================================== */
+/*      Loop over scanline chunks to process, establishing where the    */
+/*      data is going.                                                  */
+/* ==================================================================== */
+    for( j = 0; j*nOMult < nBlockYSize; j++ )
+    {
+        if( j + nTYOff >= nOBlockYSize )
+            break;
+            
+        pabyDst = pabyOTile
+            + ((j+nTYOff)*nOBlockXSize + nTXOff) * nPixelBytes;
+
+/* -------------------------------------------------------------------- */
+/*      Handler nearest resampling ... we don't even care about the     */
+/*      data type, we just do a bytewise copy.                          */
+/* -------------------------------------------------------------------- */
+        if( strncmp(pszResampling,"nearest",4) == 0
+            || strncmp(pszResampling,"NEAR",4) == 0 )
+        {
+            pabySrc = pabySrcTile + j*nOMult*nBlockXSize * nPixelGroupBytes;
+
+            for( i = 0; i*nOMult < nBlockXSize; i++ )
+            {
+                if( i + nTXOff >= nOBlockXSize )
+                    break;
+            
+                /*
+                 * For now use simple subsampling, from the top left corner
+                 * of the source block of pixels.
+                 */
+
+                for( k = 0; k < nPixelBytes; k++ )
+                {
+                    *(pabyDst++) = pabySrc[k];
+                }
+            
+                pabySrc += nOMult * nPixelGroupBytes;
+            }
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Handle the case of averaging.  For this we also have to         */
+/*      handle each sample format we are concerned with.                */
+/* -------------------------------------------------------------------- */
+        else if( strncmp(pszResampling,"averag",6) == 0 
+                 || strncmp(pszResampling,"AVERAG",6) == 0 )
+        {
+            pabySrc = pabySrcTile + j*nOMult*nBlockXSize * nPixelGroupBytes;
+
+            for( i = 0; i*nOMult < nBlockXSize; i++ )
+            {
+                double   dfTotal;
+                int      iSample;
+                int      nXSize, nYSize;
+
+                if( i + nTXOff >= nOBlockXSize )
+                    break;
+
+                nXSize = MIN(nOMult,nBlockXSize-i);
+                nYSize = MIN(nOMult,nBlockYSize-j);
+
+                TIFF_GetSourceSamples( padfSamples, pabySrc, 
+                                       nPixelBytes, nSampleFormat, 
+                                       nXSize, nYSize, 
+                                       nPixelGroupBytes, 
+                                       nPixelGroupBytes * nBlockXSize );
+                
+                dfTotal = 0;
+                for( iSample = 0; iSample < nXSize*nYSize; iSample++ )
+                {
+                    dfTotal += padfSamples[iSample];
+                }
+
+                TIFF_SetSample( pabyDst, nPixelBytes, nSampleFormat, 
+                                dfTotal / (nXSize*nYSize) );
+
+                pabySrc += nOMult * nPixelGroupBytes;
+                pabyDst += nPixelBytes;
+            }
+        }
+    }
+
+    free( padfSamples );
+}
+
+/************************************************************************/
+/*                      TIFF_ProcessFullResBlock()                      */
+/*                                                                      */
+/*      Process one block of full res data, downsampling into each      */
+/*      of the overviews.                                               */
+/************************************************************************/
+
+void TIFF_ProcessFullResBlock( TIFF *hTIFF, int nPlanarConfig,
+                               int nOverviews, int * panOvList,
+                               int nBitsPerPixel, 
+                               int nSamples, TIFFOvrCache ** papoRawBIs,
+                               int nSXOff, int nSYOff,
+                               unsigned char *pabySrcTile,
+                               int nBlockXSize, int nBlockYSize,
+                               int nSampleFormat, const char * pszResampling )
+    
+{
+    int		iOverview, iSample;
+
+    for( iSample = 0; iSample < nSamples; iSample++ )
+    {
+        /*
+         * We have to read a tile/strip for each sample for
+         * PLANARCONFIG_SEPARATE.  Otherwise, we just read all the samples
+         * at once when handling the first sample.
+         */
+        if( nPlanarConfig == PLANARCONFIG_SEPARATE || iSample == 0 )
+        {
+            if( TIFFIsTiled(hTIFF) )
+            {
+                TIFFReadEncodedTile( hTIFF,
+                                     TIFFComputeTile(hTIFF, nSXOff, nSYOff,
+                                                     0, (tsample_t)iSample ),
+                                     pabySrcTile,
+                                     TIFFTileSize(hTIFF));
+            }
+            else
+            {
+                TIFFReadEncodedStrip( hTIFF,
+                                      TIFFComputeStrip(hTIFF, nSYOff,
+                                                       (tsample_t) iSample),
+                                      pabySrcTile,
+                                      TIFFStripSize(hTIFF) );
+            }
+        }
+
+        /*        
+         * Loop over destination overview layers
+         */
+        for( iOverview = 0; iOverview < nOverviews; iOverview++ )
+        {
+            TIFFOvrCache *poRBI = papoRawBIs[iOverview];
+            unsigned char *pabyOTile;
+            int	nTXOff, nTYOff, nOXOff, nOYOff, nOMult;
+            int	nOBlockXSize = poRBI->nBlockXSize;
+            int	nOBlockYSize = poRBI->nBlockYSize;
+            int	nSkewBits, nSampleByteOffset; 
+
+            /*
+             * Fetch the destination overview tile
+             */
+            nOMult = panOvList[iOverview];
+            nOXOff = (nSXOff/nOMult) / nOBlockXSize;
+            nOYOff = (nSYOff/nOMult) / nOBlockYSize;
+            pabyOTile = TIFFGetOvrBlock( poRBI, nOXOff, nOYOff, iSample );
+                
+            /*
+             * Establish the offset into this tile at which we should
+             * start placing data.
+             */
+            nTXOff = (nSXOff - nOXOff*nOMult*nOBlockXSize) / nOMult;
+            nTYOff = (nSYOff - nOYOff*nOMult*nOBlockYSize) / nOMult;
+
+            /*
+             * Figure out the skew (extra space between ``our samples'') and
+             * the byte offset to the first sample.
+             */
+            assert( (nBitsPerPixel % 8) == 0 );
+            if( nPlanarConfig == PLANARCONFIG_SEPARATE )
+            {
+                nSkewBits = 0;
+                nSampleByteOffset = 0;
+            }
+            else
+            {
+                nSkewBits = nBitsPerPixel * (nSamples-1);
+                nSampleByteOffset = (nBitsPerPixel/8) * iSample;
+            }
+            
+            /*
+             * Perform the downsampling.
+             */
+#ifdef DBMALLOC
+            malloc_chain_check( 1 );
+#endif
+            TIFF_DownSample( pabySrcTile + nSampleByteOffset,
+                             nBlockXSize, nBlockYSize,
+                             nSkewBits, nBitsPerPixel, pabyOTile,
+                             poRBI->nBlockXSize, poRBI->nBlockYSize,
+                             nTXOff, nTYOff,
+                             nOMult, nSampleFormat, pszResampling );
+#ifdef DBMALLOC
+            malloc_chain_check( 1 );
+#endif            
+        }
+    }
+}
+
+/************************************************************************/
+/*                        TIFF_BuildOverviews()                         */
+/*                                                                      */
+/*      Build the requested list of overviews.  Overviews are           */
+/*      maintained in a bunch of temporary files and then these are     */
+/*      written back to the TIFF file.  Only one pass through the       */
+/*      source TIFF file is made for any number of output               */
+/*      overviews.                                                      */
+/************************************************************************/
+
+void TIFFBuildOverviews( TIFF *hTIFF, int nOverviews, int * panOvList,
+                         int bUseSubIFDs, const char *pszResampleMethod,
+                         int (*pfnProgress)( double, void * ),
+                         void * pProgressData )
+
+{
+    TIFFOvrCache	**papoRawBIs;
+    uint32		nXSize, nYSize, nBlockXSize, nBlockYSize;
+    uint16		nBitsPerPixel, nPhotometric, nCompressFlag, nSamples,
+                        nPlanarConfig, nSampleFormat;
+    int			bTiled, nSXOff, nSYOff, i;
+    unsigned char	*pabySrcTile;
+    uint16		*panRedMap, *panGreenMap, *panBlueMap;
+    TIFFErrorHandler    pfnWarning;
+
+/* -------------------------------------------------------------------- */
+/*      Get the base raster size.                                       */
+/* -------------------------------------------------------------------- */
+    TIFFGetField( hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize );
+    TIFFGetField( hTIFF, TIFFTAG_IMAGELENGTH, &nYSize );
+
+    TIFFGetField( hTIFF, TIFFTAG_BITSPERSAMPLE, &nBitsPerPixel );
+    TIFFGetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSamples );
+    TIFFGetFieldDefaulted( hTIFF, TIFFTAG_PLANARCONFIG, &nPlanarConfig );
+
+    TIFFGetFieldDefaulted( hTIFF, TIFFTAG_PHOTOMETRIC, &nPhotometric );
+    TIFFGetFieldDefaulted( hTIFF, TIFFTAG_COMPRESSION, &nCompressFlag );
+    TIFFGetFieldDefaulted( hTIFF, TIFFTAG_SAMPLEFORMAT, &nSampleFormat );
+
+    if( nBitsPerPixel < 8 )
+    {
+        TIFFError( "TIFFBuildOverviews",
+                   "File `%s' has samples of %d bits per sample.  Sample\n"
+                   "sizes of less than 8 bits per sample are not supported.\n",
+                   TIFFFileName(hTIFF), nBitsPerPixel );
+        return;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Turn off warnings to avoid alot of repeated warnings while      */
+/*      rereading directories.                                          */
+/* -------------------------------------------------------------------- */
+    pfnWarning = TIFFSetWarningHandler( NULL );
+
+/* -------------------------------------------------------------------- */
+/*      Get the base raster block size.                                 */
+/* -------------------------------------------------------------------- */
+    if( TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP, &(nBlockYSize) ) )
+    {
+        nBlockXSize = nXSize;
+        bTiled = FALSE;
+    }
+    else
+    {
+        TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &nBlockXSize );
+        TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &nBlockYSize );
+        bTiled = TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*	Capture the pallette if there is one.				*/
+/* -------------------------------------------------------------------- */
+    if( TIFFGetField( hTIFF, TIFFTAG_COLORMAP,
+                      &panRedMap, &panGreenMap, &panBlueMap ) )
+    {
+        uint16		*panRed2, *panGreen2, *panBlue2;
+        int             nColorCount = 1 << nBitsPerPixel;
+
+        panRed2 = (uint16 *) _TIFFmalloc(2*nColorCount);
+        panGreen2 = (uint16 *) _TIFFmalloc(2*nColorCount);
+        panBlue2 = (uint16 *) _TIFFmalloc(2*nColorCount);
+
+        memcpy( panRed2, panRedMap, 2 * nColorCount );
+        memcpy( panGreen2, panGreenMap, 2 * nColorCount );
+        memcpy( panBlue2, panBlueMap, 2 * nColorCount );
+
+        panRedMap = panRed2;
+        panGreenMap = panGreen2;
+        panBlueMap = panBlue2;
+    }
+    else
+    {
+        panRedMap = panGreenMap = panBlueMap = NULL;
+    }
+        
+/* -------------------------------------------------------------------- */
+/*      Initialize overviews.                                           */
+/* -------------------------------------------------------------------- */
+    papoRawBIs = (TIFFOvrCache **) _TIFFmalloc(nOverviews*sizeof(void*));
+
+    for( i = 0; i < nOverviews; i++ )
+    {
+        int	nOXSize, nOYSize, nOBlockXSize, nOBlockYSize;
+        uint32  nDirOffset;
+
+        nOXSize = (nXSize + panOvList[i] - 1) / panOvList[i];
+        nOYSize = (nYSize + panOvList[i] - 1) / panOvList[i];
+
+        nOBlockXSize = MIN((int)nBlockXSize,nOXSize);
+        nOBlockYSize = MIN((int)nBlockYSize,nOYSize);
+
+        if( bTiled )
+        {
+            if( (nOBlockXSize % 16) != 0 )
+                nOBlockXSize = nOBlockXSize + 16 - (nOBlockXSize % 16);
+            
+            if( (nOBlockYSize % 16) != 0 )
+                nOBlockYSize = nOBlockYSize + 16 - (nOBlockYSize % 16);
+        }
+
+        nDirOffset = TIFF_WriteOverview( hTIFF, nOXSize, nOYSize,
+                                         nBitsPerPixel, nPlanarConfig,
+                                         nSamples, nOBlockXSize, nOBlockYSize,
+                                         bTiled, nCompressFlag, nPhotometric,
+                                         nSampleFormat,
+                                         panRedMap, panGreenMap, panBlueMap,
+                                         bUseSubIFDs );
+        
+        papoRawBIs[i] = TIFFCreateOvrCache( hTIFF, nDirOffset );
+    }
+
+    if( panRedMap != NULL )
+    {
+        _TIFFfree( panRedMap );
+        _TIFFfree( panGreenMap );
+        _TIFFfree( panBlueMap );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Allocate a buffer to hold a source block.                       */
+/* -------------------------------------------------------------------- */
+    if( bTiled )
+        pabySrcTile = (unsigned char *) _TIFFmalloc(TIFFTileSize(hTIFF));
+    else
+        pabySrcTile = (unsigned char *) _TIFFmalloc(TIFFStripSize(hTIFF));
+    
+/* -------------------------------------------------------------------- */
+/*      Loop over the source raster, applying data to the               */
+/*      destination raster.                                             */
+/* -------------------------------------------------------------------- */
+    for( nSYOff = 0; nSYOff < (int) nYSize; nSYOff += nBlockYSize )
+    {
+        for( nSXOff = 0; nSXOff < (int) nXSize; nSXOff += nBlockXSize )
+        {
+            /*
+             * Read and resample into the various overview images.
+             */
+            
+            TIFF_ProcessFullResBlock( hTIFF, nPlanarConfig,
+                                      nOverviews, panOvList,
+                                      nBitsPerPixel, nSamples, papoRawBIs,
+                                      nSXOff, nSYOff, pabySrcTile,
+                                      nBlockXSize, nBlockYSize,
+                                      nSampleFormat, pszResampleMethod );
+        }
+    }
+
+    _TIFFfree( pabySrcTile );
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup the rawblockedimage files.                              */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < nOverviews; i++ )
+    {
+        TIFFDestroyOvrCache( papoRawBIs[i] );
+    }
+
+    if( papoRawBIs != NULL )
+        _TIFFfree( papoRawBIs );
+
+    TIFFSetWarningHandler( pfnWarning );
+}
diff --git a/Utilities/GDAL/frmts/gtiff/tif_ovrcache.c b/Utilities/GDAL/frmts/gtiff/tif_ovrcache.c
new file mode 100644
index 0000000000..0a09f6538e
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/tif_ovrcache.c
@@ -0,0 +1,273 @@
+/******************************************************************************
+ * tif_ovrcache.c,v 1.1 2000/11/24 18:13:43 warmerda Exp
+ *
+ * Project:  TIFF Overview Builder
+ * Purpose:  Library functions to maintain two rows of tiles or two strips
+ *           of data for output overviews as an output cache. 
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * tif_ovrcache.c,v
+ * Revision 1.1  2000/11/24 18:13:43  warmerda
+ * New
+ *
+ * Revision 1.2  2000/01/28 15:42:25  warmerda
+ * Avoid warnings on Windows.
+ *
+ * Revision 1.1  2000/01/28 15:03:44  warmerda
+ * New
+ *
+ */
+
+#include "cpl_port.h"
+#include "tiffio.h"
+#include "tif_ovrcache.h"
+#include <assert.h>
+
+CPL_CVSID("tif_ovrcache.c,v 1.2 2001/07/18 04:51:56 warmerda Exp");
+
+/************************************************************************/
+/*                         TIFFCreateOvrCache()                         */
+/*                                                                      */
+/*      Create an overview cache to hold two rows of blocks from an     */
+/*      existing TIFF directory.                                        */
+/************************************************************************/
+
+TIFFOvrCache *TIFFCreateOvrCache( TIFF *hTIFF, int nDirOffset )
+
+{
+    TIFFOvrCache	*psCache;
+    int			nBytesPerRow;
+    uint32		nBaseDirOffset;
+
+    psCache = (TIFFOvrCache *) _TIFFmalloc(sizeof(TIFFOvrCache));
+    psCache->nDirOffset = nDirOffset;
+    psCache->hTIFF = hTIFF;
+    
+/* -------------------------------------------------------------------- */
+/*      Get definition of this raster from the TIFF file itself.        */
+/* -------------------------------------------------------------------- */
+    nBaseDirOffset = TIFFCurrentDirOffset( psCache->hTIFF );
+    TIFFSetSubDirectory( hTIFF, nDirOffset );
+    
+    TIFFGetField( hTIFF, TIFFTAG_IMAGEWIDTH, &(psCache->nXSize) );
+    TIFFGetField( hTIFF, TIFFTAG_IMAGELENGTH, &(psCache->nYSize) );
+
+    TIFFGetField( hTIFF, TIFFTAG_BITSPERSAMPLE, &(psCache->nBitsPerPixel) );
+    TIFFGetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, &(psCache->nSamples) );
+
+    if( !TIFFIsTiled( hTIFF ) )
+    {
+        TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP, &(psCache->nBlockYSize) );
+        psCache->nBlockXSize = psCache->nXSize;
+        psCache->bTiled = FALSE;
+    }
+    else
+    {
+        TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &(psCache->nBlockXSize) );
+        TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &(psCache->nBlockYSize) );
+        psCache->bTiled = TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Compute some values from this.                                  */
+/* -------------------------------------------------------------------- */
+
+    psCache->nBlocksPerRow = (psCache->nXSize + psCache->nBlockXSize - 1)
+        		/ psCache->nBlockXSize;
+    psCache->nBlocksPerColumn = (psCache->nYSize + psCache->nBlockYSize - 1)
+        		/ psCache->nBlockYSize;
+    
+    psCache->nBytesPerBlock =
+        (psCache->nBlockXSize*psCache->nBlockYSize*psCache->nBitsPerPixel + 7) / 8;
+
+/* -------------------------------------------------------------------- */
+/*      Allocate and initialize the data buffers.                       */
+/* -------------------------------------------------------------------- */
+    nBytesPerRow = psCache->nBytesPerBlock * psCache->nBlocksPerRow
+                    * psCache->nSamples;
+
+    psCache->pabyRow1Blocks = (unsigned char *) _TIFFmalloc(nBytesPerRow);
+    psCache->pabyRow2Blocks = (unsigned char *) _TIFFmalloc(nBytesPerRow);
+
+    if( psCache->pabyRow1Blocks == NULL
+        || psCache->pabyRow2Blocks == NULL )
+    {
+        TIFFError( "TIFFCreateOvrCache",
+                   "Can't allocate memory for overview cache." );
+        return NULL;
+    }
+
+    _TIFFmemset( psCache->pabyRow1Blocks, 0, nBytesPerRow );
+    _TIFFmemset( psCache->pabyRow2Blocks, 0, nBytesPerRow );
+
+    psCache->nBlockOffset = 0;
+
+    TIFFSetSubDirectory( psCache->hTIFF, nBaseDirOffset );
+    
+    return psCache;
+}
+
+/************************************************************************/
+/*                          TIFFWriteOvrRow()                           */
+/*                                                                      */
+/*      Write one entire row of blocks (row 1) to the tiff file, and    */
+/*      then rotate the block buffers, essentially moving things        */
+/*      down by one block.                                              */
+/************************************************************************/
+
+static void TIFFWriteOvrRow( TIFFOvrCache * psCache )
+
+{
+    int		nRet, iSample, iTileX, iTileY = psCache->nBlockOffset;
+    unsigned char *pabyData;
+    uint32	nBaseDirOffset;
+    
+/* -------------------------------------------------------------------- */
+/*	If the output cache is multi-byte per sample, and the file	*/
+/*	being written to is of a different byte order than the current	*/
+/*	platform, we will need to byte swap the data. 			*/
+/* -------------------------------------------------------------------- */
+    if( TIFFIsByteSwapped(psCache->hTIFF) )
+    {
+        if( psCache->nBitsPerPixel == 16 )
+            TIFFSwabArrayOfShort( (uint16 *) psCache->pabyRow1Blocks,
+                      (psCache->nBytesPerBlock * psCache->nSamples) / 2 );
+
+        else if( psCache->nBitsPerPixel == 32 )
+            TIFFSwabArrayOfLong( (uint32 *) psCache->pabyRow1Blocks,
+                         (psCache->nBytesPerBlock * psCache->nSamples) / 4 );
+
+        else if( psCache->nBitsPerPixel == 64 )
+            TIFFSwabArrayOfDouble( (double *) psCache->pabyRow1Blocks,
+                         (psCache->nBytesPerBlock * psCache->nSamples) / 8 );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Record original directory position, so we can restore it at     */
+/*      end.                                                            */
+/* -------------------------------------------------------------------- */
+    nBaseDirOffset = TIFFCurrentDirOffset( psCache->hTIFF );
+    nRet = TIFFSetSubDirectory( psCache->hTIFF, psCache->nDirOffset );
+    assert( nRet == 1 );
+
+/* -------------------------------------------------------------------- */
+/*      Write blocks to TIFF file.                                      */
+/* -------------------------------------------------------------------- */
+    for( iTileX = 0; iTileX < psCache->nBlocksPerRow; iTileX++ )
+    {
+        for( iSample = 0; iSample < psCache->nSamples; iSample++ )
+        {
+            int		  nTileID;
+
+            pabyData = TIFFGetOvrBlock( psCache, iTileX, iTileY, iSample );
+
+            if( psCache->bTiled )
+            {
+                nTileID =
+                    TIFFComputeTile(psCache->hTIFF,
+                                    iTileX * psCache->nBlockXSize,
+                                    iTileY * psCache->nBlockYSize,
+                                    0, (tsample_t) iSample );
+                TIFFWriteEncodedTile( psCache->hTIFF, nTileID, 
+                                      pabyData,
+                                      TIFFTileSize(psCache->hTIFF) );
+            }
+            else
+            {
+                nTileID =
+                    TIFFComputeStrip(psCache->hTIFF,
+                                     iTileY * psCache->nBlockYSize,
+                                     (tsample_t) iSample);
+
+                TIFFWriteEncodedStrip( psCache->hTIFF, nTileID,
+                                       pabyData,
+                                       TIFFStripSize(psCache->hTIFF) );
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Rotate buffers.                                                 */
+/* -------------------------------------------------------------------- */
+    pabyData = psCache->pabyRow1Blocks;
+    psCache->pabyRow1Blocks = psCache->pabyRow2Blocks;
+    psCache->pabyRow2Blocks = pabyData;
+
+    _TIFFmemset( pabyData, 0,
+                 psCache->nBytesPerBlock * psCache->nSamples
+                 * psCache->nBlocksPerRow );
+
+    psCache->nBlockOffset++;
+
+/* -------------------------------------------------------------------- */
+/*      Restore access to original directory.                           */
+/* -------------------------------------------------------------------- */
+    TIFFFlush( psCache->hTIFF );
+    
+    TIFFSetSubDirectory( psCache->hTIFF, nBaseDirOffset );
+}
+
+/************************************************************************/
+/*                          TIFFGetOvrBlock()                           */
+/************************************************************************/
+
+unsigned char *TIFFGetOvrBlock( TIFFOvrCache *psCache, int iTileX, int iTileY,
+                                int iSample )
+
+{
+    int		nRowOffset;
+    
+    if( iTileY > psCache->nBlockOffset + 1 )
+        TIFFWriteOvrRow( psCache );
+
+    assert( iTileX >= 0 && iTileX < psCache->nBlocksPerRow );
+    assert( iTileY >= 0 && iTileY < psCache->nBlocksPerColumn );
+    assert( iTileY >= psCache->nBlockOffset
+            && iTileY < psCache->nBlockOffset+2 );
+    assert( iSample >= 0 && iSample < psCache->nSamples );
+
+    nRowOffset = ((iTileX * psCache->nSamples) + iSample)
+        		* psCache->nBytesPerBlock;
+
+    if( iTileY == psCache->nBlockOffset )
+        return psCache->pabyRow1Blocks + nRowOffset;
+    else
+        return psCache->pabyRow2Blocks + nRowOffset;
+}
+
+/************************************************************************/
+/*                        TIFFDestroyOvrCache()                         */
+/************************************************************************/
+
+void TIFFDestroyOvrCache( TIFFOvrCache * psCache )
+
+{
+    while( psCache->nBlockOffset < psCache->nBlocksPerColumn )
+        TIFFWriteOvrRow( psCache );
+
+    _TIFFfree( psCache->pabyRow1Blocks );
+    _TIFFfree( psCache->pabyRow2Blocks );
+    _TIFFfree( psCache );
+}
diff --git a/Utilities/GDAL/frmts/gtiff/tif_ovrcache.h b/Utilities/GDAL/frmts/gtiff/tif_ovrcache.h
new file mode 100644
index 0000000000..b9ba62c217
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/tif_ovrcache.h
@@ -0,0 +1,93 @@
+/******************************************************************************
+ * tif_ovrcache.h,v 1.3 2005/05/25 09:03:16 dron Exp
+ *
+ * Project:  TIFF Overview Builder
+ * Purpose:  Library functions to maintain two rows of tiles or two strips
+ *           of data for output overviews as an output cache. 
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ * This code could potentially be used by other applications wanting to
+ * manage a once-through write cache. 
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ */
+
+#ifndef TIF_OVRCACHE_H_INCLUDED
+#define TIF_OVRCACHE_H_INCLUDED
+
+#include "tiffio.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+    
+typedef struct 
+{
+    uint32	nXSize;
+    uint32	nYSize;
+
+    uint32	nBlockXSize;
+    uint32	nBlockYSize;
+    uint16	nBitsPerPixel;
+    uint16	nSamples;
+    int		nBytesPerBlock;
+
+    int		nBlocksPerRow;
+    int		nBlocksPerColumn;
+
+    int	        nBlockOffset; /* what block is the first in papabyBlocks? */
+    unsigned char *pabyRow1Blocks;
+    unsigned char *pabyRow2Blocks;
+
+    int		nDirOffset;
+    TIFF	*hTIFF;
+    int		bTiled;
+    
+} TIFFOvrCache;
+
+TIFFOvrCache *TIFFCreateOvrCache( TIFF *hTIFF, int nDirOffset );
+unsigned char *TIFFGetOvrBlock( TIFFOvrCache *, int, int, int );
+void           TIFFDestroyOvrCache( TIFFOvrCache * );
+
+void TIFFBuildOverviews( TIFF *, int, int *, int, const char *,
+                         int (*)(double,void*), void * );
+
+void TIFF_ProcessFullResBlock( TIFF *hTIFF, int nPlanarConfig,
+                               int nOverviews, int * panOvList,
+                               int nBitsPerPixel, 
+                               int nSamples, TIFFOvrCache ** papoRawBIs,
+                               int nSXOff, int nSYOff,
+                               unsigned char *pabySrcTile,
+                               int nBlockXSize, int nBlockYSize,
+                               int nSampleFormat, const char * pszResampling );
+
+uint32 TIFF_WriteOverview( TIFF *, int, int, int, int, int, int, int,
+                           int, int, int, int, unsigned short *,
+                           unsigned short *, unsigned short *, int );
+
+#if defined(__cplusplus)
+}
+#endif
+    
+#endif /* ndef TIF_OVRCACHE_H_INCLUDED */
+
diff --git a/Utilities/GDAL/frmts/gtiff/tifvsi.cpp b/Utilities/GDAL/frmts/gtiff/tifvsi.cpp
new file mode 100644
index 0000000000..d687ad971d
--- /dev/null
+++ b/Utilities/GDAL/frmts/gtiff/tifvsi.cpp
@@ -0,0 +1,169 @@
+/******************************************************************************
+ * $Id: tifvsi.cpp,v 1.5 2006/02/19 22:33:20 fwarmerdam Exp $
+ *
+ * Project:  GeoTIFF Driver
+ * Purpose:  Implement system hook functions for libtiff on top of CPL/VSI,
+ *           including > 2GB support.  Based on tif_unix.c from libtiff
+ *           distribution.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frank Warmerdam, warmerdam@pobox.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: tifvsi.cpp,v $
+ * Revision 1.5  2006/02/19 22:33:20  fwarmerdam
+ * undo last change, needs to be handled elsewhere
+ *
+ * Revision 1.4  2006/02/19 22:29:37  fwarmerdam
+ * added sys/errno.h for macos x
+ *
+ * Revision 1.3  2005/10/13 19:44:25  fwarmerdam
+ * Improve open error reporting.
+ *
+ * Revision 1.2  2005/09/12 17:06:17  fwarmerdam
+ * avoid dependence on xtiffio.h
+ *
+ * Revision 1.1  2005/09/12 00:28:35  fwarmerdam
+ * New
+ *
+ */
+
+/*
+ * TIFF Library UNIX-specific Routines.
+ */
+#include "tiffio.h"
+#include "cpl_vsi.h"
+
+// We avoid including xtiffio.h since it drags in the libgeotiff version
+// of the VSI functions.
+
+CPL_C_START
+extern TIFF CPL_DLL * XTIFFClientOpen(const char* name, const char* mode, 
+                                      thandle_t thehandle,
+                                      TIFFReadWriteProc, TIFFReadWriteProc,
+                                      TIFFSeekProc, TIFFCloseProc,
+                                      TIFFSizeProc,
+                                      TIFFMapFileProc, TIFFUnmapFileProc);
+CPL_C_END
+
+static tsize_t
+_tiffReadProc(thandle_t fd, tdata_t buf, tsize_t size)
+{
+    return VSIFReadL( buf, 1, size, (FILE *) fd );
+}
+
+static tsize_t
+_tiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size)
+{
+    return VSIFWriteL( buf, 1, size, (FILE *) fd );
+}
+
+static toff_t
+_tiffSeekProc(thandle_t fd, toff_t off, int whence)
+{
+    if( VSIFSeekL( (FILE *) fd, off, whence ) == 0 )
+        return (toff_t) VSIFTellL( (FILE *) fd );
+    else
+        return (toff_t) -1;
+}
+
+static int
+_tiffCloseProc(thandle_t fd)
+{
+    return VSIFCloseL( (FILE *) fd );
+}
+
+static toff_t
+_tiffSizeProc(thandle_t fd)
+{
+    vsi_l_offset  old_off;
+    toff_t        file_size;
+
+    old_off = VSIFTellL( (FILE *) fd );
+    VSIFSeekL( (FILE *) fd, 0, SEEK_END );
+    
+    file_size = (toff_t) VSIFTellL( (FILE *) fd );
+    VSIFSeekL( (FILE *) fd, old_off, SEEK_SET );
+
+    return file_size;
+}
+
+static int
+_tiffMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize)
+{
+	(void) fd; (void) pbase; (void) psize;
+	return (0);
+}
+
+static void
+_tiffUnmapProc(thandle_t fd, tdata_t base, toff_t size)
+{
+	(void) fd; (void) base; (void) size;
+}
+
+/*
+ * Open a TIFF file for read/writing.
+ */
+TIFF* VSI_TIFFOpen(const char* name, const char* mode)
+{
+    static const char module[] = "TIFFOpen";
+    int           i, a_out;
+    char          access[32];
+    FILE          *fp;
+    TIFF          *tif;
+
+    a_out = 0;
+    access[0] = '\0';
+    for( i = 0; mode[i] != '\0'; i++ )
+    {
+        if( mode[i] == 'r'
+            || mode[i] == 'w'
+            || mode[i] == '+'
+            || mode[i] == 'a' )
+        {
+            access[a_out++] = mode[i];
+            access[a_out] = '\0';
+        }
+    }
+
+    strcat( access, "b" );
+                    
+    fp = VSIFOpenL( name, access );
+    if (fp == NULL) {
+        if( errno >= 0 )
+            TIFFError(module,"%s: %s", name, VSIStrerror( errno ) );
+        else
+            TIFFError(module, "%s: Cannot open", name);
+        return ((TIFF *)0);
+    }
+
+    tif = XTIFFClientOpen(name, mode,
+                          (thandle_t) fp,
+                          _tiffReadProc, _tiffWriteProc,
+                          _tiffSeekProc, _tiffCloseProc, _tiffSizeProc,
+                          _tiffMapProc, _tiffUnmapProc);
+
+    if( tif == NULL )
+        VSIFCloseL( fp );
+        
+    return tif;
+}
diff --git a/Utilities/GDAL/frmts/gxf/.cvsignore b/Utilities/GDAL/frmts/gxf/.cvsignore
new file mode 100644
index 0000000000..9417f7c69a
--- /dev/null
+++ b/Utilities/GDAL/frmts/gxf/.cvsignore
@@ -0,0 +1,7 @@
+html
+man
+gxftest
+gxf3_1_0
+*.tar.gz
+*.zip
+
diff --git a/Utilities/GDAL/frmts/gxf/Doxyfile b/Utilities/GDAL/frmts/gxf/Doxyfile
new file mode 100644
index 0000000000..751deb81c4
--- /dev/null
+++ b/Utilities/GDAL/frmts/gxf/Doxyfile
@@ -0,0 +1,255 @@
+# This file describes the settings to be used by doxygen for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of word surrounded
+# by quotes) that should identify the project. 
+
+PROJECT_NAME         =	GXF
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER       =	
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY     =
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT          =
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT          =
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER          =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER          =
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS             = YES
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each page. A value of NO (the default) enables the index and the
+# value YES disables it.
+
+DISABLE_INDEX        = NO
+
+# If the EXTRACT_ALL tag is set to YES all classes and functions will be
+# included in the documentation, even if no documentation was available.
+
+EXTRACT_ALL          = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE      = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members inside documented classes or files.
+
+HIDE_UNDOC_MEMBERS   = YES
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX       = NO
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output
+
+GENERATE_HTML        = YES
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the FULL_PATH_NAMES tag is set to YES Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used
+
+FULL_PATH_NAMES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT            =	gxfopen.c gxfopen.h gxf_proj4.c gxf.dox gxf_ogcwkt.c
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+FILE_PATTERNS    =	*.*
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH     = .
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE        = NO
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+
+INPUT_FILTER     = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor 
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed.
+
+MACRO_EXPANSION = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). In the former case 1 is used as the
+# definition.
+
+PREDEFINED =
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED tag.
+
+EXPAND_ONLY_PREDEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references 
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles. 
+
+TAGFILES         =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS     = NO
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH        = /usr/local/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the search engine 
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE     = NO
+
+# The CGI_NAME tag should be the name of the CGI script that
+# starts the search engine (doxysearch) with the correct parameters.
+# A script with this name will be generated by doxygen.
+
+CGI_NAME         = search.cgi
+
+# The CGI_URL tag should be the absolute URL to the directory where the
+# cgi binaries are located. See the documentation of your http daemon for 
+# details.
+
+CGI_URL          =
+
+# The DOC_URL tag should be the absolute URL to the directory where the
+# documentation is located. If left blank the absolute path to the 
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL          =
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the
+# documentation is located. If left blank the directory on the local machine
+# will be used.
+
+DOC_ABSPATH      =
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary
+# is installed.
+
+BIN_ABSPATH      = /usr/local/bin/
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to 
+# documentation generated for other projects. This allows doxysearch to search
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS    =
diff --git a/Utilities/GDAL/frmts/gxf/GNUmakefile b/Utilities/GDAL/frmts/gxf/GNUmakefile
new file mode 100644
index 0000000000..28d4db9e51
--- /dev/null
+++ b/Utilities/GDAL/frmts/gxf/GNUmakefile
@@ -0,0 +1,64 @@
+VERSION =	1_0
+DISTDIR =	gxf3_$(VERSION)
+WEB_DIR =	/u/www/projects/gxf
+SHARED_LIB =	gdal_GXF.so
+
+
+include ../../GDALmake.opt
+
+GXFOBJ	=	gxfopen.o gxf_proj4.o gxf_ogcwkt.o
+OBJ	=	gxfdataset.o $(GXFOBJ)
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o
+
+gxftest:	gxftest.o $(GXFOBJ)
+	$(CXX) gxftest.o $(GXFOBJ) ../../port/*.o $(LIBS) -o gxftest
+
+install-obj:	$(O_OBJ)
+
+$(SHARED_LIB):	$(OBJ)
+	$(LD_SHARED) $(OBJ) $(GDAL_SLIB_LINK) $(LIBS) -o $(SHARED_LIB) 
+
+install-lib:	$(SHARED_LIB)
+	cp $(SHARED_LIB) ../../lib
+
+docs:
+	rm -rf html
+	mkdir html
+	doxygen
+
+update-web:	dist
+	cp html/* $(WEB_DIR)
+	cp $(DISTDIR).tar.gz /u/ftp/pub/outgoing
+	cp $(DISTDIR).zip /u/ftp/pub/outgoing
+
+
+dist:	docs
+	rm -rf $(DISTDIR)
+	mkdir $(DISTDIR)
+	mkdir $(DISTDIR)/html
+	cp html/* $(DISTDIR)/html
+	autoconf
+	cp *.c *.h configure Makefile.in $(DISTDIR)
+	cp makefile.vc.dist $(DISTDIR)/makefile.vc
+	rm configure
+	cp ../../port/cpl_conv.cpp $(DISTDIR)/cpl_conv.c
+	cp ../../port/cpl_conv.h $(DISTDIR)
+	cp ../../port/cpl_string.cpp $(DISTDIR)/cpl_string.c
+	cp ../../port/cpl_string.h $(DISTDIR)
+	cp ../../port/cpl_vsisimple.cpp $(DISTDIR)/cpl_vsisimple.c
+	cp ../../port/cpl_vsi.h $(DISTDIR)
+	cp ../../port/cpl_error.cpp $(DISTDIR)/cpl_error.c
+	cp ../../port/cpl_error.h $(DISTDIR)
+	cp ../../port/cpl_port.h $(DISTDIR)
+	cp ../../port/cpl_config.h $(DISTDIR)
+	cp ../../port/cpl_config.h.in $(DISTDIR)
+	rm -f $(DISTDIR)/*.o
+	tar czf $(DISTDIR).tar.gz $(DISTDIR)
+	zip -r $(DISTDIR).zip $(DISTDIR)
+
diff --git a/Utilities/GDAL/frmts/gxf/Makefile.in b/Utilities/GDAL/frmts/gxf/Makefile.in
new file mode 100644
index 0000000000..b6f3dea131
--- /dev/null
+++ b/Utilities/GDAL/frmts/gxf/Makefile.in
@@ -0,0 +1,34 @@
+
+OBJ =	gxfopen.o gxf_proj4.o gxf_ogcwkt.o \
+	\
+	cpl_error.o cpl_vsisimple.o cpl_string.o cpl_conv.o
+
+CFLAGS = @CFLAGS@
+LIBS 	= @LIBS@ -lm
+CC	= @CC@
+
+
+default:	8211view
+
+libgxf3.a:	$(OBJ)
+	ar r libgxf3.a $(OBJ)
+
+cpl_error.o:	cpl_error.c
+	$(CC) -c $(CFLAGS) cpl_error.c
+
+cpl_string.o:	cpl_string.c
+	$(CC) -c $(CFLAGS) cpl_string.c
+
+cpl_conv.o:	cpl_conv.c
+	$(CC) -c $(CFLAGS) cpl_conv.c
+
+cpl_vsisimple.o:	cpl_vsisimple.c
+	$(CC) -c $(CFLAGS) cpl_vsisimple.c
+
+#
+#	Mainlines
+#
+
+gxftest:	gxftest.c libgxf3.a
+	$(CC) $(CFLAGS) gxftest.c libgxf3.a $(LIBS) -o gxftest
+
diff --git a/Utilities/GDAL/frmts/gxf/README b/Utilities/GDAL/frmts/gxf/README
new file mode 100644
index 0000000000..60f8166527
--- /dev/null
+++ b/Utilities/GDAL/frmts/gxf/README
@@ -0,0 +1,65 @@
+Notes
+-----
+
+Gilles Clement of Global Geomatics approved this support library
+for general OpenSource release six months after it was released
+as part of OGDI.  This should be approximately September of 1999. 
+
+
+GXF Irregularities
+------------------
+
+ o The #TRANSFORM is defined to have commas between fields, but in the
+   example files it has spaces.  Treating both the same.
+
+ o The document doesn't memtion it, but it seems that the first five
+   characters have to be taken as sufficient for a field name.  For 
+   instance the old sample file I have has only #TITL for the title,
+   while new ones have #TITLE. 
+
+ o It seems according to the documentation that Geographic projected
+   files should list "Geographic" as their projection method, but 
+   samples seem to just skip the third line of the map projection
+   definition.
+
+ o Apparently geosoft files don't have to have all the map projection
+   arguments listed in the document.  For instance, eltoro.gxf's LCC
+   projection doesn't contain the false easting and northing.
+
+ o I reprojected gxf_text.gxf to lat/long producing latlong.gxf.  This
+   file contains a #MAP_PROJECTION of "NAD83 / UTM zone 19N" even though
+   everything is in degrees.  Why?  Shouldn't it read "Geographic"?
+
+
+
+GG GeoTIFF Irregularities
+-------------------------
+
+ o LCC_1SP seems to ignore the scale factor at natural origin parameter.
+   (ProjScaleAtNatOriginGeoKey) (should be +k= in PROJ.4).
+
+ o 1SP form of LCC doesn't seem to have an analog in PROJ.4 -- 
+   I have transformed the Longitude of Natural Origin as +lon_0, and the
+   scale as +k, but it isn't clear that the scale parameter is really
+   used for +proj=lcc, at least according to the documentation.  
+   This _isn't_ done by the GG Geotiff translator.
+
+ o the PROJ.4 Oblique Mercator support doesn't (apparently) include
+   support for an ``Angle from Rectified to Skew Grid'' though via the
+   the +no_rot flag it seems possible to choose between having this
+   angle be zero or -azimuth.  Currently I am just using the angle
+   to decide if +no_rot should be set. 
+
+ o The Polar Stereographic projection in GeoTIFF is translated to 
+   +proj=ups, ignoring any other polar stereographic parameters.  This
+   would appear to be an error.  For the time being I am translating as
+   Stereographic.
+
+ o It appears that PROJ.4's polyconic projection doesn't include support
+   for the scale at the origin (presumably 1.0). 
+
+
+
+
+	
+
diff --git a/Utilities/GDAL/frmts/gxf/configure.in b/Utilities/GDAL/frmts/gxf/configure.in
new file mode 100644
index 0000000000..a70e844136
--- /dev/null
+++ b/Utilities/GDAL/frmts/gxf/configure.in
@@ -0,0 +1,16 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(Makefile.in)
+AC_CONFIG_HEADER(cpl_config.h)
+
+dnl Checks for programs.
+AC_PROG_CC
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(fcntl.h unistd.h)
+
+dnl Checks for library functions.
+AC_C_BIGENDIAN
+AC_FUNC_VPRINTF
+
+AC_OUTPUT(Makefile)
diff --git a/Utilities/GDAL/frmts/gxf/gxf.dox b/Utilities/GDAL/frmts/gxf/gxf.dox
new file mode 100644
index 0000000000..0e525f5364
--- /dev/null
+++ b/Utilities/GDAL/frmts/gxf/gxf.dox
@@ -0,0 +1,91 @@
+/*! \page index
+
+<center>
+<title>GXF-3</title>
+</center>
+
+<h2>Introduction</h2>
+
+The GXF-3 library is intended to make correct implementation of GXF-3 file
+format readers easy.  It consists of free (OpenSource) C source code for 
+functions to read GXF-3 raster files, and an example program using them to 
+convert GXF data to GeoTIFF format.<p>
+
+GXF (Grid eXchange File) is a standard ASCII file format for exchanging 
+gridded data among different software systems. Software that supports the 
+GXF standard will be able to import properly formatted GXF files and export 
+grids in GXF format.  GXF-3 is an adopted standard format of the
+Gravity/Magnetics Committee of the Society of Exploration Geophysicists (SEG).
+GXF-3 is the primary gridded data interchange format for 
+<a href="http://www.Geosoft.com/">Geosoft</a>.<p>
+
+<h2>Resources</h2>
+
+<ul>
+<li> <a href="gxfopen.h.html">GXF-3 API Documentation</a>
+<li> <a href="ftp://gdal.velocet.ca/pub/outgoing/gxf3_1_0.tar.gz">GXF-3 API in .tar.gz</a>
+<li> <a href="ftp://gdal.velocet.ca/pub/outgoing/gxf3_1_0.zip">GXF-3 API in ZIP</a>
+<li> <a href="gxfr3d9_1.pdf">GXF-3 File Format Specification (pdf)</a>
+</ul>
+
+<h2>Licensing</h2>
+
+This library is offered as <a href="http://www.opensource.org">Open Source</a>.
+In particular, it is offered under the X Consortium license which doesn't
+attempt to impose any copyleft, or credit requirements on users of the code.<p>
+
+The precise license text is:<p>
+
+<em>
+ Copyright (c) 1999, Frank Warmerdam
+<p>
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+<p>
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+<p>
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+<p>
+</em>
+
+<h2>Building the Source</h2>
+
+Unix developers should be able to unpack the .tar.gz file, run configure, 
+and type make to build the library (libgxf3.a), and a simple test program
+(gxftest.c).<p>
+
+Windows developers should unpack the .zip file, and type 
+<tt>nmake /f makefile.vc</tt> to build with VC++. <p>
+
+<h2>Author and Acknowledgements</h2>
+
+The primary author of the GXF3 library is 
+<a href="http://pobox.com/warmerdam">Frank Warmerdam</a>, 
+and I can be reached at 
+<a href="mailto:warmerdam@pobox.com">warmerdam@pobox.com</a>.  I am open to
+bug reports, and suggestions.<p>
+
+I would like to thank:<p>
+
+<ul>
+<li> <a href="http://www.globalgeo.com/">Global Geomatics</a>
+who funded development the majority of the work on this library, 
+and agreed for it to be Open Source.<p>
+
+<li> Ian Macleod of Geosoft who answered a number of questions I had,
+and provided sample files.<p>
+
+</ul>
+
+*/
diff --git a/Utilities/GDAL/frmts/gxf/gxf_ogcwkt.c b/Utilities/GDAL/frmts/gxf/gxf_ogcwkt.c
new file mode 100644
index 0000000000..4dcd11fbd1
--- /dev/null
+++ b/Utilities/GDAL/frmts/gxf/gxf_ogcwkt.c
@@ -0,0 +1,637 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  GXF Reader
+ * Purpose:  Handle GXF to OGC WKT projection transformation.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: gxf_ogcwkt.c,v $
+ * Revision 1.4  2006/04/04 17:25:27  fwarmerdam
+ * updated contact info
+ *
+ * Revision 1.3  2004/04/07 02:08:11  warmerda
+ * SPHEROID belongs inside DATUM
+ *
+ * Revision 1.2  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.1  1999/10/29 17:26:48  warmerda
+ * New
+ *
+ */
+
+#include "gxfopen.h"
+
+CPL_CVSID("$Id$");
+
+/* -------------------------------------------------------------------- */
+/* the following #defines come from ogr_spatialref.h in the GDAL/OGR	*/
+/* distribution (see http://gdal.velocet.ca/projects/opengis) and	*/
+/* should be kept in sync with that file. 				*/
+/* -------------------------------------------------------------------- */
+
+#define SRS_PT_ALBERS_CONIC_EQUAL_AREA					\
+				"Albers_Conic_Equal_Area"
+#define SRS_PT_AZIMUTHAL_EQUIDISTANT "Azimuthal_Equidistant"
+#define SRS_PT_CASSINI_SOLDNER	"Cassini_Soldner"
+#define SRS_PT_CYLINDRICAL_EQUAL_AREA "Cylindrical_Equal_Area"
+#define SRS_PT_ECKERT_IV        "Eckert_IV"
+#define SRS_PT_ECKERT_VI        "Eckert_VI"
+#define SRS_PT_EQUIDISTANT_CONIC "Equidistant_Conic"
+#define SRS_PT_EQUIRECTANGULAR  "Equirectangular"
+#define SRS_PT_GALL_STEREOGRAPHIC "Gall_Stereographic"
+#define SRS_PT_GNOMONIC		"Gnomonic"
+#define SRS_PT_HOTINE_OBLIQUE_MERCATOR 					\
+                                "Hotine_Oblique_Mercator"
+#define SRS_PT_LABORDE_OBLIQUE_MERCATOR 				\
+                                "Laborde_Oblique_Mercator"
+#define SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP				\
+                                "Lambert_Conformal_Conic_1SP"
+#define SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP			        \
+                                "Lambert_Conformal_Conic_2SP"
+#define SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM		        \
+                                "Lambert_Conformal_Conic_2SP_Belgium)"
+#define SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA		        \
+                                "Lambert_Azimuthal_Equal_Area"
+#define SRS_PT_MERCATOR_1SP     "Mercator_1SP"
+#define SRS_PT_MERCATOR_2SP     "Mercator_2SP"
+#define SRS_PT_MILLER_CYLINDRICAL "Miller_Cylindrical"
+#define SRS_PT_MOLLWEIDE        "Mollweide"
+#define SRS_PT_NEW_ZEALAND_MAP_GRID					\
+                                "New_Zealand_Map_Grid"
+#define SRS_PT_OBLIQUE_STEREOGRAPHIC					\
+                                "Oblique_Stereographic"
+#define SRS_PT_ORTHOGRAPHIC	"Orthographic"
+#define SRS_PT_POLAR_STEREOGRAPHIC					\
+                                "Polar_Stereographic"
+#define SRS_PT_POLYCONIC	"Polyconic"
+#define SRS_PT_ROBINSON         "Robinson"
+#define SRS_PT_SINUSOIDAL	"Sinusoidal"
+#define SRS_PT_STEREOGRAPHIC	"Stereographic"
+#define SRS_PT_SWISS_OBLIQUE_CYLINDRICAL 				\
+                                "Swiss_Oblique_Cylindrical"
+#define SRS_PT_TRANSVERSE_MERCATOR					\
+                                "Transverse_Mercator"
+#define SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED			\
+                                "Transverse_Mercator_South_Orientated"
+#define SRS_PT_TUNISIA_MINING_GRID					\
+                                "Tunisia_Mining_Grid"
+#define SRS_PT_VANDERGRINTEN	"VanDerGrinten"
+
+#define SRS_PP_CENTRAL_MERIDIAN		"central_meridian"
+#define SRS_PP_SCALE_FACTOR     	"scale_factor"
+#define SRS_PP_STANDARD_PARALLEL_1	"standard_parallel_1"
+#define SRS_PP_STANDARD_PARALLEL_2	"standard_parallel_2"
+#define SRS_PP_LONGITUDE_OF_CENTER      "longitude_of_center"
+#define SRS_PP_LATITUDE_OF_CENTER       "latitude_of_center"
+#define SRS_PP_LONGITUDE_OF_ORIGIN      "longitude_of_origin"
+#define SRS_PP_LATITUDE_OF_ORIGIN       "latitude_of_origin"
+#define SRS_PP_FALSE_EASTING		"false_easting"
+#define SRS_PP_FALSE_NORTHING		"false_northing"
+#define SRS_PP_AZIMUTH			"azimuth"
+#define SRS_PP_LONGITUDE_OF_POINT_1     "longitude_of_point_1"
+#define SRS_PP_LATITUDE_OF_POINT_1      "latitude_of_point_1"
+#define SRS_PP_LONGITUDE_OF_POINT_2     "longitude_of_point_2"
+#define SRS_PP_LATITUDE_OF_POINT_2      "latitude_of_point_2"
+#define SRS_PP_LONGITUDE_OF_POINT_3     "longitude_of_point_3"
+#define SRS_PP_LATITUDE_OF_POINT_3      "latitude_of_point_3"
+#define SRS_PP_RECTIFIED_GRID_ANGLE	"rectified_grid_angle"
+
+/* -------------------------------------------------------------------- */
+/*      This table was copied from gt_wkt_srs.cpp in the libgeotiff     */
+/*      distribution.  Please keep changes in sync.                     */
+/* -------------------------------------------------------------------- */
+static char *papszDatumEquiv[] =
+{
+    "Militar_Geographische_Institut",
+    "Militar_Geographische_Institute",
+    "World_Geodetic_System_1984",
+    "WGS_1984",
+    "WGS_72_Transit_Broadcast_Ephemeris",
+    "WGS_1972_Transit_Broadcast_Ephemeris",
+    "World_Geodetic_System_1972",
+    "WGS_1972",
+    "European_Terrestrial_Reference_System_89",
+    "European_Reference_System_1989",
+    NULL
+};
+
+/************************************************************************/
+/*                          WKTMassageDatum()                           */
+/*                                                                      */
+/*      Massage an EPSG datum name into WMT format.  Also transform     */
+/*      specific exception cases into WKT versions.                     */
+/*                                                                      */
+/*      This function was copied from the gt_wkt_srs.cpp file in the    */
+/*      libgeotiff distribution.  Please keep changes in sync.          */
+/************************************************************************/
+
+static void WKTMassageDatum( char ** ppszDatum )
+
+{
+    int		i, j;
+    char	*pszDatum = *ppszDatum;
+
+/* -------------------------------------------------------------------- */
+/*      Translate non-alphanumeric values to underscores.               */
+/* -------------------------------------------------------------------- */
+    for( i = 0; pszDatum[i] != '\0'; i++ )
+    {
+        if( !(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z')
+            && !(pszDatum[i] >= 'a' && pszDatum[i] <= 'z')
+            && !(pszDatum[i] >= '0' && pszDatum[i] <= '9') )
+        {
+            pszDatum[i] = '_';
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Remove repeated and trailing underscores.                       */
+/* -------------------------------------------------------------------- */
+    for( i = 1, j = 0; pszDatum[i] != '\0'; i++ )
+    {
+        if( pszDatum[j] == '_' && pszDatum[i] == '_' )
+            continue;
+
+        pszDatum[++j] = pszDatum[i];
+    }
+    if( pszDatum[j] == '_' )
+        pszDatum[j] = '\0';
+    else
+        pszDatum[j+1] = '\0';
+    
+/* -------------------------------------------------------------------- */
+/*      Search for datum equivelences.  Specific massaged names get     */
+/*      mapped to OpenGIS specified names.                              */
+/* -------------------------------------------------------------------- */
+    for( i = 0; papszDatumEquiv[i] != NULL; i += 2 )
+    {
+        if( EQUAL(*ppszDatum,papszDatumEquiv[i]) )
+        {
+            CPLFree( *ppszDatum );
+            *ppszDatum = CPLStrdup( papszDatumEquiv[i+1] );
+            break;
+        }
+    }
+}
+
+/************************************************************************/
+/*                           OGCWKTSetProj()                            */
+/************************************************************************/
+
+static void OGCWKTSetProj( char * pszProjection, char ** papszMethods,
+                           const char * pszTransformName,
+                           const char * pszParm1, 
+                           const char * pszParm2, 
+                           const char * pszParm3, 
+                           const char * pszParm4, 
+                           const char * pszParm5, 
+                           const char * pszParm6,
+                           const char * pszParm7 )
+
+{
+    int		iParm, nCount = CSLCount(papszMethods);
+    const char	*apszParmNames[8];
+
+    apszParmNames[0] = pszParm1;
+    apszParmNames[1] = pszParm2;
+    apszParmNames[2] = pszParm3;
+    apszParmNames[3] = pszParm4;
+    apszParmNames[4] = pszParm5;
+    apszParmNames[5] = pszParm6;
+    apszParmNames[6] = pszParm7;
+    apszParmNames[7] = NULL;
+
+    sprintf( pszProjection,
+             "PROJECTION[\"%s\"]",
+             pszTransformName );
+
+    for( iParm = 0; iParm < nCount-1 && apszParmNames[iParm] != NULL; iParm++ )
+    {
+        sprintf( pszProjection + strlen(pszProjection),
+                 ",PARAMETER[\"%s\",%s]",
+                 apszParmNames[iParm],
+                 papszMethods[iParm+1] );
+    }
+}
+
+
+/************************************************************************/
+/*                    GXFGetMapProjectionAsOGCWKT()                     */
+/************************************************************************/
+
+/**
+ * Return the GXF Projection in OpenGIS Well Known Text format.
+ *
+ * The returned string becomes owned by the caller, and should be freed
+ * with CPLFree() or VSIFree().  The return value will be "" if
+ * no projection information is passed.
+ *
+ * The mapping of GXF projections to OGC WKT format is not complete.  Please
+ * see the gxf_ogcwkt.c code to better understand limitations of this
+ * translation.  More information about OGC WKT format can be found in
+ * the OpenGIS Simple Features specification for OLEDB/COM found on the
+ * OpenGIS web site at <a href="http://www.opengis.org/">www.opengis.org</a>.
+ * The translation uses some code cribbed from the OGR library, about which
+ * more can be learned from <a href="http://gdal.velocet.ca/projects/opengis/">
+ * http://gdal.velocet.ca/projects/opengis/</a>.
+ *
+ * For example, the following GXF definitions:
+ * <pre>
+ * #UNIT_LENGTH                        
+ * m,1
+ * #MAP_PROJECTION
+ * "NAD83 / UTM zone 19N"
+ * "GRS 1980",6378137,0.081819191,0
+ * "Transverse Mercator",0,-69,0.9996,500000,0
+ * </pre>
+ *
+ * Would translate to (without the nice formatting):
+ * <pre>
+   PROJCS["NAD83 / UTM zone 19N",
+          GEOGCS["GRS 1980",
+                 DATUM["GRS_1980",
+                     SPHEROID["GRS 1980",6378137,298.257222413684]],
+                 PRIMEM["unnamed",0],
+                 UNIT["degree",0.0174532925199433]],
+          PROJECTION["Transverse_Mercator"],
+          PARAMETER["latitude_of_origin",0],
+          PARAMETER["central_meridian",-69],
+          PARAMETER["scale_factor",0.9996],
+          PARAMETER["false_easting",500000],
+          PARAMETER["false_northing",0],
+          UNIT["m",1]]
+ * </pre>
+ *
+ * @param hGXF handle to GXF file, as returned by GXFOpen().
+ *
+ * @return string containing OGC WKT projection.
+ */
+
+char *GXFGetMapProjectionAsOGCWKT( GXFHandle hGXF )
+
+{
+    GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
+    char	**papszMethods = NULL;
+    char	szWKT[1024];
+    char	szGCS[512];
+    char	szProjection[512];
+
+/* -------------------------------------------------------------------- */
+/*      If there was nothing in the file return "unknown".              */
+/* -------------------------------------------------------------------- */
+    if( CSLCount(psGXF->papszMapProjection) < 2 )
+        return( CPLStrdup( "" ) );
+
+    strcpy( szWKT, "" );
+    strcpy( szGCS, "" );
+    strcpy( szProjection, "" );
+
+/* -------------------------------------------------------------------- */
+/*      Parse the third line, looking for known projection methods.     */
+/* -------------------------------------------------------------------- */
+    if( psGXF->papszMapProjection[2] != NULL )
+        papszMethods = CSLTokenizeStringComplex(psGXF->papszMapProjection[2],
+                                                ",", TRUE, TRUE );
+
+#ifdef DBMALLOC
+    malloc_chain_check(1);
+#endif    
+    
+/* -------------------------------------------------------------------- */
+/*      Create the PROJCS.                                              */
+/* -------------------------------------------------------------------- */
+    if( papszMethods == NULL
+        || papszMethods[0] == NULL 
+        || EQUAL(papszMethods[0],"Geographic") )
+    {
+        /* do nothing */
+    }
+
+    else if( EQUAL(papszMethods[0],"Lambert Conic Conformal (1SP)") )
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP,
+                       SRS_PP_LATITUDE_OF_ORIGIN,
+                       SRS_PP_CENTRAL_MERIDIAN,
+                       SRS_PP_SCALE_FACTOR,
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL,
+                       NULL );
+    }
+    
+    else if( EQUAL(papszMethods[0],"Lambert Conic Conformal (2SP)") )
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP,
+                       SRS_PP_STANDARD_PARALLEL_1,
+                       SRS_PP_STANDARD_PARALLEL_2,
+                       SRS_PP_LATITUDE_OF_ORIGIN,
+                       SRS_PP_CENTRAL_MERIDIAN,
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL );
+    }
+    
+    else if( EQUAL(papszMethods[0],"Lambert Conformal (2SP Belgium)") )
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM,
+                       SRS_PP_STANDARD_PARALLEL_1,
+                       SRS_PP_STANDARD_PARALLEL_2,
+                       SRS_PP_LATITUDE_OF_ORIGIN,
+                       SRS_PP_CENTRAL_MERIDIAN,
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL );
+    }
+    
+    else if( EQUAL(papszMethods[0],"Mercator (1SP)"))
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_MERCATOR_1SP,
+                       SRS_PP_LATITUDE_OF_ORIGIN,
+                       SRS_PP_CENTRAL_MERIDIAN,
+                       SRS_PP_SCALE_FACTOR,
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL,
+                       NULL );
+    }
+    
+    else if( EQUAL(papszMethods[0],"Mercator (2SP)"))
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_MERCATOR_2SP,
+                       SRS_PP_LATITUDE_OF_ORIGIN,/* should it be StdParalle1?*/
+                       SRS_PP_CENTRAL_MERIDIAN,
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL,
+                       NULL,
+                       NULL );
+    }
+    
+    else if( EQUAL(papszMethods[0],"Laborde Oblique Mercator") )
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_LABORDE_OBLIQUE_MERCATOR,
+                       SRS_PP_LATITUDE_OF_CENTER,
+                       SRS_PP_LONGITUDE_OF_CENTER,
+                       SRS_PP_AZIMUTH,
+                       SRS_PP_SCALE_FACTOR,
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL );
+
+    }
+
+    else if( EQUAL(papszMethods[0],"Hotine Oblique Mercator") )
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_HOTINE_OBLIQUE_MERCATOR,
+                       SRS_PP_LATITUDE_OF_CENTER,
+                       SRS_PP_LONGITUDE_OF_CENTER,
+                       SRS_PP_AZIMUTH,
+                       SRS_PP_RECTIFIED_GRID_ANGLE,
+                       SRS_PP_SCALE_FACTOR, /* not in normal formulation */
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING );
+    }
+
+    else if( EQUAL(papszMethods[0],"New Zealand Map Grid") )
+
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_NEW_ZEALAND_MAP_GRID,
+                       SRS_PP_LATITUDE_OF_ORIGIN,
+                       SRS_PP_CENTRAL_MERIDIAN,
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL,
+                       NULL,
+                       NULL );
+    }
+    
+    else if( EQUAL(papszMethods[0],"Oblique Stereographic") )
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_OBLIQUE_STEREOGRAPHIC,
+                       SRS_PP_LATITUDE_OF_ORIGIN,
+                       SRS_PP_CENTRAL_MERIDIAN,
+                       SRS_PP_SCALE_FACTOR,
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL,
+                       NULL );
+    }
+    
+    else if( EQUAL(papszMethods[0],"Polar Stereographic") )
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_POLAR_STEREOGRAPHIC,
+                       SRS_PP_LATITUDE_OF_ORIGIN,
+                       SRS_PP_CENTRAL_MERIDIAN,
+                       SRS_PP_SCALE_FACTOR,
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL,
+                       NULL );
+    }
+    
+    else if( EQUAL(papszMethods[0],"Swiss Oblique Cylindrical") )
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_SWISS_OBLIQUE_CYLINDRICAL,
+                       SRS_PP_LATITUDE_OF_CENTER,
+                       SRS_PP_LONGITUDE_OF_CENTER,
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL,
+                       NULL,
+                       NULL );
+    }
+    
+    else if( EQUAL(papszMethods[0],"Transverse Mercator") )
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_TRANSVERSE_MERCATOR,
+                       SRS_PP_LATITUDE_OF_ORIGIN,
+                       SRS_PP_CENTRAL_MERIDIAN,
+                       SRS_PP_SCALE_FACTOR,
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL,
+                       NULL );
+    }
+
+    else if( EQUAL(papszMethods[0],"Transverse Mercator (South Oriented)")
+          || EQUAL(papszMethods[0],"Transverse Mercator (South Orientated)"))
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED,
+                       SRS_PP_LATITUDE_OF_ORIGIN,
+                       SRS_PP_CENTRAL_MERIDIAN,
+                       SRS_PP_SCALE_FACTOR,
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL,
+                       NULL );
+    }
+
+    else if( EQUAL(papszMethods[0],"*Albers Conic") )
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_ALBERS_CONIC_EQUAL_AREA,
+                       SRS_PP_STANDARD_PARALLEL_1,
+                       SRS_PP_STANDARD_PARALLEL_2,
+                       SRS_PP_LATITUDE_OF_CENTER,
+                       SRS_PP_LONGITUDE_OF_CENTER,
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL );
+    }
+
+    else if( EQUAL(papszMethods[0],"*Equidistant Conic") )
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_EQUIDISTANT_CONIC,
+                       SRS_PP_STANDARD_PARALLEL_1,
+                       SRS_PP_STANDARD_PARALLEL_2,
+                       SRS_PP_LATITUDE_OF_CENTER,
+                       SRS_PP_LONGITUDE_OF_CENTER,
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL );
+    }
+
+    else if( EQUAL(papszMethods[0],"*Polyconic") )
+    {
+        OGCWKTSetProj( szProjection, papszMethods,
+                       SRS_PT_POLYCONIC,
+                       SRS_PP_LATITUDE_OF_ORIGIN,
+                       SRS_PP_CENTRAL_MERIDIAN,
+                       SRS_PP_SCALE_FACTOR, /* not normally expected */
+                       SRS_PP_FALSE_EASTING,
+                       SRS_PP_FALSE_NORTHING,
+                       NULL,
+                       NULL );
+    }
+
+    CSLDestroy( papszMethods );
+
+    
+/* -------------------------------------------------------------------- */
+/*      Extract the linear Units specification.                         */
+/* -------------------------------------------------------------------- */
+    if( psGXF->pszUnitName != NULL && strlen(szProjection) > 0 )
+    {
+        sprintf( szProjection+strlen(szProjection),
+                 ",UNIT[\"%s\",%.15g]",
+                 psGXF->pszUnitName, psGXF->dfUnitToMeter );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Build GEOGCS.  There are still "issues" with the generation     */
+/*      of the GEOGCS/Datum and Spheroid names.  Of these, only the     */
+/*      datum name is really significant.                               */
+/* -------------------------------------------------------------------- */
+    if( CSLCount(psGXF->papszMapProjection) > 1 )
+    {
+        char	**papszTokens;
+        
+        papszTokens = CSLTokenizeStringComplex(psGXF->papszMapProjection[1],
+                                               ",", TRUE, TRUE );
+
+
+        if( CSLCount(papszTokens) > 2 )
+        {
+            double	dfMajor = atof(papszTokens[1]);
+            double	dfEccentricity = atof(papszTokens[2]);
+            double	dfInvFlattening, dfMinor;
+            char	*pszOGCDatum;
+
+            /* translate eccentricity to inv flattening. */
+            if( dfEccentricity == 0.0 )
+                dfInvFlattening = 0.0;
+            else
+            {
+                dfMinor = dfMajor * pow(1.0-dfEccentricity*dfEccentricity,0.5);
+                dfInvFlattening = 1.0 / (1 - dfMinor/dfMajor);
+            }
+
+            pszOGCDatum = CPLStrdup(papszTokens[0]);
+            WKTMassageDatum( &pszOGCDatum );
+            
+            sprintf( szGCS,
+                     "GEOGCS[\"%s\","
+                       "DATUM[\"%s\","
+                       "SPHEROID[\"%s\",%s,%.15g]],",
+                     papszTokens[0],
+                     pszOGCDatum,
+                     papszTokens[0], /* this is datum, but should be ellipse*/
+                     papszTokens[1],
+                     dfInvFlattening );
+            CPLFree( pszOGCDatum );
+        }
+
+        if( CSLCount(papszTokens) > 3 )
+            sprintf( szGCS + strlen(szGCS),
+                     "PRIMEM[\"unnamed\",%s],",
+                     papszTokens[3] );
+        
+        strcat( szGCS, "UNIT[\"degree\",0.0174532925199433]]" );
+        
+        CSLDestroy( papszTokens );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Put this all together into a full projection.                   */
+/* -------------------------------------------------------------------- */
+    if( strlen(szProjection) > 0 )
+    {
+        if( psGXF->papszMapProjection[0][0] == '"' )
+            sprintf( szWKT,
+                     "PROJCS[%s,%s,%s]",
+                     psGXF->papszMapProjection[0],
+                     szGCS,
+                     szProjection );
+        else
+            sprintf( szWKT,
+                     "PROJCS[\"%s\",%s,%s]",
+                     psGXF->papszMapProjection[0],
+                     szGCS,
+                     szProjection );
+            
+    }
+    else
+    {
+        strcpy( szWKT, szGCS );
+    }
+
+    return( CPLStrdup( szWKT ) );
+}
+
diff --git a/Utilities/GDAL/frmts/gxf/gxf_proj4.c b/Utilities/GDAL/frmts/gxf/gxf_proj4.c
new file mode 100644
index 0000000000..9fd09be52e
--- /dev/null
+++ b/Utilities/GDAL/frmts/gxf/gxf_proj4.c
@@ -0,0 +1,630 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  GXF Reader
+ * Purpose:  Handle GXF to PROJ.4 projection transformation.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1998, Global Geomatics
+ * Copyright (c) 1998, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: gxf_proj4.c,v $
+ * Revision 1.4  2006/04/04 17:25:27  fwarmerdam
+ * updated contact info
+ *
+ * Revision 1.3  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.2  1999/10/29 17:30:03  warmerda
+ * added documentation
+ *
+ * Revision 1.1  1999/10/27 20:23:05  warmerda
+ * New
+ *
+ */
+
+#include "gxfopen.h"
+
+CPL_CVSID("$Id$");
+
+/************************************************************************/
+/*                     GXFGetMapProjectionAsPROJ4()                     */
+/************************************************************************/
+
+/**
+ * Return the GXF Projection in PROJ.4 format.
+ *
+ * The returned string becomes owned by the caller, and should be freed
+ * with CPLFree() or VSIFree().  The return value will be "unknown" if
+ * no projection information is passed.
+ *
+ * The mapping of GXF projections to PROJ.4 format is not complete.  Please
+ * see the gxf_proj4.c code to better understand limitations of this
+ * translation.  Noteable PROJ.4 knows little about datums.
+ *
+ * For example, the following GXF definitions:
+ * <pre>
+ * #UNIT_LENGTH                        
+ * m,1
+ * #MAP_PROJECTION
+ * "NAD83 / UTM zone 19N"
+ * "GRS 1980",6378137,0.081819191,0
+ * "Transverse Mercator",0,-69,0.9996,500000,0
+ * </pre>
+ *
+ * Would translate to:
+ * <pre>
+ * +proj=tmerc +lat_0=0 +lon_0=-69 +k=0.9996 +x_0=500000 +y_0=0 +ellps=GRS80
+ * </pre>
+ *
+ * @param hGXF handle to GXF file, as returned by GXFOpen().
+ *
+ * @return string containing PROJ.4 projection.
+ */
+
+char *GXFGetMapProjectionAsPROJ4( GXFHandle hGXF )
+
+{
+    GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
+    char	**papszMethods = NULL;
+    char	szPROJ4[512];
+
+/* -------------------------------------------------------------------- */
+/*      If there was nothing in the file return "unknown".              */
+/* -------------------------------------------------------------------- */
+    if( CSLCount(psGXF->papszMapProjection) < 2 )
+        return( CPLStrdup( "unknown" ) );
+
+    szPROJ4[0] = '\0';
+
+/* -------------------------------------------------------------------- */
+/*      Parse the third line, looking for known projection methods.     */
+/* -------------------------------------------------------------------- */
+    if( psGXF->papszMapProjection[2] != NULL )
+        papszMethods = CSLTokenizeStringComplex(psGXF->papszMapProjection[2],
+                                                ",", TRUE, TRUE );
+
+#ifdef DBMALLOC
+    malloc_chain_check(1);
+#endif    
+    
+    if( papszMethods == NULL
+        || papszMethods[0] == NULL 
+        || EQUAL(papszMethods[0],"Geographic") )
+    {
+        strcat( szPROJ4, "+proj=longlat" );
+    }
+
+#ifdef notdef    
+    else if( EQUAL(papszMethods[0],"Lambert Conic Conformal (1SP)")
+             && CSLCount(papszMethods) > 5 )
+    {
+        /* notdef: It isn't clear that this 1SP + scale method is even
+           supported by PROJ.4
+           Later note: It is not. */
+        
+        strcat( szPROJ4, "+proj=lcc" );
+
+        strcat( szPROJ4, " +lat_0=" );
+        strcat( szPROJ4, papszMethods[1] );
+
+        strcat( szPROJ4, " +lon_0=" );
+        strcat( szPROJ4, papszMethods[2] );
+
+        strcat( szPROJ4, " +k=" );
+        strcat( szPROJ4, papszMethods[3] );
+        
+        strcat( szPROJ4, " +x_0=" );
+        strcat( szPROJ4, papszMethods[4] );
+
+        strcat( szPROJ4, " +y_0=" );
+        strcat( szPROJ4, papszMethods[5] );
+    }
+#endif    
+    else if( EQUAL(papszMethods[0],"Lambert Conic Conformal (2SP)")
+             || EQUAL(papszMethods[0],"Lambert Conformal (2SP Belgium)") )
+    {
+        /* notdef: Note we are apparently losing whatever makes the
+           Belgium variant different than normal LCC, but hopefully
+           they are close! */
+        
+        strcat( szPROJ4, "+proj=lcc" );
+
+        if( CSLCount(papszMethods) > 1 )
+        {
+            strcat( szPROJ4, " +lat_1=" );
+            strcat( szPROJ4, papszMethods[1] );
+        }
+
+        if( CSLCount(papszMethods) > 2 )
+        {
+            strcat( szPROJ4, " +lat_2=" );
+            strcat( szPROJ4, papszMethods[2] );
+        }
+
+        if( CSLCount(papszMethods) > 3 )
+        {
+            strcat( szPROJ4, " +lat_0=" );
+            strcat( szPROJ4, papszMethods[3] );
+        }
+
+        if( CSLCount(papszMethods) > 4 )
+        {
+            strcat( szPROJ4, " +lon_0=" );
+            strcat( szPROJ4, papszMethods[4] );
+        }
+
+        if( CSLCount(papszMethods) > 5 )
+        {
+            strcat( szPROJ4, " +x_0=" );
+            strcat( szPROJ4, papszMethods[5] );
+        }
+
+        if( CSLCount(papszMethods) > 6 )
+        {
+            strcat( szPROJ4, " +y_0=" );
+            strcat( szPROJ4, papszMethods[6] );
+        }
+    }
+    
+    else if( EQUAL(papszMethods[0],"Mercator (1SP)")
+             && CSLCount(papszMethods) > 5 )
+    {
+        /* notdef: it isn't clear that +proj=merc support a scale of other 
+           than 1.0 in PROJ.4 */
+        
+        strcat( szPROJ4, "+proj=merc" );
+
+        strcat( szPROJ4, " +lat_ts=" );
+        strcat( szPROJ4, papszMethods[1] );
+
+        strcat( szPROJ4, " +lon_0=" );
+        strcat( szPROJ4, papszMethods[2] );
+
+        strcat( szPROJ4, " +k=" );
+        strcat( szPROJ4, papszMethods[3] );
+        
+        strcat( szPROJ4, " +x_0=" );
+        strcat( szPROJ4, papszMethods[4] );
+
+        strcat( szPROJ4, " +y_0=" );
+        strcat( szPROJ4, papszMethods[5] );
+    }
+    
+    else if( EQUAL(papszMethods[0],"Mercator (2SP)")
+             && CSLCount(papszMethods) > 4 )
+    {
+        /* notdef: it isn't clear that +proj=merc support a scale of other 
+           than 1.0 in PROJ.4 */
+        
+        strcat( szPROJ4, "+proj=merc" );
+
+        strcat( szPROJ4, " +lat_ts=" );
+        strcat( szPROJ4, papszMethods[1] );
+
+        strcat( szPROJ4, " +lon_0=" );
+        strcat( szPROJ4, papszMethods[2] );
+
+        strcat( szPROJ4, " +x_0=" );
+        strcat( szPROJ4, papszMethods[3] );
+
+        strcat( szPROJ4, " +y_0=" );
+        strcat( szPROJ4, papszMethods[4] );
+    }
+    
+    else if( EQUAL(papszMethods[0],"Hotine Oblique Mercator") 
+             && CSLCount(papszMethods) > 7 )
+    {
+        /* Note that only the second means of specifying omerc is supported
+           by this code in GXF. */
+        strcat( szPROJ4, "+proj=omerc" );
+
+        strcat( szPROJ4, " +lat_0=" );
+        strcat( szPROJ4, papszMethods[1] );
+        
+        strcat( szPROJ4, " +lonc=" );
+        strcat( szPROJ4, papszMethods[2] );
+        
+        strcat( szPROJ4, " +alpha=" );
+        strcat( szPROJ4, papszMethods[3] );
+
+        if( atof(papszMethods[4]) < 0.00001 )
+        {
+            strcat( szPROJ4, " +not_rot" );
+        }
+        else
+        {
+#ifdef notdef            
+            if( atof(papszMethods[4]) + atof(papszMethods[3]) < 0.00001 )
+                /* ok */;
+            else
+                /* notdef: no way to specify arbitrary angles! */;
+#endif            
+        }
+
+        strcat( szPROJ4, " +k=" );
+        strcat( szPROJ4, papszMethods[5] );
+
+        strcat( szPROJ4, " +x_0=" );
+        strcat( szPROJ4, papszMethods[6] );
+
+        strcat( szPROJ4, " +y_0=" );
+        strcat( szPROJ4, papszMethods[7] );
+    }
+
+    else if( EQUAL(papszMethods[0],"Laborde Oblique Mercator")
+             && CSLCount(papszMethods) > 6 )
+    {
+        strcat( szPROJ4, "+proj=labrd" );
+
+        strcat( szPROJ4, " +lat_0=" );
+        strcat( szPROJ4, papszMethods[1] );
+        
+        strcat( szPROJ4, " +lon_0=" );
+        strcat( szPROJ4, papszMethods[2] );
+        
+        strcat( szPROJ4, " +azi=" );
+        strcat( szPROJ4, papszMethods[3] );
+
+        strcat( szPROJ4, " +k=" );
+        strcat( szPROJ4, papszMethods[4] );
+
+        strcat( szPROJ4, " +x_0=" );
+        strcat( szPROJ4, papszMethods[5] );
+
+        strcat( szPROJ4, " +y_0=" );
+        strcat( szPROJ4, papszMethods[6] );
+    }
+    
+    else if( EQUAL(papszMethods[0],"New Zealand Map Grid")
+             && CSLCount(papszMethods) > 4 )
+    {
+        strcat( szPROJ4, "+proj=nzmg" );
+
+        strcat( szPROJ4, " +lat_0=" );
+        strcat( szPROJ4, papszMethods[1] );
+        
+        strcat( szPROJ4, " +lon_0=" );
+        strcat( szPROJ4, papszMethods[2] );
+        
+        strcat( szPROJ4, " +x_0=" );
+        strcat( szPROJ4, papszMethods[3] );
+
+        strcat( szPROJ4, " +y_0=" );
+        strcat( szPROJ4, papszMethods[4] );
+    }
+    
+    else if( EQUAL(papszMethods[0],"New Zealand Map Grid")
+             && CSLCount(papszMethods) > 4 )
+    {
+        strcat( szPROJ4, "+proj=nzmg" );
+
+        strcat( szPROJ4, " +lat_0=" );
+        strcat( szPROJ4, papszMethods[1] );
+        
+        strcat( szPROJ4, " +lon_0=" );
+        strcat( szPROJ4, papszMethods[2] );
+        
+        strcat( szPROJ4, " +x_0=" );
+        strcat( szPROJ4, papszMethods[3] );
+
+        strcat( szPROJ4, " +y_0=" );
+        strcat( szPROJ4, papszMethods[4] );
+    }
+    
+    else if( EQUAL(papszMethods[0],"Oblique Stereographic") 
+             && CSLCount(papszMethods) > 5 )
+    {
+        /* there is an option to produce +lat_ts, which we ignore */
+        
+        strcat( szPROJ4, "+proj=stere" );
+
+        strcat( szPROJ4, " +lat_0=45" );
+
+        strcat( szPROJ4, " +lat_ts=" );
+        strcat( szPROJ4, papszMethods[1] );
+        
+        strcat( szPROJ4, " +lon_0=" );
+        strcat( szPROJ4, papszMethods[2] );
+        
+        strcat( szPROJ4, " +k=" );
+        strcat( szPROJ4, papszMethods[3] );
+
+        strcat( szPROJ4, " +x_0=" );
+        strcat( szPROJ4, papszMethods[4] );
+
+        strcat( szPROJ4, " +y_0=" );
+        strcat( szPROJ4, papszMethods[5] );
+    }
+    
+    else if( EQUAL(papszMethods[0],"Polar Stereographic")
+             && CSLCount(papszMethods) > 5 )
+    {
+        /* there is an option to produce +lat_ts, which we ignore */
+        
+        strcat( szPROJ4, "+proj=stere" );
+
+        strcat( szPROJ4, " +lat_0=90" );
+
+        strcat( szPROJ4, " +lat_ts=" );
+        strcat( szPROJ4, papszMethods[1] );
+        
+        strcat( szPROJ4, " +lon_0=" );
+        strcat( szPROJ4, papszMethods[2] );
+        
+        strcat( szPROJ4, " +k=" );
+        strcat( szPROJ4, papszMethods[3] );
+
+        strcat( szPROJ4, " +x_0=" );
+        strcat( szPROJ4, papszMethods[4] );
+
+        strcat( szPROJ4, " +y_0=" );
+        strcat( szPROJ4, papszMethods[5] );
+    }
+    
+    else if( EQUAL(papszMethods[0],"Swiss Oblique Cylindrical")
+             && CSLCount(papszMethods) > 4 )
+    {
+        /* notdef: geotiff's geo_ctrans.inc says this is the same as
+           ObliqueMercator_Rosenmund, which GG's geotiff support just
+           maps directly to +proj=omerc, though I find that questionable. */
+
+        strcat( szPROJ4, "+proj=omerc" );
+
+        strcat( szPROJ4, " +lat_0=" );
+        strcat( szPROJ4, papszMethods[1] );
+        
+        strcat( szPROJ4, " +lonc=" );
+        strcat( szPROJ4, papszMethods[2] );
+        
+        strcat( szPROJ4, " +x_0=" );
+        strcat( szPROJ4, papszMethods[3] );
+
+        strcat( szPROJ4, " +y_0=" );
+        strcat( szPROJ4, papszMethods[4] );
+    }
+
+    else if( EQUAL(papszMethods[0],"Transverse Mercator")
+             && CSLCount(papszMethods) > 5 )
+    {
+        /* notdef: geotiff's geo_ctrans.inc says this is the same as
+           ObliqueMercator_Rosenmund, which GG's geotiff support just
+           maps directly to +proj=omerc, though I find that questionable. */
+
+        strcat( szPROJ4, "+proj=tmerc" );
+
+        strcat( szPROJ4, " +lat_0=" );
+        strcat( szPROJ4, papszMethods[1] );
+        
+        strcat( szPROJ4, " +lon_0=" );
+        strcat( szPROJ4, papszMethods[2] );
+        
+        strcat( szPROJ4, " +k=" );
+        strcat( szPROJ4, papszMethods[3] );
+        
+        strcat( szPROJ4, " +x_0=" );
+        strcat( szPROJ4, papszMethods[4] );
+
+        strcat( szPROJ4, " +y_0=" );
+        strcat( szPROJ4, papszMethods[5] );
+    }
+
+    else if( EQUAL(papszMethods[0],"Transverse Mercator (South Oriented)")
+             && CSLCount(papszMethods) > 5 )
+    {
+        /* notdef: I don't know how south oriented is different from
+           normal, and I don't find any mention of it in Geotiff;s geo_ctrans.
+           Translating as tmerc, but that is presumably wrong. */
+
+        strcat( szPROJ4, "+proj=tmerc" );
+
+        strcat( szPROJ4, " +lat_0=" );
+        strcat( szPROJ4, papszMethods[1] );
+        
+        strcat( szPROJ4, " +lon_0=" );
+        strcat( szPROJ4, papszMethods[2] );
+        
+        strcat( szPROJ4, " +k=" );
+        strcat( szPROJ4, papszMethods[3] );
+        
+        strcat( szPROJ4, " +x_0=" );
+        strcat( szPROJ4, papszMethods[4] );
+
+        strcat( szPROJ4, " +y_0=" );
+        strcat( szPROJ4, papszMethods[5] );
+    }
+
+    else if( EQUAL(papszMethods[0],"*Equidistant Conic")
+             && CSLCount(papszMethods) > 6 )
+    {
+        strcat( szPROJ4, "+proj=eqdc" );
+
+        strcat( szPROJ4, " +lat_1=" );
+        strcat( szPROJ4, papszMethods[1] );
+        
+        strcat( szPROJ4, " +lat_2=" );
+        strcat( szPROJ4, papszMethods[2] );
+        
+        strcat( szPROJ4, " +lat_0=" );
+        strcat( szPROJ4, papszMethods[3] );
+        
+        strcat( szPROJ4, " +lon_0=" );
+        strcat( szPROJ4, papszMethods[4] );
+        
+        strcat( szPROJ4, " +x_0=" );
+        strcat( szPROJ4, papszMethods[5] );
+
+        strcat( szPROJ4, " +y_0=" );
+        strcat( szPROJ4, papszMethods[6] );
+    }
+
+    else if( EQUAL(papszMethods[0],"*Polyconic") 
+             && CSLCount(papszMethods) > 5 )
+    {
+        strcat( szPROJ4, "+proj=poly" );
+
+        strcat( szPROJ4, " +lat_0=" );
+        strcat( szPROJ4, papszMethods[1] );
+        
+        strcat( szPROJ4, " +lon_0=" );
+        strcat( szPROJ4, papszMethods[2] );
+
+#ifdef notdef
+        /*not supported by PROJ.4 */
+        strcat( szPROJ4, " +k=" );
+        strcat( szPROJ4, papszMethods[3] );
+#endif
+        strcat( szPROJ4, " +x_0=" ); 
+        strcat( szPROJ4, papszMethods[4] );
+
+        strcat( szPROJ4, " +y_0=" );
+        strcat( szPROJ4, papszMethods[5] );
+    }
+
+    else
+    {
+        strcat( szPROJ4, "unknown" );
+    }
+
+    CSLDestroy( papszMethods );
+
+/* -------------------------------------------------------------------- */
+/*      Now get the ellipsoid parameters.  For a bunch of common        */
+/*      ones we preserve the name.  For the rest we just carry over     */
+/*      the parameters.                                                 */
+/* -------------------------------------------------------------------- */
+    if( CSLCount(psGXF->papszMapProjection) > 1 )
+    {
+        char	**papszTokens;
+        
+        papszTokens = CSLTokenizeStringComplex(psGXF->papszMapProjection[1],
+                                               ",", TRUE, TRUE );
+
+
+        if( EQUAL(papszTokens[0],"WGS 84") )
+            strcat( szPROJ4, " +ellps=WGS84" );
+        else if( EQUAL(papszTokens[0],"*WGS 72") )
+            strcat( szPROJ4, " +ellps=WGS72" );
+        else if( EQUAL(papszTokens[0],"*WGS 66") )
+            strcat( szPROJ4, " +ellps=WGS66" );
+        else if( EQUAL(papszTokens[0],"*WGS 60") )
+            strcat( szPROJ4, " +ellps=WGS60" );
+        else if( EQUAL(papszTokens[0],"Clarke 1866") )
+            strcat( szPROJ4, " +ellps=clrk66" );
+        else if( EQUAL(papszTokens[0],"Clarke 1880") )
+            strcat( szPROJ4, " +ellps=clrk80" );
+        else if( EQUAL(papszTokens[0],"GRS 1980") )
+            strcat( szPROJ4, " +ellps=GRS80" );
+        else if( CSLCount( papszTokens ) > 2 )
+        {
+            sprintf( szPROJ4+strlen(szPROJ4),
+                     " +a=%s +e=%s",
+                     papszTokens[1], papszTokens[2] );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Extract the units specification.                                */
+/* -------------------------------------------------------------------- */
+    if( psGXF->pszUnitName != NULL )
+    {
+        if( EQUAL(psGXF->pszUnitName,"ft") )
+        {
+            strcat( szPROJ4, " +units=ft" );
+        }
+        else if( EQUAL(psGXF->pszUnitName,"ftUS") )
+        {
+            strcat( szPROJ4, " +units=us-ft" );
+        }
+        else if( EQUAL(psGXF->pszUnitName,"km") )
+        {
+            strcat( szPROJ4, " +units=km" );
+        }
+        else if( EQUAL(psGXF->pszUnitName,"mm") )
+        {
+            strcat( szPROJ4, " +units=mm" );
+        }
+        else if( EQUAL(psGXF->pszUnitName,"in") )
+        {
+            strcat( szPROJ4, " +units=in" );
+        }
+        else if( EQUAL(psGXF->pszUnitName,"ftInd") )
+        {
+            strcat( szPROJ4, " +units=ind-ft" );
+        }
+        else if( EQUAL(psGXF->pszUnitName,"lk") )
+        {
+            strcat( szPROJ4, " +units=link" );
+        }
+    }
+    
+    return( CPLStrdup( szPROJ4 ) );
+}
+
+
+/************************************************************************/
+/*                        GXFGetPROJ4Position()                         */
+/*                                                                      */
+/*      Get the same information as GXFGetPosition(), but adjust        */
+/*      to units to meters if we don't ``know'' the indicated           */
+/*      units.                                                          */
+/************************************************************************/
+
+CPLErr GXFGetPROJ4Position( GXFHandle hGXF,
+                            double * pdfXOrigin, double * pdfYOrigin,
+                            double * pdfXPixelSize, double * pdfYPixelSize,
+                            double * pdfRotation )
+
+{
+    GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
+    char	*pszProj;
+
+/* -------------------------------------------------------------------- */
+/*      Get the raw position.                                           */
+/* -------------------------------------------------------------------- */
+    if( GXFGetPosition( hGXF,
+                        pdfXOrigin, pdfYOrigin,
+                        pdfXPixelSize, pdfYPixelSize,
+                        pdfRotation ) == CE_Failure )
+        return( CE_Failure );
+
+/* -------------------------------------------------------------------- */
+/*      Do we know the units in PROJ.4?  Get the PROJ.4 string, and     */
+/*      check for a +units definition.                                  */
+/* -------------------------------------------------------------------- */
+    pszProj = GXFGetMapProjectionAsPROJ4( hGXF );
+    if( strstr(pszProj,"+unit") == NULL && psGXF->pszUnitName != NULL )
+    {
+        if( pdfXOrigin != NULL )
+            *pdfXOrigin *= psGXF->dfUnitToMeter;
+        if( pdfYOrigin != NULL )
+            *pdfYOrigin *= psGXF->dfUnitToMeter;
+        if( pdfXPixelSize != NULL )
+            *pdfXPixelSize *= psGXF->dfUnitToMeter;
+        if( pdfYPixelSize != NULL )
+            *pdfYPixelSize *= psGXF->dfUnitToMeter;
+    }
+    CPLFree( pszProj );
+
+    return( CE_None );
+}
diff --git a/Utilities/GDAL/frmts/gxf/gxfdataset.cpp b/Utilities/GDAL/frmts/gxf/gxfdataset.cpp
new file mode 100644
index 0000000000..cf91938970
--- /dev/null
+++ b/Utilities/GDAL/frmts/gxf/gxfdataset.cpp
@@ -0,0 +1,388 @@
+/******************************************************************************
+ * $Id: gxfdataset.cpp,v 1.17 2005/05/05 15:52:48 fwarmerdam Exp $
+ *
+ * Project:  GXF Reader
+ * Purpose:  GDAL binding for GXF reader.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1998, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: gxfdataset.cpp,v $
+ * Revision 1.17  2005/05/05 15:52:48  fwarmerdam
+ * PAM Enabled
+ *
+ * Revision 1.16  2004/11/03 01:17:58  fwarmerdam
+ * added extra testing to avoid mis-identification
+ *
+ * Revision 1.15  2004/04/06 19:23:48  warmerda
+ * Don't forget to close the dataset!
+ *
+ * Revision 1.14  2003/10/06 20:50:45  warmerda
+ * fixed author email
+ *
+ * Revision 1.13  2003/07/08 21:20:21  warmerda
+ * avoid warnings
+ *
+ * Revision 1.12  2002/09/04 06:50:37  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.11  2002/06/12 21:12:25  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.10  2001/11/11 23:51:00  warmerda
+ * added required class keyword to friend declarations
+ *
+ * Revision 1.9  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.8  2000/11/16 14:56:16  warmerda
+ * fail testopen on zero char, reduce min header size
+ *
+ * Revision 1.7  2000/02/28 16:33:49  warmerda
+ * use SetBand method
+ *
+ * Revision 1.6  2000/02/14 16:24:57  warmerda
+ * Fixed comment.
+ *
+ * Revision 1.5  1999/10/29 17:30:23  warmerda
+ * read OGC rather than PROJ.4 definition
+ *
+ * Revision 1.4  1999/10/27 20:21:45  warmerda
+ * added projection/transform support
+ *
+ * Revision 1.3  1999/01/11 15:32:12  warmerda
+ * Added testing of file to verify it is GXF.
+ *
+ * Revision 1.2  1998/12/15 19:06:49  warmerda
+ * IReadBlock(), and GXFGetRawInfo()
+ *
+ * Revision 1.1  1998/12/06 02:53:22  warmerda
+ * New
+ *
+ */
+
+#include "gxfopen.h"
+#include "gdal_pam.h"
+
+CPL_CVSID("$Id: gxfdataset.cpp,v 1.17 2005/05/05 15:52:48 fwarmerdam Exp $");
+
+#ifndef PI
+#  define PI 3.14159265358979323846
+#endif
+
+CPL_C_START
+void	GDALRegister_GXF(void);
+CPL_C_END
+
+/************************************************************************/
+/* ==================================================================== */
+/*				GXFDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class GXFRasterBand;
+
+class GXFDataset : public GDALPamDataset
+{
+    friend class GXFRasterBand;
+    
+    GXFHandle	hGXF;
+
+    char	*pszProjection;
+
+  public:
+                GXFDataset();
+		~GXFDataset();
+    
+    static GDALDataset *Open( GDALOpenInfo * );
+
+    CPLErr 	GetGeoTransform( double * padfTransform );
+    const char *GetProjectionRef();
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            GXFRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class GXFRasterBand : public GDALPamRasterBand
+{
+    friend class GXFDataset;
+    
+  public:
+
+    		GXFRasterBand( GXFDataset *, int );
+    
+    virtual CPLErr IReadBlock( int, int, void * );
+};
+
+
+/************************************************************************/
+/*                           GXFRasterBand()                            */
+/************************************************************************/
+
+GXFRasterBand::GXFRasterBand( GXFDataset *poDS, int nBand )
+
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+    
+    eDataType = GDT_Float32;
+
+    nBlockXSize = poDS->GetRasterXSize();
+    nBlockYSize = 1;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr GXFRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+
+{
+    GXFDataset	*poGXF_DS = (GXFDataset *) poDS;
+    double	*padfBuffer;
+    float	*pafBuffer = (float *) pImage;
+    int		i;
+    CPLErr	eErr;
+
+    CPLAssert( nBlockXOff == 0 );
+
+    padfBuffer = (double *) CPLMalloc(sizeof(double) * nBlockXSize);
+    eErr = GXFGetRawScanline( poGXF_DS->hGXF, nBlockYOff, padfBuffer );
+    
+    for( i = 0; i < nBlockXSize; i++ )
+        pafBuffer[i] = (float) padfBuffer[i];
+
+    CPLFree( padfBuffer );
+    
+    return eErr;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*				GXFDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                             GXFDataset()                             */
+/************************************************************************/
+
+GXFDataset::GXFDataset()
+
+{
+    pszProjection = NULL;
+    hGXF = NULL;
+}
+
+/************************************************************************/
+/*                            ~GXFDataset()                             */
+/************************************************************************/
+
+GXFDataset::~GXFDataset()
+
+{
+    FlushCache();
+    if( hGXF != NULL )
+        GXFClose( hGXF );
+    CPLFree( pszProjection );
+}
+
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr GXFDataset::GetGeoTransform( double * padfTransform )
+
+{
+    CPLErr	eErr;
+    double	dfXOrigin, dfYOrigin, dfXSize, dfYSize, dfRotation;
+
+    eErr = GXFGetPosition( hGXF, &dfXOrigin, &dfYOrigin,
+                           &dfXSize, &dfYSize, &dfRotation );
+
+    if( eErr != CE_None )
+        return eErr;
+
+    // Transform to radians. 
+    dfRotation = (dfRotation / 360.0) * 2 * PI;
+
+    padfTransform[1] = dfXSize * cos(dfRotation);
+    padfTransform[2] = dfYSize * sin(dfRotation);
+    padfTransform[4] = dfXSize * sin(dfRotation);
+    padfTransform[5] = -1 * dfYSize * cos(dfRotation);
+
+    // take into account that GXF is point or center of pixel oriented.
+    padfTransform[0] = dfXOrigin - 0.5*padfTransform[1] - 0.5*padfTransform[2];
+    padfTransform[3] = dfYOrigin - 0.5*padfTransform[4] - 0.5*padfTransform[5];
+    
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *GXFDataset::GetProjectionRef()
+
+{
+    return( pszProjection );
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *GXFDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    GXFHandle	hGXF;
+    int		i, bFoundKeyword, bFoundIllegal;
+    
+/* -------------------------------------------------------------------- */
+/*      Before trying GXFOpen() we first verify that there is at        */
+/*      least one "\n#keyword" type signature in the first chunk of     */
+/*      the file.                                                       */
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->fp == NULL || poOpenInfo->nHeaderBytes < 50 )
+        return NULL;
+
+    bFoundKeyword = FALSE;
+    bFoundIllegal = FALSE;
+    for( i = 0; i < poOpenInfo->nHeaderBytes-1; i++ )
+    {
+        if( (poOpenInfo->pabyHeader[i] == 10
+             || poOpenInfo->pabyHeader[i] == 13)
+            && poOpenInfo->pabyHeader[i+1] == '#' )
+        {
+            bFoundKeyword = TRUE;
+        }
+        if( poOpenInfo->pabyHeader[i] == 0 )
+        {
+            bFoundIllegal = TRUE;
+            break;
+        }
+    }
+
+    if( !bFoundKeyword || bFoundIllegal )
+        return NULL;
+    
+    
+/* -------------------------------------------------------------------- */
+/*      At this point it is plausible that this is a GXF file, but      */
+/*      we also now verify that there is a #GRID keyword before         */
+/*      passing it off to GXFOpen().  We check in the first 50K.        */
+/* -------------------------------------------------------------------- */
+    int nBytesRead, bGotGrid = FALSE;
+    char szBigBuf[50000];
+    FILE *fp;
+
+    fp = VSIFOpen( poOpenInfo->pszFilename, "rb" );
+    if( fp == NULL )
+        return NULL;
+
+    nBytesRead = VSIFRead( szBigBuf, 1, sizeof(szBigBuf), fp );
+    VSIFClose( fp );
+
+    for( i = 0; i < nBytesRead - 5 && !bGotGrid; i++ )
+    {
+        if( szBigBuf[i] == '#' && EQUALN(szBigBuf+i+1,"GRID",4) )
+            bGotGrid = TRUE;
+    }
+
+    if( !bGotGrid )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Try opening the dataset.                                        */
+/* -------------------------------------------------------------------- */
+    
+    hGXF = GXFOpen( poOpenInfo->pszFilename );
+    
+    if( hGXF == NULL )
+        return( NULL );
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    GXFDataset 	*poDS;
+
+    poDS = new GXFDataset();
+
+    poDS->hGXF = hGXF;
+    
+/* -------------------------------------------------------------------- */
+/*	Establish the projection.					*/
+/* -------------------------------------------------------------------- */
+    poDS->pszProjection = GXFGetMapProjectionAsOGCWKT( hGXF );
+
+/* -------------------------------------------------------------------- */
+/*      Capture some information from the file that is of interest.     */
+/* -------------------------------------------------------------------- */
+    GXFGetRawInfo( hGXF, &(poDS->nRasterXSize), &(poDS->nRasterYSize), NULL,
+                   NULL, NULL, NULL );
+    
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    poDS->nBands = 1;
+    poDS->SetBand( 1, new GXFRasterBand( poDS, 1 ));
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                          GDALRegister_GXF()                          */
+/************************************************************************/
+
+void GDALRegister_GXF()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "GXF" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "GXF" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "GeoSoft Grid Exchange Format" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_various.html#GXF" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gxf" );
+
+        poDriver->pfnOpen = GXFDataset::Open;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
diff --git a/Utilities/GDAL/frmts/gxf/gxfopen.c b/Utilities/GDAL/frmts/gxf/gxfopen.c
new file mode 100644
index 0000000000..c05df3bedd
--- /dev/null
+++ b/Utilities/GDAL/frmts/gxf/gxfopen.c
@@ -0,0 +1,1012 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  GXF Reader
+ * Purpose:  Majority of Geosoft GXF reading code.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1998, Global Geomatics
+ * Copyright (c) 1998, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: gxfopen.c,v $
+ * Revision 1.16  2006/04/04 17:25:27  fwarmerdam
+ * updated contact info
+ *
+ * Revision 1.15  2004/08/23 14:24:14  warmerda
+ * Added GXFClose() on failed open to recover resources.
+ *
+ * Revision 1.14  2004/04/27 14:30:55  warmerda
+ * Cast CPLErr properly.
+ *
+ * Revision 1.13  2004/04/07 13:08:56  warmerda
+ * fixed write past end of scanline offset array
+ *
+ * Revision 1.12  2004/04/07 02:04:14  warmerda
+ * ensure CPLReadLine() buffer is cleared on close
+ *
+ * Revision 1.11  2003/09/22 14:57:18  warmerda
+ * Fixed bug recognising #MAP_PROJECTION when a #MAP_TRANSFORM was in
+ * the file (see 1133_resmag.gxf).  Also fixed a bug with the allocation of
+ * the scanline offset buffer which can cause memory corruption for files
+ * longer than they are wide.
+ * http://bugzilla.remotesensing.org/show_bug.cgi?id=395
+ *
+ * Revision 1.10  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.9  2000/11/16 14:55:55  warmerda
+ * ensure correct operation if no title available
+ *
+ * Revision 1.8  2000/07/28 16:33:07  warmerda
+ * Fixed mixup between RawXSize and RawYSize in GXFGetRawScanline().
+ * See http://bugzilla.remotesensing.org/show_bug.cgi?id=5
+ * Thanks to msalazar@schaferdc.com.
+ *
+ * Revision 1.7  1999/10/27 20:22:33  warmerda
+ * Added Doxygen style documentation.
+ * Added GXFGetPosition() function.
+ *
+ * Revision 1.6  1999/01/22 05:27:28  warmerda
+ * Fixed some problems with oblique and polar stereographic projection
+ * support.
+ *
+ * Revision 1.5  1999/01/11 15:32:54  warmerda
+ * Added support for PROJ4 adjusted positions, and better proj support
+ *
+ * Revision 1.4  1998/12/15 19:07:40  warmerda
+ * Add Close, move Readline, add zmin/max, add readscanline
+ *
+ * Revision 1.3  1998/12/14 04:52:06  warmerda
+ * Added projection support, fixed bugs in compressed image support.
+ *
+ * Revision 1.2  1998/12/06 02:54:10  warmerda
+ * Raw read access now working.
+ *
+ * Revision 1.1  1998/12/02 19:37:04  warmerda
+ * New
+ */
+
+#include <ctype.h>
+#include "gxfopen.h"
+
+CPL_CVSID("$Id$");
+
+
+/* this is also defined in gdal.h which we avoid in this separable component */
+#define CPLE_WrongFormat	200
+
+/************************************************************************/
+/*                         GXFReadHeaderValue()                         */
+/*                                                                      */
+/*      Read one entry from the file header, and return it and it's     */
+/*      value in clean form.                                            */
+/************************************************************************/
+
+static char **GXFReadHeaderValue( FILE * fp, char * pszHTitle )
+
+{
+    const char	*pszLine;
+    char	**papszReturn = NULL;
+    int		i;
+    
+/* -------------------------------------------------------------------- */
+/*      Try to read a line.  If we fail or if this isn't a proper       */
+/*      header value then return the failure.                           */
+/* -------------------------------------------------------------------- */
+    pszLine = CPLReadLine( fp );
+    if( pszLine == NULL )
+    {
+        strcpy( pszHTitle, "#EOF" );
+        return( NULL );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Extract the title.  It should be terminated by some sort of     */
+/*      white space.                                                    */
+/* -------------------------------------------------------------------- */
+    for( i = 0; !isspace(pszLine[i]) && pszLine[i] != '\0' && i < 70; i++ ) {}
+
+    strncpy( pszHTitle, pszLine, i );
+    pszHTitle[i] = '\0';
+
+/* -------------------------------------------------------------------- */
+/*      If this is #GRID, then return ... we are at the end of the      */
+/*      header.                                                         */
+/* -------------------------------------------------------------------- */
+    if( EQUAL(pszHTitle,"#GRID") )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Skip white space.                                               */
+/* -------------------------------------------------------------------- */
+    while( isspace(pszLine[i]) )
+        i++;
+
+/* -------------------------------------------------------------------- */
+/*    If we have reached the end of the line, try to read another line. */
+/* -------------------------------------------------------------------- */
+    if( pszLine[i] == '\0' )
+    {
+        pszLine = CPLReadLine( fp );
+        if( pszLine == NULL )
+        {
+            strcpy( pszHTitle, "#EOF" );
+            return( NULL );
+        }
+        i = 0;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Keeping adding the value stuff as new lines till we reach a     */
+/*      `#' mark at the beginning of a new line.                        */
+/* -------------------------------------------------------------------- */
+    do {
+        int		nNextChar;
+        char		*pszTrimmedLine;
+
+        pszTrimmedLine = CPLStrdup( pszLine );
+
+        for( i = strlen(pszLine)-1; i >= 0 && pszLine[i] == ' '; i-- ) 
+            pszTrimmedLine[i] = '\0';
+        
+        papszReturn = CSLAddString( papszReturn, pszTrimmedLine );
+        CPLFree( pszTrimmedLine );
+        
+        nNextChar = VSIFGetc( fp );
+        VSIUngetc( nNextChar, fp );
+        
+        if( nNextChar == '#' )
+            pszLine = NULL;
+        else
+            pszLine = CPLReadLine( fp );
+    } while( pszLine != NULL );
+
+    return( papszReturn );
+}
+
+/************************************************************************/
+/*                              GXFOpen()                               */
+/************************************************************************/
+
+/**
+ * Open a GXF file, and collect contents of the header.
+ *
+ * @param pszFilename the name of the file to open.
+ *
+ * @return a handle for use with other GXF functions to access the file.  This
+ * will be NULL if the access fails.
+ */
+
+GXFHandle GXFOpen( const char * pszFilename )
+
+{
+    FILE	*fp;
+    GXFInfo_t	*psGXF;
+    char	szTitle[71];
+    char	**papszList;
+
+/* -------------------------------------------------------------------- */
+/*      We open in binary to ensure that we can efficiently seek()      */
+/*      to any location when reading scanlines randomly.  If we         */
+/*      opened as text we might still be able to seek(), but I          */
+/*      believe that on Windows, the C library has to read through      */
+/*      all the data to find the right spot taking into account DOS     */
+/*      CRs.                                                            */
+/* -------------------------------------------------------------------- */
+    fp = VSIFOpen( pszFilename, "rb" );
+
+    if( fp == NULL )
+    {
+        /* how to effectively communicate this error out? */
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Unable to open file: %s\n", pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create the GXF Information object.                              */
+/* -------------------------------------------------------------------- */
+    psGXF = (GXFInfo_t *) VSICalloc( sizeof(GXFInfo_t), 1 );
+    psGXF->fp = fp;
+    psGXF->dfTransformScale = 1.0;
+    psGXF->nSense = GXFS_LL_RIGHT;
+    psGXF->dfXPixelSize = 1.0;
+    psGXF->dfYPixelSize = 1.0;
+    psGXF->dfSetDummyTo = -1e12;
+
+    psGXF->dfUnitToMeter = 1.0;
+    psGXF->pszTitle = VSIStrdup("");
+    
+/* -------------------------------------------------------------------- */
+/*      Read the header, one line at a time.                            */
+/* -------------------------------------------------------------------- */
+    while( (papszList = GXFReadHeaderValue( fp, szTitle)) != NULL )
+    {
+        if( EQUALN(szTitle,"#TITL",5) )
+        {
+            CPLFree( psGXF->pszTitle );
+            psGXF->pszTitle = CPLStrdup( papszList[0] );
+        }
+        else if( EQUALN(szTitle,"#POIN",5) )
+        {
+            psGXF->nRawXSize = atoi(papszList[0]);
+        }
+        else if( EQUALN(szTitle,"#ROWS",5) )
+        {
+            psGXF->nRawYSize = atoi(papszList[0]);
+        }
+        else if( EQUALN(szTitle,"#PTSE",5) )
+        {
+            psGXF->dfXPixelSize = atof(papszList[0]);
+        }
+        else if( EQUALN(szTitle,"#RWSE",5) )
+        {
+            psGXF->dfYPixelSize = atof(papszList[0]);
+        }
+        else if( EQUALN(szTitle,"#DUMM",5) )
+        {
+            strcpy( psGXF->szDummy, papszList[0] );
+            psGXF->dfSetDummyTo = atof(papszList[0]);
+        }
+        else if( EQUALN(szTitle,"#XORI",5) )
+        {
+            psGXF->dfXOrigin = atof(papszList[0]);
+        }
+        else if( EQUALN(szTitle,"#YORI",5) )
+        {
+            psGXF->dfYOrigin = atof(papszList[0]);
+        }
+        else if( EQUALN(szTitle,"#ZMIN",5) )
+        {
+            psGXF->dfZMinimum = atof(papszList[0]);
+        }
+        else if( EQUALN(szTitle,"#ZMAX",5) )
+        {
+            psGXF->dfZMaximum = atof(papszList[0]);
+        }
+        else if( EQUALN(szTitle,"#SENS",5) )
+        {
+            psGXF->nSense = atoi(papszList[0]);
+        }
+        else if( EQUALN(szTitle,"#MAP_PROJECTION",8) )
+        {
+            psGXF->papszMapProjection = papszList;
+            papszList = NULL;
+        }
+        else if( EQUALN(szTitle,"#MAP_D",5) )
+        {
+            psGXF->papszMapDatumTransform = papszList;
+            papszList = NULL;
+        }
+        else if( EQUALN(szTitle,"#UNIT",5) )
+        {
+            char	**papszFields;
+
+            papszFields = CSLTokenizeStringComplex( papszList[0], ", ",
+                                                    TRUE, TRUE );
+
+            if( CSLCount(papszFields) > 1 )
+            {
+                psGXF->pszUnitName = VSIStrdup( papszFields[0] );
+                psGXF->dfUnitToMeter = atof( papszFields[1] );
+                if( psGXF->dfUnitToMeter == 0.0 )
+                    psGXF->dfUnitToMeter = 1.0;
+            }
+
+            CSLDestroy( papszFields );
+        }
+        else if( EQUALN(szTitle,"#TRAN",5) )
+        {
+            char	**papszFields;
+
+            papszFields = CSLTokenizeStringComplex( papszList[0], ", ",
+                                                    TRUE, TRUE );
+
+            if( CSLCount(papszFields) > 1 )
+            {
+                psGXF->dfTransformScale = atof(papszFields[0]);
+                psGXF->dfTransformOffset = atof(papszFields[1]);
+            }
+
+            if( CSLCount(papszFields) > 2 )
+                psGXF->pszTransformName = CPLStrdup( papszFields[2] );
+
+            CSLDestroy( papszFields );
+        }
+        else if( EQUALN(szTitle,"#GTYPE",5) )
+        {
+            psGXF->nGType = atoi(papszList[0]);
+        }
+
+        CSLDestroy( papszList );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Did we find the #GRID?                                          */
+/* -------------------------------------------------------------------- */
+    if( !EQUALN(szTitle,"#GRID",5) )
+    {
+        GXFClose( psGXF );
+        CPLError( CE_Failure, CPLE_WrongFormat,
+                  "Didn't parse through to #GRID successfully in.\n"
+                  "file `%s'.\n",
+                  pszFilename );
+        
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Allocate, and initialize the raw scanline offset array.         */
+/* -------------------------------------------------------------------- */
+    psGXF->panRawLineOffset = (long *)
+        CPLCalloc( sizeof(long), psGXF->nRawYSize+1 );
+
+    psGXF->panRawLineOffset[0] = VSIFTell( psGXF->fp );
+
+/* -------------------------------------------------------------------- */
+/*      Update the zmin/zmax values to take into account #TRANSFORM     */
+/*      information.                                                    */
+/* -------------------------------------------------------------------- */
+    if( psGXF->dfZMinimum != 0.0 || psGXF->dfZMaximum != 0.0 )
+    {
+        psGXF->dfZMinimum = (psGXF->dfZMinimum * psGXF->dfTransformScale)
+            			+ psGXF->dfTransformOffset;
+        psGXF->dfZMaximum = (psGXF->dfZMaximum * psGXF->dfTransformScale)
+            			+ psGXF->dfTransformOffset;
+    }
+    
+    return( (GXFHandle) psGXF );
+}
+
+/************************************************************************/
+/*                              GXFClose()                              */
+/************************************************************************/
+
+/**
+ * Close GXF file opened with GXFOpen().
+ *
+ * @param hGXF handle to GXF file.
+ */
+
+void GXFClose( GXFHandle hGXF )
+
+{
+    GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
+
+    CPLFree( psGXF->panRawLineOffset );
+    CPLFree( psGXF->pszUnitName );
+    CSLDestroy( psGXF->papszMapDatumTransform );
+    CSLDestroy( psGXF->papszMapProjection );
+    CPLFree( psGXF->pszTitle );
+
+    VSIFClose( psGXF->fp );
+
+    CPLReadLine( NULL );
+
+    CPLFree( psGXF );
+}
+
+/************************************************************************/
+/*                           GXFParseBase90()                           */
+/*                                                                      */
+/*      Parse a base 90 number ... exceptions (repeat, and dummy)       */
+/*      values have to be recognised outside this function.             */
+/************************************************************************/
+
+double GXFParseBase90( GXFInfo_t * psGXF, const char * pszText,
+                       int bScale )
+
+{
+    int		i = 0, nValue = 0;
+
+    while( i < psGXF->nGType )
+    {
+        nValue = nValue*90 + (pszText[i] - 37);
+        i++;
+    }
+
+    if( bScale ) 
+        return( (nValue * psGXF->dfTransformScale) + psGXF->dfTransformOffset);
+    else
+        return( nValue );
+}
+
+
+/************************************************************************/
+/*                       GXFReadRawScanlineFrom()                       */
+/************************************************************************/
+
+static int GXFReadRawScanlineFrom( GXFInfo_t * psGXF, long iOffset,
+                                   long * pnNewOffset, double * padfLineBuf )
+
+{
+    const char	*pszLine;
+    int		nValuesRead = 0, nValuesSought = psGXF->nRawXSize;
+    
+    VSIFSeek( psGXF->fp, iOffset, SEEK_SET );
+
+    while( nValuesRead < nValuesSought )
+    {
+        pszLine = CPLReadLine( psGXF->fp );
+        if( pszLine == NULL )
+            break;
+
+/* -------------------------------------------------------------------- */
+/*      Uncompressed case.                                              */
+/* -------------------------------------------------------------------- */
+        if( psGXF->nGType == 0 )
+        {
+            /* we could just tokenize the line, but that's pretty expensive.
+               Instead I will parse on white space ``by hand''. */
+            while( *pszLine != '\0' && nValuesRead < nValuesSought )
+            {
+                int		i;
+                
+                /* skip leading white space */
+                for( ; isspace(*pszLine); pszLine++ ) {}
+
+                /* Skip the data value (non white space) */
+                for( i = 0; pszLine[i] != '\0' && !isspace(pszLine[i]); i++) {}
+
+                if( strncmp(pszLine,psGXF->szDummy,i) == 0 )
+                {
+                    padfLineBuf[nValuesRead++] = psGXF->dfSetDummyTo;
+                }
+                else
+                {
+                    padfLineBuf[nValuesRead++] = atof(pszLine);
+                }
+
+                /* skip further whitespace */
+                for( pszLine += i; isspace(*pszLine); pszLine++ ) {}
+            }
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Compressed case.                                                */
+/* -------------------------------------------------------------------- */
+        else
+        {
+            while( *pszLine != '\0' && nValuesRead < nValuesSought )
+            {
+                if( pszLine[0] == '!' )
+                {
+                    padfLineBuf[nValuesRead++] = psGXF->dfSetDummyTo;
+                }
+                else if( pszLine[0] == '"' )
+                {
+                    int		nCount, i;
+                    double	dfValue;
+
+                    pszLine += psGXF->nGType;
+                    if( (int) strlen(pszLine) < psGXF->nGType )
+                        pszLine = CPLReadLine( psGXF->fp );
+                    
+                    nCount = (int) GXFParseBase90( psGXF, pszLine, FALSE);
+                    pszLine += psGXF->nGType;
+                    
+                    if( (int) strlen(pszLine) < psGXF->nGType )
+                        pszLine = CPLReadLine( psGXF->fp );
+                    
+                    if( *pszLine == '!' )
+                        dfValue = psGXF->dfSetDummyTo;
+                    else
+                        dfValue = GXFParseBase90( psGXF, pszLine, TRUE );
+
+                    CPLAssert( nValuesRead + nCount <= nValuesSought );
+                    
+                    for( i=0; i < nCount && nValuesRead < nValuesSought; i++ )
+                        padfLineBuf[nValuesRead++] = dfValue;
+                }
+                else
+                {
+                    padfLineBuf[nValuesRead++] =
+                        GXFParseBase90( psGXF, pszLine, TRUE );
+                }
+
+                pszLine += psGXF->nGType;
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Return the new offset, if requested.                            */
+/* -------------------------------------------------------------------- */
+    if( pnNewOffset != NULL )
+    {
+        *pnNewOffset = VSIFTell( psGXF->fp );
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           GXFGetScanline()                           */
+/************************************************************************/
+
+/**
+ * Read a scanline of raster data from GXF file.
+ *
+ * This function operates similarly to GXFGetRawScanline(), but it
+ * attempts to mirror data horizontally or vertically based on the #SENSE
+ * flag to return data in a top to bottom, and left to right organization.
+ * If the file is organized in columns (#SENSE is GXFS_UR_DOWN, GXFS_UL_DOWN,
+ * GXFS_LR_UP, or GXFS_LL_UP) then this function will fail, returning
+ * CE_Failure, and reporting a sense error.
+ *
+ * See GXFGetRawScanline() for other notes.
+ *
+ * @param hGXF the GXF file handle, as returned from GXFOpen().
+ * @param iScanline the scanline to read, zero is the top scanline.
+ * @param padfLineBuf a buffer of doubles into which the scanline pixel
+ * values are read.  This must be at least as long as a scanline.
+ *
+ * @return CE_None if access succeeds or CE_Failure if something goes wrong.
+ */
+
+CPLErr GXFGetScanline( GXFHandle hGXF, int iScanline, double * padfLineBuf )
+
+{
+    GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
+    CPLErr	nErr;
+    int		iRawScanline;
+
+    if( psGXF->nSense == GXFS_LL_RIGHT
+        || psGXF->nSense == GXFS_LR_LEFT )
+    {
+        iRawScanline = psGXF->nRawYSize - iScanline - 1;
+    }
+
+    else if( psGXF->nSense == GXFS_UL_RIGHT
+             || psGXF->nSense == GXFS_UR_LEFT )
+    {
+        iRawScanline = iScanline;
+    }
+    else
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Unable to support vertically oriented images." );
+        return( CE_Failure );
+    }
+
+    nErr = GXFGetRawScanline( hGXF, iRawScanline, padfLineBuf );
+
+    if( nErr == CE_None
+        && (psGXF->nSense == GXFS_LR_LEFT || psGXF->nSense == GXFS_UR_LEFT) )
+    {
+        int	i;
+        double	dfTemp;
+        
+        for( i = psGXF->nRawXSize / 2 - 1; i >= 0; i-- )
+        {
+            dfTemp = padfLineBuf[i];
+            padfLineBuf[i] = padfLineBuf[psGXF->nRawXSize-i-1];
+            padfLineBuf[psGXF->nRawXSize-i-1] = dfTemp;
+        }
+    }
+
+    return( nErr );
+}
+
+/************************************************************************/
+/*                         GXFGetRawScanline()                          */
+/************************************************************************/
+
+/**
+ * Read a scanline of raster data from GXF file.
+ *
+ * This function will read a row of data from the GXF file.  It is "Raw"
+ * in the sense that it doesn't attempt to account for the #SENSE flag as
+ * the GXFGetScanline() function does.  Unlike GXFGetScanline(), this function
+ * supports column organized files.
+ *
+ * Any dummy pixels are assigned the dummy value indicated by GXFGetRawInfo().
+ *
+ * @param hGXF the GXF file handle, as returned from GXFOpen().
+ * @param iScanline the scanline to read, zero is the first scanline in the
+ * file. 
+ * @param padfLineBuf a buffer of doubles into which the scanline pixel
+ * values are read.  This must be at least as long as a scanline.
+ *
+ * @return CE_None if access succeeds or CE_Failure if something goes wrong.
+ */
+
+CPLErr GXFGetRawScanline( GXFHandle hGXF, int iScanline, double * padfLineBuf )
+
+{
+    GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
+    CPLErr	nErr;
+    
+/* -------------------------------------------------------------------- */
+/*      Validate scanline.                                              */
+/* -------------------------------------------------------------------- */
+    if( iScanline < 0 || iScanline >= psGXF->nRawYSize )
+    {
+        CPLError( CE_Failure, CPLE_IllegalArg,
+                  "GXFGetRawScanline(): Scanline `%d' does not exist.\n",
+                  iScanline );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If we don't have the requested scanline, fetch preceeding       */
+/*      scanlines to find the pointer to this scanline.                 */
+/* -------------------------------------------------------------------- */
+    if( psGXF->panRawLineOffset[iScanline] == 0 )
+    {
+        int		i;
+        
+        CPLAssert( iScanline > 0 );
+
+        for( i = 0; i < iScanline; i++ )
+        {
+            if( psGXF->panRawLineOffset[i+1] == 0 )
+            {
+                nErr = GXFGetRawScanline( hGXF, i, padfLineBuf );
+                if( nErr != CE_None )
+                    return( nErr );
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get this scanline, and update the offset for the next line.     */
+/* -------------------------------------------------------------------- */
+    nErr = (CPLErr)
+        GXFReadRawScanlineFrom( psGXF, psGXF->panRawLineOffset[iScanline],
+                                psGXF->panRawLineOffset+iScanline+1,
+                                padfLineBuf );
+
+    return nErr;
+}
+
+/************************************************************************/
+/*                         GXFScanForZMinMax()                          */
+/*                                                                      */
+/*      The header doesn't contain the ZMin/ZMax values, but the        */
+/*      application has requested it ... scan the entire image for      */
+/*      it.                                                             */
+/************************************************************************/
+
+static void GXFScanForZMinMax( GXFHandle hGXF )
+
+{
+    GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
+    int		iLine, iPixel;
+    double	*padfScanline;
+    
+
+    padfScanline = (double *) VSICalloc(sizeof(double),psGXF->nRawXSize);
+    if( padfScanline == NULL )
+        return;
+
+    psGXF->dfZMinimum = 1e50;
+    psGXF->dfZMaximum = -1e50;
+
+    for( iLine = 0; iLine < psGXF->nRawYSize; iLine++ )
+    {
+        if( GXFGetRawScanline( hGXF, iLine, padfScanline ) != CE_None )
+            break;
+
+        for( iPixel = 0; iPixel < psGXF->nRawXSize; iPixel++ )
+        {
+            if( padfScanline[iPixel] != psGXF->dfSetDummyTo )
+            {
+                psGXF->dfZMinimum =
+                    MIN(psGXF->dfZMinimum,padfScanline[iPixel]);
+                psGXF->dfZMaximum =
+                    MAX(psGXF->dfZMaximum,padfScanline[iPixel]);
+            }
+        }
+    }
+
+    VSIFree( padfScanline );
+
+/* -------------------------------------------------------------------- */
+/*      Did we get any real data points?                                */
+/* -------------------------------------------------------------------- */
+    if( psGXF->dfZMinimum > psGXF->dfZMaximum )
+    {
+        psGXF->dfZMinimum = 0.0;
+        psGXF->dfZMaximum = 0.0;
+    }
+}
+
+/************************************************************************/
+/*                             GXFGetRawInfo()                          */
+/************************************************************************/
+
+/**
+ * Fetch header information about a GXF file.
+ *
+ * Note that the X and Y sizes are of the raw raster and don't take into
+ * account the #SENSE flag.  If the file is column oriented (rows in the
+ * files are actually columns in the raster) these values would need to be
+ * transposed for the actual raster.
+ *
+ * The legal pnSense values are:
+ * <ul>
+ * <li> GXFS_LL_UP(-1): lower left origin, scanning up.
+ * <li> GXFS_LL_RIGHT(1): lower left origin, scanning right.
+ * <li> GXFS_UL_RIGHT(-2): upper left origin, scanning right.
+ * <li> GXFS_UL_DOWN(2): upper left origin, scanning down.
+ * <li> GXFS_UR_DOWN(-3): upper right origin, scanning down.
+ * <li> GXFS_UR_LEFT(3): upper right origin, scanning left.
+ * <li> GXFS_LR_LEFT(-4): lower right origin, scanning left.
+ * <li> GXFS_LR_UP(4): lower right origin, scanning up.
+ * </ul>
+ *
+ * Note that the GXFGetScanline() function attempts to provide a GXFS_UL_RIGHT
+ * view onto files, but doesn't handle the *_DOWN and *_UP oriented files.
+ *
+ * The Z min and max values may not occur in the GXF header.  If they are
+ * requested, and aren't available in the header the entire file is scanned
+ * in order to establish them.  This can be expensive.
+ *
+ * If no #DUMMY value was specified in the file, a default of -1e12 is used.
+ * 
+ * @param hGXF handle to GXF file returned by GXFOpen().
+ * @param pnXSize int to be set with the width of the raw raster.  May be NULL.
+ * @param pnYSize int to be set with the height of the raw raster. May be NULL.
+ * @param pnSense int to set with #SENSE flag, may be NULL.
+ * @param pdfZMin double to set with minimum raster value, may be NULL.
+ * @param pdfZMax double to set with minimum raster value, may be NULL.
+ * @param pdfDummy double to set with dummy (nodata / invalid data) pixel
+ * value.
+ */ 
+
+CPLErr GXFGetRawInfo( GXFHandle hGXF, int *pnXSize, int *pnYSize,
+                      int * pnSense, double * pdfZMin, double * pdfZMax,
+                      double * pdfDummy )
+
+{
+    GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
+
+    if( pnXSize != NULL )
+        *pnXSize = psGXF->nRawXSize;
+
+    if( pnYSize != NULL )
+        *pnYSize = psGXF->nRawYSize;
+
+    if( pnSense != NULL )
+        *pnSense = psGXF->nSense;
+
+    if( (pdfZMin != NULL || pdfZMax != NULL)
+        && psGXF->dfZMinimum == 0.0 && psGXF->dfZMaximum == 0.0 )
+    {
+        GXFScanForZMinMax( hGXF );
+    }
+    
+    if( pdfZMin != NULL )
+        *pdfZMin = psGXF->dfZMinimum;
+
+    if( pdfZMax != NULL )
+        *pdfZMax = psGXF->dfZMaximum;
+
+    if( pdfDummy != NULL )
+        *pdfDummy = psGXF->dfSetDummyTo;
+
+    return( CE_None );
+}
+
+/************************************************************************/
+/*                        GXFGetMapProjection()                         */
+/************************************************************************/
+
+/**
+ * Return the lines related to the map projection.  It is up to   
+ * the caller to parse them and interprete.  The return result    
+ * will be NULL if no #MAP_PROJECTION line was found in the header.
+ * 
+ * @param hGXF the GXF file handle.
+ *
+ * @return a NULL terminated array of string pointers containing the
+ * projection, or NULL.  The strings remained owned by the GXF API, and
+ * should not be modified or freed by the caller.  
+ */
+
+char **GXFGetMapProjection( GXFHandle hGXF )
+
+{
+    return( ((GXFInfo_t *) hGXF)->papszMapProjection );
+}
+
+/************************************************************************/
+/*                      GXFGetMapDatumTransform()                       */
+/************************************************************************/
+
+/**
+ * Return the lines related to the datum transformation.  It is up to   
+ * the caller to parse them and interpret.  The return result    
+ * will be NULL if no #MAP_DATUM_TRANSFORM line was found in the header.
+ * 
+ * @param hGXF the GXF file handle.
+ *
+ * @return a NULL terminated array of string pointers containing the
+ * datum, or NULL.  The strings remained owned by the GXF API, and
+ * should not be modified or freed by the caller.  
+ */
+
+char **GXFGetMapDatumTransform( GXFHandle hGXF )
+
+{
+    return( ((GXFInfo_t *) hGXF)->papszMapDatumTransform );
+}
+
+/************************************************************************/
+/*                         GXFGetRawPosition()                          */
+/************************************************************************/
+
+/**
+ * Get the raw grid positioning information.
+ *
+ * Note that these coordinates refer to the raw grid, and are in the units
+ * specified by the #UNITS field.  See GXFGetPosition() for a similar
+ * function that takes into account the #SENSE values similarly to
+ * GXFGetScanline().
+ *
+ * Note that the pixel values are considered to be point values in GXF,
+ * and thus the origin is for the first point.  If you consider the pixels
+ * to be areas, then the origin is for the center of the origin pixel, not
+ * the outer corner.
+ *
+ * @param hGXF the GXF file handle.
+ * @param pdfXOrigin X position of the origin in the base coordinate system.
+ * @param pdfYOrigin Y position of the origin in the base coordinate system.
+ * @param pdfXPixelSize X pixel size in base coordinates.
+ * @param pdfYPixelSize Y pixel size in base coordinates.
+ * @param pdfRotation rotation in degrees counter-clockwise from the
+ * base coordinate system.
+ *
+ * @return Returns CE_None if successful, or CE_Failure if no posiitioning
+ * information was found in the file.
+ */
+ 
+
+CPLErr GXFGetRawPosition( GXFHandle hGXF,
+                          double * pdfXOrigin, double * pdfYOrigin,
+                          double * pdfXPixelSize, double * pdfYPixelSize,
+                          double * pdfRotation )
+
+{
+    GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
+    
+    if( pdfXOrigin != NULL )
+        *pdfXOrigin = psGXF->dfXOrigin;
+    if( pdfYOrigin != NULL )
+        *pdfYOrigin = psGXF->dfYOrigin;
+    if( pdfXPixelSize != NULL )
+        *pdfXPixelSize = psGXF->dfXPixelSize;
+    if( pdfYPixelSize != NULL )
+        *pdfYPixelSize = psGXF->dfYPixelSize;
+    if( pdfRotation != NULL )
+        *pdfRotation = psGXF->dfRotation;
+
+    if( psGXF->dfXOrigin == 0.0 && psGXF->dfYOrigin == 0.0
+        && psGXF->dfXPixelSize == 0.0 && psGXF->dfYPixelSize == 0.0 )
+        return( CE_Failure );
+    else
+        return( CE_None );
+}
+
+
+/************************************************************************/
+/*                           GXFGetPosition()                           */
+/************************************************************************/
+
+/**
+ * Get the grid positioning information.
+ *
+ * Note that these coordinates refer to the grid positioning after taking
+ * into account the #SENSE flag (as is done by the GXFGetScanline()) function.
+ *
+ * Note that the pixel values are considered to be point values in GXF,
+ * and thus the origin is for the first point.  If you consider the pixels
+ * to be areas, then the origin is for the center of the origin pixel, not
+ * the outer corner.
+ *
+ * This function does not support vertically oriented images, nor does it
+ * properly transform rotation for images with a SENSE other than
+ * GXFS_UL_RIGHT.
+ *
+ * @param hGXF the GXF file handle.
+ * @param pdfXOrigin X position of the origin in the base coordinate system.
+ * @param pdfYOrigin Y position of the origin in the base coordinate system.
+ * @param pdfXPixelSize X pixel size in base coordinates.
+ * @param pdfYPixelSize Y pixel size in base coordinates.
+ * @param pdfRotation rotation in degrees counter-clockwise from the
+ * base coordinate system.
+ *
+ * @return Returns CE_None if successful, or CE_Failure if no posiitioning
+ * information was found in the file.
+ */
+ 
+
+CPLErr GXFGetPosition( GXFHandle hGXF,
+                       double * pdfXOrigin, double * pdfYOrigin,
+                       double * pdfXPixelSize, double * pdfYPixelSize,
+                       double * pdfRotation )
+
+{
+    GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
+    double	dfCXOrigin, dfCYOrigin, dfCXPixelSize, dfCYPixelSize;
+
+    switch( psGXF->nSense )
+    {
+      case GXFS_UL_RIGHT:
+        dfCXOrigin = psGXF->dfXOrigin;
+        dfCYOrigin = psGXF->dfYOrigin;
+        dfCXPixelSize = psGXF->dfXPixelSize;
+        dfCYPixelSize = psGXF->dfYPixelSize;
+        break;
+        
+      case GXFS_UR_LEFT:
+        dfCXOrigin = psGXF->dfXOrigin
+            	     - (psGXF->nRawXSize-1) * psGXF->dfXPixelSize;
+        dfCYOrigin = psGXF->dfYOrigin;
+        dfCXPixelSize = psGXF->dfXPixelSize;
+        dfCYPixelSize = psGXF->dfYPixelSize;
+        break;
+        
+      case GXFS_LL_RIGHT:
+        dfCXOrigin = psGXF->dfXOrigin;
+        dfCYOrigin = psGXF->dfYOrigin
+                     + (psGXF->nRawYSize-1) * psGXF->dfYPixelSize;
+        dfCXPixelSize = psGXF->dfXPixelSize;
+        dfCYPixelSize = psGXF->dfYPixelSize;
+        break;
+        
+      case GXFS_LR_LEFT:
+        dfCXOrigin = psGXF->dfXOrigin
+            	     - (psGXF->nRawXSize-1) * psGXF->dfXPixelSize;
+        dfCYOrigin = psGXF->dfYOrigin
+                     + (psGXF->nRawYSize-1) * psGXF->dfYPixelSize;
+        dfCXPixelSize = psGXF->dfXPixelSize;
+        dfCYPixelSize = psGXF->dfYPixelSize;
+        break;
+
+      default:
+        CPLError( CE_Failure, CPLE_AppDefined,
+           "GXFGetPosition() doesn't support vertically organized images." );
+        return CE_Failure;
+    }
+
+    if( pdfXOrigin != NULL )
+        *pdfXOrigin = dfCXOrigin;
+    if( pdfYOrigin != NULL )
+        *pdfYOrigin = dfCYOrigin;
+    if( pdfXPixelSize != NULL )
+        *pdfXPixelSize = dfCXPixelSize;
+    if( pdfYPixelSize != NULL )
+        *pdfYPixelSize = dfCYPixelSize;
+    if( pdfRotation != NULL )
+        *pdfRotation = psGXF->dfRotation;
+
+    if( psGXF->dfXOrigin == 0.0 && psGXF->dfYOrigin == 0.0
+        && psGXF->dfXPixelSize == 0.0 && psGXF->dfYPixelSize == 0.0 )
+        return( CE_Failure );
+    else
+        return( CE_None );
+}
+
+
diff --git a/Utilities/GDAL/frmts/gxf/gxfopen.h b/Utilities/GDAL/frmts/gxf/gxfopen.h
new file mode 100644
index 0000000000..468be7295f
--- /dev/null
+++ b/Utilities/GDAL/frmts/gxf/gxfopen.h
@@ -0,0 +1,154 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  GXF Reader
+ * Purpose:  GXF-3 access function declarations.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1998, Global Geomatics
+ * Copyright (c) 1998, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: gxfopen.h,v $
+ * Revision 1.8  2006/04/04 17:25:27  fwarmerdam
+ * updated contact info
+ *
+ * Revision 1.7  1999/10/29 17:30:40  warmerda
+ * added prototype
+ *
+ * Revision 1.6  1999/10/27 20:22:33  warmerda
+ * Added Doxygen style documentation.
+ * Added GXFGetPosition() function.
+ *
+ * Revision 1.5  1999/01/11 15:33:06  warmerda
+ * Added function
+ *
+ * Revision 1.4  1998/12/15 19:07:40  warmerda
+ * Add Close, move Readline, add zmin/max, add readscanline
+ *
+ * Revision 1.3  1998/12/14 04:51:30  warmerda
+ * Added some functions, clarified raw vs. not raw.
+ *
+ * Revision 1.2  1998/12/06 02:54:26  warmerda
+ * new functions
+ *
+ * Revision 1.1  1998/12/02 19:37:04  warmerda
+ * New
+ *
+ */
+
+#ifndef _GXFOPEN_H_INCLUDED
+#define _GXFOPEN_H_INCLUDED
+
+/**
+ * \file gxfopen.h
+ *
+ * Public GXF-3 function definitions.
+ */
+
+/* -------------------------------------------------------------------- */
+/*      Include standard portability stuff.                             */
+/* -------------------------------------------------------------------- */
+#include "cpl_conv.h"
+#include "cpl_string.h"
+
+CPL_C_START
+
+typedef void *GXFHandle;
+
+GXFHandle GXFOpen( const char * pszFilename );
+
+CPLErr   GXFGetRawInfo( GXFHandle hGXF, int *pnXSize, int *pnYSize,
+                        int *pnSense, double * pdfZMin, double * pdfZMax,
+                        double * pdfDummy );
+CPLErr   GXFGetInfo( GXFHandle hGXF, int *pnXSize, int *pnYSize );
+
+CPLErr   GXFGetRawScanline( GXFHandle, int iScanline, double * padfLineBuf );
+CPLErr   GXFGetScanline( GXFHandle, int iScanline, double * padfLineBuf );
+
+char	**GXFGetMapProjection( GXFHandle );
+char	**GXFGetMapDatumTransform( GXFHandle );
+char	*GXFGetMapProjectionAsPROJ4( GXFHandle );
+char	*GXFGetMapProjectionAsOGCWKT( GXFHandle );
+
+CPLErr  GXFGetRawPosition( GXFHandle, double *, double *, double *, double *,
+                           double * );
+CPLErr  GXFGetPosition( GXFHandle, double *, double *, double *, double *,
+                        double * );
+
+CPLErr  GXFGetPROJ4Position( GXFHandle, double *, double *, double *, double *,
+                             double * );
+
+void     GXFClose( GXFHandle hGXF );
+
+#define GXFS_LL_UP	-1
+#define GXFS_LL_RIGHT	1
+#define GXFS_UL_RIGHT	-2
+#define GXFS_UL_DOWN	2
+#define GXFS_UR_DOWN	-3
+#define GXFS_UR_LEFT	3
+#define GXFS_LR_LEFT	-4
+#define GXFS_LR_UP	4
+
+CPL_C_END
+
+/* -------------------------------------------------------------------- */
+/*      This is consider to be a private structure.                     */
+/* -------------------------------------------------------------------- */
+typedef struct {
+    FILE	*fp;
+
+    int		nRawXSize;
+    int		nRawYSize;
+    int		nSense;		/* GXFS_ codes */
+    int		nGType;		/* 0 is uncompressed */
+
+    double	dfXPixelSize;
+    double	dfYPixelSize;
+    double	dfRotation;
+    double	dfXOrigin;	/* lower left corner */
+    double	dfYOrigin;	/* lower left corner */
+
+    char	szDummy[64];
+    double	dfSetDummyTo;
+
+    char	*pszTitle;
+
+    double	dfTransformScale;
+    double	dfTransformOffset;
+    char	*pszTransformName;
+
+    char	**papszMapProjection;
+    char	**papszMapDatumTransform;
+
+    char	*pszUnitName;
+    double	dfUnitToMeter;
+    
+
+    double	dfZMaximum;
+    double	dfZMinimum;
+
+    long	*panRawLineOffset;
+    
+} GXFInfo_t;
+
+#endif /* ndef _GXFOPEN_H_INCLUDED */
diff --git a/Utilities/GDAL/frmts/gxf/makefile.vc b/Utilities/GDAL/frmts/gxf/makefile.vc
new file mode 100644
index 0000000000..0ae3bc0d71
--- /dev/null
+++ b/Utilities/GDAL/frmts/gxf/makefile.vc
@@ -0,0 +1,15 @@
+
+OBJ	=	gxfopen.obj gxfdataset.obj gxf_ogcwkt.obj 
+
+EXTRAFLAGS = 	-I..\iso8211
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/gxf/makefile.vc.dist b/Utilities/GDAL/frmts/gxf/makefile.vc.dist
new file mode 100644
index 0000000000..6bdc0e0de6
--- /dev/null
+++ b/Utilities/GDAL/frmts/gxf/makefile.vc.dist
@@ -0,0 +1,14 @@
+
+OBJ =	gxfopen.obj gxf_proj4.obj gxf_ogcwkt.obj \
+	\
+	cpl_error.obj cpl_vsisimple.obj cpl_string.obj cpl_conv.obj
+
+default:	gxftest.exe
+
+gxf3.lib:	$(OBJ)
+	lib /out:gxf3.lib $(OBJ)
+
+gxftest.exe:	gxftest.c gxf3.lib
+	$(CC) $(CFLAGS) gxftest.c gxf3.lib
+
+
diff --git a/Utilities/GDAL/frmts/hdf5/GNUmakefile b/Utilities/GDAL/frmts/hdf5/GNUmakefile
new file mode 100644
index 0000000000..04ab3fca6e
--- /dev/null
+++ b/Utilities/GDAL/frmts/hdf5/GNUmakefile
@@ -0,0 +1,16 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	hdf5dataset.o hdf5imagedataset.o
+
+HDFEOS_OPTS	=	
+SUBLIBS 	=
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(HDF5_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ) $(SUBLIBS)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+install-obj:	$(SUBLIBS) $(O_OBJ)
diff --git a/Utilities/GDAL/frmts/hdf5/frmt_hdf5.html b/Utilities/GDAL/frmts/hdf5/frmt_hdf5.html
new file mode 100644
index 0000000000..7b4d20ad1a
--- /dev/null
+++ b/Utilities/GDAL/frmts/hdf5/frmt_hdf5.html
@@ -0,0 +1,231 @@
+<!-- $Id: frmt_hdf5.html,v 1.1 2005/08/13 00:12:59 dnadeau Exp $ -->
+
+<html>
+<head>
+<title>HDF5 --- Hierarchical Data Format Release 5 (HDF5)</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>HDF5 --- Hierarchical Data Format Release 5 (HDF5)</h1>
+
+This driver intended for HDF5 file formats importing.   This 
+modification is
+suited for use with remote sensing data and fully compatible with
+underlying HDF5.  This driver can import HDF5-EOS files.  Currently EOS use
+HDF5 for data storing (telemetry form `Aura' satellites). In
+the future they will switch to HDF5 format, which will be used for
+telemetry from `Aura' satellite.<p>
+
+<h2>Multiple Image Handling (Subdatasets)</h2>
+
+Hierarchical Data Format is a container for several different datasets.
+For data storing. HDF contains multidimensional arrays filled by data.  
+One HDF file may contain several arrays.  They may differ in size, 
+number of dimensions.<p>
+
+The first step is to get a report of the components images (arrays) in the 
+file using <b>gdalinfo</b>, and
+then to import the desired images using gdal_translate.
+
+The <b>gdalinfo</b> utility lists all multidimensional subdatasets from the 
+input HDF file. The name of individual images (subdatasets) are assigned to 
+the <b>SUBDATASET_n_NAME</b> metadata item.  The description for each image is
+found in the <b>SUBDATASET_n_DESC</b> metadata item.  For HDF5 images the
+subdataset names will be formatted like this:<p>
+
+<i>HDF5:file_name:subdataset</i><p>
+
+where:<br> <i>file_name</i> is the name of the input file, and<br>
+<i>subdataset</i> is the dataset name of the array to use (for internal use in 
+GDAL).<p>
+
+On the second step you should provide this name for <b>gdalinfo</b> or
+<b>gdal_translate</b> for actual reading of the data.<p>
+
+For example, we want to read data from the OMI/Aura Ozone (O3)  dataset:<p>
+<pre>
+$ gdalinfo OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5
+Driver: HDF5/Hierarchical Data Format Release 5
+Size is 512, 512
+Coordinate System is `'
+
+Subdatasets:
+  SUBDATASET_-1_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":APrioriLayerO3
+  SUBDATASET_-1_DESC=[1645x60x11] APrioriLayerO3 (32-bit floating-point)
+  SUBDATASET_0_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":AlgorithmFlags
+  SUBDATASET_0_DESC=[1645x60] AlgorithmFlags (8-bit unsigned character)
+  SUBDATASET_1_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":CloudFraction
+  SUBDATASET_1_DESC=[1645x60] CloudFraction (32-bit floating-point)
+  SUBDATASET_2_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":CloudTopPressure
+  SUBDATASET_2_DESC=[1645x60] CloudTopPressure (32-bit floating-point)
+  SUBDATASET_3_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":ColumnAmountO3
+  SUBDATASET_3_DESC=[1645x60] ColumnAmountO3 (32-bit floating-point)
+  SUBDATASET_4_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":LayerEfficiency
+  SUBDATASET_4_DESC=[1645x60x11] LayerEfficiency (32-bit floating-point)
+  SUBDATASET_5_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":NValue
+  SUBDATASET_5_DESC=[1645x60x10] NValue (32-bit floating-point)
+  SUBDATASET_6_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":O3BelowCloud
+  SUBDATASET_6_DESC=[1645x60] O3BelowCloud (32-bit floating-point)
+  SUBDATASET_7_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":QualityFlags
+  SUBDATASET_7_DESC=[1645x60] QualityFlags (8-bit unsigned integer)
+  SUBDATASET_8_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":Reflectivity331
+  SUBDATASET_8_DESC=[1645x60] Reflectivity331 (32-bit floating-point)
+  SUBDATASET_9_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":Reflectivity360
+  SUBDATASET_9_DESC=[1645x60] Reflectivity360 (32-bit floating-point)
+  SUBDATASET_10_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":Residual
+  SUBDATASET_10_DESC=[1645x60x10] Residual (32-bit floating-point)
+  SUBDATASET_11_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":ResidualStep1
+  SUBDATASET_11_DESC=[1645x60x10] ResidualStep1 (32-bit floating-point)
+  SUBDATASET_12_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":ResidualStep2
+  SUBDATASET_12_DESC=[1645x60x10] ResidualStep2 (32-bit floating-point)
+  SUBDATASET_13_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":SO2index
+  SUBDATASET_13_DESC=[1645x60] SO2index (32-bit floating-point)
+  SUBDATASET_14_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":Sensitivity
+  SUBDATASET_14_DESC=[1645x60x10] Sensitivity (32-bit floating-point)
+  SUBDATASET_15_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":StepOneO3
+  SUBDATASET_15_DESC=[1645x60] StepOneO3 (32-bit floating-point)
+  SUBDATASET_16_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":StepTwoO3
+  SUBDATASET_16_DESC=[1645x60] StepTwoO3 (32-bit floating-point)
+  SUBDATASET_17_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":TerrainPressure
+  SUBDATASET_17_DESC=[1645x60] TerrainPressure (32-bit floating-point)
+  SUBDATASET_18_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":UVAerosolIndex
+  SUBDATASET_18_DESC=[1645x60] UVAerosolIndex (32-bit floating-point)
+  SUBDATASET_19_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":dN_dR
+  SUBDATASET_19_DESC=[1645x60x10] dN_dR (32-bit floating-point)
+  SUBDATASET_20_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":dN_dT
+  SUBDATASET_20_DESC=[1645x60x10] dN_dT (32-bit floating-point)
+  SUBDATASET_21_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":GroundPixelQualityFlags
+  SUBDATASET_21_DESC=[1645x60] GroundPixelQualityFlags (8-bit unsigned integer)
+  SUBDATASET_22_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":Latitude
+  SUBDATASET_22_DESC=[1645x60] Latitude (32-bit floating-point)
+  SUBDATASET_23_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":Longitude
+  SUBDATASET_23_DESC=[1645x60] Longitude (32-bit floating-point)
+  SUBDATASET_24_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":RelativeAzimuthAngle
+  SUBDATASET_24_DESC=[1645x60] RelativeAzimuthAngle (32-bit floating-point)
+  SUBDATASET_25_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":SolarAzimuthAngle
+  SUBDATASET_25_DESC=[1645x60] SolarAzimuthAngle (32-bit floating-point)
+  SUBDATASET_26_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":SolarZenithAngle
+  SUBDATASET_26_DESC=[1645x60] SolarZenithAngle (32-bit floating-point)
+  SUBDATASET_27_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":TerrainHeight
+  SUBDATASET_27_DESC=[1645x60] TerrainHeight (8-bit integer)
+  SUBDATASET_28_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":ViewingAzimuthAngle
+  SUBDATASET_28_DESC=[1645x60] ViewingAzimuthAngle (32-bit floating-point)
+  SUBDATASET_29_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":ViewingZenithAngle
+  SUBDATASET_29_DESC=[1645x60] ViewingZenithAngle (32-bit floating-point)
+Corner Coordinates:
+Upper Left  (    0.0,    0.0)
+Lower Left  (    0.0,  512.0)
+Upper Right (  512.0,    0.0)
+Lower Right (  512.0,  512.0)
+Center      (  256.0,  256.0)
+</pre>
+
+Now select one of the subdatasets, described as
+<tt>[1645x60] CloudFraction (32-bit floating-point)</tt>:<p>
+<pre>
+$ gdalinfo HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":CloudFraction 
+Driver: HDF5Image/HDF5 Dataset
+Size is 60, 1645
+Coordinate System is:
+GEOGCS["WGS 84",
+    DATUM["WGS_1984",
+        SPHEROID["WGS 84",6378137,298.257223563,
+            AUTHORITY["EPSG","7030"]],
+        TOWGS84[0,0,0,0,0,0,0],
+        AUTHORITY["EPSG","6326"]],
+    PRIMEM["Greenwich",0,
+        AUTHORITY["EPSG","8901"]],
+    UNIT["degree",0.0174532925199433,
+        AUTHORITY["EPSG","9108"]],
+    AXIS["Lat",NORTH],
+    AXIS["Long",EAST],
+    AUTHORITY["EPSG","4326"]]
+GCP Projection = GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9108"]],AXIS["Lat",NORTH],AXIS["Long",EAST],AUTHORITY["EPSG","4326"]]
+GCP[  0]: Id=, Info=
+          (0.5,0.5) -> (261.575,-84.3495,0)
+GCP[  1]: Id=, Info=
+          (2.5,0.5) -> (240.826,-85.9928,0)
+GCP[  2]: Id=, Info=
+          (4.5,0.5) -> (216.754,-86.5932,0)
+GCP[  3]: Id=, Info=
+          (6.5,0.5) -> (195.5,-86.5541,0)
+GCP[  4]: Id=, Info=
+          (8.5,0.5) -> (180.265,-86.2009,0)
+GCP[  5]: Id=, Info=
+          (10.5,0.5) -> (170.011,-85.7315,0)
+GCP[  6]: Id=, Info=
+          (12.5,0.5) -> (162.987,-85.2337,0)
+<pre>... 3000 GCPs are read from the file if Latitude and Longitude arrays are presents </pre>
+Corner Coordinates:
+Upper Left  (    0.0,    0.0)
+Lower Left  (    0.0, 1645.0)
+Upper Right (   60.0,    0.0)
+Lower Right (   60.0, 1645.0)
+Center      (   30.0,  822.5)
+Band 1 Block=60x1 Type=Float32, ColorInterp=Undefined
+Open GDAL Datasets:
+  1 N DriverIsNULL 512x512x0
+</pre>
+
+You may use <b>gdal_translate</b> for reading image bands from this
+dataset.<p>
+
+Note that you should provide exactly the contents of the line marked 
+<b>SUBDATASET_n_NAME</b> to GDAL, including the <b>HDF5:</b> prefix.<p>
+
+This driver is intended only for importing remote sensing and geospatial 
+datasets in form of raster images(2D or 3D arrays). If you want explore all 
+data contained in HDF file you should use another tools (you can find 
+information about different HDF tools using links at end of this page).
+
+<h2>Georeference</h2>
+
+There is no universal way of storing georeferencing in HDF files.  However,
+some product types have mechanisms for saving georeferencing, and some of
+these are supported by GDAL.  Currently supported are (<i>subdataset_type</i>
+shown in parenthesis):<p>
+
+<ul>
+	<li> HDF5 OMI/Aura Ozone (O3) Total Column 1-Orbit L2 Swath 13x24km (<B>Level-2 OMTO3</B>)
+</ul>
+
+<h2>Metadata</h2>
+
+No Metadata are read at this time from the HDF5 files.
+
+<h2>Driver building</h2>
+
+This driver builded on top of NCSA HDF5 library, so you need to dowload 
+prebuild HDF5 librariesI HDF5-1.6.4 library or higher.  You also need zlib 1.2 
+and szlib 2.0.  For windows user be sure to set the attributes writable 
+(especialy if you are using cygwin) and that the DLLs can be located 
+somewhere by your PATH environment variable.
+
+You may also download source code  NCSA HDF
+Home Page (see links below).<p>
+
+
+<h2>See Also:</h2>
+
+<ul>
+<li> Implemented as <tt>gdal/frmts/hdf5/hdf5dataset.cpp</tt>
+and <tt>gdal/frmts/hdf5/hdf5imagedataset.cpp</tt>.<p>
+
+<li> <a href="http://hdf.ncsa.uiuc.edu/HDF5/release/obtain5.html">The NCSA HDF5 Dowload Page</a>
+at the
+<a href="http://www.ncsa.uiuc.edu/">
+National Center for Supercomputing Applications
+</a>
+<li> <a href="http://hdf.ncsa.uiuc.edu/hdf-java-html/hdfview/">The HDFView is a visual tool for browsing and editing NCSA HDF4 and HDF5 files.</a><p>
+
+Documentation to individual products, supported by this driver:<p>
+<ul>
+
+<li> <a href="http://disc.gsfc.nasa.gov/Aura/OMI/omto3.shtml">
+Aura OMI  Total Ozone Data Product-OMTO3</a>
+
+</ul>
+
+</body>
+</html>
diff --git a/Utilities/GDAL/frmts/hdf5/hdf5dataset.cpp b/Utilities/GDAL/frmts/hdf5/hdf5dataset.cpp
new file mode 100644
index 0000000000..f2ce99d901
--- /dev/null
+++ b/Utilities/GDAL/frmts/hdf5/hdf5dataset.cpp
@@ -0,0 +1,919 @@
+/******************************************************************************
+ * $Id: hdf5dataset.cpp,v 1.11 2006/01/21 17:50:45 dnadeau Exp $
+ *
+ * Project:  Hierarchical Data Format Release 5 (HDF5)
+ * Purpose:  HDF5 Datasets. Open HDF5 file, fetch metadata and list of
+ *           subdatasets.
+ *           This driver initially based on code supplied by Markus Neteler
+ * Author:  Denis Nadeau <denis.nadeau@gmail.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: hdf5dataset.cpp,v $
+ * Revision 1.11  2006/01/21 17:50:45  dnadeau
+ * added find object by path where space are changed with underscore.  Create SUBSETDATA with HDF5 Path.  Dataset sometimes have the same name.
+ *
+ * Revision 1.10  2005/09/15 02:37:10  fwarmerdam
+ * improved file header pre-test
+ *
+ * Revision 1.9  2005/09/15 00:51:36  fwarmerdam
+ * fixed memory leak of poDS
+ *
+ * Revision 1.8  2005/09/13 02:33:07  fwarmerdam
+ * Clean up more carefully on failed opens to avoid leaks.
+ *
+ * Revision 1.7  2005/08/24 22:08:28  fwarmerdam
+ * fixed header attribution
+ *
+ * Revision 1.6  2005/08/23 19:45:17  dnadeau
+ * HDF5 add Metadata
+ *
+ * Revision 1.5  2005/07/27 16:42:55  dnadeau
+ * change variable to hdf5imagedataset class
+ *
+ * Revision 1.4  2005/07/27 02:02:41  dnadeau
+ * correct memory leak problem.
+ */
+
+#include "hdf5.h"
+
+#include "gdal_priv.h"
+#include "cpl_string.h"
+#include "hdf5dataset.h"
+
+CPL_CVSID("$Id: hdf5dataset.cpp,v 1.11 2006/01/21 17:50:45 dnadeau Exp $");
+
+CPL_C_START
+void	GDALRegister_HDF5(void);
+CPL_C_END
+
+
+
+/************************************************************************/
+/* ==================================================================== */
+/*				HDF5Dataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                        GDALRegister_HDF5()				*/
+/************************************************************************/
+void GDALRegister_HDF5()
+    
+{
+    GDALDriver	*poDriver;
+    if( GDALGetDriverByName("HDF5") == NULL )
+	{
+	    poDriver = new GDALDriver();
+	    poDriver->SetDescription("HDF5");
+	    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, 
+				      "Hierarchical Data Format Release 5");
+	    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, 
+				      "frmt_hdf5.html");
+	    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "hdf5");
+	    poDriver->pfnOpen = HDF5Dataset::Open;
+	    GetGDALDriverManager()->RegisterDriver(poDriver);
+	}
+}
+
+/************************************************************************/
+/*                           HDF5Dataset()                      	*/
+/************************************************************************/
+HDF5Dataset::HDF5Dataset()
+{
+    papszSubDatasets    = NULL;
+    papszMetadata       = NULL;
+    poH5RootGroup       = NULL;
+    pszFilename         = NULL;
+    nSubDataCount       = 0;
+    hHDF5               = -1;
+    hDatasetID          = -1;
+    hGroupID            = -1;
+    bIsHDFEOS           = FALSE;
+    nDatasetType        = -1;
+}
+
+/************************************************************************/
+/*                            ~HDF5Dataset()                            */
+/************************************************************************/
+HDF5Dataset::~HDF5Dataset()
+{
+    if( papszMetadata )
+	CSLDestroy( papszMetadata );
+    if( hHDF5 > 0 )
+	H5Fclose( hHDF5 );
+    if( hGroupID > 0 )
+	H5Gclose( hGroupID );
+    if( papszSubDatasets )
+	CSLDestroy( papszSubDatasets );
+    if( pszFilename != NULL )
+	CPLFree( pszFilename );
+    if( poH5RootGroup != NULL ){
+	DestroyH5Objects( poH5RootGroup );
+	CPLFree( poH5RootGroup->pszName );
+	CPLFree( poH5RootGroup->pszPath );
+	CPLFree( poH5RootGroup->pszUnderscorePath );
+	CPLFree( poH5RootGroup->poHchild );
+	delete poH5RootGroup;
+    }
+}
+
+/************************************************************************/
+/*                            GetDataType()                             */
+/*                                                                      */
+/*      Transform HDF5 datatype to GDAL datatype                        */
+/************************************************************************/
+GDALDataType HDF5Dataset::GetDataType(hid_t TypeID) 
+{
+    if( H5Tequal( H5T_NATIVE_CHAR,        TypeID ) )
+	return GDT_Byte;
+    else if( H5Tequal( H5T_NATIVE_UCHAR,  TypeID ) ) 
+	return GDT_Byte;
+    else if( H5Tequal( H5T_NATIVE_SHORT,  TypeID ) )
+	return GDT_Int16;
+    else if( H5Tequal( H5T_NATIVE_USHORT, TypeID ) ) 
+	return GDT_UInt16;
+    else if( H5Tequal( H5T_NATIVE_INT,    TypeID ) ) 
+	return GDT_Int16;      
+    else if( H5Tequal( H5T_NATIVE_UINT,   TypeID ) ) 
+	return GDT_UInt16;
+    else if( H5Tequal( H5T_NATIVE_LONG,   TypeID ) ) 
+	return GDT_Int32;      
+    else if( H5Tequal( H5T_NATIVE_ULONG,  TypeID ) ) 
+	return GDT_UInt32;
+    else if( H5Tequal( H5T_NATIVE_FLOAT,  TypeID ) ) 
+	return GDT_Float32;
+    else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) ) 
+	return GDT_Float64;
+    else if( H5Tequal( H5T_NATIVE_LLONG,  TypeID ) ) 
+	return GDT_Unknown;
+    else if( H5Tequal( H5T_NATIVE_ULLONG, TypeID ) ) 
+	return GDT_Unknown;
+    else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) ) 
+	return GDT_Unknown;
+
+    return GDT_Unknown;
+}
+
+/************************************************************************/
+/*                          GetDataTypeName()                           */
+/*                                                                      */
+/*      Return the human readable name of data type                     */
+/************************************************************************/
+const char *HDF5Dataset::GetDataTypeName(hid_t TypeID)
+{
+    if( H5Tequal( H5T_NATIVE_CHAR,        TypeID ) )
+	return "8-bit character";
+    else if( H5Tequal( H5T_NATIVE_UCHAR,  TypeID ) ) 
+	return "8-bit unsigned character";    
+    else if( H5Tequal( H5T_NATIVE_SHORT,  TypeID ) )
+	return "8-bit integer";
+    else if( H5Tequal( H5T_NATIVE_USHORT, TypeID ) ) 
+	return "8-bit unsigned integer";
+    else if( H5Tequal( H5T_NATIVE_INT,    TypeID ) ) 
+	return "16-bit integer";
+    else if( H5Tequal( H5T_NATIVE_UINT,   TypeID ) ) 
+	return "16-bit unsigned integer";
+    else if( H5Tequal( H5T_NATIVE_LONG,   TypeID ) ) 
+	return "32-bit integer";
+    else if( H5Tequal( H5T_NATIVE_ULONG,  TypeID ) ) 
+	return "32-bit unsigned integer";
+    else if( H5Tequal( H5T_NATIVE_FLOAT,  TypeID ) ) 
+	return "32-bit floating-point";
+    else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) ) 
+	return "64-bit floating-point";
+    else if( H5Tequal( H5T_NATIVE_LLONG,  TypeID ) ) 
+	return "64-bit integer";
+    else if( H5Tequal( H5T_NATIVE_ULLONG, TypeID ) ) 
+	return "64-bit unsigned integer";
+    else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) ) 
+      return "64-bit floating-point";
+    
+    return "Unknown";
+}
+
+/************************************************************************/
+/*                            GetMetadata()                             */
+/************************************************************************/
+char **HDF5Dataset::GetMetadata( const char *pszDomain )
+{
+    if( pszDomain != NULL && EQUALN( pszDomain, "SUBDATASETS", 11 ) )
+        return papszSubDatasets;
+    else
+        return GDALDataset::GetMetadata( pszDomain );
+}
+
+ 
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+GDALDataset *HDF5Dataset::Open( GDALOpenInfo * poOpenInfo )
+{
+    HDF5Dataset *poDS;
+    CPLErr      Err;
+    
+    if( poOpenInfo->nHeaderBytes < 32 )
+        return NULL;
+
+    if( !EQUALN((const char *) (poOpenInfo->pabyHeader+1), "HDF",3) )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*  We have special routine in the HDF library for format checking!     */
+/* -------------------------------------------------------------------- */
+    if( !H5Fis_hdf5( poOpenInfo->pszFilename ) ) {
+	return NULL;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Create datasource.                                              */
+/* -------------------------------------------------------------------- */
+    poDS = new HDF5Dataset();
+    
+    poDS->fp = poOpenInfo->fp;
+    poOpenInfo->fp = NULL;
+    
+    poDS->pszFilename = strdup( poOpenInfo->pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*      Try opening the dataset.                                        */
+/* -------------------------------------------------------------------- */
+    poDS->hHDF5 = H5Fopen( poOpenInfo->pszFilename, 
+			   H5F_ACC_RDONLY, 
+			   H5P_DEFAULT );
+    if( poDS->hHDF5 < 0 )  {
+        delete poDS;
+   	return NULL;
+    }
+    
+    poDS->hGroupID = H5Gopen( poDS->hHDF5, "/" ); 
+    if( poDS->hGroupID < 0 ){
+	poDS->bIsHDFEOS=false;
+        delete poDS;
+	return NULL;
+    }
+    
+    poDS->bIsHDFEOS=true;
+    Err = poDS->ReadGlobalAttributes( true );
+
+    poDS->SetMetadata( poDS->papszMetadata  );
+    
+    return( poDS );
+}
+
+/************************************************************************/
+/*                          DestroyH5Objects()                          */
+/*                                                                      */
+/*      Erase all objects                                               */
+/************************************************************************/
+void HDF5Dataset::DestroyH5Objects( HDF5GroupObjects *poH5Object )
+{
+    int i;
+
+
+/* -------------------------------------------------------------------- */
+/*      Visit all objects                                               */
+/* -------------------------------------------------------------------- */
+
+    for( i=0; i < poH5Object->nbObjs; i++ )
+	if( poH5Object->poHchild+i != NULL )
+	    DestroyH5Objects( poH5Object->poHchild+i );
+
+    if( poH5Object->poHparent ==NULL )
+	return;
+
+/* -------------------------------------------------------------------- */
+/*      Erase some data                                                 */
+/* -------------------------------------------------------------------- */
+    if( poH5Object->paDims != NULL ) {
+	CPLFree( poH5Object->paDims );
+    }
+
+    if( poH5Object->pszPath != NULL ) {
+    	CPLFree( poH5Object->pszPath );
+    }
+
+    if( poH5Object->pszName != NULL ) {
+	CPLFree( poH5Object->pszName );
+    }
+/* -------------------------------------------------------------------- */
+/*      All Children are visited and can be deleted.                    */
+/* -------------------------------------------------------------------- */
+    if( ( i==poH5Object->nbObjs ) && ( poH5Object->nbObjs!=0 ) ) {
+	CPLFree( poH5Object->poHchild );
+    }
+
+}
+
+/************************************************************************/
+/*                             CreatePath()                             */
+/*                                                                      */
+/*      Find Dataset path for HDopen                                    */
+/************************************************************************/
+char* CreatePath( HDF5GroupObjects *poH5Object )
+{
+    char pszPath[8192];
+    char pszUnderscoreSpaceInName[8192];
+    char *popszPath;
+    int  i;
+    char **papszPath;
+
+/* -------------------------------------------------------------------- */
+/*      Recurse to the root path                                        */
+/* -------------------------------------------------------------------- */
+    pszPath[0]='\0';
+    if( poH5Object->poHparent !=NULL ) {
+	popszPath=CreatePath( poH5Object->poHparent );
+	strcpy( pszPath,popszPath );
+    }
+	
+/* -------------------------------------------------------------------- */
+/*      add name to the path                                            */
+/* -------------------------------------------------------------------- */
+    if( !EQUAL( poH5Object->pszName,"/" ) ){
+	strcat( pszPath,"/" );
+	strcat( pszPath,poH5Object->pszName );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      fill up path for each object                                    */
+/* -------------------------------------------------------------------- */
+    if( poH5Object->pszPath == NULL ) {
+
+	if( strlen( poH5Object->pszName ) == 1 ) {
+	    strcat(pszPath, poH5Object->pszName );
+	    strcat(pszUnderscoreSpaceInName, poH5Object->pszName);
+	}
+	else {
+/* -------------------------------------------------------------------- */
+/*      Change space for underscore                                     */
+/* -------------------------------------------------------------------- */
+	    papszPath = CSLTokenizeString2( pszPath,
+					    " ", CSLT_HONOURSTRINGS );
+	    
+	    strcpy(pszUnderscoreSpaceInName,papszPath[0]);
+	    for( i=1; i < CSLCount( papszPath ); i++ ) {
+		strcat( pszUnderscoreSpaceInName, "_" );
+		strcat( pszUnderscoreSpaceInName, papszPath[ i ] );
+	    }
+	    CSLDestroy(papszPath);
+
+	}
+	poH5Object->pszUnderscorePath  = 
+	    (char *)strdup( pszUnderscoreSpaceInName );
+	poH5Object->pszPath  = (char *)strdup( pszPath );
+    }
+
+    return( poH5Object->pszPath );
+}
+
+
+/************************************************************************/
+/*                      HDF5CreateGroupObjs()                           */
+/*                                                                      */
+/*      Create HDF5 hierarchy into a linked list                        */
+/************************************************************************/
+herr_t HDF5CreateGroupObjs(hid_t hHDF5, const char *pszObjName, 
+			   void *poHObjParent)
+{
+    herr_t	ret;			/* error return status */
+    hid_t	hGroupID;		/* identifier of group */
+    hid_t	hDatasetID;		/* identifier of dataset */
+    hsize_t     nbObjs=0;		/* number of objects in a group */
+    int         nbAttrs=0;		/* number of attributes in object */
+    int		idx;
+    int         n_dims;
+    H5G_stat_t  oStatbuf;
+    hsize_t     *dims=NULL;
+    hsize_t     *maxdims=NULL;
+    hid_t       datatype;
+    hid_t       dataspace;
+    hid_t       native;
+    herr_t status;
+
+    char *CreatePath( HDF5GroupObjects *poH5Object );
+
+    HDF5GroupObjects *poHchild;
+    HDF5GroupObjects *poHparent;
+
+    poHparent = ( HDF5GroupObjects * ) poHObjParent;
+    poHchild=poHparent->poHchild;
+
+    if( H5Gget_objinfo( hHDF5, pszObjName, FALSE, &oStatbuf ) < 0  )
+	return -1;
+
+    
+/* -------------------------------------------------------------------- */
+/*      Look for next child                                             */
+/* -------------------------------------------------------------------- */
+    for( idx=0; idx < poHparent->nbObjs; idx++ )	{
+	if( poHchild->pszName == NULL ) break;
+	poHchild++;
+    }
+
+    if( idx == poHparent->nbObjs ) 
+	return -1;  // all children parsed
+    
+/* -------------------------------------------------------------------- */
+/*      Save child information                                          */
+/* -------------------------------------------------------------------- */
+    poHchild->pszName  = (char *)strdup( pszObjName );
+    
+    poHchild->nType  = oStatbuf.type;
+    poHchild->nIndex = idx;
+    poHchild->poHparent = poHparent;
+    poHchild->nRank     = 0;
+    poHchild->paDims    = 0;
+    poHchild->HDatatype = 0;
+    if( poHchild->pszPath == NULL ) {
+	poHchild->pszPath  = CreatePath( poHchild );
+    }
+    if( poHparent->pszPath == NULL ) {
+	poHparent->pszPath = CreatePath( poHparent );
+    }
+
+
+    switch ( oStatbuf.type ) 
+	{
+	case H5G_LINK:
+	    poHchild->nbAttrs = 0;
+	    poHchild->nbObjs = 0;
+	    poHchild->poHchild = NULL;
+	    poHchild->nRank      = 0;
+	    poHchild->paDims    = 0;
+	    poHchild->HDatatype = 0;
+	    break;
+	    
+	case H5G_GROUP:
+	    if( ( hGroupID = H5Gopen( hHDF5, pszObjName ) ) == -1  ) {
+		printf( "Error: unable to access \"%s\" group.\n", 
+			pszObjName );
+		return -1;
+	    }
+	    nbAttrs          = H5Aget_num_attrs( hGroupID );
+	    ret              = H5Gget_num_objs( hGroupID, &nbObjs );
+	    poHchild->nbAttrs= nbAttrs;
+	    poHchild->nbObjs = nbObjs;
+	    poHchild->nRank      = 0;
+	    poHchild->paDims    = 0;
+	    poHchild->HDatatype = 0;
+	    
+	    if( nbObjs > 0 ) {
+		poHchild->poHchild =( HDF5GroupObjects * )
+		    CPLCalloc( nbObjs, sizeof( HDF5GroupObjects ) );
+		memset( poHchild->poHchild,0,
+			sizeof( HDF5GroupObjects ) * nbObjs );
+	    }
+	    else 
+		poHchild->poHchild = NULL;
+	    
+	    ret = H5Giterate( hHDF5, pszObjName, NULL, 
+			     HDF5CreateGroupObjs,  (void*) poHchild );
+	    ret = H5Gclose( hGroupID );
+	    break;
+	    
+	case H5G_DATASET:
+	    
+	    if( ( hDatasetID = H5Dopen( hHDF5, pszObjName ) ) == -1  ) {
+		printf( "Error: unable to access \"%s\" dataset.\n", 
+		       pszObjName );
+		return -1;
+	    }
+	    nbAttrs      = H5Aget_num_attrs( hDatasetID );
+	    datatype     = H5Dget_type( hDatasetID );
+	    dataspace    = H5Dget_space( hDatasetID );
+	    n_dims       = H5Sget_simple_extent_ndims( dataspace );
+	    native       = H5Tget_native_type( datatype, H5T_DIR_ASCEND );
+	    
+	    if( n_dims > 0 ) {
+		dims     = (hsize_t *) CPLCalloc( n_dims,sizeof( hsize_t ) );
+		maxdims  = (hsize_t *) CPLCalloc( n_dims,sizeof( hsize_t ) );
+	    }
+	    status     = H5Sget_simple_extent_dims( dataspace, dims, maxdims );
+	    if( maxdims != NULL )
+		CPLFree( maxdims );
+	    
+	    if( n_dims > 0 ) {
+		poHchild->nRank     = n_dims;   // rank of the array
+		poHchild->paDims    = dims;      // dimmension of the array.
+		poHchild->HDatatype = datatype;  // HDF5 datatype
+	    }
+	    else  {
+		poHchild->nRank     = -1;
+		poHchild->paDims    = NULL;
+		poHchild->HDatatype = 0;
+	}
+	    poHchild->nbAttrs   = nbAttrs;
+	    poHchild->nbObjs    = 0;
+	    poHchild->poHchild  = NULL;
+	    poHchild->native    = native;
+	    ret                 = H5Dclose( hDatasetID );
+	    break;
+	    
+	case H5G_TYPE:
+	    poHchild->nbAttrs = 0;
+	    poHchild->nbObjs = 0;
+	    poHchild->poHchild = NULL;
+	    poHchild->nRank      = 0;
+	    poHchild->paDims    = 0;
+	    poHchild->HDatatype = 0;
+	    break;
+	    
+	default:
+	    break;
+	}
+    
+    return 0;
+}
+
+
+/************************************************************************/
+/*                          HDF5AttrIterate()                           */
+/************************************************************************/
+
+herr_t HDF5AttrIterate( hid_t hH5ObjID, 
+			const char *AttrName, 
+			void *pDS )
+{
+    hid_t           hAttrID;
+    hid_t           hAttrTypeID;
+    hid_t           hAttrNativeType;
+    hid_t           hAttrSpace;
+
+    char            szData[8192];
+    hsize_t        nSize[64];
+    unsigned int            nAttrElmts;
+    hsize_t        nAttrSize;
+    hsize_t        i;
+    void           *buf;
+    unsigned int             nAttrDims;
+
+
+    HDF5Dataset    *poDS;
+    char            szTemp[8192];
+    char            szValue[8192];
+
+    poDS = (HDF5Dataset *) pDS;
+    sprintf( szTemp, "%s:%s", poDS->poH5CurrentObject->pszName, 
+	     AttrName );
+
+    hAttrID          = H5Aopen_name( hH5ObjID, AttrName );
+    hAttrTypeID      = H5Aget_type( hAttrID );
+    hAttrNativeType  = H5Tget_native_type( hAttrTypeID, H5T_DIR_DEFAULT );
+    hAttrSpace       = H5Aget_space( hAttrID );
+    nAttrDims        = H5Sget_simple_extent_dims( hAttrSpace, nSize, NULL );
+
+
+    szValue[0] ='\0';
+
+    if( H5Tget_class( hAttrNativeType ) == H5T_STRING ) {
+	nAttrSize = H5Tget_size( hAttrTypeID );
+	H5Aread( hAttrID, hAttrNativeType, szData  );
+	szData[nAttrSize]='\0';
+	sprintf( szValue, "%s", szData );
+
+    }
+    else {
+	nAttrElmts = 1;
+	for( i=0; i < nAttrDims; i++ ) {
+	    nAttrElmts *= nSize[i];
+	}
+	if( nAttrElmts > 0 ){
+	    buf = (void *) CPLMalloc( nAttrElmts*
+				      H5Tget_size( hAttrNativeType ));
+	    H5Aread( hAttrID, hAttrNativeType, buf );
+	}
+	if( H5Tequal( H5T_NATIVE_CHAR, hAttrNativeType ) ){
+	    for( i=0; i < nAttrElmts; i++ ) {
+		sprintf( szData, "%c ", ((char *) buf)[i]);
+		strcat(szValue,szData);
+	    }
+	}
+	else if( H5Tequal( H5T_NATIVE_UCHAR,  hAttrNativeType ) ) {
+	    for( i=0; i < nAttrElmts; i++ ) {
+		sprintf( szData, "%c", ((char *) buf)[i] );
+		strcat(szValue,szData);
+	    }
+	}
+	else if( H5Tequal( H5T_NATIVE_SHORT,  hAttrNativeType ) ) {
+	    for( i=0; i < nAttrElmts; i++ ) {
+		sprintf( szData, "%d ", ((short *) buf)[i] );
+		strcat(szValue,szData);
+	    }
+	}
+	else if( H5Tequal( H5T_NATIVE_USHORT, hAttrNativeType ) ) {
+	    for( i=0; i < nAttrElmts; i++ ) {
+		sprintf( szData, "%ud ", ((unsigned short *) buf)[i] );
+		strcat(szValue,szData);
+	    }
+	}
+	else if( H5Tequal( H5T_NATIVE_INT,    hAttrNativeType ) ) {
+	    for( i=0; i < nAttrElmts; i++ ) {
+		sprintf( szData, "%d ", ((int *) buf)[i] );
+		strcat(szValue,szData);
+	    }
+	}
+	else if( H5Tequal( H5T_NATIVE_UINT,   hAttrNativeType ) ) {
+	    for( i=0; i < nAttrElmts; i++ ) {
+		sprintf( szData, "%ud ", ((unsigned int *) buf)[i] );
+		strcat(szValue,szData);
+	    }
+	}
+	else if( H5Tequal( H5T_NATIVE_LONG,   hAttrNativeType ) ) {
+	    for( i=0; i < nAttrElmts; i++ ) {
+		sprintf( szData, "%ld ", ((long *)buf)[i] );
+		strcat(szValue,szData);
+	    }
+	}
+	else if( H5Tequal( H5T_NATIVE_ULONG,  hAttrNativeType ) ) {
+	    for( i=0; i < nAttrElmts; i++ ) {
+		sprintf( szData, "%ld ", ((unsigned long *)buf)[i] );
+		strcat(szValue,szData);
+	    }
+	}
+	else if( H5Tequal( H5T_NATIVE_FLOAT,  hAttrNativeType ) ) {
+	    for( i=0; i < nAttrElmts; i++ ) {
+		sprintf( szData, "%f ",  ((float *)buf)[i] );
+		strcat(szValue,szData);
+	    }
+	}
+	else if( H5Tequal( H5T_NATIVE_DOUBLE, hAttrNativeType ) ) {
+	    for( i=0; i < nAttrElmts; i++ ) {
+		sprintf( szData, "%g ",  ((double *)buf)[i] );
+		strcat(szValue,szData);
+	    }
+	}
+	CPLFree( buf );
+
+    }
+    H5Aclose( hAttrID );
+    //printf( "%s = %s\n",szTemp, szValue );
+    poDS->papszMetadata =
+	CSLSetNameValue( poDS->papszMetadata, szTemp,  
+			 CPLSPrintf( "%s", szValue ) );
+
+    return 0;
+}
+
+/************************************************************************/
+/*                           CreateMetadata()                           */
+/************************************************************************/
+CPLErr HDF5Dataset::CreateMetadata( HDF5GroupObjects *poH5Object, int nType)
+{
+    hid_t	hGroupID;		/* identifier of group */
+    hid_t       hDatasetID;
+    int         nbAttrs;
+    herr_t      ret;
+
+    HDF5Dataset *poDS;
+
+    poDS = this;
+
+    poH5CurrentObject = poH5Object;
+    nbAttrs = poH5Object->nbAttrs;
+
+    if( EQUAL(poH5Object->pszPath, "" ) )
+	return CE_None;
+
+    switch( nType ) {
+
+    case H5G_GROUP:
+
+	hGroupID = H5Gopen( hHDF5, poH5Object->pszPath );
+	if( nbAttrs > 0 ) { 
+	    ret = H5Aiterate( hGroupID, NULL, 
+			      HDF5AttrIterate, (void *)poDS  );
+	    ret = H5Gclose( hGroupID );
+	}
+
+	break;
+
+    case H5G_DATASET:
+
+	hDatasetID =  H5Dopen(hHDF5, poH5Object->pszPath );
+
+	if( nbAttrs > 0 ) { 
+	    ret = H5Aiterate( hDatasetID, NULL, 
+			      HDF5AttrIterate, (void *)poDS );
+	    ret = H5Dclose( hDatasetID );
+	}
+	break;
+
+    default:
+	break;
+    }
+
+    return CE_None;
+}
+
+
+/************************************************************************/
+/*                       HDF5FindDatasetObjectsbyPath()                 */
+/*      Find object by name                                             */
+/************************************************************************/
+HDF5GroupObjects* HDF5Dataset::HDF5FindDatasetObjectsbyPath
+    ( HDF5GroupObjects *poH5Objects, char* pszDatasetPath )
+{
+    int i;
+    HDF5Dataset *poDS;
+    HDF5GroupObjects *poObjectsFound;
+    poDS=this;
+    
+    if( poH5Objects->nType == H5G_DATASET &&
+       EQUAL( poH5Objects->pszUnderscorePath,pszDatasetPath ) )	{
+	    /*      printf("found it! %ld\n",(long) poH5Objects);*/
+	    return( poH5Objects );
+	}
+    
+    if( poH5Objects->nbObjs >0 )
+	for( i=0; i <poH5Objects->nbObjs; i++ )   {
+	    poObjectsFound=
+		poDS->HDF5FindDatasetObjectsbyPath( poH5Objects->poHchild+i, 
+						    pszDatasetPath );
+/* -------------------------------------------------------------------- */
+/*      Is this our dataset??                                           */
+/* -------------------------------------------------------------------- */
+	    if( poObjectsFound != NULL ) return( poObjectsFound );
+	}
+/* -------------------------------------------------------------------- */
+/*      Dataset has not been found!                                     */
+/* -------------------------------------------------------------------- */
+    return( NULL );
+    
+}
+
+
+/************************************************************************/
+/*                       HDF5FindDatasetObjects()                       */
+/*      Find object by name                                             */
+/************************************************************************/
+HDF5GroupObjects* HDF5Dataset::HDF5FindDatasetObjects
+    ( HDF5GroupObjects *poH5Objects, char* pszDatasetName )
+{
+    int i;
+    HDF5Dataset *poDS;
+    HDF5GroupObjects *poObjectsFound;
+    poDS=this;
+    
+    if( poH5Objects->nType == H5G_DATASET &&
+       EQUAL( poH5Objects->pszName,pszDatasetName ) )	{
+	    /*      printf("found it! %ld\n",(long) poH5Objects);*/
+	    return( poH5Objects );
+	}
+    
+    if( poH5Objects->nbObjs >0 )
+	for( i=0; i <poH5Objects->nbObjs; i++ )   {
+	    poObjectsFound=
+		poDS->HDF5FindDatasetObjects( poH5Objects->poHchild+i, 
+					      pszDatasetName );
+/* -------------------------------------------------------------------- */
+/*      Is this our dataset??                                           */
+/* -------------------------------------------------------------------- */
+	    if( poObjectsFound != NULL ) return( poObjectsFound );
+	    
+	}
+/* -------------------------------------------------------------------- */
+/*      Dataset has not been found!                                     */
+/* -------------------------------------------------------------------- */
+    return( NULL );
+    
+}
+
+
+/************************************************************************/
+/*                        HDF5ListGroupObjects()                        */
+/*                                                                      */
+/*      List all objects in HDF5                                        */
+/************************************************************************/
+CPLErr HDF5Dataset::HDF5ListGroupObjects( HDF5GroupObjects *poRootGroup,
+					  int bSUBDATASET )
+{
+    int i;
+    char szTemp[8192];
+    char szDim[8192];
+    HDF5Dataset *poDS;
+    poDS=this;
+    
+    if( poRootGroup->nbObjs >0 )  
+	for( i=0; i < poRootGroup->nbObjs; i++ ) {
+	    poDS->HDF5ListGroupObjects( poRootGroup->poHchild+i, bSUBDATASET );
+	}
+    
+
+    if( poRootGroup->nType == H5G_GROUP ) {
+	CreateMetadata( poRootGroup, H5G_GROUP );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create Sub dataset list                                         */
+/* -------------------------------------------------------------------- */
+    if( (poRootGroup->nType == H5G_DATASET ) && bSUBDATASET ) {
+	
+	szDim[0]='\0';
+	switch( poRootGroup->nRank ) {
+	case 3: 
+	    sprintf( szTemp,"%dx%dx%d",
+		    (int)poRootGroup->paDims[0],
+		    (int)poRootGroup->paDims[1],
+		    (int)poRootGroup->paDims[2] );
+	    break;
+	    
+  	case 2: 
+	    sprintf( szTemp,"%dx%d",
+		    (int)poRootGroup->paDims[0],
+		    (int)poRootGroup->paDims[1] );
+	  break;
+        default:
+	    return CE_None;
+	    
+	}
+	strcat( szDim,szTemp );
+	
+	sprintf( szTemp, "SUBDATASET_%d_NAME", poDS->nSubDataCount );
+
+
+	poDS->papszSubDatasets =
+	    CSLSetNameValue( poDS->papszSubDatasets, szTemp,
+			    CPLSPrintf( "HDF5:\"%s\":%s",
+					poDS->pszFilename,
+					poRootGroup->pszUnderscorePath ) );
+	
+	sprintf(  szTemp, "SUBDATASET_%d_DESC", poDS->nSubDataCount++ );
+	
+	poDS->papszSubDatasets =
+	    CSLSetNameValue( poDS->papszSubDatasets, szTemp,
+			    CPLSPrintf( "[%s] %s (%s)", 
+					szDim,
+					poRootGroup->pszUnderscorePath,
+					poDS->GetDataTypeName
+					( poRootGroup->native ) ) );
+
+    }
+    
+    return CE_None;
+}
+
+
+/************************************************************************/
+/*                       ReadGlobalAttributes()                         */
+/************************************************************************/
+CPLErr HDF5Dataset::ReadGlobalAttributes(int bSUBDATASET)
+{
+    
+    HDF5GroupObjects *poRootGroup;
+    
+    poRootGroup = new HDF5GroupObjects;
+    
+    poH5RootGroup=poRootGroup;
+    poRootGroup->pszName   = strdup( "/" );
+    poRootGroup->nType     = H5G_GROUP;
+    poRootGroup->poHparent = NULL;
+    poRootGroup->pszPath = NULL;
+    
+    if( hHDF5 < 0 )  {
+	printf( "hHDF5 <0!!\n" );
+	return CE_None;
+    }
+    
+    hGroupID = H5Gopen( hHDF5, "/" ); 
+    if( hGroupID < 0 ){
+	printf( "hGroupID <0!!\n" );
+	return CE_None;
+    }
+    
+    poRootGroup->nbAttrs = H5Aget_num_attrs( hGroupID );
+
+    H5Gget_num_objs( hGroupID, (hsize_t *) &( poRootGroup->nbObjs ) );
+    
+    if( poRootGroup->nbObjs > 0 ) {
+	poRootGroup->poHchild = ( HDF5GroupObjects * ) 
+	    CPLCalloc( poRootGroup->nbObjs,
+		sizeof( HDF5GroupObjects ) );
+	H5Giterate( hGroupID, "/", NULL, 
+		   HDF5CreateGroupObjs, (void *)poRootGroup );
+    }
+    else poRootGroup->poHchild = NULL;
+    
+    HDF5ListGroupObjects( poRootGroup, bSUBDATASET );
+    return CE_None;
+}
diff --git a/Utilities/GDAL/frmts/hdf5/hdf5dataset.h b/Utilities/GDAL/frmts/hdf5/hdf5dataset.h
new file mode 100644
index 0000000000..64dc374278
--- /dev/null
+++ b/Utilities/GDAL/frmts/hdf5/hdf5dataset.h
@@ -0,0 +1,124 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  Hierarchical Data Format Release 5 (HDF5)
+ * Purpose:  Header file for HDF5 datasets reader.
+ * Author:   Denis Nadeau (denis.nadeau@gmail.com)
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: hdf5dataset.h,v $
+ * Revision 1.6  2006/04/04 03:27:19  fwarmerdam
+ * Fixed copyright holder line.
+ *
+ * Revision 1.5  2006/01/23 14:16:31  dnadeau
+ * Add pszUnderscorePath variable to HDF5Dataset class
+ *
+ * Revision 1.4  2005/08/23 20:11:46  dnadeau
+ * HDF5 add Metadata
+ *
+ * Revision 1.3  2005/07/27 16:42:13  dnadeau
+ * change variable to hdf5imagedataset class
+ *
+ *
+ *
+ */
+
+#ifndef _HDF5DATASET_H_INCLUDED_
+#define _HDF5DATASET_H_INCLUDED_
+
+#include "cpl_list.h"
+
+typedef struct HDF5GroupObjects {
+  char	       *pszName;
+  char         *pszPath;
+  char         *pszUnderscorePath;
+  char         *pszTemp;
+  int		nType;
+  int		nIndex;
+  int		nbObjs;
+  int		nbAttrs;
+  int           nRank;
+  hsize_t       *paDims;
+  hid_t          native;
+  hid_t          HDatatype;
+  struct HDF5GroupObjects *poHparent;
+  struct HDF5GroupObjects *poHchild;
+} HDF5GroupObjects;
+
+
+herr_t HDF5CreateGroupObjs(hid_t, const char *,void *);
+
+/************************************************************************/
+/* ==================================================================== */
+/*				HDF5Dataset				*/
+/* ==================================================================== */
+/************************************************************************/
+class HDF5Dataset : public GDALDataset
+{
+
+  protected:
+
+  FILE             *fp;
+  hid_t            hHDF5;     
+  hid_t            hDatasetID;
+  hid_t            hGroupID; /* H handler interface */
+  char             **papszSubDatasets;
+  int              bIsHDFEOS;
+  int              nDatasetType;
+  int              nSubDataCount;
+  char             *pszFilename;
+
+
+  HDF5GroupObjects *poH5RootGroup; /* Contain hdf5 Groups information */
+
+  CPLErr ReadGlobalAttributes(int);
+  CPLErr HDF5ListGroupObjects(HDF5GroupObjects *, int );
+  CPLErr CreateMetadata( HDF5GroupObjects *, int );
+
+  HDF5GroupObjects* HDF5FindDatasetObjects( HDF5GroupObjects *, char * );
+  HDF5GroupObjects* HDF5FindDatasetObjectsbyPath( HDF5GroupObjects *, char * );
+  char* CreatePath(HDF5GroupObjects *);
+  void DestroyH5Objects(HDF5GroupObjects *);
+ 
+  GDALDataType GetDataType(hid_t);
+  const char * GetDataTypeName(hid_t);
+
+  public:
+
+  char	           **papszMetadata;
+  HDF5GroupObjects *poH5CurrentObject; 
+
+  HDF5Dataset();
+  ~HDF5Dataset();
+    
+  virtual char **GetMetadata(const char * pszDomain = "");
+
+  static GDALDataset *Open(GDALOpenInfo *);
+
+};
+
+
+
+#endif /* _HDF5DATASET_H_INCLUDED_ */
+
diff --git a/Utilities/GDAL/frmts/hdf5/hdf5imagedataset.cpp b/Utilities/GDAL/frmts/hdf5/hdf5imagedataset.cpp
new file mode 100644
index 0000000000..ba12ca0b5f
--- /dev/null
+++ b/Utilities/GDAL/frmts/hdf5/hdf5imagedataset.cpp
@@ -0,0 +1,683 @@
+/******************************************************************************
+ * $Id: hdf5imagedataset.cpp,v 1.15 2006/01/21 17:51:50 dnadeau Exp $
+ *
+ * Project:  Hierarchical Data Format Release 5 (HDF5)
+ * Purpose:  Read subdatasets of HDF5 file.
+ * Author:   Denis Nadeau <denis.nadeau@gmail.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: hdf5imagedataset.cpp,v $
+ * Revision 1.15  2006/01/21 17:51:50  dnadeau
+ * added find object by path where space are changed with underscore.  Fix up pszProjections problem.
+ *
+ * Revision 1.14  2005/10/12 20:16:10  fwarmerdam
+ * use SetMetadata()
+ *
+ * Revision 1.13  2005/09/13 02:34:58  fwarmerdam
+ * Cleanup more carefully in ::Open() to avoid leaks.
+ *
+ * Revision 1.12  2005/08/25 15:35:57  fwarmerdam
+ * Handle offset type to hyperslab based on lib version
+ *
+ * Revision 1.11  2005/08/24 22:08:28  fwarmerdam
+ * fixed header attribution
+ *
+ * Revision 1.10  2005/08/24 15:53:52  dron
+ * The type of start offset array fixed again (goes away in the last commit).
+ *
+ * Revision 1.9  2005/08/23 20:11:30  dnadeau
+ * HDF5 add Metadata
+ *
+ * Revision 1.8  2005/08/19 15:03:20  dron
+ * Fixed type of start offset array in IReadBlock().
+ *
+ * Revision 1.7  2005/08/13 02:22:01  dnadeau
+ * check for windows drive letter in filename
+ *
+ * Revision 1.6  2005/08/12 23:39:50  dnadeau
+ * add GCPs and projection class members
+ *
+ * Revision 1.5  2005/07/29 15:44:01  fwarmerdam
+ * minor formatting change
+ *
+ * Revision 1.4  2005/07/27 16:41:46  dnadeau
+ * take care of memory leak
+ *
+ * Revision 1.3  2005/07/20 21:16:10  dnadeau
+ * fix commit problem with file
+ *
+ */
+#include "hdf5.h"
+
+#include "gdal_pam.h"
+#include "gdal_priv.h"
+#include "cpl_string.h"
+#include "hdf5dataset.h"
+#include "ogr_spatialref.h"
+
+CPL_CVSID("$Id: hdf5imagedataset.cpp,v 1.15 2006/01/21 17:51:50 dnadeau Exp $");
+
+CPL_C_START
+void    GDALRegister_HDF5Image(void);
+CPL_C_END
+
+/* release 1.6.3 or 1.6.4 changed the type of count in some api functions */
+
+#if H5_VERS_MAJOR == 1 && H5_VERS_MINOR <= 6 \
+       && (H5_VERS_MINOR < 6 || H5_VERS_RELEASE < 3)
+#  define H5OFFSET_TYPE hssize_t
+#else
+#  define H5OFFSET_TYPE  hsize_t
+#endif
+
+class HDF5ImageDataset : public HDF5Dataset
+{
+
+    friend class HDF5ImageRasterBand;
+
+    char        *pszProjection;
+    char        *pszGCPProjection;
+    GDAL_GCP    *pasGCPList;
+    int         nGCPCount;
+    OGRSpatialReference oSRS;
+
+    hsize_t      *dims,*maxdims;
+    char          **papszName;
+    HDF5GroupObjects *poH5Objects;
+    int          ndims,dimensions;
+    hid_t        dataset_id;
+    hid_t        dataspace_id;
+    hsize_t      size;
+    int          address;
+    hid_t        datatype;
+    hid_t        native;
+    H5T_class_t  clas;
+
+public:
+    HDF5ImageDataset();
+    ~HDF5ImageDataset();
+    
+    CPLErr CreateProjections( );
+    static GDALDataset  *Open( GDALOpenInfo * );
+    const char          *GetProjectionRef();
+    virtual CPLErr      SetProjection( const char * );
+    virtual int         GetGCPCount( );
+    virtual const char  *GetGCPProjection();
+    virtual const GDAL_GCP *GetGCPs( ); 
+    
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                              HDF5ImageDataset                        */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                           HDF5ImageDataset()                         */
+/************************************************************************/
+HDF5ImageDataset::HDF5ImageDataset()
+{
+
+    fp=NULL;
+    nGCPCount       = -1;
+    pszProjection   = NULL;
+    pasGCPList      = NULL;
+    papszName       = NULL;
+    pszFilename     = NULL;
+    poH5Objects     = NULL;
+    poH5RootGroup   = NULL;
+    dims            = NULL;
+    maxdims         = NULL;
+    papszName       = NULL;
+    papszMetadata   = NULL;
+
+}
+
+/************************************************************************/
+/*                            ~HDF5ImageDataset()                       */
+/************************************************************************/
+HDF5ImageDataset::~HDF5ImageDataset( )
+{
+
+
+    if( papszName != NULL )
+    	CSLDestroy( papszName );
+
+    if( dims )
+	CPLFree( dims );
+
+    if( maxdims )
+	CPLFree( maxdims );
+
+    if( nGCPCount > 0 )
+    {
+        for( int i = 0; i < nGCPCount; i++ )
+        {
+            if( pasGCPList[i].pszId )
+                CPLFree( pasGCPList[i].pszId );
+            if( pasGCPList[i].pszInfo )
+                CPLFree( pasGCPList[i].pszInfo );
+	}
+
+        CPLFree( pasGCPList );
+    }
+
+
+
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            Hdf5imagerasterband                       */
+/* ==================================================================== */
+/************************************************************************/
+class HDF5ImageRasterBand : public GDALPamRasterBand
+{
+    friend class HDF5ImageDataset;
+
+    int         bNoDataSet;
+    double      dfNoDataValue;
+    char        *pszFilename;
+    
+
+public:
+  
+    HDF5ImageRasterBand( HDF5ImageDataset *, int, GDALDataType );
+    ~HDF5ImageRasterBand();
+
+    virtual CPLErr          IReadBlock( int, int, void * );
+    virtual double	    GetNoDataValue( int * ); 
+    virtual CPLErr	    SetNoDataValue( double );
+    /*  virtual CPLErr          IWriteBlock( int, int, void * ); */
+};
+
+/************************************************************************/
+/*                        ~HDF5ImageRasterBand()                        */
+/************************************************************************/
+
+HDF5ImageRasterBand::~HDF5ImageRasterBand()
+{
+
+}
+/************************************************************************/
+/*                           HDF5ImageRasterBand()                      */
+/************************************************************************/
+HDF5ImageRasterBand::HDF5ImageRasterBand( HDF5ImageDataset *poDS, int nBand,
+                                          GDALDataType eType )
+
+{
+    char          **papszMetaGlobal;
+    this->poDS    = poDS;
+    this->nBand   = nBand;
+    eDataType     = eType;
+    bNoDataSet    = FALSE;
+    dfNoDataValue = -9999;
+    nBlockXSize   = poDS->GetRasterXSize( );
+    nBlockYSize   = 1;
+
+/* -------------------------------------------------------------------- */
+/*      Take a copy of Global Metadata since  I can't pass Raster       */
+/*      variable to Iterate function.                                   */
+/* -------------------------------------------------------------------- */
+    papszMetaGlobal = CSLDuplicate( poDS->papszMetadata );
+    CSLDestroy( poDS->papszMetadata );
+    poDS->papszMetadata = NULL;
+
+    if( poDS->poH5Objects->nType == H5G_DATASET ) {
+	poDS->CreateMetadata( poDS->poH5Objects, H5G_DATASET );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Recover Global Metadat and set Band Metadata                    */
+/* -------------------------------------------------------------------- */
+
+    SetMetadata( poDS->papszMetadata );
+
+    CSLDestroy( poDS->papszMetadata );
+    poDS->papszMetadata = CSLDuplicate( papszMetaGlobal );
+    CSLDestroy( papszMetaGlobal );
+}
+
+/************************************************************************/
+/*                           GetNoDataValue()                           */
+/************************************************************************/
+double HDF5ImageRasterBand::GetNoDataValue( int * pbSuccess )
+
+{
+    if( pbSuccess )
+	*pbSuccess = bNoDataSet;
+
+    return dfNoDataValue;
+}
+
+/************************************************************************/
+/*                           SetNoDataValue()                           */
+/************************************************************************/
+CPLErr HDF5ImageRasterBand::SetNoDataValue( double dfNoData )
+
+{
+    bNoDataSet = TRUE;
+    dfNoDataValue = dfNoData;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+CPLErr HDF5ImageRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                        void * pImage )
+{
+    herr_t      status;
+    hsize_t     count[3];
+    H5OFFSET_TYPE offset[3];
+    int         nSizeOfData;
+    hid_t       memspace;
+    hsize_t     col_dims[3];
+    hsize_t     rank;
+
+    HDF5ImageDataset    *poGDS = ( HDF5ImageDataset * ) poDS;
+   
+    if( poGDS->eAccess == GA_Update ) {
+	memset( pImage, 0,
+		nBlockXSize * nBlockYSize * 
+		GDALGetDataTypeSize( eDataType )/8 );
+	return CE_None;
+    }
+
+    rank=2;
+
+    if( poGDS->ndims == 3 ){
+	rank=3;
+	offset[2] = nBand-1;
+	count[2]  = 1;
+	col_dims[2] = 1;
+    }
+
+    offset[0] = nBlockYOff;
+    offset[1] = nBlockXOff;
+    count[0]  = 1;
+    count[1]  = poGDS->GetRasterXSize( );
+
+    nSizeOfData = H5Tget_size( poGDS->native );
+    memset( pImage,0,count[1]-offset[1]*nSizeOfData );
+
+/* -------------------------------------------------------------------- */
+/*      Select 1 line                                                   */
+/* -------------------------------------------------------------------- */
+    status =  H5Sselect_hyperslab( poGDS->dataspace_id, 
+				  H5S_SELECT_SET, 
+				  offset, NULL, 
+				  count, NULL );
+   
+
+/* -------------------------------------------------------------------- */
+/*      Create memory space to receive the data                         */
+/* -------------------------------------------------------------------- */
+    col_dims[0]=count[1];
+    col_dims[1]=1;
+    memspace = H5Screate_simple( rank,col_dims, NULL );
+
+    status = H5Dread ( poGDS->dataset_id,
+		      poGDS->native, 
+		      memspace,
+		      poGDS->dataspace_id,
+		      H5P_DEFAULT, 
+		      pImage );
+
+    return CE_None;
+}
+
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+GDALDataset *HDF5ImageDataset::Open( GDALOpenInfo * poOpenInfo )
+{
+    int i;
+    HDF5ImageDataset    *poDS;
+    int nDatasetPos = 2;
+    char szFilename[2048];
+
+    if(!EQUALN( poOpenInfo->pszFilename, "HDF5:", 5 ) )
+	return NULL;
+  
+    poDS = new HDF5ImageDataset();
+
+    poDS->fp = poOpenInfo->fp;
+    poOpenInfo->fp = NULL;
+  
+    /* -------------------------------------------------------------------- */
+    /*      Create a corresponding GDALDataset.                             */
+    /* -------------------------------------------------------------------- */
+    /* printf("poOpenInfo->pszFilename %s\n",poOpenInfo->pszFilename); */
+    poDS->papszName = CSLTokenizeString2(  poOpenInfo->pszFilename,
+				    ":", CSLT_HONOURSTRINGS );
+
+    if( !((CSLCount(poDS->papszName) == 3) || 
+          (CSLCount(poDS->papszName) == 4)) ){
+        CSLDestroy(poDS->papszName);
+        delete poDS;
+        return NULL;
+    }
+
+    poDS->pszFilename = CPLStrdup( poOpenInfo->pszFilename );
+  
+    if( !EQUAL( poDS->papszName[0], "HDF5" ) ) {
+        delete poDS;
+	return NULL;
+    }
+  
+    /* -------------------------------------------------------------------- */
+    /*    Check for drive name in windows HDF5:"D:\...                      */
+    /* -------------------------------------------------------------------- */
+    strcpy(szFilename, poDS->papszName[1]);
+
+    if( strlen(poDS->papszName[1]) == 1 ) {
+	strcat(szFilename, ":");
+	strcat(szFilename, poDS->papszName[2]);
+        nDatasetPos = 3;
+    }
+    printf("szFilenname %s\n",szFilename);
+    if( !H5Fis_hdf5(szFilename) ) {
+        delete poDS;
+        return NULL;
+    }
+
+    /* -------------------------------------------------------------------- */
+    /*      Try opening the dataset.                                        */
+    /* -------------------------------------------------------------------- */
+    poDS->hHDF5 = H5Fopen(szFilename,
+			  H5F_ACC_RDONLY, 
+			  H5P_DEFAULT );
+  
+    if( poDS->hHDF5 < 0 )  {
+        delete poDS;
+	return NULL;
+    }
+  
+    poDS->hGroupID = H5Gopen( poDS->hHDF5, "/" ); 
+    if( poDS->hGroupID < 0 ){
+	poDS->bIsHDFEOS=false;
+        delete poDS;
+	return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      THIS IS AN HDF5 FILE                                            */
+/* -------------------------------------------------------------------- */
+    poDS->bIsHDFEOS=TRUE;
+    poDS->ReadGlobalAttributes( FALSE );
+  
+/* -------------------------------------------------------------------- */
+/*      Create HDF5 Data Hierarchy in a link list                       */
+/* -------------------------------------------------------------------- */
+    poDS->poH5Objects = 
+	poDS->HDF5FindDatasetObjectsbyPath( poDS->poH5RootGroup, 
+				      poDS->papszName[nDatasetPos] );
+
+    if( poDS->poH5Objects == NULL ) {
+	return NULL;
+    }
+/* -------------------------------------------------------------------- */
+/*      Retrieve HDF5 data information                                  */
+/* -------------------------------------------------------------------- */
+    poDS->dataset_id   = H5Dopen( poDS->hHDF5,poDS->poH5Objects->pszPath ); 
+    poDS->dataspace_id = H5Dget_space( poDS->dataset_id );                       
+    poDS->ndims        = H5Sget_simple_extent_ndims( poDS->dataspace_id );
+    poDS->dims         = ( hsize_t * )CPLCalloc( poDS->ndims, 
+						 sizeof( hsize_t ) );
+    poDS->maxdims      = ( hsize_t * )CPLCalloc( poDS->ndims, 
+						 sizeof( hsize_t ) );
+    poDS->dimensions   = H5Sget_simple_extent_dims( poDS->dataspace_id,
+						   poDS->dims,
+						   poDS->maxdims );
+    poDS->datatype = H5Dget_type( poDS->dataset_id );
+    poDS->clas     = H5Tget_class( poDS->datatype );
+    poDS->size     = H5Tget_size( poDS->datatype );
+    poDS->address = H5Dget_offset( poDS->dataset_id );
+    poDS->native  = H5Tget_native_type( poDS->datatype, H5T_DIR_ASCEND );
+
+    poDS->nRasterYSize=poDS->dims[0];
+    poDS->nRasterXSize=poDS->dims[1];
+
+    poDS->nBands=1;
+
+    if( poDS->ndims == 3 ) poDS->nBands=poDS->dims[poDS->ndims-1];
+
+
+    for(  i = 1; i <= poDS->nBands; i++ ) {
+	HDF5ImageRasterBand *poBand = 
+	    new HDF5ImageRasterBand( poDS, i, 
+				     poDS->GetDataType( poDS->native ) );
+	
+	poDS->SetBand( i, poBand );
+	if( poBand->bNoDataSet )
+		poBand->SetNoDataValue( 255 );
+    }
+
+    
+    poDS->oSRS.SetWellKnownGeogCS( "WGS84" );
+    poDS->oSRS.exportToWkt( &poDS->pszProjection );
+    poDS->CreateProjections( );
+
+    poDS->SetMetadata( poDS->papszMetadata );
+    return( poDS );
+}
+
+
+/************************************************************************/
+/*                        GDALRegister_HDF5Image()                      */
+/************************************************************************/
+void GDALRegister_HDF5Image( )
+
+{
+    GDALDriver  *poDriver;
+
+    if(  GDALGetDriverByName( "HDF5Image" ) == NULL )
+	{
+	    poDriver = new GDALDriver( );
+        
+	    poDriver->SetDescription( "HDF5Image" );
+	    poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+				      "HDF5 Dataset" );
+	    poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+				      "frmt_hdf5.html" );
+	    poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+				      "Byte Int16 UInt16 Int32 UInt32 Float32 Float64"  );
+	    poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, 
+				      "<CreationOptionList>"
+				      "   <Option name='RANK' type='int' description='Rank of output file'/>"
+				      "</CreationOptionList>" );
+	    poDriver->pfnOpen = HDF5ImageDataset::Open;
+
+	    GetGDALDriverManager( )->RegisterDriver( poDriver );
+	}
+}
+
+/************************************************************************/
+/*                         CreateProjections()                          */
+/************************************************************************/
+CPLErr HDF5ImageDataset::CreateProjections()
+{
+#define NBGCPLAT 100
+#define NBGCPLON 30
+
+    hid_t LatitudeDatasetID  = -1;
+    hid_t LongitudeDatasetID = -1;
+    hid_t LatitudeDataspaceID;
+    hid_t LongitudeDataspaceID;
+    float* Latitude;
+    float* Longitude;
+    int    i,j;
+    int   nDeltaLat;
+    int   nDeltaLon;
+
+    nDeltaLat = nRasterYSize / NBGCPLAT;
+    nDeltaLon = nRasterXSize / NBGCPLON;
+
+/* -------------------------------------------------------------------- */
+/*      Create HDF5 Data Hierarchy in a link list                       */
+/* -------------------------------------------------------------------- */
+    poH5Objects=HDF5FindDatasetObjects( poH5RootGroup,  "Latitude" );
+    if( !poH5Objects ) {
+	return CE_None;
+    }
+/* -------------------------------------------------------------------- */
+/*      The Lattitude and Longitude arrays must have a rank of 2 to     */
+/*      retrieve GCPs.                                                  */
+/* -------------------------------------------------------------------- */
+    if( poH5Objects->nRank != 2 ) {
+	return CE_None;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Retrieve HDF5 data information                                  */
+/* -------------------------------------------------------------------- */
+    LatitudeDatasetID   = H5Dopen( hHDF5,poH5Objects->pszPath ); 
+    LatitudeDataspaceID = H5Dget_space( dataset_id );                       
+
+    poH5Objects=HDF5FindDatasetObjects( poH5RootGroup, "Longitude" );
+    LongitudeDatasetID   = H5Dopen( hHDF5,poH5Objects->pszPath ); 
+    LongitudeDataspaceID = H5Dget_space( dataset_id );                       
+
+    if( ( LatitudeDatasetID > 0 ) && ( LongitudeDatasetID > 0) ) {
+	
+	Latitude         = ( float * ) CPLCalloc(  nRasterYSize*nRasterXSize, 
+						sizeof( float ) );
+	Longitude         = ( float * ) CPLCalloc( nRasterYSize*nRasterXSize, 
+						 sizeof( float ) );
+	memset( Latitude, 0, nRasterXSize*nRasterYSize*sizeof(  float ) );
+	memset( Longitude, 0, nRasterXSize*nRasterYSize*sizeof( float ) );
+	
+	H5Dread ( LatitudeDatasetID,
+		  H5T_NATIVE_FLOAT, 
+		  H5S_ALL,
+		  H5S_ALL,
+		  H5P_DEFAULT, 
+		  Latitude );
+	
+	H5Dread ( LongitudeDatasetID,
+		  H5T_NATIVE_FLOAT, 
+		  H5S_ALL,
+		  H5S_ALL,
+		  H5P_DEFAULT, 
+		  Longitude );
+	
+	oSRS.SetWellKnownGeogCS( "WGS84" );
+	oSRS.exportToWkt( &pszProjection );
+	oSRS.exportToWkt( &pszGCPProjection );
+	
+/* -------------------------------------------------------------------- */
+/*  Fill the GCPs list.                                                 */
+/* -------------------------------------------------------------------- */
+	nGCPCount = nRasterYSize/nDeltaLat * nRasterXSize/nDeltaLon;
+
+	pasGCPList = ( GDAL_GCP * )
+	    CPLCalloc( nGCPCount, sizeof( GDAL_GCP ) );
+	
+	GDALInitGCPs( nGCPCount, pasGCPList );
+	int k=0;
+
+	int nYLimit = ((int)nRasterYSize/nDeltaLat) * nDeltaLat;
+	int nXLimit = ((int)nRasterXSize/nDeltaLon) * nDeltaLon;
+	for( j = 0; j < nYLimit; j+=nDeltaLat ) {
+	    for( i = 0; i < nXLimit; i+=nDeltaLon ) {
+		int iGCP =  j * nRasterXSize + i;
+		pasGCPList[k].dfGCPX = ( double ) Longitude[iGCP]+180.0;
+		pasGCPList[k].dfGCPY = ( double ) Latitude[iGCP];
+		
+		pasGCPList[k].dfGCPPixel = i + 0.5;
+		pasGCPList[k++].dfGCPLine =  j + 0.5;
+		
+	    }
+	}
+	
+	CPLFree( Latitude );
+	CPLFree( Longitude );
+    }
+    return CE_None;
+
+}
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *HDF5ImageDataset::GetProjectionRef( )
+    
+{
+    return pszProjection;
+}
+
+/************************************************************************/
+/*                          SetProjection()                             */
+/************************************************************************/
+
+CPLErr HDF5ImageDataset::SetProjection( const char *pszNewProjection )
+
+{
+    if( pszProjection )
+	CPLFree( pszProjection );
+    pszProjection = CPLStrdup( pszNewProjection );
+    
+    return CE_None;
+}
+
+/************************************************************************/
+/*                            GetGCPCount()                             */
+/************************************************************************/
+
+int HDF5ImageDataset::GetGCPCount( )
+    
+{
+    return nGCPCount;
+}
+
+/************************************************************************/
+/*                          GetGCPProjection()                          */
+/************************************************************************/
+
+const char *HDF5ImageDataset::GetGCPProjection( )
+
+{
+    if( nGCPCount > 0 )
+	return pszGCPProjection;
+    else
+	return "";
+}
+
+/************************************************************************/
+/*                               GetGCPs()                              */
+/************************************************************************/
+
+const GDAL_GCP *HDF5ImageDataset::GetGCPs( )
+{
+    return pasGCPList;
+}
+
+
+
+
+
diff --git a/Utilities/GDAL/frmts/hdf5/makefile.vc b/Utilities/GDAL/frmts/hdf5/makefile.vc
new file mode 100644
index 0000000000..63c04f037e
--- /dev/null
+++ b/Utilities/GDAL/frmts/hdf5/makefile.vc
@@ -0,0 +1,14 @@
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+OBJ	=	hdf5dataset.obj  hdf5imagedataset.obj
+
+EXTRAFLAGS = 	-I$(HDF5_DIR)\include -DFRMT_hdf5 -DWIN32 -D_HDF5USEDLL_ 
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/ilwis/GNUmakefile b/Utilities/GDAL/frmts/ilwis/GNUmakefile
new file mode 100644
index 0000000000..ccf229308d
--- /dev/null
+++ b/Utilities/GDAL/frmts/ilwis/GNUmakefile
@@ -0,0 +1,13 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	ilwisdataset.o ilwiscoordinatesystem.o
+
+CPPFLAGS	=	$(GDAL_INCLUDE)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/ilwis/frmt_ilwis.html b/Utilities/GDAL/frmts/ilwis/frmt_ilwis.html
new file mode 100644
index 0000000000..e412d67846
--- /dev/null
+++ b/Utilities/GDAL/frmts/ilwis/frmt_ilwis.html
@@ -0,0 +1,322 @@
+<html xmlns:v="urn:schemas-microsoft-com:vml"
+xmlns:o="urn:schemas-microsoft-com:office:office"
+xmlns:w="urn:schemas-microsoft-com:office:word"
+xmlns="http://www.w3.org/TR/REC-html40">
+
+<head>
+<meta http-equiv=Content-Type content="text/html; charset=us-ascii">
+<meta name=ProgId content=Word.Document>
+<meta name=Generator content="Microsoft Word 11">
+<meta name=Originator content="Microsoft Word 11">
+<link rel=File-List href="frmt_lilwis_files/filelist.xml">
+<title>Various Supported GDAL Raster Formats</title>
+<!--[if gte mso 9]><xml>
+ <o:DocumentProperties>
+  <o:Author>Lichun Wang</o:Author>
+  <o:LastAuthor>Lichun Wang</o:LastAuthor>
+  <o:Revision>2</o:Revision>
+  <o:TotalTime>12</o:TotalTime>
+  <o:Created>2004-12-10T15:18:00Z</o:Created>
+  <o:LastSaved>2004-12-10T15:18:00Z</o:LastSaved>
+  <o:Pages>1</o:Pages>
+  <o:Words>167</o:Words>
+  <o:Characters>954</o:Characters>
+  <o:Company>itc</o:Company>
+  <o:Lines>7</o:Lines>
+  <o:Paragraphs>2</o:Paragraphs>
+  <o:CharactersWithSpaces>1119</o:CharactersWithSpaces>
+  <o:Version>11.5703</o:Version>
+ </o:DocumentProperties>
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <w:WordDocument>
+  <w:SpellingState>Clean</w:SpellingState>
+  <w:GrammarState>Clean</w:GrammarState>
+  <w:ValidateAgainstSchemas/>
+  <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid>
+  <w:IgnoreMixedContent>false</w:IgnoreMixedContent>
+  <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText>
+  <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel>
+ </w:WordDocument>
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <w:LatentStyles DefLockedState="false" LatentStyleCount="156">
+ </w:LatentStyles>
+</xml><![endif]-->
+<style>
+<!--
+ /* Style Definitions */
+ p.MsoNormal, li.MsoNormal, div.MsoNormal
+	{mso-style-parent:"";
+	margin:0in;
+	margin-bottom:.0001pt;
+	mso-pagination:widow-orphan;
+	font-size:12.0pt;
+	font-family:"Times New Roman";
+	mso-fareast-font-family:"Times New Roman";}
+h1
+	{mso-margin-top-alt:auto;
+	margin-right:0in;
+	mso-margin-bottom-alt:auto;
+	margin-left:0in;
+	mso-pagination:widow-orphan;
+	mso-outline-level:1;
+	font-size:24.0pt;
+	font-family:"Times New Roman";
+	font-weight:bold;}
+h2
+	{mso-margin-top-alt:auto;
+	margin-right:0in;
+	mso-margin-bottom-alt:auto;
+	margin-left:0in;
+	mso-pagination:widow-orphan;
+	mso-outline-level:2;
+	font-size:18.0pt;
+	font-family:"Times New Roman";
+	font-weight:bold;}
+a:link, span.MsoHyperlink
+	{color:blue;
+	text-decoration:underline;
+	text-underline:single;}
+a:visited, span.MsoHyperlinkFollowed
+	{color:blue;
+	text-decoration:underline;
+	text-underline:single;}
+p
+	{mso-margin-top-alt:auto;
+	margin-right:0in;
+	mso-margin-bottom-alt:auto;
+	margin-left:0in;
+	mso-pagination:widow-orphan;
+	font-size:12.0pt;
+	font-family:"Times New Roman";
+	mso-fareast-font-family:"Times New Roman";}
+pre
+	{margin:0in;
+	margin-bottom:.0001pt;
+	mso-pagination:widow-orphan;
+	tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;
+	font-size:10.0pt;
+	font-family:"Courier New";
+	mso-fareast-font-family:"Times New Roman";}
+tt
+	{font-family:"Courier New";
+	mso-ascii-font-family:"Courier New";
+	mso-fareast-font-family:"Times New Roman";
+	mso-hansi-font-family:"Courier New";
+	mso-bidi-font-family:"Courier New";}
+span.SpellE
+	{mso-style-name:"";
+	mso-spl-e:yes;}
+span.GramE
+	{mso-style-name:"";
+	mso-gram-e:yes;}
+@page Section1
+	{size:8.5in 11.0in;
+	margin:1.0in 1.25in 1.0in 1.25in;
+	mso-header-margin:35.4pt;
+	mso-footer-margin:35.4pt;
+	mso-paper-source:0;}
+div.Section1
+	{page:Section1;}
+ /* List Definitions */
+ @list l0
+	{mso-list-id:595022293;
+	mso-list-template-ids:650171908;}
+@list l0:level1
+	{mso-level-number-format:bullet;
+	mso-level-text:\F0B7;
+	mso-level-tab-stop:.5in;
+	mso-level-number-position:left;
+	text-indent:-.25in;
+	mso-ansi-font-size:10.0pt;
+	font-family:Symbol;}
+@list l1
+	{mso-list-id:1293826076;
+	mso-list-template-ids:-1676775702;}
+@list l1:level1
+	{mso-level-number-format:bullet;
+	mso-level-text:\F0B7;
+	mso-level-tab-stop:.5in;
+	mso-level-number-position:left;
+	text-indent:-.25in;
+	mso-ansi-font-size:10.0pt;
+	font-family:Symbol;}
+@list l2
+	{mso-list-id:1693459492;
+	mso-list-template-ids:1175630894;}
+@list l2:level1
+	{mso-level-number-format:bullet;
+	mso-level-text:\F0B7;
+	mso-level-tab-stop:.5in;
+	mso-level-number-position:left;
+	text-indent:-.25in;
+	mso-ansi-font-size:10.0pt;
+	font-family:Symbol;}
+@list l2:level2
+	{mso-level-tab-stop:1.0in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l2:level3
+	{mso-level-tab-stop:1.5in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l2:level4
+	{mso-level-tab-stop:2.0in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l2:level5
+	{mso-level-tab-stop:2.5in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l2:level6
+	{mso-level-tab-stop:3.0in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l2:level7
+	{mso-level-tab-stop:3.5in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l2:level8
+	{mso-level-tab-stop:4.0in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l2:level9
+	{mso-level-tab-stop:4.5in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l3
+	{mso-list-id:1939218821;
+	mso-list-template-ids:1068388388;}
+@list l3:level1
+	{mso-level-number-format:bullet;
+	mso-level-text:\F0B7;
+	mso-level-tab-stop:.5in;
+	mso-level-number-position:left;
+	text-indent:-.25in;
+	mso-ansi-font-size:10.0pt;
+	font-family:Symbol;}
+@list l3:level2
+	{mso-level-tab-stop:1.0in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l3:level3
+	{mso-level-tab-stop:1.5in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l3:level4
+	{mso-level-tab-stop:2.0in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l3:level5
+	{mso-level-tab-stop:2.5in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l3:level6
+	{mso-level-tab-stop:3.0in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l3:level7
+	{mso-level-tab-stop:3.5in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l3:level8
+	{mso-level-tab-stop:4.0in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+@list l3:level9
+	{mso-level-tab-stop:4.5in;
+	mso-level-number-position:left;
+	text-indent:-.25in;}
+ol
+	{margin-bottom:0in;}
+ul
+	{margin-bottom:0in;}
+-->
+</style>
+<!--[if gte mso 10]>
+<style>
+ /* Style Definitions */
+ table.MsoNormalTable
+	{mso-style-name:"Table Normal";
+	mso-tstyle-rowband-size:0;
+	mso-tstyle-colband-size:0;
+	mso-style-noshow:yes;
+	mso-style-parent:"";
+	mso-padding-alt:0in 5.4pt 0in 5.4pt;
+	mso-para-margin:0in;
+	mso-para-margin-bottom:.0001pt;
+	mso-pagination:widow-orphan;
+	font-size:10.0pt;
+	font-family:"Times New Roman";
+	mso-ansi-language:#0400;
+	mso-fareast-language:#0400;
+	mso-bidi-language:#0400;}
+</style>
+<![endif]--><!--[if gte mso 9]><xml>
+ <o:shapedefaults v:ext="edit" spidmax="3074"/>
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <o:shapelayout v:ext="edit">
+  <o:idmap v:ext="edit" data="1"/>
+ </o:shapelayout></xml><![endif]-->
+</head>
+
+<body bgcolor=white lang=EN-US link=blue vlink=blue style='tab-interval:.5in'>
+
+<div class=Section1>
+
+<h2><a name=ILWIS>ILWIS -- Raster Map</a></h2>
+
+<p class=MsoNormal><span style='mso-bookmark:ILWIS'>This driver implements
+reading and writing of ILWIS raster maps and map lists. Select the raster files
+with <span class=SpellE>the.mpr</span> (for raster map) or .<span class=SpellE>mpl</span>
+(for <span class=SpellE>maplist</span>) extensions </span></p>
+
+<p><span style='mso-bookmark:ILWIS'>Features: </span></p>
+
+<ul type=disc>
+ <li class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;
+     mso-list:l2 level1 lfo3;tab-stops:list .5in'><span style='mso-bookmark:
+     ILWIS'>Support for Byte, Int16, <span class=GramE>Int32</span> and Float64
+     pixel data types.</span></li>
+ <li class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;
+     mso-list:l2 level1 lfo3;tab-stops:list .5in'><span style='mso-bookmark:
+     ILWIS'>Supports map lists with an associated set of ILWIS raster maps.</span></li>
+ <li class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;
+     mso-list:l2 level1 lfo3;tab-stops:list .5in'><span style='mso-bookmark:
+     ILWIS'>Read and write geo-reference (.<span class=SpellE>grf</span>).
+     Support for geo-referencing transform is limited to north-oriented <span
+     class=SpellE>GeoRefCorner</span> only. If possible the affine transform is
+     computed from the corner coordinates.</span></li>
+ <li class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;
+     mso-list:l2 level1 lfo3;tab-stops:list .5in'><span style='mso-bookmark:
+     ILWIS'>Read and write coordinate files (.<span class=SpellE>csy</span>).
+     Support is limited to: Projection type of Projection and Lat/Lon type that
+     are defined in .<span class=SpellE>csy</span> file, the rest of
+     pre-defined projection types are ignored.</span></li>
+</ul>
+
+<p class=MsoNormal><span style='mso-bookmark:ILWIS'>Limitations: </span></p>
+
+<ul type=disc>
+ <li class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;
+     mso-list:l3 level1 lfo6;tab-stops:list .5in'><span style='mso-bookmark:
+     ILWIS'>Map lists with internal raster map storage (such as produced
+     through Import General Raster) are not supported.</span></li>
+ <li class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;
+     mso-list:l3 level1 lfo6;tab-stops:list .5in'><span style='mso-bookmark:
+     ILWIS'>ILWIS domain (.<span class=SpellE>dom</span>) and representation (.<span
+     class=SpellE>rpr</span>) files are currently ignored.</span></li>
+</ul>
+
+<span style='mso-bookmark:ILWIS'></span>
+
+<p>NOTE: Implemented as <span class=SpellE><tt><span style='font-size:10.0pt'>gdal/frmts/ilwis/ilwisdataset.cpp</span></tt></span><tt><span
+style='font-size:10.0pt'> and <span class=SpellE>gdal/frmts/ilwis/ilwiscoordinatesystem.cpp</span></span></tt>.</p>
+
+<p>See Also: <a href="http://www.itc.nl/ilwis/default.asp"><span
+style='mso-spacerun:yes'>&nbsp;</span>http://www.itc.nl/ilwis/default.asp</a> .</p>
+
+</div>
+
+</body>
+
+</html>
diff --git a/Utilities/GDAL/frmts/ilwis/ilwiscoordinatesystem.cpp b/Utilities/GDAL/frmts/ilwis/ilwiscoordinatesystem.cpp
new file mode 100644
index 0000000000..92e1690ce3
--- /dev/null
+++ b/Utilities/GDAL/frmts/ilwis/ilwiscoordinatesystem.cpp
@@ -0,0 +1,1197 @@
+/******************************************************************************
+ *
+ * Purpose: Translation from ILWIS coordinate system information.
+ * Author:   Lichun Wang, lichun@itc.nl
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, ITC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: ilwiscoordinatesystem.cpp,v $
+ * Revision 1.6  2006/04/04 03:34:52  fwarmerdam
+ * fixed up header after review of log history
+ *
+ * Revision 1.5  2005/09/14 13:37:18  dron
+ * Avoid warnings.
+ *
+ * Revision 1.4  2005/08/04 15:26:53  fwarmerdam
+ * added log headers
+ *
+ */
+#include "cpl_conv.h"
+#include "ilwisdataset.h"
+
+//using namespace std;
+
+typedef struct 
+{
+    const char  *pszIlwisDatum;
+		const char  *pszWKTDatum; 
+    int   nEPSGCode;
+} IlwisDatums;
+
+typedef struct 
+{
+    const char  *pszIlwisEllips;
+    int   nEPSGCode;
+		double semiMajor;
+		double invFlattening; 
+} IlwisEllips;
+
+string ReadElement(string section, string entry, string filename);
+bool WriteElement(string sSection, string sEntry, string fn, string sValue);
+bool WriteElement(string sSection, string sEntry, string fn, int nValue);
+bool WriteElement(string sSection, string sEntry, string fn, double dValue);
+
+static IlwisDatums iwDatums[] =
+{
+    { "Adindan", "Adindan", 4201 },								
+    { "Afgooye", "Afgooye", 4205 },								
+		//AGREF --- skipped
+    { "Ain el Abd 1970", "Ain_el_Abd_1970", 4204 },				
+		{ "American Samoa 1962", "American_Samoa_1962", 4169 },    
+		//Anna 1 Astro 1965 --- skipped
+		{ "Antigua Island Astro 1943", "Antigua_1943", 4601 },    
+		{ "Arc 1950", "Arc_1950", 4209 },    //Arc 1950
+		{ "Arc 1960", "Arc_1960", 4210 },    //Arc 1960
+		//Ascension Island 1958
+		//Astro Beacon E 1945
+		//Astro DOS 71/4
+		//Astro Tern Island (FRIG) 1961
+		//Astronomical Station 1952
+		{ "Australian Geodetic 1966", "Australian_Geodetic_Datum_1966", 4202 },    
+		{ "Australian Geodetic 1984", "Australian_Geodetic_Datum_1984", 4203 },
+		//Ayabelle Lighthouse
+		//Bellevue (IGN)
+		{ "Bermuda 1957", "Bermuda_1957", 4216 },
+		{ "Bissau", "Bissau", 4165 },
+		{ "Bogota Observatory  (1975)", "Bogota", 4218 },
+		{ "Bukit Rimpah", "Bukit_Rimpah", 4219 },
+		//Camp Area Astro
+		{ "Campo Inchauspe", "Campo_Inchauspe", 4221 },
+		//Canton Astro 1966
+		{ "Cape", "Cape", 4222 },
+		//Cape Canaveral
+		{ "Carthage", "Carthage", 4223 },
+		{ "CH1903", "CH1903", 4149 },
+		//Chatham Island Astro 1971
+		{ "Chua Astro", "Chua", 4224 },
+		{ "Corrego Alegre", "Corrego_Alegre", 4225 },
+		//Croatia
+		//D-PAF (Orbits)
+		{ "Dabola", "Dabola_1981", 4155 },
+		//Deception Island
+		//Djakarta (Batavia)
+		//DOS 1968
+		//Easter Island 1967
+		//Estonia 1937
+		{ "European 1950 (ED 50)", "European_Datum_1950", 4154 },
+		//European 1979 (ED 79
+		//Fort Thomas 1955
+		{ "Gan 1970", "Gandajika_1970", 4233 },
+		//Geodetic Datum 1949
+		//Graciosa Base SW 1948
+		//Guam 1963
+		{ "Gunung Segara", "Gunung_Segara", 4613 },
+		//GUX 1 Astro
+		{ "Herat North", "Herat_North", 4255 },
+		//Hermannskogel
+		//Hjorsey 1955
+		//Hong Kong 1963
+		{ "Hu-Tzu-Shan", "Hu_Tzu_Shan", 4236 },
+		//Indian (Bangladesh)
+		//Indian (India, Nepal)
+		//Indian (Pakistan)
+		{ "Indian 1954", "Indian_1954", 4239 },
+		{ "Indian 1960", "Indian_1960", 4131 },
+		{ "Indian 1975", "Indian_1975", 4240 },
+		{ "Indonesian 1974", "Indonesian_Datum_1974", 4238 },
+		//Ireland 1965
+		//ISTS 061 Astro 1968
+		//ISTS 073 Astro 1969
+		//Johnston Island 1961
+		{ "Kandawala", "Kandawala", 4244 },
+		//Kerguelen Island 1949
+		{ "Kertau 1948", "Kertau", 4245 },
+		//Kusaie Astro 1951
+		//L. C. 5 Astro 1961
+		{ "Leigon", "Leigon", 4250 },
+		{ "Liberia 1964", "Liberia_1964", 4251 },
+		{ "Luzon", "Luzon_1911", 4253 },
+		//M'Poraloko
+		{ "Mahe 1971", "Mahe_1971", 4256 },
+		{ "Massawa", "Massawa", 4262 },
+		{ "Merchich", "Merchich", 4261 },
+		{ "MGI (Hermannskogel)", "Militar_Geographische_Institute",4312 },
+		//Midway Astro 1961
+		{ "Minna", "Minna", 4263 },
+		{ "Montserrat Island Astro 1958", "Montserrat_1958", 4604 },
+		{ "Nahrwan", "Nahrwan_1967", 4270 },
+		{ "Naparima BWI", "Naparima_1955", 4158 },
+		{ "North American 1927 (NAD 27)", "North_American_Datum_1927", 4267 },
+		{ "North American 1983 (NAD 83)", "North_American_Datum_1983", 4269 },
+		//North Sahara 1959
+		{ "NTF (Nouvelle Triangulation de France)", "Nouvelle_Triangulation_Francaise", 4807 },
+		//Observatorio Meteorologico 1939
+		//Old Egyptian 1907
+		{ "Old Hawaiian", "Old_Hawaiian", 4135 },
+		//Oman
+		//Ordnance Survey Great Britain 1936
+		//Pico de las Nieves
+		//Pitcairn Astro 1967
+		//Point 58
+		{ "Pointe Noire 1948", "Pointe_Noire", 4282 },
+		{ "Porto Santo 1936", "Porto_Santo",4615 },
+		//Potsdam (Rauenburg)
+		{ "Potsdam (Rauenburg)", "Deutsches_Hauptdreiecksnetz", 4314 },
+		{ "Provisional South American 1956", "Provisional_South_American_Datum_1956", 4248 },
+		//Provisional South Chilean 1963
+		{ "Puerto Rico", "Puerto_Rico", 4139 },
+		{ "Pulkovo 1942", "Pulkovo_1942", 4178 },
+		//{ "Qatar National", "Qatar_National_Datum_1995", 4614 },
+		{ "Qornoq", "Qornoq", 4287 },
+		{ "Puerto Rico", "Puerto_Rico", 4139 },
+		//Reunion
+		{ "Rome 1940", "Monte_Mario", 4806 },
+		{ "RT90", "Rikets_koordinatsystem_1990", 4124 },
+		{ "Rijks Driehoeksmeting", "Amersfoort", 4289 },
+		{ "S-42 (Pulkovo 1942)", "Pulkovo_1942", 4178 },
+		//{ "S-JTSK", "Jednotne_Trigonometricke_Site_Katastralni", 4156 },
+		//Santo (DOS) 1965
+		//Sao Braz
+		{ "Sapper Hill 1943", "Sapper_Hill_1943", 4292 },
+		{ "Schwarzeck", "Schwarzeck", 4293 },
+		{ "Selvagem Grande 1938", "Selvagem_Grande", 4616 },
+		//vSGS 1985
+		//Sierra Leone 1960
+		{ "South American 1969", "South_American_Datum_1969", 4291 },
+		//South Asia
+		{ "Tananarive Observatory 1925", "Tananarive_1925", 4297 },
+		{ "Timbalai 1948", "Timbalai_1948", 4298 },
+		{ "Tokyo", "Tokyo", 4301 },
+		//Tristan Astro 1968
+		//Viti Levu 1916
+		{ "Voirol 1874", "Voirol_1875", 4304 },
+		//Voirol 1960
+		//Wake Island Astro 1952
+		//Wake-Eniwetok 1960
+		{ "WGS 1972", "WGS_1972", 4322 },
+		{ "WGS 1984", "WGS_1984", 4326 },
+		{ "Yacare", "Yacare", 4309 },
+		{ "Zanderij", "Zanderij", 4311 },
+    { NULL, NULL, 0 }
+};
+
+static IlwisEllips iwEllips[] =
+{
+    { "Sphere", 7035, 6371007, 0.0  },	//rad 6370997 m (normal sphere)   
+    { "Airy 1830", 7031, 6377563.396, 299.3249646 },   
+    { "Modified Airy", 7002, 6377340.189, 299.3249646 },   
+    { "ATS77", 7204, 6378135.0, 298.257000006 },   
+    { "Australian National", 7003, 6378160, 298.249997276 },   
+    { "Bessel 1841", 7042, 6377397.155, 299.1528128},   
+		{ "Bessel 1841 (Japan By Law)", 7046 , 6377397.155, 299.152815351 }, 
+    { "Bessel 1841 (Namibia)", 7006, 6377483.865, 299.1528128 },   
+    { "Clarke 1866", 7008, 6378206.4, 294.9786982 },   
+    { "Clarke 1880", 7034, 6378249.145, 293.465 },   
+    { "Clarke 1880 (IGN)", 7011, 6378249.2, 293.466  },   
+    // FIXME: D-PAF (Orbits) --- skipped
+    // FIXME: Du Plessis Modified --- skipped
+    // FIXME: Du Plessis Reconstituted --- skipped
+    { "Everest (India 1830)", 7015, 6377276.345, 300.8017 },
+    // Everest (India 1956) --- skipped
+		// Everest (Malaysia 1969) --- skipped
+    { "Everest (E. Malaysia and Brunei)", 7016, 6377298.556, 300.8017 },   
+    { "Everest (Malay. and Singapore 1948)", 7018, 6377304.063, 300.8017 },   
+		{ "Everest (Pakistan)", 7044, 6377309.613, 300.8017 }, 
+		// Everest (Sabah Sarawak) --- skipped
+		// Fischer 1960 --- skipped
+		// Fischer 1960 (Modified) --- skipped
+		// Fischer 1968 --- skipped
+		{ "GRS 80", 7019, 6378137, 298.257222101  },   
+		{ "Helmert 1906", 7020, 6378200, 298.3 }, 
+		// Hough 1960 --- skipped
+		{ "Indonesian 1974", 7021, 6378160, 298.247 }, 
+		{ "International 1924", 7022, 6378388, 297 }, 
+		{ "Krassovsky 1940", 7024, 6378245, 298.3 }, 
+		// New_International 1967
+		// SGS 85 
+		// South American 1969
+		// WGS 60
+		// WGS 66
+		{ "WGS 72", 7020, 6378135.0, 298.259998590  }, 
+		{ "WGS 84", 7030, 6378137, 298.257223563 }, 
+    { NULL, 0 }
+};
+
+#ifndef PI
+#  define PI 3.14159265358979323846
+#endif
+
+#ifndef R2D
+#  define R2D	(180/PI)
+#endif
+#ifndef D2R
+#  define D2R	(PI/180)
+#endif
+
+/* ==================================================================== */
+/*      Some "standard" strings.                                        */
+/* ==================================================================== */
+
+#define ILW_False_Easting "False Easting"
+#define ILW_False_Northing "False Northing"
+#define ILW_Central_Meridian "Central Meridian"
+#define ILW_Central_Parallel "Central Parallel"
+#define ILW_Standard_Parallel_1 "Standard Parallel 1"
+#define ILW_Standard_Parallel_2 "Standard Parallel 2"
+#define ILW_Scale_Factor "Scale Factor"
+#define ILW_Latitude_True_Scale "Latitude of True Scale"
+#define ILW_Height_Persp_Center "Height Persp. Center"
+
+double ReadPrjParms(string section, string entry, string filename)
+{
+		string str = ReadElement(section, entry, filename);
+		//string str="";
+		if (str.length() != 0)
+			return atof(str.c_str());
+		else
+			return 0;
+}
+
+static int fetchParms(string csyFileName, double * padfPrjParams)
+{
+		int     i;
+
+		//Fill all projection parameters with zero
+    for ( i = 0; i < 13; i++ )
+        padfPrjParams[i] = 0.0;
+		
+		string pszProj = ReadElement("CoordSystem", "Projection", csyFileName);
+		string pszEllips = ReadElement("CoordSystem", "Ellipsoid", csyFileName);
+
+		//fetch info about a custom ellipsoid
+		if( EQUALN( pszEllips.c_str(), "User Defined", 12 ) )
+		{
+				padfPrjParams[0] = ReadPrjParms("Ellipsoid", "a", csyFileName);
+				padfPrjParams[2] = ReadPrjParms("Ellipsoid", "1/f", csyFileName);
+		}
+		else if( EQUALN( pszEllips.c_str(), "Sphere", 6 ) )
+		{
+				padfPrjParams[0] = ReadPrjParms("CoordSystem", "Sphere Radius", csyFileName);
+		}
+
+		padfPrjParams[3] = ReadPrjParms("Projection", "False Easting", csyFileName);
+		padfPrjParams[4] = ReadPrjParms("Projection", "False Northing", csyFileName);
+
+		padfPrjParams[5] = ReadPrjParms("Projection", "Central Parallel", csyFileName);
+		padfPrjParams[6] = ReadPrjParms("Projection", "Central Meridian", csyFileName);
+
+		padfPrjParams[7] = ReadPrjParms("Projection", "Standard Parallel 1", csyFileName);
+		padfPrjParams[8] = ReadPrjParms("Projection", "Standard Parallel 2", csyFileName);
+
+		padfPrjParams[9] = ReadPrjParms("Projection", "Scale Factor", csyFileName);
+		padfPrjParams[10] = ReadPrjParms("Projection", "Latitude of True Scale", csyFileName);
+		padfPrjParams[11] = ReadPrjParms("Projection", "Zone", csyFileName);
+    padfPrjParams[12] = ReadPrjParms("Projection", ILW_Height_Persp_Center, csyFileName);
+
+		return true;
+}
+
+/************************************************************************/
+/*                          mapTMParms                                  */
+/************************************************************************/
+/**
+ * fetch the parameters from ILWIS projection definition for
+ * --- Gauss-Krueger Germany.
+ * --- Gauss Colombia
+ * --- Gauss-Boaga Italy
+**/
+static int mapTMParms(string sProj, double dfZone, double &dfFalseEasting, double &dfCentralMeridian)
+{
+		if( EQUALN( sProj.c_str(), "Gauss-Krueger Germany", 21 ) )
+		{
+			//Zone number must be in the range 1 to 3
+			dfCentralMeridian = 6.0 + (dfZone - 1) * 3;
+			dfFalseEasting = 2500000 + (dfZone - 1) * 1000000; 
+		}
+		else if( EQUALN( sProj.c_str(), "Gauss-Boaga Italy", 17 ) )
+		{
+			if ( dfZone == 1)
+			{
+				dfCentralMeridian = 9;
+				dfFalseEasting = 1500000;
+			}
+			else if ( dfZone == 2)
+			{
+				dfCentralMeridian = 15;
+				dfFalseEasting = 2520000;
+			}
+			else
+				return false;
+		}
+		else if( EQUALN( sProj.c_str(), "Gauss Colombia", 14 ) )
+		{
+			//Zone number must be in the range 1 to 4
+			dfCentralMeridian = -77.08097220 + (dfZone - 1) * 3;
+		}
+		return true;
+}
+
+/************************************************************************/
+/*                          scaleFromLATTS()                             */
+/************************************************************************/
+/**
+ * Compute the scale factor from Latitude_Of_True_Scale parameter.
+ *
+**/
+static int scaleFromLATTS( string sEllips, double phits, double &scale )  
+{
+		if( EQUALN( sEllips.c_str(), "Sphere", 6 ) ) 
+		{
+			scale = cos(phits);
+			return true;
+		}
+		else
+		{
+			IlwisEllips *piwEllips =  iwEllips;
+			double e2 = 0.0;
+			while ( piwEllips->pszIlwisEllips )
+			{
+					if( EQUALN( sEllips.c_str(), piwEllips->pszIlwisEllips, strlen(piwEllips->pszIlwisEllips) ) )
+					{
+						double a = piwEllips->semiMajor;
+						double b = a * ( 1 - piwEllips->invFlattening);
+						e2 = ( a*a - b*b ) /( a*a );
+						break;
+					}
+					piwEllips++;
+			}
+			scale = cos(phits) / sqrt (1. - e2 * sin(phits) * sin(phits));
+			return true;
+		}
+		return false;
+}
+
+/************************************************************************/
+/*                          ReadProjection()                           */
+/************************************************************************/
+
+/**
+ * Import coordinate system from ILWIS projection definition.
+ *
+ * The method will import projection definition in ILWIS, 
+ * It uses 13 parameters to define the coordinate system
+ * and datum/ellipsoid specieied in the padfPrjParams array. 
+ *
+ * @param padfPrjParams Array of 10 coordinate system parameters:
+ *
+ * [0]  Spheroid semi major axis
+ * [1]  Spheroid semi minor axis
+ * [2]  Spheroid inverse flattening
+ * [3]  False Easting
+ * [4]  False Northing
+ * [5]  Central Parallel // latitude_of_origin
+ * [6]  Central Meridian 
+ * [7]  First Standard Parallel
+ * [8]  Second Standard Parallel
+ * [9]  Scale Factor
+ * [10] Latitude_Of_True_Scale
+ * [11] Zone
+ * [12] Satellite Height
+**/ 
+
+CPLErr ILWISDataset::ReadProjection( string csyFileName )
+{
+		string pszEllips;
+		string pszDatum;
+		string pszProj;
+		
+		//translate ILWIS pre-defined coordinate systems
+		if( EQUALN( csyFileName.c_str(), "latlon.csy", 10 )) 
+		{
+				pszProj = "LatLon";
+				pszDatum = "";
+				pszEllips = "Sphere";
+		}	
+		else if ( EQUALN( csyFileName.c_str(), "LatlonWGS84.csy", 15 ))			
+		{
+				pszProj = "LatLon";
+				pszDatum = "WGS 1984";
+				pszEllips = "WGS 84";
+		}
+		else
+		{
+				pszProj = ReadElement("CoordSystem", "Type", csyFileName);
+				if( !EQUALN( pszProj.c_str(), "LatLon", 7 ) )
+					pszProj = ReadElement("CoordSystem", "Projection", csyFileName);
+				pszDatum = ReadElement("CoordSystem", "Datum", csyFileName);
+				pszEllips = ReadElement("CoordSystem", "Ellipsoid", csyFileName);
+		}
+
+/* -------------------------------------------------------------------- */
+/*      Fetch array containing 13 coordinate system parameters          */
+/* -------------------------------------------------------------------- */
+    double     padfPrjParams[13];
+		fetchParms(csyFileName, padfPrjParams);
+		
+		OGRSpatialReference oSRS;
+/* -------------------------------------------------------------------- */
+/*      Operate on the basis of the projection name.                    */
+/* -------------------------------------------------------------------- */
+		if( EQUALN( pszProj.c_str(), "LatLon", 7 ) )
+    {
+			//set datum later
+    }
+    else if( EQUALN( pszProj.c_str(), "Albers EqualArea Conic", 22  ) )
+    {
+				oSRS.SetProjCS("Albers EqualArea Conic");
+        oSRS.SetACEA( padfPrjParams[7], padfPrjParams[8],
+											padfPrjParams[5], padfPrjParams[6],
+											padfPrjParams[3], padfPrjParams[4] );
+                 
+    }
+		else if( EQUALN( pszProj.c_str(), "Azimuthal Equidistant", 21 ) )
+		{
+				oSRS.SetProjCS("Azimuthal Equidistant");
+				oSRS.SetAE( padfPrjParams[5], padfPrjParams[6],
+										padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "Central Cylindrical", 19 ) )
+		{
+				//Use Central Parallel for dfStdP1
+				//padfPrjParams[5] is always to zero
+				oSRS.SetProjCS("Central Cylindrical");
+				oSRS.SetCEA( padfPrjParams[5], padfPrjParams[6],
+										 padfPrjParams[3], padfPrjParams[4] ); 
+		}
+		else if( EQUALN( pszProj.c_str(), "Cassini", 7 ) )
+		{
+				//Use Latitude_Of_True_Scale for dfCenterLat 
+				//Scale Factor 1.0 should always be defined
+				oSRS.SetProjCS("Cassini");
+				oSRS.SetCS(  padfPrjParams[10], padfPrjParams[6],  
+										 padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "DutchRD", 7 ) )
+		{
+				oSRS.SetProjCS("DutchRD");
+				oSRS.SetStereographic  (  52.156160556,  5.387638889,
+																	0.9999079,  
+																	155000,  463000);
+																	 
+ 		}
+		else if( EQUALN( pszProj.c_str(), "Equidistant Conic", 17 ) )
+		{
+				oSRS.SetProjCS("Equidistant Conic");
+				oSRS.SetEC(  padfPrjParams[7], padfPrjParams[8],
+										 padfPrjParams[5], padfPrjParams[6],	
+										 padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "Gauss-Krueger Germany", 21 ) )
+		{
+				//FalseNorthing and CenterLat are always set to 0
+				//Scale 1.0 is defined
+				//FalseEasting and CentralMeridian are defined by the selected zone
+				mapTMParms("Gauss-Krueger Germany", padfPrjParams[11], 
+										padfPrjParams[3], padfPrjParams[6]);
+				oSRS.SetProjCS("Gauss-Krueger Germany");
+				oSRS.SetTM(  0, padfPrjParams[6],
+										 1.0, 	
+										 padfPrjParams[3], 0 );
+		}
+		else if ( EQUALN( pszProj.c_str(),"Gauss-Boaga Italy", 17 ) )
+		{
+			  //FalseNorthing and CenterLat are always set to 0
+				//Scale 0.9996 is defined
+				//FalseEasting and CentralMeridian are defined by the selected zone
+				mapTMParms("Gauss-Boaga Italy", padfPrjParams[11], 
+										padfPrjParams[3], padfPrjParams[6]);
+				oSRS.SetProjCS("Gauss-Boaga Italy");
+				oSRS.SetTM(  0, padfPrjParams[6],
+										 0.9996, 	
+										 padfPrjParams[3], 0 );
+		}
+		else if ( EQUALN( pszProj.c_str(),"Gauss Colombia", 14 ))
+		{
+				// 1000000 used for FalseNorthing and FalseEasting
+				// 1.0 used for scale 
+				// CenterLat is defined 45.1609259259259 
+				// CentralMeridian is defined by the selected zone
+				mapTMParms("Gauss Colombia", padfPrjParams[11], 
+										padfPrjParams[3], padfPrjParams[6]);
+				oSRS.SetProjCS("Gauss Colombia");
+				oSRS.SetTM(  45.1609259259259, padfPrjParams[6],
+										 1.0, 	
+										 1000000, 1000000 );
+		}
+		else if( EQUALN( pszProj.c_str(), "Gnomonic", 8 ) )
+		{
+				oSRS.SetProjCS("Gnomonic");
+				oSRS.SetGnomonic( padfPrjParams[5], padfPrjParams[6],
+													padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "Lambert Conformal Conic", 23 ) )
+		{
+				// should use 1.0 for scale factor in Ilwis definition 
+				oSRS.SetProjCS("Lambert Conformal Conic");
+				oSRS.SetLCC(	padfPrjParams[7], padfPrjParams[8],
+											padfPrjParams[5], padfPrjParams[6],
+											padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "Lambert Cylind EqualArea", 24 ) )
+		{
+				// Latitude_Of_True_Scale used for dfStdP1 ?
+				oSRS.SetProjCS("Lambert Conformal Conic");
+				oSRS.SetCEA(	padfPrjParams[10], 
+											padfPrjParams[6], 
+											padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "Mercator", 8 ) )
+		{
+				// use 0 for CenterLat, scale is computed from the 
+				// Latitude_Of_True_Scale
+				scaleFromLATTS( pszEllips, padfPrjParams[10], padfPrjParams[9] );
+				oSRS.SetProjCS("Mercator");
+				oSRS.SetMercator(	0, padfPrjParams[6], 
+											padfPrjParams[9], 
+											padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "Miller", 6 ) )
+		{
+				// use 0 for CenterLat
+				oSRS.SetProjCS("Miller");
+				oSRS.SetMC(	0, padfPrjParams[6], 
+										padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "Mollweide", 9 ) )
+		{
+				oSRS.SetProjCS("Mollweide");
+				oSRS.SetMollweide(	padfPrjParams[6], 
+										        padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "Orthographic", 12 ) )
+		{
+				oSRS.SetProjCS("Orthographic");
+				oSRS.SetOrthographic (	padfPrjParams[5], padfPrjParams[6], 
+										            padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "Plate Carree", 12 ) ||
+						 EQUALN( pszProj.c_str(), "Plate Rectangle", 15 ))
+		{
+				// set 0.0 for CenterLat for Plate Carree projection
+				// skipp Latitude_Of_True_Scale for Plate Rectangle projection definition
+				oSRS.SetProjCS(pszProj.c_str());				
+				oSRS.SetEquirectangular(	padfPrjParams[5], padfPrjParams[6], 
+										              padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "PolyConic", 9 ) )
+		{
+				// skipp scale factor
+				oSRS.SetProjCS("PolyConic");
+				oSRS.SetPolyconic(	padfPrjParams[5], padfPrjParams[6], 
+										        padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "Robinson", 8 ) )
+		{
+				oSRS.SetProjCS("Robinson");
+				oSRS.SetRobinson(	padfPrjParams[6],
+										      padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "Sinusoidal", 10 ) )
+		{
+				oSRS.SetProjCS("Sinusoidal");	
+				oSRS.SetSinusoidal(	padfPrjParams[6], 
+										        padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "Stereographic", 13 ) )
+		{
+				oSRS.SetProjCS("Stereographic");	
+				oSRS.SetStereographic(	padfPrjParams[5], padfPrjParams[6],
+																padfPrjParams[9],
+																padfPrjParams[3], padfPrjParams[4] );
+	
+		}
+		else if( EQUALN( pszProj.c_str(), "Transverse Mercator", 19 ) )
+		{
+				oSRS.SetProjCS("Transverse Mercator");	
+				oSRS.SetStereographic(	padfPrjParams[5], padfPrjParams[6],
+																padfPrjParams[9],
+																padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "UTM", 3 ) )
+		{
+				string pszNH = ReadElement("Projection", "Northern Hemisphere", csyFileName);
+				oSRS.SetProjCS("UTM");
+				if( EQUALN( pszNH.c_str(), "Yes", 3 ) )
+					oSRS.SetUTM( (int) padfPrjParams[11], 1);  
+				else
+					oSRS.SetUTM( (int) padfPrjParams[11], 0);  
+		}
+		else if( EQUALN( pszProj.c_str(), "VanderGrinten", 13 ) )
+		{
+				oSRS.SetVDG(	padfPrjParams[6],
+											padfPrjParams[3], padfPrjParams[4] );
+		}
+		else if( EQUALN( pszProj.c_str(), "GeoStationary Satellite", 23 ) )
+		{
+        oSRS.SetGEOS( padfPrjParams[6], 
+                 padfPrjParams[12], 
+                 padfPrjParams[3], 
+                 padfPrjParams[4] );
+    }
+		else if( EQUALN( pszProj.c_str(), "MSG Perspective", 15 ) )
+		{
+        oSRS.SetGEOS( padfPrjParams[6], 
+                 padfPrjParams[12], 
+                 padfPrjParams[3], 
+                 padfPrjParams[4] );
+    }
+		else
+    {
+        oSRS.SetLocalCS( pszProj.c_str() );
+		}
+/* -------------------------------------------------------------------- */
+/*      Try to translate the datum/spheroid.                            */
+/* -------------------------------------------------------------------- */
+
+    if ( !oSRS.IsLocal() )
+    {
+        IlwisDatums   *piwDatum = iwDatums;
+
+        // Search for matching datum
+        while ( piwDatum->pszIlwisDatum )
+        {
+            if( EQUALN( pszDatum.c_str(), piwDatum->pszIlwisDatum, strlen(piwDatum->pszIlwisDatum) ) )
+            {
+								OGRSpatialReference oOGR;
+                oOGR.importFromEPSG( piwDatum->nEPSGCode );
+                oSRS.CopyGeogCSFrom( &oOGR );
+                break;
+            }
+            piwDatum++;
+        } //end of searchong for matching datum
+
+
+/* -------------------------------------------------------------------- */
+/*      If no matching for datum definition, fetch info about an        */
+/*			ellipsoid.  semi major axis is always	returned in meters        */ 
+/* -------------------------------------------------------------------- */
+				IlwisEllips *piwEllips =  iwEllips;
+				if (pszEllips.length() == 0)
+					pszEllips="Sphere";
+				if ( !piwDatum->pszIlwisDatum )  
+																				 
+        {
+           while ( piwEllips->pszIlwisEllips )
+            {
+                if( EQUALN( pszEllips.c_str(), piwEllips->pszIlwisEllips, strlen(piwEllips->pszIlwisEllips) ) )
+                {
+										if( EQUALN( pszEllips.c_str(), "Sphere", 6 ) && padfPrjParams[0] != 0 )
+										{	
+											piwEllips->semiMajor = padfPrjParams[0];
+										}
+                    oSRS.SetGeogCS( CPLSPrintf(
+                                   "Unknown datum based upon the %s ellipsoid",
+                                   piwEllips->pszIlwisEllips ),
+																		CPLSPrintf(
+                                   "Not specified (based on %s spheroid)",
+                                   piwEllips->pszIlwisEllips ),
+																	 piwEllips->pszIlwisEllips, 
+																	 piwEllips->semiMajor,
+																	 piwEllips->invFlattening,
+																	 NULL, 0.0, NULL, 0.0 );
+                    oSRS.SetAuthority( "SPHEROID", "EPSG", piwEllips->nEPSGCode );
+										
+                    break;
+                }
+                piwEllips++;
+            } //end of searching for matching ellipsoid
+        } 
+				
+/* -------------------------------------------------------------------- */
+/*      If no matching for ellipsoid definition, fetch info about an    */
+/*			user defined ellipsoid. If cannot find, default to WGS 84       */
+/* -------------------------------------------------------------------- */
+				if ( !piwEllips->pszIlwisEllips )      
+        {
+
+					  if( EQUALN( pszEllips.c_str(), "User Defined", 12 ) )
+            {
+                
+                oSRS.SetGeogCS( "Unknown datum based upon the custom ellipsoid",
+                           "Not specified (based on custom ellipsoid)",
+                           "Custom ellipsoid",
+                           padfPrjParams[0], padfPrjParams[2],
+                           NULL, 0, NULL, 0 );
+            }
+            else
+            {
+                //if cannot find the user defined ellips, default to WGS84
+								oSRS.SetWellKnownGeogCS( "WGS84" );
+            }
+        }
+
+    } // end of if ( !IsLocal() )
+		
+/* -------------------------------------------------------------------- */
+/*      Units translation                                          */
+/* -------------------------------------------------------------------- */
+    if( oSRS.IsLocal() || oSRS.IsProjected() )
+    {
+        oSRS.SetLinearUnits( SRS_UL_METER, 1.0 );
+    }
+		oSRS.FixupOrdering();
+		oSRS.exportToWkt( &pszProjection );
+    
+
+    return CE_None;
+}
+
+void WriteFalseEastNorth(string csFileName, OGRSpatialReference oSRS)
+{
+			WriteElement("Projection", ILW_False_Easting, csFileName, 
+										oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0));
+			WriteElement("Projection", ILW_False_Northing, csFileName, 
+										oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0));
+}
+
+void WriteProjectionName(string csFileName, string stProjection)
+{
+			WriteElement("CoordSystem", "Type", csFileName, "Projection");
+			WriteElement("CoordSystem", "Projection", csFileName, stProjection);
+}
+
+void WriteUTM(string csFileName, OGRSpatialReference oSRS)
+{
+		int	bNorth, nZone;
+
+    nZone = oSRS.GetUTMZone( &bNorth );
+		WriteElement("CoordSystem", "Type", csFileName, "Projection");
+		WriteElement("CoordSystem", "Projection", csFileName, "UTM");
+		if (bNorth)
+			WriteElement("Projection", "Northern Hemisphere", csFileName, "Yes");
+		else
+			WriteElement("Projection", "Northern Hemisphere", csFileName, "No");
+		WriteElement("Projection", "Zone", csFileName, nZone);
+}
+
+void WriteAlbersConicEqualArea(string csFileName, OGRSpatialReference oSRS)
+{
+				WriteProjectionName(csFileName, "Albers EqualArea Conic");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+				WriteElement("Projection", ILW_Central_Parallel, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
+				WriteElement("Projection", ILW_Standard_Parallel_1, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0));
+				WriteElement("Projection", ILW_Standard_Parallel_2, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0));
+}
+void WriteAzimuthalEquidistant(string csFileName, OGRSpatialReference oSRS)
+{
+			  WriteProjectionName(csFileName, "Azimuthal Equidistant");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+				WriteElement("Projection", ILW_Central_Parallel, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
+				WriteElement("Projection", ILW_Scale_Factor, csFileName, "1.0000000000"); 
+}
+void WriteCylindricalEqualArea(string csFileName, OGRSpatialReference oSRS)
+{
+	      WriteProjectionName(csFileName, "Central Cylindrical");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+}
+
+void WriteCassiniSoldner(string csFileName, OGRSpatialReference oSRS)
+{
+				WriteProjectionName(csFileName, "Cassini");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+				WriteElement("Projection", ILW_Latitude_True_Scale, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
+				WriteElement("Projection", ILW_Scale_Factor, csFileName, "1.0000000000"); 
+}
+
+void WriteStereographic(string csFileName, OGRSpatialReference oSRS)
+{
+	      WriteProjectionName(csFileName, "Stereographic");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+				WriteElement("Projection", ILW_Central_Parallel, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
+				WriteElement("Projection", ILW_Scale_Factor, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 0.0)); 
+}
+
+void WriteEquidistantConic(string csFileName, OGRSpatialReference oSRS)
+{
+	      WriteProjectionName(csFileName, "Equidistant Conic");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+				WriteElement("Projection", ILW_Central_Parallel, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
+				WriteElement("Projection", ILW_Standard_Parallel_1, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0)); 
+				WriteElement("Projection", ILW_Standard_Parallel_2, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0));
+}
+
+void WriteTransverseMercator(string csFileName, OGRSpatialReference oSRS)
+{
+	      WriteProjectionName(csFileName, "Transverse Mercator");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+				WriteElement("Projection", ILW_Central_Parallel, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
+				WriteElement("Projection", ILW_Scale_Factor, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 0.0)); 
+}
+
+void WriteGnomonic(string csFileName, OGRSpatialReference oSRS)
+{
+				WriteProjectionName(csFileName, "Gnomonic");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+				WriteElement("Projection", ILW_Central_Parallel, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
+}
+
+void WriteLambertConformalConic(string csFileName, OGRSpatialReference oSRS)
+{
+	      WriteProjectionName(csFileName, "Lambert Conformal Conic");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+				WriteElement("Projection", ILW_Central_Parallel, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
+				WriteElement("Projection", ILW_Scale_Factor, csFileName, "1.0000000000"); 
+}
+
+void WriteLambertAzimuthalEqualArea(string csFileName, OGRSpatialReference oSRS)
+{
+	      WriteProjectionName(csFileName, "Lambert Azimuthal EqualArea");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+				WriteElement("Projection", ILW_Central_Parallel, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
+}
+
+void WriteMercator_1SP(string csFileName, OGRSpatialReference oSRS)
+{
+				WriteProjectionName(csFileName, "Mercator");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+				WriteElement("Projection", ILW_Latitude_True_Scale, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
+}
+
+void WriteMillerCylindrical(string csFileName, OGRSpatialReference oSRS)
+{
+	      WriteProjectionName(csFileName, "Miller");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+}
+
+void WriteMolleweide(string csFileName, OGRSpatialReference oSRS)
+{
+				WriteProjectionName(csFileName, "Mollweide");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+}
+
+void WriteOrthographic(string csFileName, OGRSpatialReference oSRS)
+{
+				WriteProjectionName(csFileName, "Orthographic");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+				WriteElement("Projection", ILW_Central_Parallel, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
+}
+
+void WritePlateRectangle(string csFileName, OGRSpatialReference oSRS)
+{
+				WriteProjectionName(csFileName, "Plate Rectangle");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+				WriteElement("Projection", ILW_Central_Parallel, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
+				WriteElement("Projection", ILW_Latitude_True_Scale, csFileName, "0.0000000000"); 
+}
+
+void WritePolyConic(string csFileName, OGRSpatialReference oSRS)
+{
+				WriteProjectionName(csFileName, "PolyConic");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+				WriteElement("Projection", ILW_Central_Parallel, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
+				WriteElement("Projection", ILW_Scale_Factor, csFileName, "1.0000000000"); 
+}
+
+void WriteRobinson(string csFileName, OGRSpatialReference oSRS)
+{
+				WriteProjectionName(csFileName, "Robinson");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+}
+
+void WriteSinusoidal(string csFileName, OGRSpatialReference oSRS)
+{
+				WriteProjectionName(csFileName, "Sinusoidal");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+}
+
+void WriteVanderGrinten(string csFileName, OGRSpatialReference oSRS)
+{
+				WriteProjectionName(csFileName, "VanderGrinten");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+}
+
+void WriteGeoStatSat(string csFileName, OGRSpatialReference oSRS)
+{
+				WriteProjectionName(csFileName, "GeoStationary Satellite");
+				WriteFalseEastNorth(csFileName, oSRS);
+				WriteElement("Projection", ILW_Central_Meridian, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
+				WriteElement("Projection", ILW_Scale_Factor, csFileName, "1.0000000000"); 
+				WriteElement("Projection", ILW_Height_Persp_Center, csFileName, 
+											oSRS.GetNormProjParm(SRS_PP_SATELLITE_HEIGHT, 35785831.0));
+}
+
+/************************************************************************/
+/*                          WriteProjection()                           */
+/************************************************************************/
+/**
+ * Export coordinate system in ILWIS projection definition.
+ *
+ * Converts the loaded coordinate reference system into ILWIS projection
+ * definition to the extent possible.																		*/	
+
+CPLErr ILWISDataset::WriteProjection() 
+
+{
+
+		OGRSpatialReference oSRS;
+		OGRSpatialReference *poGeogSRS = NULL;
+		int                 bHaveSRS;
+		char		*pszP = pszProjection;
+		
+		string csFileName = CPLResetExtension(pszFileName, "csy" );
+		string pszBaseName = string(CPLStrdup( CPLGetBasename( pszFileName )));
+		string pszPath = string(CPLStrdup( CPLGetPath( pszFileName )));
+		bool fProjection = ((strlen(pszProjection)>0) && (pszProjection != NULL));
+		if( fProjection && (oSRS.importFromWkt( &pszP ) == OGRERR_NONE) )
+		{
+		    bHaveSRS = TRUE;
+		}
+    else
+        bHaveSRS = FALSE;
+		
+		IlwisDatums   *piwDatum = iwDatums;
+		string pszEllips;
+		string pszDatum;
+		string pszProj;
+		
+/* -------------------------------------------------------------------- */
+/*      Collect datum/ellips information.                                      */
+/* -------------------------------------------------------------------- */
+    if( bHaveSRS )
+    {
+        poGeogSRS = oSRS.CloneGeogCS();
+		}
+
+		string grFileName = CPLResetExtension(pszFileName, "grf" );
+		string csy;
+		if( poGeogSRS )
+    {
+				csy = pszBaseName + ".csy";
+				
+				WriteElement("Ilwis", "Type", csFileName, "CoordSystem");
+        pszDatum = poGeogSRS->GetAttrValue( "GEOGCS|DATUM" );
+
+        /* WKT to ILWIS translation */
+				while ( piwDatum->pszWKTDatum)
+        {
+            if( EQUALN( pszDatum.c_str(), piwDatum->pszWKTDatum, strlen(piwDatum->pszWKTDatum) ) )
+            {
+								WriteElement("CoordSystem", "Datum", csFileName, piwDatum->pszIlwisDatum);
+								break;
+            }
+            piwDatum++;
+        } //end of searchong for matching datum
+				WriteElement("CoordSystem", "Width", csFileName, 28);
+				double a, b, f;
+				pszEllips = poGeogSRS->GetAttrValue( "GEOGCS|DATUM|SPHEROID" );
+				a = poGeogSRS->GetSemiMajor();
+				b = poGeogSRS->GetSemiMinor();
+				f = poGeogSRS->GetInvFlattening();
+				WriteElement("CoordSystem", "Ellipsoid", csFileName, "User Defined");
+				WriteElement("Ellipsoid", "a", csFileName, a);
+				WriteElement("Ellipsoid", "1/f", csFileName, f);
+		}
+		else
+				csy = "unknown.csy";
+
+/* -------------------------------------------------------------------- */
+/*	Determine to write a geo-referencing file for the dataset to create */
+/* -------------------------------------------------------------------- */
+		if( GetGeoTransform( adfGeoTransform ) == CE_None
+        && (adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0
+            || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0
+            || adfGeoTransform[4] != 0.0 || fabs(adfGeoTransform[5]) != 1.0))
+				WriteElement("GeoRef", "CoordSystem", grFileName, csy);
+
+/* -------------------------------------------------------------------- */
+/*  Recognise various projections.                                      */
+/* -------------------------------------------------------------------- */
+    const char * pszProjName = NULL;
+
+    if( bHaveSRS )
+        pszProjName = oSRS.GetAttrValue( "PROJCS|PROJECTION" );
+
+    if( pszProjName == NULL )
+    {
+        if( bHaveSRS && oSRS.IsGeographic() )
+        {
+            WriteElement("CoordSystem", "Type", csFileName, "LatLon");
+        }
+    }
+    else if( oSRS.GetUTMZone( NULL ) != 0 )
+    {
+    		WriteUTM(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_ALBERS_CONIC_EQUAL_AREA) )
+    {
+				WriteAlbersConicEqualArea(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_AZIMUTHAL_EQUIDISTANT) )
+    {
+				WriteAzimuthalEquidistant(csFileName, oSRS);
+    }
+		else if( EQUAL(pszProjName,SRS_PT_CYLINDRICAL_EQUAL_AREA) )
+    {
+				WriteCylindricalEqualArea(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_CASSINI_SOLDNER) )
+    {
+				WriteCassiniSoldner(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_STEREOGRAPHIC) )
+    {
+				WriteStereographic(csFileName, oSRS);		
+		}
+		else if( EQUAL(pszProjName,SRS_PT_EQUIDISTANT_CONIC) )
+    {
+				WriteEquidistantConic(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_TRANSVERSE_MERCATOR) )
+    {
+				WriteTransverseMercator(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_GNOMONIC) )
+    {
+        WriteGnomonic(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,"Lambert_Conformal_Conic") )
+    {
+				WriteLambertConformalConic(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) )
+    {
+				WriteLambertAzimuthalEqualArea(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_MERCATOR_1SP) )
+    {
+				WriteMercator_1SP(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_MILLER_CYLINDRICAL) )
+    {
+				WriteMillerCylindrical(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_MOLLWEIDE) )
+    {
+				WriteMolleweide(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_ORTHOGRAPHIC) )
+    {
+        WriteOrthographic(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_EQUIRECTANGULAR) )
+    {
+        WritePlateRectangle(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_POLYCONIC) )
+    {
+        WritePolyConic(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_ROBINSON) )
+    {
+        WriteRobinson(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_SINUSOIDAL) )
+    {
+        WriteSinusoidal(csFileName, oSRS);
+		}
+		else if( EQUAL(pszProjName,SRS_PT_VANDERGRINTEN) )
+    {
+				WriteVanderGrinten(csFileName, oSRS);  
+		}
+		else if( EQUAL(pszProjName,SRS_PT_GEOSTATIONARY_SATELLITE) )
+    {
+				WriteGeoStatSat(csFileName, oSRS);  
+		}
+		else
+    {
+        // Projection unknown by ILWIS
+				
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    if( poGeogSRS != NULL )
+        delete poGeogSRS;
+
+    return CE_None;
+}
+
diff --git a/Utilities/GDAL/frmts/ilwis/ilwisdataset.cpp b/Utilities/GDAL/frmts/ilwis/ilwisdataset.cpp
new file mode 100644
index 0000000000..8b9629966c
--- /dev/null
+++ b/Utilities/GDAL/frmts/ilwis/ilwisdataset.cpp
@@ -0,0 +1,1907 @@
+/******************************************************************************
+ *
+ * Project:  ILWIS Driver
+ * Purpose:  GDALDataset driver for ILWIS translator for read/write support.
+ * Author:   Lichun Wang, lichun@itc.nl
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, ITC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: ilwisdataset.cpp,v $
+ * Revision 1.15  2005/09/14 13:37:18  dron
+ * Avoid warnings.
+ *
+ * Revision 1.14  2005/08/04 15:26:53  fwarmerdam
+ * added log headers
+ *
+ */
+
+
+#include "ilwisdataset.h"
+#include <float.h>
+
+// IniFile.cpp: implementation of the IniFile class.
+//
+//////////////////////////////////////////////////////////////////////
+bool CompareAsNum::operator() (const string& s1, const string& s2) const
+{
+	long Num1 = atoi(s1.c_str());
+	long Num2 = atoi(s2.c_str());
+	return Num1 < Num2;
+}
+
+string TrimSpaces(const string& input)
+{
+    // find first non space
+    if ( input.empty()) 
+        return string();
+
+    unsigned int iFirstNonSpace = input.find_first_not_of(' ');
+    unsigned int iFindLastSpace = input.find_last_not_of(' ');
+    if (iFirstNonSpace == string::npos || iFindLastSpace == string::npos)
+        return string();
+
+    return input.substr(iFirstNonSpace, iFindLastSpace - iFirstNonSpace + 1);
+}
+
+char line[1024];
+
+string GetLine(FILE* fil)
+{
+	char *p = fgets(line, 1024, fil);
+	if (p == NULL)
+		return string();
+
+	p = line + strlen(line) - 1; // move to last char in buffer
+	while ((p >= line) && isspace(*p))
+		--p;         // isspace is succesful at least once, because of the "\n"
+	*(p + 1) = '\0';   // therefore this will not fail
+
+	return string(line);
+}
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+IniFile::IniFile()
+{
+
+}
+
+IniFile::~IniFile()
+{
+
+}
+
+void IniFile::Open(const string& filenam)
+{
+    filename = filenam;
+
+    Load();
+}
+
+void IniFile::Close()
+{
+    Flush();
+
+    for (Sections::iterator iter = sections.begin(); iter != sections.end(); ++iter)
+    {
+        (*(*iter).second).clear();
+        delete (*iter).second;
+    }
+
+    sections.clear();
+}
+
+void IniFile::SetKeyValue(const string& section, const string& key, const string& value)
+{
+    Sections::iterator iterSect = sections.find(section);
+    if (iterSect == sections.end())
+    {
+        // Add a new section, with one new key/value entry
+        SectionEntries *entries = new SectionEntries;
+        (*entries)[key] = value;
+        sections[section] = entries;
+    }
+    else
+    {
+        // Add one new key/value entry in an existing section
+        SectionEntries *entries = (*iterSect).second;
+        (*entries)[key] = value;
+    }
+}
+string IniFile::GetKeyValue(const string& section, const string& key)
+{
+	Sections::iterator iterSect = sections.find(section);
+	if (iterSect != sections.end())
+	{
+		SectionEntries *entries = (*iterSect).second;
+		SectionEntries::iterator iterEntry = (*entries).find(key);
+		if (iterEntry != (*entries).end())
+			return (*iterEntry).second;
+	}
+
+	return string();
+}
+
+void IniFile::RemoveKeyValue(const string& section, const string& key)
+{
+	Sections::iterator iterSect = sections.find(section);
+	if (iterSect != sections.end())
+	{
+		// The section exists, now erase entry "key"
+		SectionEntries *entries = (*iterSect).second;
+		(*entries).erase(key);
+	}
+}
+
+void IniFile::RemoveSection(const string& section)
+{
+	Sections::iterator iterSect = sections.find(section);
+	if (iterSect != sections.end())
+	{
+		// The section exists, so remove it and all its entries.
+		SectionEntries *entries = (*iterSect).second;
+		(*entries).clear();
+		sections.erase(iterSect);
+	}
+}
+
+void IniFile::Load()
+{
+    enum ParseState { FindSection, FindKey, ReadFindKey, StoreKey, None } state;
+    FILE *filIni = fopen(filename.c_str(), "r");
+    if (filIni == NULL)
+        return;
+
+    string section, key, value;
+    state = FindSection;
+    string s;
+    while (!feof(filIni))
+    {
+        switch (state)
+        {
+          case FindSection:
+            s = GetLine(filIni);
+            if (s.empty())
+                continue;
+
+            if (s[0] == '[')
+            {
+                unsigned int iLast = s.find_first_of(']');
+                if (iLast != string::npos)
+                {
+                    section = s.substr(1, iLast - 1);
+                    state = ReadFindKey;
+                }
+            }
+            else
+                state = FindKey;
+            break;
+          case ReadFindKey:
+            s = GetLine(filIni); // fall through (no break)
+          case FindKey:
+          {
+              unsigned int iEqu = s.find_first_of('=');
+              if (iEqu != string::npos)
+              {
+                  key = s.substr(0, iEqu);
+                  value = s.substr(iEqu + 1);
+                  state = StoreKey;
+              }
+              else
+                  state = ReadFindKey;
+          }
+          break;
+          case StoreKey:
+            SetKeyValue(section, key, value);
+            state = FindSection;
+            break;
+            
+          case None:
+            // Do we need to do anything?  Perhaps this never occurs.
+            break;
+        }
+    }
+
+    fclose(filIni);
+}
+
+void IniFile::Flush()
+{
+	FILE *filIni = fopen(filename.c_str(), "w+");
+	if (filIni == NULL)
+		return;
+
+	Sections::iterator iterSect;
+	for (iterSect = sections.begin(); iterSect != sections.end(); ++iterSect)
+	{
+		// write the section name
+		fprintf(filIni, "[%s]\n", (*iterSect).first.c_str());
+		SectionEntries *entries = (*iterSect).second;
+		SectionEntries::iterator iterEntry;
+		for (iterEntry = (*entries).begin(); iterEntry != (*entries).end(); ++iterEntry)
+		{
+			string key = (*iterEntry).first;
+			fprintf(filIni, "%s=%s\n", TrimSpaces(key).c_str(), (*iterEntry).second.c_str());
+		}
+
+		fprintf(filIni, "\n");
+	}
+
+	fclose(filIni);
+}
+
+// End of the implementation of IniFile class. ///////////////////////
+//////////////////////////////////////////////////////////////////////
+
+static long longConv(double x) {
+    if ((x == rUNDEF) || (x > LONG_MAX) || (x < LONG_MIN))
+      return iUNDEF;
+    else
+      return (long)floor(x + 0.5);
+}
+
+string ReadElement(string section, string entry, string filename)
+{
+		if (section.length() == 0)
+			return string();
+		if (entry.length() == 0)
+			return string();
+		if (filename.length() == 0)
+			return string();
+
+		IniFile MyIniFile;
+		MyIniFile = IniFile();
+		MyIniFile.Open(filename);
+
+		return MyIniFile.GetKeyValue(section, entry);
+}
+
+bool WriteElement(string sSection, string sEntry,
+                             string fn, string sValue)
+{
+  if (0 == fn.length())
+    return false;
+
+	IniFile MyIniFile;
+	MyIniFile = IniFile();
+	MyIniFile.Open(fn);
+
+	MyIniFile.SetKeyValue(sSection, sEntry, sValue);
+	MyIniFile.Close();
+	return true;
+}
+
+bool WriteElement(string sSection, string sEntry,
+                             string fn, int nValue)
+{
+	if (0 == fn.length())
+    return false;
+
+	char strdouble[45];
+	sprintf(strdouble, "%d", nValue);
+	string sValue = string(strdouble);
+  return WriteElement(sSection, sEntry, fn, sValue) != 0;
+}
+
+bool WriteElement(string sSection, string sEntry,
+                             string fn, double dValue)
+{
+	if (0 == fn.length())
+    return false;
+
+	char strdouble[45];
+	sprintf(strdouble, "%.6f", dValue);
+	string sValue = string(strdouble);
+  return WriteElement(sSection, sEntry, fn, sValue) != 0;
+}
+
+static CPLErr GetRowCol(string str,int &Row, int &Col)
+{
+    string delimStr = " ,;";
+    unsigned int iPos = str.find_first_of(delimStr);
+    if (iPos != string::npos)
+    {
+        Row = atoi(str.substr(0, iPos).c_str());
+    }
+    else 
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Read of RowCol failed.");
+        return CE_Failure;
+    }
+    iPos = str.find_last_of(delimStr);
+    if (iPos != string::npos)
+    {
+        Col = atoi(str.substr(iPos+1, str.length()-iPos).c_str());
+    }
+    return CE_None;
+}
+
+//! Converts ILWIS data type to GDAL data type.
+static GDALDataType ILWIS2GDALType(ilwisStoreType stStoreType)
+{
+  GDALDataType eDataType = GDT_Unknown;
+
+  switch (stStoreType){
+	  case stByte: {
+	    eDataType = GDT_Byte;
+      break;
+		} 
+    case stInt:{
+      eDataType = GDT_Int16;
+      break;
+		}
+    case stLong:{
+      eDataType = GDT_Int32;
+      break;
+		}
+    case stFloat:{
+      eDataType = GDT_Float32;
+      break;
+    }
+    case stReal:{
+      eDataType = GDT_Float64;
+      break;
+    }
+    default: {
+      break;
+    }
+  }
+
+  return eDataType;
+}
+
+//Determine store type of ILWIS raster
+static string GDALType2ILWIS(GDALDataType type)
+{
+	string sStoreType;
+	sStoreType = "";
+	switch( type )
+  {
+	  case GDT_Byte:{
+      sStoreType = "Byte";
+      break;
+		}
+    case GDT_Int16:
+    case GDT_UInt16:{
+      sStoreType = "Int";
+      break;
+		}
+    case GDT_Int32:
+    case GDT_UInt32:{
+      sStoreType = "Long";
+      break;
+		}
+    case GDT_Float32:{
+      sStoreType = "Float";
+      break;
+    }
+    case GDT_Float64:{
+      sStoreType = "Real";
+      break;
+		}
+    default:{
+      CPLError( CE_Failure, CPLE_NotSupported,
+                "Data type %s not supported by ILWIS format.\n",
+                GDALGetDataTypeName( type ) );
+      break;
+		}	
+  }
+	return sStoreType;
+}
+
+static CPLErr GetStoreType(string pszFileName, ilwisStoreType &stStoreType)
+{
+    string st = ReadElement("MapStore", "Type", pszFileName.c_str());
+    transform(st.begin(), st.end(), st.begin(), tolower);
+		
+    if( EQUAL(st.c_str(),"byte"))
+    {
+        stStoreType = stByte;
+    }
+    else if( EQUAL(st.c_str(),"int"))
+    {
+        stStoreType = stInt;
+    }
+    else if( EQUAL(st.c_str(),"long"))
+    {
+        stStoreType = stLong;
+    }
+    else if( EQUAL(st.c_str(),"float"))
+    {
+        stStoreType = stFloat;
+    }
+    else if( EQUAL(st.c_str(),"real"))
+    {
+        stStoreType = stReal;
+    }
+    else 
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Unsupported ILWIS store type.");
+        return CE_Failure;
+    }
+    return CE_None;
+}
+
+
+ILWISDataset::ILWISDataset()
+
+{
+		bGeoDirty = FALSE;
+		bNewDataset = FALSE;
+    pszProjection = CPLStrdup("");
+		adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+}
+
+/************************************************************************/
+/*                  ~ILWISDataset()                                     */
+/************************************************************************/
+
+ILWISDataset::~ILWISDataset()
+
+{
+    FlushCache();
+    CPLFree( pszProjection );
+}
+
+/************************************************************************/
+/*                        CollectTransformCoef()                        */
+/*                                                                      */
+/*      Collect the geotransform, support for the GeoRefCorners         */
+/*      georeferencing only; We use the extent of the coordinates       */
+/*      to determine the pixelsize in X and Y direction. Then calculate */
+/*      the transform coefficients from the extent and pixelsize        */
+/************************************************************************/
+
+void ILWISDataset::CollectTransformCoef(string &pszRefName)
+
+{
+    pszRefName = "";
+    string georef;
+    if ( EQUAL(pszFileType.c_str(),"Map") )
+        georef = ReadElement("Map", "GeoRef", pszFileName);
+    else
+        georef = ReadElement("MapList", "GeoRef", pszFileName);
+
+    transform(georef.begin(), georef.end(), georef.begin(), tolower);
+
+    //Capture the geotransform, only if the georef is not 'none', 
+    //otherwise, the default transform should be returned.
+    if( (georef.length() != 0) && !EQUAL(georef.c_str(),"none"))
+    {
+        //Form the geo-referencing name
+        string pszBaseName = string(CPLStrdup( CPLGetBasename(georef.c_str()) ));
+        string pszPath = string(CPLStrdup(CPLGetPath( pszFileName )));
+        pszRefName = string(CPLFormFilename(pszPath.c_str(),
+                                            pszBaseName.c_str(),"grf" ));
+
+        //Check the geo-reference type,support for the GeoRefCorners only 
+        string georeftype = ReadElement("GeoRef", "Type", pszRefName);
+        if (EQUAL(georeftype.c_str(),"GeoRefCorners"))
+        {
+            //Center or top-left corner of the pixel approach? 
+            string IsCorner = ReadElement("GeoRefCorners", "CornersOfCorners", pszRefName);
+
+            //Collect the extent of the coordinates 
+            string sMinX = ReadElement("GeoRefCorners", "MinX", pszRefName);
+            string sMinY = ReadElement("GeoRefCorners", "MinY", pszRefName);
+            string sMaxX = ReadElement("GeoRefCorners", "MaxX", pszRefName);
+            string sMaxY = ReadElement("GeoRefCorners", "MaxY", pszRefName);
+				
+            //Calculate pixel size in X and Y direction from the extent
+            double PixelSizeX = floor((atof(sMaxX.c_str()) - 
+                                       atof(sMinX.c_str())) / nRasterXSize + 0.5);
+            double PixelSizeY = floor((atof(sMaxY.c_str()) - 
+                                       atof(sMinY.c_str())) / nRasterYSize + 0.5);
+				
+            if (EQUAL(IsCorner.c_str(),"Yes"))
+            {
+                adfGeoTransform[0] = atof(sMinX.c_str());
+                adfGeoTransform[3] = atof(sMaxY.c_str());
+            }
+            else
+            {
+                adfGeoTransform[0] = atof(sMinX.c_str()) - PixelSizeX/2.0;
+                adfGeoTransform[3] = atof(sMaxY.c_str()) + PixelSizeY/2.0;
+            }
+
+            adfGeoTransform[1] = PixelSizeX;
+            adfGeoTransform[2] = 0.0;
+            adfGeoTransform[4] = 0.0;
+            adfGeoTransform[5] = -PixelSizeY;
+        }
+
+    }
+
+}
+
+/************************************************************************/
+/*                         WriteGeoReference()                          */
+/*                                                                      */
+/*      Try to write a geo-reference file for the dataset to create     */
+/************************************************************************/
+
+CPLErr ILWISDataset::WriteGeoReference()
+{
+    string grFileName = CPLResetExtension(pszFileName, "grf" );
+    double dLLLat, dLLLong, dURLat, dURLong;
+    string georef;
+		
+    int   nXSize = GetRasterXSize();
+    int   nYSize = GetRasterYSize();
+		
+    if( GetGeoTransform( adfGeoTransform ) == CE_None
+        && (adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0
+            || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0
+            || adfGeoTransform[4] != 0.0 || fabs(adfGeoTransform[5]) != 1.0))
+    {
+        SetGeoTransform( adfGeoTransform );
+        if (adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0)
+        {
+            //check wheather we should write out a georeference file. 
+            //dataset must be north up 
+            dLLLat = (adfGeoTransform[3] 
+                      + GetRasterYSize() * adfGeoTransform[5] );
+            dLLLong = (adfGeoTransform[0] );
+            dURLat  = (adfGeoTransform[3] );
+            dURLong = (adfGeoTransform[0] 
+                       + GetRasterXSize() * adfGeoTransform[1] );
+            WriteElement("Ilwis", "Type", grFileName, "GeoRef");
+            WriteElement("GeoRef", "lines", grFileName, nYSize);
+            WriteElement("GeoRef", "columns", grFileName, nXSize);
+            WriteElement("GeoRef", "Type", grFileName, "GeoRefCorners");
+            WriteElement("GeoRefCorners", "CornersOfCorners", grFileName, "Yes");
+            WriteElement("GeoRefCorners", "MinX", grFileName, dLLLong);
+            WriteElement("GeoRefCorners", "MinY", grFileName, dLLLat);
+            WriteElement("GeoRefCorners", "MaxX", grFileName, dURLong);
+            WriteElement("GeoRefCorners", "MaxY", grFileName, dURLat);
+
+            //Re-write the GeoRef property to raster ODF
+            //Form band file name  
+            string sBaseName = string(CPLStrdup( CPLGetBasename(pszFileName) ));
+            string sPath = string(CPLStrdup(CPLGetPath(pszFileName)));
+            if (nBands == 1) 
+            {
+                WriteElement("Map", "GeoRef", pszFileName, sBaseName + ".grf");
+            }
+            else
+            {
+                for( int iBand = 0; iBand < nBands; iBand++ )
+                {
+                    if (iBand == 0)
+                      WriteElement("MapList", "GeoRef", pszFileName, sBaseName + ".grf");
+                    char pszName[100];
+                    sprintf(pszName, "%s_band_%d", sBaseName.c_str(),iBand + 1 );
+                    string pszODFName = string(CPLFormFilename(sPath.c_str(),pszName,"mpr"));
+                    WriteElement("Map", "GeoRef", pszODFName, sBaseName + ".grf");
+                }
+            }
+        }
+    }
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *ILWISDataset::GetProjectionRef()
+
+{
+   return ( pszProjection );
+}
+
+/************************************************************************/
+/*                           SetProjection()                            */
+/************************************************************************/
+
+CPLErr ILWISDataset::SetProjection( const char * pszNewProjection )
+
+{
+    CPLFree( pszProjection );
+    pszProjection = CPLStrdup( pszNewProjection );
+    bGeoDirty = TRUE;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr ILWISDataset::GetGeoTransform( double * padfTransform )
+
+{
+    memcpy( padfTransform,  adfGeoTransform, sizeof(double) * 6 ); 
+    return( CE_None );
+}
+
+/************************************************************************/
+/*                          SetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr ILWISDataset::SetGeoTransform( double * padfTransform )
+
+{
+    memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 );
+		if (adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0)
+			bGeoDirty = TRUE;
+
+    return CE_None;
+}
+
+bool CheckASCII(unsigned char * buf, int size)
+{
+	for (int i = 0; i < size; ++i)
+		if (!isascii(buf[i]))
+			return false;
+
+	return true;
+}
+/************************************************************************/
+/*                       Open()                                         */
+/************************************************************************/
+
+GDALDataset *ILWISDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Does this look like an ILWIS file                               */
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->fp == NULL)
+        return NULL;
+
+    string sExt = CPLGetExtension( poOpenInfo->pszFilename );
+    if (!EQUAL(sExt.c_str(),"mpr") && !EQUAL(sExt.c_str(),"mpl"))
+        return NULL;
+
+    if (!CheckASCII(poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes))
+        return NULL;
+
+    string ilwistype = ReadElement("Ilwis", "Type", poOpenInfo->pszFilename);
+    if( ilwistype.length() == 0)
+        return NULL;
+		
+    string sFileType;	//map or map list
+    int    iBandCount;
+    string mapsize;
+    string maptype = ReadElement("BaseMap", "Type", poOpenInfo->pszFilename);
+    string sBaseName = string(CPLStrdup( CPLGetBasename(poOpenInfo->pszFilename) ));
+    string sPath = string(CPLStrdup(CPLGetPath( poOpenInfo->pszFilename)));
+							
+    //Verify whether it is a map list or a map
+    if( EQUAL(ilwistype.c_str(),"MapList") )
+    {
+        sFileType = string("MapList");
+        string sMaps = ReadElement("MapList", "Maps", poOpenInfo->pszFilename);
+        iBandCount = atoi(sMaps.c_str());
+        mapsize = ReadElement("MapList", "Size", poOpenInfo->pszFilename);
+        for (int iBand = 0; iBand < iBandCount; ++iBand )
+        {
+            //Form the band file name.
+            char cBandName[45];
+            sprintf( cBandName, "Map%d", iBand);
+            string sBandName = ReadElement("MapList", string(cBandName), poOpenInfo->pszFilename);
+            string pszBandBaseName = string(CPLStrdup( CPLGetBasename(sBandName.c_str()) ));
+            string pszBandPath = string(CPLStrdup(CPLGetPath( sBandName.c_str())));
+            if ( 0 == pszBandPath.length() )
+            { 
+                sBandName = string(CPLFormFilename(sPath.c_str(),
+                                                   pszBandBaseName.c_str(),"mpr" ));
+            }
+            //Verify the file exetension, it must be an ILWIS raw data file
+            //with extension .mp#, otherwise, unsupported 
+            //This drive only supports a map list which stores a set of ILWIS raster maps, 
+            string sMapStoreName = ReadElement("MapStore", "Data", sBandName);
+            string sExt = CPLGetExtension( sMapStoreName.c_str() );
+            if ( !EQUALN( sExt.c_str(), "mp#", 3 ))
+            {
+                CPLError( CE_Failure, CPLE_AppDefined,
+                          "Unsupported ILWIS data file. \n"
+                          "can't treat as raster.\n" );
+                return FALSE;
+            }
+        }
+    }	
+    else if(EQUAL(ilwistype.c_str(),"BaseMap") && EQUAL(maptype.c_str(),"Map"))
+    {
+        sFileType = "Map";
+        iBandCount = 1;
+        mapsize = ReadElement("Map", "Size", poOpenInfo->pszFilename);
+        string sMapType = ReadElement("Map", "Type", poOpenInfo->pszFilename);
+        ilwisStoreType stStoreType;
+        if (  
+            GetStoreType(string(poOpenInfo->pszFilename), stStoreType) != CE_None )
+        {
+            //CPLError( CE_Failure, CPLE_AppDefined,
+            //			"Unsupported ILWIS data file. \n"
+            //			"can't treat as raster.\n" );
+            return FALSE;
+        }
+    }
+    else
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Unsupported ILWIS data file. \n"
+                  "can't treat as raster.\n" );
+        return FALSE;
+    }	
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    ILWISDataset 	*poDS;
+    poDS = new ILWISDataset();
+		
+    poOpenInfo->fp = NULL;
+    
+/* -------------------------------------------------------------------- */
+/*      Capture raster size from ILWIS file (.mpr).                     */
+/* -------------------------------------------------------------------- */
+    int Row = 0, Col = 0;
+    if ( GetRowCol(mapsize, Row, Col) != CE_None)
+        return FALSE;
+    poDS->nRasterXSize = Col;
+    poDS->nRasterYSize = Row;
+    poDS->pszFileName = poOpenInfo->pszFilename;
+    poDS->pszFileType = sFileType;  
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    //poDS->pszFileName = new char[strlen(poOpenInfo->pszFilename) + 1];
+    poDS->nBands = iBandCount;
+    for( int iBand = 0; iBand < poDS->nBands; iBand++ )
+    {
+        poDS->SetBand( iBand+1, new ILWISRasterBand( poDS, iBand+1 ) );
+    }
+		
+/* -------------------------------------------------------------------- */
+/*      Collect the geotransform coefficients                           */
+/* -------------------------------------------------------------------- */
+    string pszGeoRef;
+    poDS->CollectTransformCoef(pszGeoRef);
+
+/* -------------------------------------------------------------------- */
+/*      Translation from ILWIS coordinate system definition             */
+/* -------------------------------------------------------------------- */
+    if( (pszGeoRef.length() != 0) && !EQUAL(pszGeoRef.c_str(),"none"))
+    {
+		
+        //	Fetch coordinate system
+        string csy = ReadElement("GeoRef", "CoordSystem", pszGeoRef);
+        transform(csy.begin(), csy.end(), csy.begin(), tolower);
+
+        string pszProj;
+        if( (csy.length() != 0) && !EQUAL(csy.c_str(),"unknown.csy"))
+        {
+
+            //Form the coordinate system file name
+            if( !(EQUALN( csy.c_str(), "latlon.csy", 10 )) && 
+                !(EQUALN( csy.c_str(), "LatlonWGS84.csy", 15 )))			
+            {
+                string pszBaseName = string(CPLStrdup( CPLGetBasename(csy.c_str()) ));
+                string pszPath = string(CPLStrdup(CPLGetPath( poDS->pszFileName )));
+                csy = string(CPLFormFilename(pszPath.c_str(),
+                                             pszBaseName.c_str(),"csy" ));
+                pszProj = ReadElement("CoordSystem", "Type", csy);
+                if (pszProj.length() == 0 ) //default to projection
+                    pszProj = "Projection";
+            }
+            else
+            {
+                pszProj = "LatLon";
+            }
+					
+            if( (EQUALN( pszProj.c_str(), "LatLon", 6 )) || 
+                (EQUALN( pszProj.c_str(), "Projection", 10 )))			
+                poDS->ReadProjection( csy );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                             FlushCache()                             */
+/************************************************************************/
+
+void ILWISDataset::FlushCache()
+
+{
+    GDALDataset::FlushCache();
+
+    if( bGeoDirty == TRUE )
+		{
+				WriteGeoReference();
+        WriteProjection();
+				bGeoDirty = FALSE;
+		}
+}
+
+/************************************************************************/
+/*                               Create()                               */
+/*                                                                      */
+/*      Create a new ILWIS file.                                         */
+/************************************************************************/
+
+GDALDataset *ILWISDataset::Create(const char* pszFilename,
+                                  int nXSize, int nYSize, 
+                                  int nBands, GDALDataType eType,
+                                  char** papszParmList) 
+{
+    ILWISDataset	*poDS;
+    int 		iBand;
+
+/* -------------------------------------------------------------------- */
+/*      Verify input options.                                           */
+/* -------------------------------------------------------------------- */
+    if( eType != GDT_Byte && eType != GDT_Int16 && eType != GDT_Int32
+        && eType != GDT_Float32 && eType != GDT_Float64 && eType != GDT_UInt16 && eType != GDT_UInt32)
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Attempt to create ILWIS dataset with an illegal\n"
+                  "data type (%s).\n",
+                  GDALGetDataTypeName(eType) );
+
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Translate the data type.                                        */
+/*			Determine store type of ILWIS raster                            */
+/* -------------------------------------------------------------------- */
+    string sDomain= "value.dom";
+    double stepsize = 1;
+		string sStoreType = GDALType2ILWIS(eType);
+		if( EQUAL(sStoreType.c_str(),""))
+			return NULL;
+		else if( EQUAL(sStoreType.c_str(),"Real") || EQUAL(sStoreType.c_str(),"float"))
+			stepsize = 0;
+
+    string pszBaseName = string(CPLStrdup( CPLGetBasename( pszFilename )));
+    string pszPath = string(CPLStrdup( CPLGetPath( pszFilename )));
+		
+/* -------------------------------------------------------------------- */
+/*      Write out object definition file for each band                    */
+/* -------------------------------------------------------------------- */
+    string pszODFName;
+    string pszDataBaseName;
+    string pszFileName;
+
+    char strsize[45];
+    sprintf(strsize, "%d %d", nYSize, nXSize);
+    
+    //Form map/maplist name. 
+    if ( nBands == 1 )
+    {
+        pszODFName = string(CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"mpr"));
+        pszDataBaseName = pszBaseName;
+        pszFileName = CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"mpr");
+    }
+    else
+    {
+        pszFileName = CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"mpl");
+        WriteElement("Ilwis", "Type", string(pszFileName), "MapList");
+        WriteElement("MapList", "GeoRef", string(pszFileName), "none.grf");
+        WriteElement("MapList", "Size", string(pszFileName), string(strsize));
+        WriteElement("MapList", "Maps", string(pszFileName), nBands);
+    }
+
+    for( iBand = 0; iBand < nBands; iBand++ )
+    {
+        if ( nBands > 1 )
+        {
+            char pszBandName[100];
+            sprintf(pszBandName, "%s_band_%d", pszBaseName.c_str(),iBand + 1 );
+            pszODFName = string(pszBandName) + ".mpr";
+            pszDataBaseName = string(pszBandName);
+            sprintf(pszBandName, "Map%d", iBand);	
+            WriteElement("MapList", string(pszBandName), string(pszFileName), pszODFName);
+            pszODFName = CPLFormFilename(pszPath.c_str(),pszDataBaseName.c_str(),"mpr");
+        }
+/* -------------------------------------------------------------------- */
+/*      Write data definition per band (.mpr)                 */
+/* -------------------------------------------------------------------- */
+				
+        WriteElement("Ilwis", "Type", pszODFName, "BaseMap");
+        WriteElement("BaseMap", "Type", pszODFName, "Map");
+        WriteElement("Map", "Type", pszODFName, "MapStore");
+				
+        double adfMinMax[2];
+        adfMinMax[0] = -9999999.9;
+        adfMinMax[1] = 9999999.9;
+        
+        WriteElement("BaseMap", "Domain", pszODFName, sDomain);
+        string pszDataName = pszDataBaseName + ".mp#";
+        WriteElement("MapStore", "Data", pszODFName, pszDataName);
+        WriteElement("MapStore", "Structure", pszODFName, "Line");
+        WriteElement("MapStore", "Type", pszODFName, sStoreType);
+
+        char strdouble[45];
+        sprintf(strdouble, "%.3f:%.3f:%3f:offset=0", adfMinMax[0], adfMinMax[1],stepsize);
+        string range = string(strdouble);
+        WriteElement("BaseMap", "Range", pszODFName, range);
+        WriteElement("Map", "GeoRef", pszODFName, "none.grf");
+        WriteElement("Map", "Size", pszODFName, string(strsize));
+							
+/* -------------------------------------------------------------------- */
+/*      Try to create the data file.                                    */
+/* -------------------------------------------------------------------- */
+        pszDataName = CPLResetExtension(pszODFName.c_str(), "mp#" );
+//				FILE  *fp = fopen( pszDataName.c_str(), "wb" );
+        FILE  *fp = VSIFOpenL( pszDataName.c_str(), "wb" );
+
+        if( fp == NULL )
+        {
+            CPLError( CE_Failure, CPLE_OpenFailed,
+                      "Unable to create file %s.\n", pszDataName.c_str() );
+            return NULL;
+        }
+        VSIFCloseL( fp );
+    }
+    poDS = new ILWISDataset();
+    poDS->nRasterXSize = nXSize;
+    poDS->nRasterYSize = nYSize;
+    poDS->nBands = nBands;
+    poDS->eAccess = GA_Update;
+		poDS->bNewDataset = TRUE;
+    poDS->SetDescription(pszFilename);
+		poDS->pszProjection = CPLStrdup("");
+    poDS->pszFileName = pszFileName.c_str();
+		poDS->pszIlwFileName = string(pszFileName);
+		if ( nBands == 1 )
+        poDS->pszFileType = "Map";
+    else
+        poDS->pszFileType = "MapList";
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+
+    for( iBand = 1; iBand <= poDS->nBands; iBand++ )
+    {
+        poDS->SetBand( iBand, new ILWISRasterBand( poDS, iBand ) );
+    }
+
+		return poDS;
+    //return (GDALDataset *) GDALOpen( pszFilename, GA_Update );
+}
+
+/************************************************************************/
+/*                             CreateCopy()                             */
+/************************************************************************/
+
+GDALDataset *
+ILWISDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
+                        int bStrict, char ** papszOptions,
+                        GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    
+    ILWISDataset	*poDS;
+    GDALDataType eType = GDT_Byte;
+    int iBand;
+    (void) bStrict;
+
+		
+    int   nXSize = poSrcDS->GetRasterXSize();
+    int   nYSize = poSrcDS->GetRasterYSize();
+    int   nBands = poSrcDS->GetRasterCount();
+
+    if( !pfnProgress( 0.0, NULL, pProgressData ) )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Create the basic dataset.                                       */
+/* -------------------------------------------------------------------- */
+    for( iBand = 0; iBand < nBands; iBand++ )
+    {
+        GDALRasterBand *poBand = poSrcDS->GetRasterBand( iBand+1 );
+        eType = GDALDataTypeUnion( eType, poBand->GetRasterDataType() );
+    }
+
+    poDS = (ILWISDataset *) Create( pszFilename,
+                                    poSrcDS->GetRasterXSize(),
+                                    poSrcDS->GetRasterYSize(),
+                                    nBands,
+                                    eType, papszOptions );
+
+    if( poDS == NULL )
+        return NULL;
+    string pszBaseName = string(CPLStrdup( CPLGetBasename( pszFilename )));
+    string pszPath = string(CPLStrdup( CPLGetPath( pszFilename )));
+				
+/* -------------------------------------------------------------------- */
+/*  Copy and geo-transform and projection information.                                    */
+/* -------------------------------------------------------------------- */
+    double adfGeoTransform[6];
+    string georef;
+    const char  *pszProj;
+
+    if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None
+        && (adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0
+            || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0
+            || adfGeoTransform[4] != 0.0 || fabs(adfGeoTransform[5]) != 1.0))
+    {
+        poDS->SetGeoTransform( adfGeoTransform );
+        if (adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0)
+        {
+            //check wheather we should create georeference file. 
+            //source dataset must be north up 
+            georef = pszBaseName + ".grf";
+        }
+        else
+        {
+            georef = "none.grf";
+        }
+    }
+
+    pszProj = poSrcDS->GetProjectionRef();
+    if( pszProj != NULL && strlen(pszProj) > 0 )
+        poDS->SetProjection( pszProj );
+
+/* -------------------------------------------------------------------- */
+/*      Create the output raster files for each band                    */
+/* -------------------------------------------------------------------- */
+		
+    for( iBand = 0; iBand < nBands; iBand++ )
+    {
+        FILE *fpData;
+        GByte *pData;
+        
+        GDALRasterBand *poBand = poSrcDS->GetRasterBand( iBand+1 );
+        ILWISRasterBand *desBand = (ILWISRasterBand *) poDS->GetRasterBand( iBand+1 );
+        
+/* -------------------------------------------------------------------- */
+/*      Translate the data type.                                        */
+/* -------------------------------------------------------------------- */
+        double stepsize = 1;
+        double dNoDataValue;
+        int pbSuccess;
+        dNoDataValue = poBand->GetNoDataValue(&pbSuccess); 
+        int nLineSize =  nXSize * GDALGetDataTypeSize(eType) / 8;
+        pData = (GByte *) CPLMalloc( nLineSize );
+        				
+        //Determine store type of ILWIS raster
+        string sStoreType = GDALType2ILWIS( eType );
+				if( EQUAL(sStoreType.c_str(),""))
+  		  	return NULL;
+	    	else if( EQUAL(sStoreType.c_str(),"Real") || EQUAL(sStoreType.c_str(),"float"))
+			    stepsize = 0;
+
+        //Form the image file name, create the object definition file. 
+        string pszODFName;
+        string pszDataBaseName;
+        if (nBands == 1) 
+        {
+            pszODFName = string(CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"mpr"));
+            pszDataBaseName = pszBaseName;
+        }
+        else
+        {
+            char pszName[100];
+            sprintf(pszName, "%s_band_%d", pszBaseName.c_str(),iBand + 1 );
+            pszODFName = string(CPLFormFilename(pszPath.c_str(),pszName,"mpr"));
+            pszDataBaseName = string(pszName);
+        }
+/* -------------------------------------------------------------------- */
+/*      Write data definition file for each band (.mpr)                 */
+/* -------------------------------------------------------------------- */
+				
+        double adfMinMax[2];
+        int    bGotMin, bGotMax;
+
+        adfMinMax[0] = poBand->GetMinimum( &bGotMin );
+        adfMinMax[1] = poBand->GetMaximum( &bGotMax );
+        if( ! (bGotMin && bGotMax) )
+            GDALComputeRasterMinMax((GDALRasterBandH)poBand, FALSE, adfMinMax);
+				if ((!CPLIsNan(adfMinMax[0])) && CPLIsFinite(adfMinMax[0]) && (!CPLIsNan(adfMinMax[1])) && CPLIsFinite(adfMinMax[1]))
+				{
+					// only write a range if we got a correct one from the source dataset (otherwise ILWIS can't show the map properly)
+					char strdouble[45];
+					sprintf(strdouble, "%.3f:%.3f:%3f:offset=0", adfMinMax[0], adfMinMax[1],stepsize);
+					string range = string(strdouble);
+					WriteElement("BaseMap", "Range", pszODFName, range);
+				}
+        WriteElement("Map", "GeoRef", pszODFName, georef);
+				
+/* -------------------------------------------------------------------- */
+/*      Loop over image, copy the image data.                           */
+/* -------------------------------------------------------------------- */
+        CPLErr      eErr = CE_None;
+			
+        //For file name for raw data, and create binary files. 
+        string pszDataFileName = CPLResetExtension(pszODFName.c_str(), "mp#" );
+				
+        fpData = desBand->fpRaw;
+        if( fpData == NULL )
+        {
+            CPLError( CE_Failure, CPLE_OpenFailed,
+                      "Attempt to create file `%s' failed.\n",
+                      pszFilename );
+            return NULL;
+        }
+
+        for( int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ )
+        {
+            eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, 
+                                     pData, nXSize, 1, eType, 
+                                     0, 0 );
+
+            if( eErr == CE_None )
+            {
+                //Translater the NoDataValue from each band to ILWIS
+                for (int iCol = 0; iCol < nXSize; iCol++ )
+                {
+                    if( EQUAL(sStoreType.c_str(),"Byte"))
+                    {
+                        if ( pbSuccess && ((GByte * )pData)[iCol] == dNoDataValue )
+                            (( GByte * )pData)[iCol] = 0;
+                    }
+                    else if( EQUAL(sStoreType.c_str(),"Int"))	
+                    {
+                        if ( pbSuccess && ((GInt16 * )pData)[iCol] == dNoDataValue )
+                            (( GInt16 * )pData)[iCol] = shUNDEF;
+                    }
+                    else if( EQUAL(sStoreType.c_str(),"Long"))	
+                    {
+                        if ( pbSuccess && ((GInt32 * )pData)[iCol] == dNoDataValue )
+                            (( GInt32 * )pData)[iCol] = iUNDEF;
+                    }
+                    else if( EQUAL(sStoreType.c_str(),"float"))	
+                    {
+                        float fNoDataValue = (float)dNoDataValue; // needed for comparing for NoDataValue
+                        if (( pbSuccess && ((float * )pData)[iCol] == fNoDataValue ) || (CPLIsNan((( float * )pData)[iCol])))
+                            (( float * )pData)[iCol] = flUNDEF;
+                    }
+                    else if( EQUAL(sStoreType.c_str(),"Real"))	
+                    {
+                        if (( pbSuccess && ((double * )pData)[iCol] == dNoDataValue ) || (CPLIsNan((( double * )pData)[iCol])))
+                                (( double * )pData)[iCol] = rUNDEF;
+                    }
+                }
+                int iSize = VSIFWrite( pData, 1, nLineSize, desBand->fpRaw );
+                if ( iSize < 1 )
+                {
+                    CPLFree( pData );
+                    //CPLFree( pData32 );
+                    CPLError( CE_Failure, CPLE_FileIO, 
+                              "Write of file failed with fwrite error.");
+                    return NULL;
+                }
+            }
+            if( !pfnProgress(iLine / (nYSize * nBands), NULL, pProgressData ) )
+                return NULL;
+        }
+        CPLFree( pData );
+		}
+
+    poDS->FlushCache();
+
+    if( !pfnProgress( 1.0, NULL, pProgressData ) )
+    {
+        CPLError( CE_Failure, CPLE_UserInterrupt,
+                  "User terminated" );
+        delete poDS;
+
+        GDALDriver *poILWISDriver =
+            (GDALDriver *) GDALGetDriverByName( "ILWIS" );
+        poILWISDriver->Delete( pszFilename );
+        return NULL;
+    }
+
+    poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                       ILWISRasterBand()                              */
+/************************************************************************/
+
+ILWISRasterBand::ILWISRasterBand( ILWISDataset *poDS, int nBand )
+
+{
+    string sBandName;
+    if ( EQUAL(poDS->pszFileType.c_str(),"Map"))  		
+        sBandName = string(poDS->pszFileName);
+    else //map list
+    {
+        //Form the band name
+        char cBandName[45];
+        sprintf( cBandName, "Map%d", nBand-1);
+        sBandName = ReadElement("MapList", string(cBandName), string(poDS->pszFileName));
+        string sInputPath = string(CPLStrdup(CPLGetPath( poDS->pszFileName)));	
+        string sBandPath = string(CPLStrdup(CPLGetPath( sBandName.c_str())));
+        string sBandBaseName = string(CPLStrdup(CPLGetBasename( sBandName.c_str())));
+        if ( 0==sBandPath.length() )
+            sBandName = string(CPLFormFilename(sInputPath.c_str(),sBandBaseName.c_str(),"mpr" ));		
+        else
+            sBandName = string(CPLFormFilename(sBandPath.c_str(),sBandBaseName.c_str(),"mpr" ));		
+    }
+
+		if (poDS->bNewDataset)  //for Create() function
+		{
+      GetStoreType(sBandName, psInfo.stStoreType);
+			eDataType = ILWIS2GDALType(psInfo.stStoreType);
+		}					
+		else
+			GetILWISInfo(sBandName);
+    this->poDS = poDS;
+    this->nBand = nBand;
+    nBlockXSize = poDS->GetRasterXSize();
+		nBlockYSize = 1;
+    switch (psInfo.stStoreType)
+    {
+      case stByte: 
+        nSizePerPixel = GDALGetDataTypeSize(GDT_Byte) / 8;
+        break;
+      case stInt:
+        nSizePerPixel = GDALGetDataTypeSize(GDT_Int16) / 8;
+        break;
+      case stLong:
+        nSizePerPixel = GDALGetDataTypeSize(GDT_Int32) / 8;
+        break;
+      case stFloat:
+        nSizePerPixel = GDALGetDataTypeSize(GDT_Float32) / 8;
+        break;
+      case stReal:
+        nSizePerPixel = GDALGetDataTypeSize(GDT_Float64) / 8;
+        break;
+    }
+    ILWISOpen(sBandName);
+}
+
+/************************************************************************/
+/*                             ILWISOpen()                             */
+/************************************************************************/
+void ILWISRasterBand::ILWISOpen(  string pszFileName)
+{
+    string pszDataFile;
+    pszDataFile = string(CPLResetExtension( pszFileName.c_str(), "mp#" ));
+    //both for reading and writing, the file must exist
+
+#ifdef WIN32
+    if (_access(pszDataFile.c_str(), 2) == 0)
+#else
+    if (access(pszDataFile.c_str(), 2) == 0)
+#endif
+        fpRaw = VSIFOpen( pszDataFile.c_str(), "rb+");
+    else
+        fpRaw = VSIFOpen( pszDataFile.c_str(), "rb");
+}
+
+/************************************************************************/
+/*                       GetILWISInfo()                                 */
+/************************************************************************/
+CPLErr ILWISRasterBand::GetILWISInfo(string pszFileName)
+{
+    string domName = ReadElement("BaseMap", "Domain", pszFileName.c_str());
+    string pszBaseName = string(CPLStrdup( CPLGetBasename( domName.c_str()) ));
+    transform(pszBaseName.begin(), pszBaseName.end(), pszBaseName.begin(), tolower);
+    string pszPath = string(CPLStrdup( CPLGetPath( pszFileName.c_str()) ));
+		
+    if (GetStoreType(pszFileName, psInfo.stStoreType) != CE_None)
+    {
+        return CE_Failure;
+    }
+    psInfo.bValue = false;
+    psInfo.stDomain = "";
+
+    if( EQUAL(pszBaseName.c_str(),"value") 
+        || EQUAL(pszBaseName.c_str(),"count") 
+        || EQUAL(pszBaseName.c_str(),"distance") 
+        || EQUAL(pszBaseName.c_str(),"min1to1") 
+        || EQUAL(pszBaseName.c_str(),"noaa") 
+        || EQUAL(pszBaseName.c_str(),"perc") 
+        || EQUAL(pszBaseName.c_str(),"radar") )
+    {
+        if (psInfo.stStoreType == stFloat)
+          eDataType = GDT_Float32;
+        else
+          eDataType = GDT_Float64;
+        psInfo.bValue = true;
+    }
+    else if( EQUAL(pszBaseName.c_str(),"bool") 
+             || EQUAL(pszBaseName.c_str(),"byte") 
+             || EQUAL(pszBaseName.c_str(),"image") 
+             || EQUAL(pszBaseName.c_str(),"colorcmp") 
+             || EQUAL(pszBaseName.c_str(),"flowdirection") 
+             || EQUAL(pszBaseName.c_str(),"yesno") )
+    {
+        eDataType = GDT_Byte;
+        if( EQUAL(pszBaseName.c_str(),"image") 
+            || EQUAL(pszBaseName.c_str(),"colorcmp"))
+            psInfo.stDomain = pszBaseName;
+    }
+    else if( EQUAL(pszBaseName.c_str(),"color") 
+             || EQUAL(pszBaseName.c_str(),"none" )
+             || EQUAL(pszBaseName.c_str(),"coordbuf") 
+             || EQUAL(pszBaseName.c_str(),"binary") 
+             || EQUAL(pszBaseName.c_str(),"string") )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Unsupported ILWIS domain type.");
+        return CE_Failure;		
+    }
+    else
+    {
+        string pszDomainName;
+        pszDomainName = string(CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"dom" ));
+				
+        string domType = ReadElement("Domain", "Type", pszDomainName.c_str());
+        transform(domType.begin(), domType.end(), domType.begin(), tolower);
+        if EQUAL(domType.c_str(),"domainvalue")  
+        {
+            if (psInfo.stStoreType == stFloat)
+              eDataType = GDT_Float32;
+            else
+              eDataType = GDT_Float64;
+            psInfo.bValue = true; 
+        }
+        else if((!EQUAL(domType.c_str(),"domainbit")) 
+                && (!EQUAL(domType.c_str(),"domainstring")) 
+                && (!EQUAL(domType.c_str(),"domaincolor"))
+                && (!EQUAL(domType.c_str(),"domainbinary")) 
+                && (!EQUAL(domType.c_str(),"domaincoordBuf")) 
+                && (!EQUAL(domType.c_str(),"domaincoord")))
+        {
+            eDataType = ILWIS2GDALType(psInfo.stStoreType);   
+        }
+        else
+        {
+            CPLError( CE_Failure, CPLE_AppDefined, 
+                      "Unsupported ILWIS domain type.");
+            return CE_Failure;		
+        }
+    }
+		
+    if (psInfo.bValue)
+    {
+        string rangeString = ReadElement("BaseMap", "Range", pszFileName.c_str());
+        psInfo.vr = ValueRange(rangeString);
+    }
+    return CE_None;
+}
+
+/** This driver defines a Block to be the entire raster; The method reads
+    each line as a block. it reads the data into pImage.  
+
+    @param nBlockXOff This must be zero for this driver
+    @param pImage Dump the data here
+
+    @return A CPLErr code. This implementation returns a CE_Failure if the
+    block offsets are non-zero, If successful, returns CE_None. */
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+CPLErr ILWISRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+
+{
+	  char * pBuffer;
+		
+    // If the x block offsets is non-zero, something is wrong.
+    CPLAssert( nBlockXOff == 0 );
+		
+    int nBlockSize =  nBlockXSize * nBlockYSize * nSizePerPixel;
+		if( fpRaw == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Failed to open ILWIS data file.");  
+        return( CE_Failure );
+    }
+    
+
+
+/* -------------------------------------------------------------------- */
+/*	Handle the case of a strip in a writable file that doesn't	*/
+/*	exist yet, but that we want to read.  Just set to zeros and	*/
+/*	return.								*/
+/* -------------------------------------------------------------------- */
+		ILWISDataset* poIDS = (ILWISDataset*) poDS;
+    if( poIDS->bNewDataset && (poIDS->eAccess == GA_Update))
+    {
+      FillWithNoData(pImage);
+      return CE_None;
+    }
+
+		VSIFSeek( fpRaw, nBlockSize*nBlockYOff, SEEK_SET );
+    pBuffer = (char *)CPLMalloc(nBlockSize);
+    if (VSIFRead( pBuffer, 1, nBlockSize, fpRaw ) < 1)
+    {
+        CPLFree( pBuffer );
+        CPLError( CE_Failure, CPLE_FileIO, 
+                  "Read of file failed with fread error.");
+        return CE_Failure;
+    }
+
+    switch (psInfo.stStoreType)
+    {
+        int i;
+      case stByte:
+        for( i = 0; i < nBlockXSize; i++ )
+        {
+            if (psInfo.bValue)
+                ((double *) pImage)[i] = psInfo.vr.rValue( (GByte)pBuffer[i]);
+            else
+                ((GByte *)pImage)[i] = (GByte)pBuffer[i];
+
+        }
+        break;
+      case stInt:
+        for( i = 0; i < nBlockXSize; i++ )
+        {
+            if (psInfo.bValue)
+                ((double *) pImage)[i] = psInfo.vr.rValue( ((GInt16 *) pBuffer)[i]);
+            else
+                ((GInt16 *) pImage)[i] = ((GInt16 *) pBuffer)[i];
+        }
+        break;
+      case stLong:
+        for( i = 0; i < nBlockXSize; i++ )
+            if (psInfo.bValue)
+                ((double *) pImage)[i] = psInfo.vr.rValue( ((GInt32 *) pBuffer)[i]);
+            else
+                ((GInt32 *) pImage)[i] = ((GInt32 *) pBuffer)[i];
+        break;
+      case stFloat:
+        for( i = 0; i < nBlockXSize; i++ )
+            ((float *) pImage)[i] = ((float *) pBuffer)[i];
+        break;  
+      case stReal:
+        for( i = 0; i < nBlockXSize; i++ )
+            ((double *) pImage)[i] = ((double *) pBuffer)[i];
+        break;
+    }
+    return CE_None;
+}
+
+void ILWISRasterBand::FillWithNoData(void * pImage)
+{
+    if (psInfo.stStoreType == stByte)
+      memset(pImage, 0, nBlockXSize * nBlockYSize);
+    else
+    {
+      switch (psInfo.stStoreType)
+      {
+        case stInt:
+          ((GInt16*)pImage)[0] = shUNDEF;
+          break;
+        case stLong:
+          ((GInt32*)pImage)[0] = iUNDEF;
+          break;
+        case stFloat:
+          ((float*)pImage)[0] = flUNDEF;
+          break;
+        case stReal:
+          ((double*)pImage)[0] = rUNDEF;
+          break;
+        default: // should there be handling for stByte? 
+          break;
+      }
+      int iItemSize = GDALGetDataTypeSize(eDataType) / 8;
+      for (int i = 1; i < nBlockXSize * nBlockYSize; ++i)
+        memcpy( ((char*)pImage) + iItemSize * i, (char*)pImage + iItemSize * (i - 1), iItemSize);
+    }
+}
+
+/************************************************************************/
+/*                            IWriteBlock()                             */
+/*                                                                      */
+/************************************************************************/
+
+CPLErr ILWISRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
+				   void* pImage) {
+
+    ILWISDataset* dataset = (ILWISDataset*) poDS;
+
+    CPLAssert( dataset != NULL
+               && nBlockXOff == 0
+               && nBlockYOff >= 0
+               && pImage != NULL );
+
+    CPLErr eErr = CE_None;
+    int nXSize = dataset->GetRasterXSize();
+    int nBlockSize = nBlockXSize * nBlockYSize * nSizePerPixel;
+    void *pData;
+    pData = CPLMalloc(nBlockSize);
+    
+		VSIFSeek( fpRaw, nBlockSize * nBlockYOff, SEEK_SET );
+
+    bool fDataExists = (VSIFRead( pData, 1, nBlockSize, fpRaw ) >= 1);
+
+    if( eErr == CE_None )
+    {
+        //Translater the NoDataValue per band to ILWIS
+        for (int iCol = 0; iCol < nXSize; iCol++ )
+        {
+            switch (psInfo.stStoreType)
+            {
+              case stByte:
+                if (fDataExists)
+                {
+                  if ((( GByte * )pData)[iCol] == 0)
+                  {
+                    (( GByte * )pData)[iCol] = ((GByte* )pImage)[iCol];
+                  } // else do not overwrite the existing data in pData
+                }
+                else
+                {
+                  (( GByte * )pData)[iCol] = ((GByte * )pImage)[iCol];
+                }
+                break;
+              case stInt:
+                if (fDataExists)
+                {
+                  if ((( GInt16 * )pData)[iCol] == shUNDEF)
+                  {
+                    (( GInt16 * )pData)[iCol] = ((GInt16* )pImage)[iCol];
+                  } // else do not overwrite the existing data in pData
+                }
+                else
+                {
+                  (( GInt16 * )pData)[iCol] = ((GInt16* )pImage)[iCol];
+                }
+                break;
+              case stLong:
+                if (fDataExists)
+                {
+                  if ((( GInt32 * )pData)[iCol] == shUNDEF)
+                  {
+                    (( GInt32 * )pData)[iCol] = ((GInt32* )pImage)[iCol];
+                  } // else do not overwrite the existing data in pData
+                }
+                else
+                {
+                  (( GInt32 * )pData)[iCol] = ((GInt32* )pImage)[iCol];
+                }
+                break;
+              case stFloat:
+                if (fDataExists)
+                {
+                  if ((( float * )pData)[iCol] == flUNDEF)
+                  {
+                      (( float * )pData)[iCol] = ((float* )pImage)[iCol];
+                  }
+                    // else do not overwrite the existing data in pData
+                }
+                else
+                {  
+                      (( float * )pData)[iCol] = ((float* )pImage)[iCol];
+                }
+                break;
+              case stReal:
+                if (fDataExists)
+                {
+                  if ((( double * )pData)[iCol] == rUNDEF)
+                  {
+                      (( double * )pData)[iCol] = ((double* )pImage)[iCol];
+                  }
+                    // else do not overwrite the existing data in pData
+                }
+                else
+                {  
+                      (( double * )pData)[iCol] = ((double* )pImage)[iCol];
+                }
+                break;
+            }
+        }
+
+				VSIFSeek( fpRaw, nBlockSize * nBlockYOff, SEEK_SET );
+
+        if (VSIFWrite( pData, 1, nBlockSize, fpRaw ) < 1)
+        {
+            CPLFree( pData );
+            CPLError( CE_Failure, CPLE_FileIO, 
+                      "Write of file failed with fwrite error.");
+            return CE_Failure;
+        }
+    }
+    CPLFree( pData );
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           GetNoDataValue()                           */
+/************************************************************************/
+double ILWISRasterBand::GetNoDataValue( int *pbSuccess )
+
+{
+    if( pbSuccess != NULL )
+        *pbSuccess = TRUE;
+
+    if( eDataType == GDT_Float64 )
+        return rUNDEF;
+    else if( eDataType == GDT_Int32)
+        return iUNDEF;
+    else if( eDataType == GDT_Int16)
+        return shUNDEF;
+    else if( eDataType == GDT_Float32)
+        return flUNDEF;
+    else if( EQUAL(psInfo.stDomain.c_str(),"image") 
+             || EQUAL(psInfo.stDomain.c_str(),"colorcmp")) 
+    {
+        *pbSuccess = false;
+        return 0;
+    }
+    else
+        return 0;
+}
+
+/************************************************************************/
+/*                      ValueRange()                                    */
+/************************************************************************/
+double Max(double a, double b) 
+{ return (a>=b && a!=rUNDEF) ? a : b; }
+
+static double doubleConv(const char* s)
+{
+    if (s == 0) return rUNDEF;
+    char *endptr;
+    char *begin = const_cast<char*>(s);
+
+    // skip leading spaces; strtol will return 0 on a string with only spaces
+    // which is not what we want
+    while (isspace(*begin)) ++begin;
+
+    if (strlen(begin) == 0) return rUNDEF;
+    errno = 0;
+    double r = strtod(begin, &endptr);
+    if ((0 == *endptr) && (errno==0))
+        return r;
+    while (*endptr != 0) { // check trailing spaces
+        if (*endptr != ' ')
+            return rUNDEF;
+        endptr++;
+    }
+    return r;
+}
+
+ValueRange::ValueRange(string sRng)
+{
+    char* sRange = new char[sRng.length() + 1];
+    for (unsigned int i = 0; i < sRng.length(); ++i)
+        sRange[i] = sRng[i];
+    sRange[sRng.length()] = 0;
+	
+    char *p1 = strchr(sRange, ':');
+    if (0 == p1)
+        return;
+
+    char *p3 = strstr(sRange, ",offset=");
+    if (0 == p3)
+        p3 = strstr(sRange, ":offset=");
+    _r0 = rUNDEF;
+    if (0 != p3) {
+        _r0 = doubleConv(p3+8);
+        *p3 = 0;
+    }
+    char *p2 = strrchr(sRange, ':');
+    _rStep = 1;
+    if (p1 != p2) { // step
+        _rStep = doubleConv(p2+1);
+        *p2 = 0;
+    }
+
+    p2 = strchr(sRange, ':');
+    if (p2 != 0) {
+        *p2 = 0;
+        _rLo = atof(sRange);
+        _rHi = atof(p2+1);
+    }
+    else {
+        _rLo = atof(sRange);
+        _rHi = _rLo;
+    }  
+    init(_r0);
+	 
+    delete [] sRange;
+}
+
+ValueRange::ValueRange(double min, double max)	// step = 1
+{
+    _rLo = min;
+    _rHi = max;
+    _rStep = 1;
+    init();
+}
+
+ValueRange::ValueRange(double min, double max, double step)	
+{
+    _rLo = min;
+    _rHi = max;
+    _rStep = step;
+    init();
+}
+
+static ilwisStoreType stNeeded(unsigned long iNr)
+{
+    if (iNr <= 256)
+        return stByte;
+    if (iNr <= SHRT_MAX)
+        return stInt;
+    return stLong;
+}
+
+void ValueRange::init()
+{
+    init(rUNDEF);
+}
+
+void ValueRange::init(double rRaw0)
+{
+    try {
+        _iDec = 0;
+        if (_rStep < 0)
+            _rStep = 0;
+        double r = _rStep;
+        if (r <= 1e-20)
+            _iDec = 3;
+        else while (r - floor(r) > 1e-20) {
+            r *= 10;
+            _iDec++;
+            if (_iDec > 10)
+                break;
+        }
+
+        short iBeforeDec = 1;
+        double rMax = MAX(fabs(get_rLo()), fabs(get_rHi()));
+        if (rMax != 0)
+            iBeforeDec = (short)floor(log10(rMax)) + 1;
+        if (get_rLo() < 0)
+            iBeforeDec++;
+        _iWidth = iBeforeDec + _iDec;
+        if (_iDec > 0)
+            _iWidth++;
+        if (_iWidth > 12)
+            _iWidth = 12;
+        if (_rStep < 1e-06)
+        {
+            st = stReal;
+            _rStep = 0;
+        }			
+        else {
+            r = get_rHi() - get_rLo();
+            if (r <= ULONG_MAX) {
+                r /= _rStep;
+                r += 1;
+            }
+            r += 1; 
+            if (r > LONG_MAX)
+                st = stReal;
+            else {
+                st = stNeeded((unsigned long)floor(r+0.5));
+                if (st < stByte)
+                    st = stByte;
+            }
+        }
+        if (rUNDEF != rRaw0)
+            _r0 = rRaw0;
+        else {
+            _r0 = 0;
+            if (st <= stByte)
+                _r0 = -1;
+        }
+        if (st > stInt)
+            iRawUndef = iUNDEF;
+        else if (st == stInt)
+            iRawUndef = shUNDEF;
+        else
+            iRawUndef = 0;
+    }
+    catch (exception*) {
+        st = stReal;
+        _r0 = 0;
+        _rStep = 0.0001;
+        _rHi = 1e300;
+        _rLo = -1e300;
+        iRawUndef = iUNDEF;
+    }
+}
+
+string ValueRange::ToString()
+{
+    char buffer[200];
+    if (fabs(get_rLo()) > 1.0e20 || fabs(get_rHi()) > 1.0e20)
+        sprintf(buffer, "%g:%g:%f:offset=%g", get_rLo(), get_rHi(), get_rStep(), get_rRaw0());
+    else if (get_iDec() >= 0)
+        sprintf(buffer, "%.*f:%.*f:%.*f:offset=%.0f", get_iDec(), get_rLo(), get_iDec(), get_rHi(), get_iDec(), get_rStep(), get_rRaw0());
+    else
+        sprintf(buffer, "%f:%f:%f:offset=%.0f", get_rLo(), get_rHi(), get_rStep(), get_rRaw0());
+    return string(buffer);
+}
+
+double ValueRange::rValue(int iRaw)
+{
+    if (iRaw == iUNDEF || iRaw == iRawUndef)
+        return rUNDEF;
+    double rVal = iRaw + _r0;
+    rVal *= _rStep;
+    if (get_rLo() == get_rHi())
+        return rVal;
+    double rEpsilon = _rStep == 0.0 ? 1e-6 : _rStep / 3.0; // avoid any rounding problems with an epsilon directly based on the
+    // the stepsize
+    if ((rVal - get_rLo() < -rEpsilon) || (rVal - get_rHi() > rEpsilon))
+        return rUNDEF;
+    return rVal;
+}
+
+int ValueRange::iRaw(double rValue)
+{
+    if (rValue == rUNDEF) // || !fContains(rValue))
+        return iUNDEF;
+    double rEpsilon = _rStep == 0.0 ? 1e-6 : _rStep / 3.0;	
+    if (rValue - get_rLo() < -rEpsilon) // take a little rounding tolerance
+        return iUNDEF;
+    else if (rValue - get_rHi() > rEpsilon) // take a little rounding tolerance
+        return iUNDEF;
+    rValue /= _rStep;
+    double rVal = floor(rValue+0.5);
+    rVal -= _r0;
+    long iVal;
+    iVal = longConv(rVal);
+    return iVal;
+}
+
+
+/************************************************************************/
+/*                    GDALRegister_ILWIS()                              */
+/************************************************************************/
+
+void GDALRegister_ILWIS()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "ILWIS" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "ILWIS" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "ILWIS Raster Map" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "mpr/mpl" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte Int16 Int32 Float64" );
+
+        poDriver->pfnOpen = ILWISDataset::Open;
+        poDriver->pfnCreate = ILWISDataset::Create;
+        poDriver->pfnCreateCopy = ILWISDataset::CreateCopy;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
diff --git a/Utilities/GDAL/frmts/ilwis/ilwisdataset.h b/Utilities/GDAL/frmts/ilwis/ilwisdataset.h
new file mode 100644
index 0000000000..6b65ee6ab8
--- /dev/null
+++ b/Utilities/GDAL/frmts/ilwis/ilwisdataset.h
@@ -0,0 +1,229 @@
+/******************************************************************************
+ *
+ * Project:  ILWIS Driver
+ * Purpose:  GDALDataset driver for ILWIS translator for read/write support.
+ * Author:   Lichun Wang, lichun@itc.nl
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, ITC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: ilwisdataset.h,v $
+ * Revision 1.8  2005/08/04 15:25:52  fwarmerdam
+ * Added log header.
+ *
+ */
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4786)
+#pragma warning(disable : 4503)
+#endif
+
+#include "gdal_pam.h"
+#include "cpl_csv.h"
+#include "ogr_spatialref.h"
+//#include "Windows.h"
+#include <string>
+#include <algorithm>
+#include <map>
+
+#ifdef WIN32
+#include  <io.h>
+#endif
+
+#include  <stdio.h>
+#include  <stdlib.h>
+
+
+using namespace std;
+
+CPL_C_START
+void	GDALRegister_ILWIS(void);
+CPL_C_END
+
+#define shUNDEF	-32767
+#define iUNDEF  -2147483647
+#define flUNDEF ((float)-1e38)
+#define	rUNDEF  ((double)-1e308)
+
+enum ilwisStoreType
+{	
+		stByte,
+		stInt,
+		stLong,
+    stFloat,
+		stReal
+};
+
+class ValueRange
+{
+public:
+	ValueRange(double min, double max);	// step = 1
+	ValueRange(double min, double max, double step);	
+	ValueRange(string str);
+	string ToString();
+	double get_Minimum() { return _rLo; }
+	double get_Maximum() { return _rHi; }
+	double get_StepSize() { return _rStep; }
+
+public:
+	ilwisStoreType get_NeededStoreType() { return st; }
+	double get_rLo() { return _rLo; }
+	double get_rHi() { return _rHi; }
+	double get_rStep() { return _rStep; }
+	double get_rRaw0() { return _r0; }	
+	int get_iDec() { return _iDec; }	
+	double rValue(int raw);
+	int iRaw(double value);
+private:
+	void init(double rRaw0);
+	void init();
+  double _rLo, _rHi;
+  double _rStep;
+  int _iDec;
+  double _r0;
+  int iRawUndef;
+  short _iWidth;
+  ilwisStoreType st;
+};
+
+/************************************************************************/
+/*                     ILWISInfo                                        */
+/************************************************************************/
+
+struct ILWISInfo
+{
+		ILWISInfo() : vr(0, 0){}
+		bool bValue;
+		ValueRange vr;
+		ilwisStoreType stStoreType;
+		string stDomain;
+};
+
+/************************************************************************/
+/*                           ILWISRasterBand                            */
+/************************************************************************/
+
+class ILWISDataset;
+
+class ILWISRasterBand : public GDALPamRasterBand
+{
+  friend class ILWISDataset;
+	public:
+				FILE *fpRaw;
+				ILWISInfo psInfo;
+				int nSizePerPixel;
+
+				ILWISRasterBand( ILWISDataset *, int );
+				CPLErr GetILWISInfo(string pszFileName);
+				void ILWISOpen( string pszFilename);
+				
+  virtual CPLErr IReadBlock( int, int, void * );
+	virtual CPLErr IWriteBlock( int, int, void * ); 
+	virtual double GetNoDataValue( int *pbSuccess );
+
+  private:
+        void FillWithNoData(void * pImage);
+};
+
+/************************************************************************/
+/*	                   ILWISDataset					*/
+/************************************************************************/
+class ILWISDataset : public GDALPamDataset
+{
+    friend class ILWISRasterBand;
+    const char *pszFileName;
+    string pszIlwFileName;
+    char	 *pszProjection;
+    double adfGeoTransform[6];
+    int    bGeoDirty;
+    int		 bNewDataset;            /* product of Create() */
+    string pszFileType; //indicating the input dataset: Map/MapList
+    CPLErr ReadProjection( string csyFileName);
+    CPLErr WriteProjection();
+    CPLErr WriteGeoReference();
+    void   CollectTransformCoef(string &pszRefFile );
+		
+public:
+    ILWISDataset();
+    ~ILWISDataset();
+
+    static GDALDataset *Open( GDALOpenInfo * );
+		
+    static GDALDataset *CreateCopy( const char * pszFilename,
+                                    GDALDataset *poSrcDS,
+                                    int bStrict, char ** papszOptions,
+                                    GDALProgressFunc pfnProgress,
+                                    void * pProgressData );
+
+    static GDALDataset *Create(const char* pszFilename,
+                               int nXSize, int nYSize, 
+                               int nBands, GDALDataType eType,
+                               char** papszParmList); 
+		
+    virtual CPLErr 	GetGeoTransform( double * padfTransform );
+    virtual CPLErr  SetGeoTransform( double * );
+
+    virtual const char *GetProjectionRef(void);
+    virtual CPLErr SetProjection( const char * );
+
+    virtual void   FlushCache( void );
+};
+
+// IniFile.h: interface for the IniFile class.
+// 
+//////////////////////////////////////////////////////////////////////
+
+using std::map;
+
+class CompareAsNum
+{
+public:
+	bool operator() (const string&, const string&) const;
+};
+
+typedef map<string, string>          SectionEntries;
+typedef map<string, SectionEntries*> Sections;
+
+class IniFile  
+{
+public:
+	IniFile();
+	virtual ~IniFile();
+
+	void Open(const string& filename);
+	void Close();
+
+	void SetKeyValue(const string& section, const string& key, const string& value);
+	string GetKeyValue(const string& section, const string& key);
+
+	void RemoveKeyValue(const string& section, const string& key);
+	void RemoveSection(const string& section);
+
+private:
+	string filename;
+	Sections sections;
+
+	void Load();
+	void Flush();
+};
+
+
diff --git a/Utilities/GDAL/frmts/ilwis/makefile.vc b/Utilities/GDAL/frmts/ilwis/makefile.vc
new file mode 100644
index 0000000000..68813291fa
--- /dev/null
+++ b/Utilities/GDAL/frmts/ilwis/makefile.vc
@@ -0,0 +1,15 @@
+
+OBJ	=	ilwisdataset.obj ilwiscoordinatesystem.obj
+
+EXTRAFLAGS = 	-I..\iso8211
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/iso8211/.cvsignore b/Utilities/GDAL/frmts/iso8211/.cvsignore
new file mode 100644
index 0000000000..2dacede86f
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/.cvsignore
@@ -0,0 +1,5 @@
+html
+timetest
+8211dump
+8211view
+
diff --git a/Utilities/GDAL/frmts/iso8211/8211dump.cpp b/Utilities/GDAL/frmts/iso8211/8211dump.cpp
new file mode 100644
index 0000000000..e0a621f077
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/8211dump.cpp
@@ -0,0 +1,134 @@
+/******************************************************************************
+ * $Id: 8211dump.cpp,v 1.7 2006/04/04 04:24:06 fwarmerdam Exp $
+ *
+ * Project:  SDTS Translator
+ * Purpose:  Dump 8211 file in verbose form - just a junk program. 
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: 8211dump.cpp,v $
+ * Revision 1.7  2006/04/04 04:24:06  fwarmerdam
+ * update contact info
+ *
+ * Revision 1.6  2003/11/11 20:53:53  warmerda
+ * Report file offset before each record dumped.
+ *
+ * Revision 1.5  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.4  2000/06/16 18:02:08  warmerda
+ * added SetRepeatingFlag hack support
+ *
+ * Revision 1.3  1999/11/18 19:03:04  warmerda
+ * expanded tabs
+ *
+ * Revision 1.2  1999/05/08 20:15:19  warmerda
+ * Added malloc_dump to watch for memory leaks
+ *
+ * Revision 1.1  1999/04/27 18:45:54  warmerda
+ * New
+ *
+ * Revision 1.1  1999/03/23 13:56:13  warmerda
+ * New
+ *
+ */
+
+#include <stdio.h>
+#include "iso8211.h"
+#include "cpl_vsi.h"
+
+CPL_CVSID("$Id: 8211dump.cpp,v 1.7 2006/04/04 04:24:06 fwarmerdam Exp $");
+
+
+int main( int nArgc, char ** papszArgv )
+
+{
+    DDFModule   oModule;
+    const char  *pszFilename = NULL;
+    int         bFSPTHack = FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Check arguments.                                                */
+/* -------------------------------------------------------------------- */
+    for( int iArg = 1; iArg < nArgc; iArg++ )
+    {
+        if( EQUAL(papszArgv[iArg],"-fspt_repeating") )
+            bFSPTHack = TRUE;
+        else
+            pszFilename = papszArgv[iArg];
+    }
+
+    if( pszFilename == NULL )
+    {
+        printf( "Usage: 8211dump filename\n" );
+        exit( 1 );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Open file.                                                      */
+/* -------------------------------------------------------------------- */
+    if( !oModule.Open( pszFilename ) )
+        exit( 1 );
+
+/* -------------------------------------------------------------------- */
+/*      Apply FSPT hack if required.                                    */
+/* -------------------------------------------------------------------- */
+    if( bFSPTHack )
+    {
+        DDFFieldDefn *poFSPT = oModule.FindFieldDefn( "FSPT" );
+
+        if( poFSPT == NULL )
+            fprintf( stderr, 
+                     "unable to find FSPT field to set repeating flag.\n" );
+        else
+            poFSPT->SetRepeatingFlag( TRUE );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Dump header, and all recodrs.                                   */
+/* -------------------------------------------------------------------- */
+    DDFRecord       *poRecord;
+    oModule.Dump( stdout );
+    long nStartLoc;
+
+    nStartLoc = VSIFTell( oModule.GetFP() );
+    for( poRecord = oModule.ReadRecord();
+         poRecord != NULL; poRecord = oModule.ReadRecord() )
+    {
+        printf( "File Offset: %ld\n", nStartLoc );
+        poRecord->Dump( stdout );
+
+        nStartLoc = VSIFTell( oModule.GetFP() );
+    }
+
+    oModule.Close();
+    
+#ifdef DBMALLOC
+    malloc_dump(1);
+#endif
+
+}
+
+
+
diff --git a/Utilities/GDAL/frmts/iso8211/8211view.cpp b/Utilities/GDAL/frmts/iso8211/8211view.cpp
new file mode 100644
index 0000000000..6385c8d75b
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/8211view.cpp
@@ -0,0 +1,277 @@
+/* ****************************************************************************
+ * $Id: 8211view.cpp,v 1.8 2006/04/04 04:24:06 fwarmerdam Exp $
+ *
+ * Project:  SDTS Translator
+ * Purpose:  Example program dumping data in 8211 data to stdout.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: 8211view.cpp,v $
+ * Revision 1.8  2006/04/04 04:24:06  fwarmerdam
+ * update contact info
+ *
+ * Revision 1.7  2003/08/21 21:22:46  warmerda
+ * Special reporting facilities for the S-57 LNAM and NAME bitfields (provided
+ * by Rodney Jensen).
+ * Report DDFInt fields as unsigned if they come from an unsigned binary
+ * field.
+ *
+ * Revision 1.6  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.5  2000/06/16 18:02:08  warmerda
+ * added SetRepeatingFlag hack support
+ *
+ * Revision 1.4  1999/11/18 19:03:04  warmerda
+ * expanded tabs
+ *
+ * Revision 1.3  1999/05/06 14:25:15  warmerda
+ * added support for binary strings
+ *
+ * Revision 1.2  1999/04/28 05:16:47  warmerda
+ * added usage
+ *
+ * Revision 1.1  1999/04/27 22:09:30  warmerda
+ * New
+ *
+ */
+
+#include <stdio.h>
+#include "iso8211.h"
+
+CPL_CVSID("$Id: 8211view.cpp,v 1.8 2006/04/04 04:24:06 fwarmerdam Exp $");
+
+static void ViewRecordField( DDFField * poField );
+static int ViewSubfield( DDFSubfieldDefn *poSFDefn,
+                         const char * pachFieldData,
+                         int nBytesRemaining );
+
+/* **********************************************************************/
+/*                                main()                                */
+/* **********************************************************************/
+
+int main( int nArgc, char ** papszArgv )
+
+{
+    DDFModule   oModule;
+    const char  *pszFilename = NULL;
+    int         bFSPTHack = FALSE;
+
+    for( int iArg = 1; iArg < nArgc; iArg++ )
+    {
+        if( EQUAL(papszArgv[iArg],"-fspt_repeating") )
+            bFSPTHack = TRUE;
+        else
+            pszFilename = papszArgv[iArg];
+    }
+
+    if( pszFilename == NULL )
+    {
+        printf( "Usage: 8211view filename\n" );
+        exit( 1 );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Open the file.  Note that by default errors are reported to     */
+/*      stderr, so we don't bother doing it ourselves.                  */
+/* -------------------------------------------------------------------- */
+    if( !oModule.Open( pszFilename ) )
+    {
+        exit( 1 );
+    }
+
+    if( bFSPTHack )
+    {
+        DDFFieldDefn *poFSPT = oModule.FindFieldDefn( "FSPT" );
+
+        if( poFSPT == NULL )
+            fprintf( stderr,
+                     "unable to find FSPT field to set repeating flag.\n" );
+        else
+            poFSPT->SetRepeatingFlag( TRUE );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Loop reading records till there are none left.                  */
+/* -------------------------------------------------------------------- */
+    DDFRecord   *poRecord;
+    int         iRecord = 0;
+
+    while( (poRecord = oModule.ReadRecord()) != NULL )
+    {
+        printf( "Record %d (%d bytes)\n",
+                ++iRecord, poRecord->GetDataSize() );
+
+        /* ------------------------------------------------------------ */
+        /*      Loop over each field in this particular record.         */
+        /* ------------------------------------------------------------ */
+        for( int iField = 0; iField < poRecord->GetFieldCount(); iField++ )
+        {
+            DDFField    *poField = poRecord->GetField( iField );
+
+            ViewRecordField( poField );
+        }
+    }
+}
+
+/* **********************************************************************/
+/*                          ViewRecordField()                           */
+/*                                                                      */
+/*      Dump the contents of a field instance in a record.              */
+/* **********************************************************************/
+
+static void ViewRecordField( DDFField * poField )
+
+{
+    int         nBytesRemaining;
+    const char  *pachFieldData;
+    DDFFieldDefn *poFieldDefn = poField->GetFieldDefn();
+
+    // Report general information about the field.
+    printf( "    Field %s: %s\n",
+            poFieldDefn->GetName(), poFieldDefn->GetDescription() );
+
+    // Get pointer to this fields raw data.  We will move through
+    // it consuming data as we report subfield values.
+
+    pachFieldData = poField->GetData();
+    nBytesRemaining = poField->GetDataSize();
+
+    /* -------------------------------------------------------- */
+    /*      Loop over the repeat count for this fields          */
+    /*      subfields.  The repeat count will almost            */
+    /*      always be one.                                      */
+    /* -------------------------------------------------------- */
+    int         iRepeat;
+
+    for( iRepeat = 0; iRepeat < poField->GetRepeatCount(); iRepeat++ )
+    {
+
+        /* -------------------------------------------------------- */
+        /*   Loop over all the subfields of this field, advancing   */
+        /*   the data pointer as we consume data.                   */
+        /* -------------------------------------------------------- */
+        int     iSF;
+
+        for( iSF = 0; iSF < poFieldDefn->GetSubfieldCount(); iSF++ )
+        {
+            DDFSubfieldDefn *poSFDefn = poFieldDefn->GetSubfield( iSF );
+            int         nBytesConsumed;
+
+            nBytesConsumed = ViewSubfield( poSFDefn, pachFieldData,
+                                           nBytesRemaining );
+
+            nBytesRemaining -= nBytesConsumed;
+            pachFieldData += nBytesConsumed;
+        }
+    }
+}
+
+/* **********************************************************************/
+/*                            ViewSubfield()                            */
+/* **********************************************************************/
+
+static int ViewSubfield( DDFSubfieldDefn *poSFDefn,
+                         const char * pachFieldData,
+                         int nBytesRemaining )
+
+{
+    int         nBytesConsumed = 0;
+
+    switch( poSFDefn->GetType() )
+    {
+      case DDFInt:
+        if( poSFDefn->GetBinaryFormat() == DDFSubfieldDefn::UInt )
+            printf( "        %s = %u\n",
+                    poSFDefn->GetName(),
+                    poSFDefn->ExtractIntData( pachFieldData, nBytesRemaining,
+                                              &nBytesConsumed ) );
+        else
+            printf( "        %s = %d\n",
+                    poSFDefn->GetName(),
+                    poSFDefn->ExtractIntData( pachFieldData, nBytesRemaining,
+                                              &nBytesConsumed ) );
+        break;
+
+      case DDFFloat:
+        printf( "        %s = %f\n",
+                poSFDefn->GetName(),
+                poSFDefn->ExtractFloatData( pachFieldData, nBytesRemaining,
+                                            &nBytesConsumed ) );
+        break;
+
+      case DDFString:
+        printf( "        %s = `%s'\n",
+                poSFDefn->GetName(),
+                poSFDefn->ExtractStringData( pachFieldData, nBytesRemaining,
+                                             &nBytesConsumed ) );
+        break;
+
+      case DDFBinaryString:
+      {
+          int   i;
+          //rjensen 19-Feb-2002 5 integer variables to decode NAME and LNAM
+          int vrid_rcnm=0;
+          int vrid_rcid=0;
+          int foid_agen=0;
+          int foid_find=0;
+          int foid_fids=0;
+
+          GByte *pabyBString = (GByte *)
+              poSFDefn->ExtractStringData( pachFieldData, nBytesRemaining,
+                                           &nBytesConsumed );
+
+          printf( "        %s = 0x", poSFDefn->GetName() );
+          for( i = 0; i < MIN(nBytesConsumed,24); i++ )
+              printf( "%02X", pabyBString[i] );
+
+          if( nBytesConsumed > 24 )
+              printf( "%s", "..." );
+
+          // rjensen 19-Feb-2002 S57 quick hack. decode NAME and LNAM bitfields
+          if ( EQUAL(poSFDefn->GetName(),"NAME") )
+          {
+              vrid_rcnm=pabyBString[0];
+              vrid_rcid=pabyBString[1] + (pabyBString[2]*256)+
+                  (pabyBString[3]*65536)+ (pabyBString[4]*16777216);
+              printf("\tVRID RCNM = %d,RCID = %u",vrid_rcnm,vrid_rcid);
+          }
+          else if ( EQUAL(poSFDefn->GetName(),"LNAM") )
+          {
+              foid_agen=pabyBString[0] + (pabyBString[1]*256);
+              foid_find=pabyBString[2] + (pabyBString[3]*256)+
+                  (pabyBString[4]*65536)+ (pabyBString[5]*16777216);
+              foid_fids=pabyBString[6] + (pabyBString[7]*256);
+              printf("\tFOID AGEN = %u,FIDN = %u,FIDS = %u",
+                     foid_agen,foid_find,foid_fids);
+          }
+
+          printf( "\n" );
+      }
+      break;
+
+    }
+
+    return nBytesConsumed;
+}
diff --git a/Utilities/GDAL/frmts/iso8211/Doxyfile b/Utilities/GDAL/frmts/iso8211/Doxyfile
new file mode 100644
index 0000000000..773cdd23ab
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/Doxyfile
@@ -0,0 +1,255 @@
+# This file describes the settings to be used by doxygen for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of word surrounded
+# by quotes) that should identify the project. 
+
+PROJECT_NAME         =	ISO8211Lib
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER       =	
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY     =
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT          =
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT          =
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER          =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER          =
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS             = YES
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each page. A value of NO (the default) enables the index and the
+# value YES disables it.
+
+DISABLE_INDEX        = NO
+
+# If the EXTRACT_ALL tag is set to YES all classes and functions will be
+# included in the documentation, even if no documentation was available.
+
+EXTRACT_ALL          = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE      = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members inside documented classes or files.
+
+HIDE_UNDOC_MEMBERS   = YES
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX       = NO
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output
+
+GENERATE_HTML        = YES
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the FULL_PATH_NAMES tag is set to YES Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used
+
+FULL_PATH_NAMES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT            =	.
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+FILE_PATTERNS    =	*.h *.cpp *.c *.dox
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH     = .
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE        = NO
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+
+INPUT_FILTER     = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor 
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed.
+
+MACRO_EXPANSION = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). In the former case 1 is used as the
+# definition.
+
+PREDEFINED =
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED tag.
+
+EXPAND_ONLY_PREDEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references 
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles. 
+
+TAGFILES         =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS     = NO
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH        = /usr/local/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the search engine 
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE     = NO
+
+# The CGI_NAME tag should be the name of the CGI script that
+# starts the search engine (doxysearch) with the correct parameters.
+# A script with this name will be generated by doxygen.
+
+CGI_NAME         = search.cgi
+
+# The CGI_URL tag should be the absolute URL to the directory where the
+# cgi binaries are located. See the documentation of your http daemon for 
+# details.
+
+CGI_URL          =
+
+# The DOC_URL tag should be the absolute URL to the directory where the
+# documentation is located. If left blank the absolute path to the 
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL          =
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the
+# documentation is located. If left blank the directory on the local machine
+# will be used.
+
+DOC_ABSPATH      =
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary
+# is installed.
+
+BIN_ABSPATH      = /usr/local/bin/
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to 
+# documentation generated for other projects. This allows doxysearch to search
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS    =
diff --git a/Utilities/GDAL/frmts/iso8211/GNUmakefile b/Utilities/GDAL/frmts/iso8211/GNUmakefile
new file mode 100644
index 0000000000..077746debf
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/GNUmakefile
@@ -0,0 +1,91 @@
+
+include ../../GDALmake.opt
+
+VERSION =	1.3
+DISTDIR =	iso8211lib-$(VERSION)
+WEB_DIR =	/u/www/projects/iso8211
+
+CPPFLAGS	:=	-I. -I../../port $(CPPFLAGS)
+
+ISOLIB = libiso8211.a
+OBJ =	ddfmodule.o ddfutils.o ddffielddefn.o ddfrecord.o ddffield.o \
+	ddfsubfielddefn.o
+
+default:	$(ISOLIB)
+
+all:	$(ISOLIB) 8211dump 8211view mkcatalog docs
+
+clean:
+	rm -rf *.o 8211dump 8211view $(DISTDIR) $(DISTDIR).tar.gz html/* \
+		$(ISOLIB)
+
+dist-clean:	clean
+	rm -rf $(DISTDIR)
+
+$(ISOLIB):	$(OBJ)
+	$(AR) r $(ISOLIB) $?
+	$(RANLIB) $(ISOLIB)
+
+8211dump:	8211dump.o $(ISOLIB)
+	$(CXX) $(CXX_ODFLAGS) 8211dump.o \
+		$(ISOLIB) $(GDAL_LIB) $(LIBS) -o 8211dump
+
+8211view:	8211view.o $(ISOLIB)
+	$(CXX) $(CXX_ODFLAGS) 8211view.o \
+		$(ISOLIB) $(GDAL_LIB) $(LIBS) -o 8211view
+
+timetest:	timetest.o $(ISOLIB)
+	$(CXX) $(CXX_ODFLAGS) timetest.o \
+		$(ISOLIB) $(GDAL_LIB) $(LIBS) -o timetest
+
+upd_test:	upd_test.o $(ISOLIB)
+	$(CXX) $(CXX_ODFLAGS) upd_test.o \
+		$(ISOLIB) $(GDAL_LIB) $(LIBS) -o upd_test
+
+mkcatalog:	mkcatalog.o $(ISOLIB)
+	$(CXX) $(CXX_ODFLAGS) mkcatalog.o \
+		$(ISOLIB) $(GDAL_LIB) $(LIBS) -o mkcatalog
+
+docs:
+	rm -rf html
+	mkdir html
+	doxygen
+	rm html/index.html
+	cp html/ISO8211Lib.html html/index.html
+
+dist:	docs
+	rm -rf $(DISTDIR)
+	mkdir $(DISTDIR)
+	mkdir $(DISTDIR)/html
+	cp html/* $(DISTDIR)/html
+#	mkdir $(DISTDIR)/testdata
+#	cp testdata/*.* $(DISTDIR)/testdata
+#	cp teststream.sh teststream.out $(DISTDIR)
+	autoconf
+	cp *.cpp *.h configure Makefile.in $(DISTDIR)
+	rm configure
+	cp ../../port/{cpl_error*,cpl_port*,cpl_string*} $(DISTDIR)
+	cp ../../port/{cpl_vsisimple.cpp,cpl_config.h.in} $(DISTDIR)
+	cp ../../port/{cpl_vsi.h,cpl_conv.*} $(DISTDIR)
+	cp ../../port/cpl_config.h.vc $(DISTDIR)
+	rm $(DISTDIR)/*.o
+	tar czf $(DISTDIR).tar.gz $(DISTDIR)
+	zip -r $(DISTDIR).zip $(DISTDIR)
+
+update-web:	dist docs
+	cp html/* $(WEB_DIR)
+	cp $(DISTDIR).tar.gz $(DISTDIR).zip $(WEB_DIR)
+	cp $(DISTDIR).tar.gz $(DISTDIR).zip /u/ftp/pub/outgoing
+
+
+test:	8211dump
+	@./teststream.sh > t1.out
+	@if test "`diff t1.out teststream.out`" = '' ; then \
+	    echo "******* Stream 1 Succeeded *********"; \
+	    rm t1.out; \
+	else \
+	    echo "******* Stream 1 Failed *********"; \
+	    diff t1.out teststream.out; \
+	fi
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/iso8211/Makefile.in b/Utilities/GDAL/frmts/iso8211/Makefile.in
new file mode 100644
index 0000000000..e9ae8e0805
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/Makefile.in
@@ -0,0 +1,72 @@
+
+OBJ =	ddfmodule.o ddfutils.o ddffielddefn.o ddfrecord.o ddffield.o \
+	ddfsubfielddefn.o \
+	\
+	cpl_error.o cpl_vsisimple.o cpl_string.o cpl_conv.o
+
+CXXFLAGS = @CXXFLAGS@ @CXX_WFLAGS@
+LIBS 	= @LIBS@ -lm
+CXX	= @CXX@
+
+
+default:	8211view
+
+libiso8211.a:	$(OBJ)
+	ar r libiso8211.a $(OBJ)
+
+ddfmodule.o:	ddfmodule.cpp
+	$(CXX) -c $(CXXFLAGS) ddfmodule.cpp 
+
+ddfutils.o:	ddfutils.cpp
+	$(CXX) -c $(CXXFLAGS) ddfutils.cpp 
+
+ddffielddefn.o:	ddffielddefn.cpp
+	$(CXX) -c $(CXXFLAGS) ddffielddefn.cpp 
+
+ddfrecord.o:	ddfrecord.cpp
+	$(CXX) -c $(CXXFLAGS) ddfrecord.cpp 
+
+ddffield.o:	ddffield.cpp
+	$(CXX) -c $(CXXFLAGS) ddffield.cpp 
+
+ddfsubfielddefn.o:	ddfsubfielddefn.cpp
+	$(CXX) -c $(CXXFLAGS) ddfsubfielddefn.cpp 
+
+cpl_error.o:	cpl_error.cpp
+	$(CXX) -c $(CXXFLAGS) cpl_error.cpp 
+
+cpl_string.o:	cpl_string.cpp
+	$(CXX) -c $(CXXFLAGS) cpl_string.cpp 
+
+cpl_conv.o:	cpl_conv.cpp
+	$(CXX) -c $(CXXFLAGS) cpl_conv.cpp 
+
+cpl_vsisimple.o:	cpl_vsisimple.cpp
+	$(CXX) -c $(CXXFLAGS) cpl_vsisimple.cpp 
+
+#
+#	Mainlines
+#
+
+8211view.o:	8211view.cpp
+	$(CXX) -c $(CXXFLAGS) 8211view.cpp 
+
+8211dump.o:	8211dump.cpp
+	$(CXX) -c $(CXXFLAGS) 8211dump.cpp 
+
+8211view:	8211view.o libiso8211.a
+	$(CXX) $(CXXFLAGS) 8211view.o libiso8211.a $(LIBS) -o 8211view
+
+8211dump:	8211dump.o libiso8211.a
+	$(CXX) $(CXXFLAGS) 8211dump.o libiso8211.a $(LIBS) -o 8211dump
+
+
+test:	8211dump
+	@./teststream.sh > t1.out
+	@if test "`diff t1.out teststream.out`" = '' ; then \
+	    echo "******* Stream 1 Succeeded *********"; \
+	    rm t1.out; \
+	else \
+	    echo "******* Stream 1 Failed *********"; \
+	    diff t1.out teststream.out; \
+	fi
diff --git a/Utilities/GDAL/frmts/iso8211/aclocal.m4 b/Utilities/GDAL/frmts/iso8211/aclocal.m4
new file mode 100644
index 0000000000..d4d18f50c9
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/aclocal.m4
@@ -0,0 +1,15 @@
+AC_DEFUN(AC_COMPILER_WFLAGS,
+[
+	# Remove -g from compile flags, we will add via CFG variable if
+	# we need it.
+	CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-g //"`
+	CFLAGS=`echo "$CFLAGS " | sed "s/-g //"`
+
+	# check for GNU compiler, and use -Wall
+	if test "$GXX" = "yes"; then
+		CXX_WFLAGS="-Wall"
+		AC_DEFINE(USE_GNUCC)
+	fi
+	AC_SUBST(CXX_WFLAGS,$CXX_WFLAGS)
+	AC_SUBST(C_WFLAGS,$C_WFLAGS)
+])
diff --git a/Utilities/GDAL/frmts/iso8211/configure.in b/Utilities/GDAL/frmts/iso8211/configure.in
new file mode 100644
index 0000000000..b3a8a31f25
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/configure.in
@@ -0,0 +1,18 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(Makefile.in)
+AC_CONFIG_HEADER(cpl_config.h)
+
+dnl Checks for programs.
+AC_PROG_CXX
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(fcntl.h unistd.h)
+
+dnl Checks for library functions.
+AC_C_BIGENDIAN
+AC_FUNC_VPRINTF
+
+AC_COMPILER_WFLAGS
+
+AC_OUTPUT(Makefile)
diff --git a/Utilities/GDAL/frmts/iso8211/ddffield.cpp b/Utilities/GDAL/frmts/iso8211/ddffield.cpp
new file mode 100644
index 0000000000..0c7d6d8d4e
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/ddffield.cpp
@@ -0,0 +1,387 @@
+/******************************************************************************
+ * $Id: ddffield.cpp,v 1.17 2006/04/04 04:24:06 fwarmerdam Exp $
+ *
+ * Project:  ISO 8211 Access
+ * Purpose:  Implements the DDFField class.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: ddffield.cpp,v $
+ * Revision 1.17  2006/04/04 04:24:06  fwarmerdam
+ * update contact info
+ *
+ * Revision 1.16  2003/07/03 15:38:46  warmerda
+ * some write capabilities added
+ *
+ * Revision 1.15  2001/08/30 21:08:19  warmerda
+ * expand tabs
+ *
+ * Revision 1.14  2001/08/30 02:14:50  warmerda
+ * Provide for control of max number of field instances dumped via environment.
+ *
+ * Revision 1.13  2001/08/27 19:09:00  warmerda
+ * added GetInstanceData() method on DDFField
+ *
+ * Revision 1.12  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.11  2000/09/19 14:09:11  warmerda
+ * fixed dump of binary info
+ *
+ * Revision 1.10  2000/06/13 13:38:39  warmerda
+ * Improved reporting of binary data in Dump method.
+ * Fixed GetRepeatCount() so that short field data can be properly detected.
+ *
+ * Revision 1.9  1999/11/18 19:03:04  warmerda
+ * expanded tabs
+ *
+ * Revision 1.8  1999/11/03 14:04:57  warmerda
+ * Made use of GetSubfieldData() with repeated fixed length
+ * fields much more efficient.
+ *
+ * Revision 1.7  1999/09/21 16:25:32  warmerda
+ * Fixed bug with repeating variable length fields and running out of data.
+ *
+ * Revision 1.6  1999/06/23 02:14:08  warmerda
+ * added support for variable width repeaters to GetRepeatCount()
+ *
+ * Revision 1.5  1999/06/01 19:10:38  warmerda
+ * don't assert on variable length repeating fields
+ *
+ * Revision 1.4  1999/05/07 14:10:49  warmerda
+ * added maxbytes to getsubfielddata()
+ *
+ * Revision 1.3  1999/05/06 15:39:26  warmerda
+ * avoid printing non-ASCII characters
+ *
+ * Revision 1.2  1999/04/27 22:09:50  warmerda
+ * updated docs
+ *
+ * Revision 1.1  1999/04/27 18:45:05  warmerda
+ * New
+ *
+ */
+
+#include "iso8211.h"
+#include "cpl_conv.h"
+
+CPL_CVSID("$Id: ddffield.cpp,v 1.17 2006/04/04 04:24:06 fwarmerdam Exp $");
+
+// Note, we implement no constructor for this class to make instantiation
+// cheaper.  It is required that the Initialize() be called before anything
+// else.
+
+/************************************************************************/
+/*                             Initialize()                             */
+/************************************************************************/
+
+void DDFField::Initialize( DDFFieldDefn *poDefnIn, const char * pachDataIn,
+                           int nDataSizeIn )
+
+{
+    pachData = pachDataIn;
+    nDataSize = nDataSizeIn;
+    poDefn = poDefnIn;
+}
+
+/************************************************************************/
+/*                                Dump()                                */
+/************************************************************************/
+
+/**
+ * Write out field contents to debugging file.
+ *
+ * A variety of information about this field, and all it's
+ * subfields is written to the given debugging file handle.  Note that
+ * field definition information (ala DDFFieldDefn) isn't written.
+ *
+ * @param fp The standard io file handle to write to.  ie. stderr
+ */
+
+void DDFField::Dump( FILE * fp )
+
+{
+    int         nMaxRepeat = 8;
+
+    if( getenv("DDF_MAXDUMP") != NULL )
+        nMaxRepeat = atoi(getenv("DDF_MAXDUMP"));
+
+    fprintf( fp, "  DDFField:\n" );
+    fprintf( fp, "      Tag = `%s'\n", poDefn->GetName() );
+    fprintf( fp, "      DataSize = %d\n", nDataSize );
+
+    fprintf( fp, "      Data = `" );
+    for( int i = 0; i < MIN(nDataSize,40); i++ )
+    {
+        if( pachData[i] < 32 || pachData[i] > 126 )
+            fprintf( fp, "\\%02X", ((unsigned char *) pachData)[i] );
+        else
+            fprintf( fp, "%c", pachData[i] );
+    }
+
+    if( nDataSize > 40 )
+        fprintf( fp, "..." );
+    fprintf( fp, "'\n" );
+
+/* -------------------------------------------------------------------- */
+/*      dump the data of the subfields.                                 */
+/* -------------------------------------------------------------------- */
+    int         iOffset = 0, nLoopCount;
+
+    for( nLoopCount = 0; nLoopCount < GetRepeatCount(); nLoopCount++ )
+    {
+        if( nLoopCount > nMaxRepeat )
+        {
+            fprintf( fp, "      ...\n" );
+            break;
+        }
+        
+        for( int i = 0; i < poDefn->GetSubfieldCount(); i++ )
+        {
+            int         nBytesConsumed;
+
+            poDefn->GetSubfield(i)->DumpData( pachData + iOffset,
+                                              nDataSize - iOffset, fp );
+        
+            poDefn->GetSubfield(i)->GetDataLength( pachData + iOffset,
+                                                   nDataSize - iOffset,
+                                                   &nBytesConsumed );
+
+            iOffset += nBytesConsumed;
+        }
+    }
+}
+
+/************************************************************************/
+/*                          GetSubfieldData()                           */
+/************************************************************************/
+
+/**
+ * Fetch raw data pointer for a particular subfield of this field.
+ *
+ * The passed DDFSubfieldDefn (poSFDefn) should be acquired from the
+ * DDFFieldDefn corresponding with this field.  This is normally done
+ * once before reading any records.  This method involves a series of
+ * calls to DDFSubfield::GetDataLength() in order to track through the
+ * DDFField data to that belonging to the requested subfield.  This can
+ * be relatively expensive.<p>
+ *
+ * @param poSFDefn The definition of the subfield for which the raw
+ * data pointer is desired.
+ * @param pnMaxBytes The maximum number of bytes that can be accessed from
+ * the returned data pointer is placed in this int, unless it is NULL.
+ * @param iSubfieldIndex The instance of this subfield to fetch.  Use zero
+ * (the default) for the first instance.
+ *
+ * @return A pointer into the DDFField's data that belongs to the subfield.
+ * This returned pointer is invalidated by the next record read
+ * (DDFRecord::ReadRecord()) and the returned pointer should not be freed
+ * by the application.
+ */
+
+const char *DDFField::GetSubfieldData( DDFSubfieldDefn *poSFDefn,
+                                       int *pnMaxBytes, int iSubfieldIndex )
+
+{
+    int         iOffset = 0;
+    
+    if( poSFDefn == NULL )
+        return NULL;
+
+    if( iSubfieldIndex > 0 && poDefn->GetFixedWidth() > 0 )
+    {
+        iOffset = poDefn->GetFixedWidth() * iSubfieldIndex;
+        iSubfieldIndex = 0;
+    }
+
+    while( iSubfieldIndex >= 0 )
+    {
+        for( int iSF = 0; iSF < poDefn->GetSubfieldCount(); iSF++ )
+        {
+            int nBytesConsumed;
+            DDFSubfieldDefn * poThisSFDefn = poDefn->GetSubfield( iSF );
+            
+            if( poThisSFDefn == poSFDefn && iSubfieldIndex == 0 )
+            {
+                if( pnMaxBytes != NULL )
+                    *pnMaxBytes = nDataSize - iOffset;
+                
+                return pachData + iOffset;
+            }
+            
+            poThisSFDefn->GetDataLength( pachData+iOffset, nDataSize - iOffset,
+                                         &nBytesConsumed);
+            iOffset += nBytesConsumed;
+        }
+
+        iSubfieldIndex--;
+    }
+
+    // We didn't find our target subfield or instance!
+    return NULL;
+}
+
+/************************************************************************/
+/*                           GetRepeatCount()                           */
+/************************************************************************/
+
+/**
+ * How many times do the subfields of this record repeat?  This    
+ * will always be one for non-repeating fields.
+ *
+ * @return The number of times that the subfields of this record occur
+ * in this record.  This will be one for non-repeating fields.
+ *
+ * @see <a href="example.html">8211view example program</a>
+ * for demonstation of handling repeated fields properly.
+ */
+
+int DDFField::GetRepeatCount()
+
+{
+    if( !poDefn->IsRepeating() )
+        return 1;
+
+/* -------------------------------------------------------------------- */
+/*      The occurance count depends on how many copies of this          */
+/*      field's list of subfields can fit into the data space.          */
+/* -------------------------------------------------------------------- */
+    if( poDefn->GetFixedWidth() )
+    {
+        return nDataSize / poDefn->GetFixedWidth();
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Note that it may be legal to have repeating variable width      */
+/*      subfields, but I don't have any samples, so I ignore it for     */
+/*      now.                                                            */
+/*                                                                      */
+/*      The file data/cape_royal_AZ_DEM/1183XREF.DDF has a repeating    */
+/*      variable length field, but the count is one, so it isn't        */
+/*      much value for testing.                                         */
+/* -------------------------------------------------------------------- */
+    int         iOffset = 0, iRepeatCount = 1;
+    
+    while( TRUE )
+    {
+        for( int iSF = 0; iSF < poDefn->GetSubfieldCount(); iSF++ )
+        {
+            int nBytesConsumed;
+            DDFSubfieldDefn * poThisSFDefn = poDefn->GetSubfield( iSF );
+
+            if( poThisSFDefn->GetWidth() > nDataSize - iOffset )
+                nBytesConsumed = poThisSFDefn->GetWidth();
+            else
+                poThisSFDefn->GetDataLength( pachData+iOffset, 
+                                             nDataSize - iOffset,
+                                             &nBytesConsumed);
+
+            iOffset += nBytesConsumed;
+            if( iOffset > nDataSize )
+                return iRepeatCount - 1;
+        }
+
+        if( iOffset > nDataSize - 2 )
+            return iRepeatCount;
+
+        iRepeatCount++;
+    }
+}
+
+/************************************************************************/
+/*                          GetInstanceData()                           */
+/************************************************************************/
+
+/**
+ * Get field instance data and size.
+ *
+ * The returned data pointer and size values are suitable for use with
+ * DDFRecord::SetFieldRaw(). 
+ *
+ * @param nInstance a value from 0 to GetRepeatCount()-1.  
+ * @param pnInstanceSize a location to put the size (in bytes) of the
+ * field instance data returned.  This size will include the unit terminator
+ * (if any), but not the field terminator.  This size pointer may be NULL
+ * if not needed.
+ *
+ * @return the data pointer, or NULL on error. 
+ */
+
+const char *DDFField::GetInstanceData( int nInstance, 
+                                       int *pnInstanceSize )
+
+{
+    int nRepeatCount = GetRepeatCount();
+    const char *pachWrkData;
+
+    if( nInstance < 0 || nInstance >= nRepeatCount )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Special case for fields without subfields (like "0001").  We    */
+/*      don't currently handle repeating simple fields.                 */
+/* -------------------------------------------------------------------- */
+    if( poDefn->GetSubfieldCount() == 0 )
+    {
+        pachWrkData = GetData();
+        if( pnInstanceSize != 0 )
+            *pnInstanceSize = GetDataSize();
+        return pachWrkData;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get a pointer to the start of the existing data for this        */
+/*      iteration of the field.                                         */
+/* -------------------------------------------------------------------- */
+    int         nBytesRemaining1, nBytesRemaining2;
+    DDFSubfieldDefn *poFirstSubfield;
+
+    poFirstSubfield = poDefn->GetSubfield(0);
+
+    pachWrkData = GetSubfieldData(poFirstSubfield, &nBytesRemaining1,
+                               nInstance);
+
+/* -------------------------------------------------------------------- */
+/*      Figure out the size of the entire field instance, including     */
+/*      unit terminators, but not any trailing field terminator.        */
+/* -------------------------------------------------------------------- */
+    if( pnInstanceSize != NULL )
+    {
+        DDFSubfieldDefn *poLastSubfield;
+        int              nLastSubfieldWidth;
+        const char          *pachLastData;
+        
+        poLastSubfield = poDefn->GetSubfield(poDefn->GetSubfieldCount()-1);
+        
+        pachLastData = GetSubfieldData( poLastSubfield, &nBytesRemaining2, 
+                                        nInstance );
+        poLastSubfield->GetDataLength( pachLastData, nBytesRemaining2, 
+                                       &nLastSubfieldWidth );
+        
+        *pnInstanceSize = 
+            nBytesRemaining1 - (nBytesRemaining2 - nLastSubfieldWidth);
+    }
+
+    return pachWrkData;
+}
diff --git a/Utilities/GDAL/frmts/iso8211/ddffielddefn.cpp b/Utilities/GDAL/frmts/iso8211/ddffielddefn.cpp
new file mode 100644
index 0000000000..fcca6a70d6
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/ddffielddefn.cpp
@@ -0,0 +1,953 @@
+/******************************************************************************
+ * $Id: ddffielddefn.cpp,v 1.24 2006/03/07 04:21:34 fwarmerdam Exp $
+ *
+ * Project:  ISO 8211 Access
+ * Purpose:  Implements the DDFFieldDefn class.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: ddffielddefn.cpp,v $
+ * Revision 1.24  2006/03/07 04:21:34  fwarmerdam
+ * Added support for AR2D which has repeating structures with substructures
+ *
+ * Revision 1.23  2005/10/17 14:51:24  fwarmerdam
+ * Removed extra newlines from error messages.
+ *
+ * Revision 1.22  2005/10/17 14:27:10  fwarmerdam
+ * Failure to parse field defns is now a warning, not error
+ *
+ * Revision 1.21  2004/02/18 14:10:07  warmerda
+ * doc fixups
+ *
+ * Revision 1.20  2004/01/06 18:59:18  warmerda
+ * make enum identifiers more unique
+ *
+ * Revision 1.19  2004/01/06 18:53:41  warmerda
+ * made data_type_code and data_struct_code global for HP C++ builds
+ *
+ * Revision 1.18  2003/12/15 20:24:58  warmerda
+ * expand tabs
+ *
+ * Revision 1.17  2003/09/17 21:11:34  warmerda
+ * fixed handling of the field terminator on write
+ *
+ * Revision 1.16  2003/09/15 20:45:47  warmerda
+ * initialize nFixedWidth
+ *
+ * Revision 1.15  2003/09/05 19:13:02  warmerda
+ * added repeating support when creating fields
+ *
+ * Revision 1.14  2003/09/03 20:36:26  warmerda
+ * added subfield writing support
+ *
+ * Revision 1.13  2003/07/18 20:45:30  warmerda
+ * be careful to avoid pszDest buffer overrun
+ *
+ * Revision 1.12  2003/07/03 15:38:46  warmerda
+ * some write capabilities added
+ *
+ * Revision 1.11  2003/05/22 19:44:26  warmerda
+ * Fixed another bug like the last.
+ *
+ * Revision 1.10  2003/05/22 19:14:51  warmerda
+ * Fixed possible problem with writing one byte past end of
+ * pszDest in ExpandFormat() as reported by Ben Discoe.
+ *
+ * Revision 1.9  2003/02/06 03:21:04  warmerda
+ * Modified ExpandFormat() to dynamically allocate the target buffer. It was
+ * overrunning the 400 character szDest buffer on some files, such as
+ * the data/sdts/gainsville/BEDRCATD.DDF dataset.
+ *
+ * Revision 1.8  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.7  2001/06/22 19:22:16  warmerda
+ * Made some oddidies in field definitions non-fatal.
+ *
+ * Revision 1.6  2000/11/30 20:33:18  warmerda
+ * make having more formats than data items a warning, not an error
+ *
+ * Revision 1.5  2000/06/16 18:05:02  warmerda
+ * expanded tabs
+ *
+ * Revision 1.4  2000/01/31 18:03:38  warmerda
+ * completely rewrote format expansion to make more general
+ *
+ * Revision 1.3  1999/11/18 19:03:04  warmerda
+ * expanded tabs
+ *
+ * Revision 1.2  1999/09/20 19:29:16  warmerda
+ * make forgiving of UNIT/FIELD terminator mixup in Tiger SDTS files
+ *
+ * Revision 1.1  1999/04/27 18:45:05  warmerda
+ * New
+ *
+ */
+
+#include "iso8211.h"
+#include "cpl_string.h"
+#include <ctype.h>
+
+CPL_CVSID("$Id: ddffielddefn.cpp,v 1.24 2006/03/07 04:21:34 fwarmerdam Exp $");
+
+#define CPLE_DiscardedFormat   1301
+
+/************************************************************************/
+/*                            DDFFieldDefn()                            */
+/************************************************************************/
+
+DDFFieldDefn::DDFFieldDefn()
+
+{
+    poModule = NULL;
+    pszTag = NULL;
+    _fieldName = NULL;
+    _arrayDescr = NULL;
+    _formatControls = NULL;
+    nSubfieldCount = 0;
+    papoSubfields = NULL;
+    bRepeatingSubfields = FALSE;
+    nFixedWidth = 0;
+}
+
+/************************************************************************/
+/*                           ~DDFFieldDefn()                            */
+/************************************************************************/
+
+DDFFieldDefn::~DDFFieldDefn()
+
+{
+    int   i;
+
+    CPLFree( pszTag );
+    CPLFree( _fieldName );
+    CPLFree( _arrayDescr );
+    CPLFree( _formatControls );
+
+    for( i = 0; i < nSubfieldCount; i++ )
+        delete papoSubfields[i];
+    CPLFree( papoSubfields );
+}
+
+/************************************************************************/
+/*                            AddSubfield()                             */
+/************************************************************************/
+
+void DDFFieldDefn::AddSubfield( const char *pszName, 
+                                const char *pszFormat )
+
+{
+    DDFSubfieldDefn *poSFDefn = new DDFSubfieldDefn;
+
+    poSFDefn->SetName( pszName );
+    poSFDefn->SetFormat( pszFormat );
+    AddSubfield( poSFDefn );
+}
+
+/************************************************************************/
+/*                            AddSubfield()                             */
+/************************************************************************/
+
+void DDFFieldDefn::AddSubfield( DDFSubfieldDefn *poNewSFDefn,
+                                int bDontAddToFormat )
+
+{
+    nSubfieldCount++;
+    papoSubfields = (DDFSubfieldDefn ** )
+        CPLRealloc( papoSubfields, sizeof(void*) * nSubfieldCount );
+    papoSubfields[nSubfieldCount-1] = poNewSFDefn;
+    
+    if( bDontAddToFormat )
+        return;
+
+/* -------------------------------------------------------------------- */
+/*      Add this format to the format list.  We don't bother            */
+/*      aggregating formats here.                                       */
+/* -------------------------------------------------------------------- */
+    if( _formatControls == NULL || strlen(_formatControls) == 0 )
+    {
+        CPLFree( _formatControls );
+        _formatControls = CPLStrdup( "()" );
+    }
+    
+    int nOldLen = strlen(_formatControls);
+    
+    char *pszNewFormatControls = (char *) 
+        CPLMalloc(nOldLen+3+strlen(poNewSFDefn->GetFormat()));
+    
+    strcpy( pszNewFormatControls, _formatControls );
+    pszNewFormatControls[nOldLen-1] = '\0';
+    if( pszNewFormatControls[nOldLen-2] != '(' )
+        strcat( pszNewFormatControls, "," );
+    
+    strcat( pszNewFormatControls, poNewSFDefn->GetFormat() );
+    strcat( pszNewFormatControls, ")" );
+    
+    CPLFree( _formatControls );
+    _formatControls = pszNewFormatControls;
+
+/* -------------------------------------------------------------------- */
+/*      Add the subfield name to the list.                              */
+/* -------------------------------------------------------------------- */
+    if( _arrayDescr == NULL )
+        _arrayDescr = CPLStrdup("");
+
+    _arrayDescr = (char *) 
+        CPLRealloc(_arrayDescr, 
+                   strlen(_arrayDescr)+strlen(poNewSFDefn->GetName())+2);
+    if( strlen(_arrayDescr) > 0 )
+        strcat( _arrayDescr, "!" );
+    strcat( _arrayDescr, poNewSFDefn->GetName() );
+}
+
+/************************************************************************/
+/*                               Create()                               */
+/*                                                                      */
+/*      Initialize a new field defn from application input, instead     */
+/*      of from an existing file.                                       */
+/************************************************************************/
+
+int DDFFieldDefn::Create( const char *pszTag, const char *pszFieldName, 
+                          const char *pszDescription, 
+                          DDF_data_struct_code eDataStructCode,
+                          DDF_data_type_code   eDataTypeCode,
+                          const char *pszFormat )
+
+{
+    CPLAssert( this->pszTag == NULL );
+    poModule = NULL;
+    this->pszTag = CPLStrdup( pszTag );
+    _fieldName = CPLStrdup( pszFieldName );
+    _arrayDescr = CPLStrdup( pszDescription );
+    _formatControls = CPLStrdup( "" );
+    
+    _data_struct_code = eDataStructCode;
+    _data_type_code = eDataTypeCode;
+
+    if( pszFormat != NULL )
+        _formatControls = CPLStrdup( pszFormat );
+
+    if( pszDescription != NULL && *pszDescription == '*' )
+        bRepeatingSubfields = TRUE;
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                          GenerateDDREntry()                          */
+/************************************************************************/
+
+int DDFFieldDefn::GenerateDDREntry( char **ppachData, 
+                                    int *pnLength )
+
+{
+    *pnLength = 9 + strlen(_fieldName) + 1 
+        + strlen(_arrayDescr) + 1
+        + strlen(_formatControls) + 1;
+
+    if( strlen(_formatControls) == 0 )
+        *pnLength -= 1;
+
+    if( ppachData == NULL )
+        return TRUE;
+
+    *ppachData = (char *) CPLMalloc( *pnLength+1 );
+    
+    if( _data_struct_code == dsc_elementary )
+        (*ppachData)[0] = '0';
+    else if( _data_struct_code == dsc_vector )
+        (*ppachData)[0] = '1';
+    else if( _data_struct_code == dsc_array )
+        (*ppachData)[0] = '2';
+    else if( _data_struct_code == dsc_concatenated )
+        (*ppachData)[0] = '3';
+    
+    if( _data_type_code == dtc_char_string )
+        (*ppachData)[1] = '0';
+    else if( _data_type_code == dtc_implicit_point )
+        (*ppachData)[1] = '1';
+    else if( _data_type_code == dtc_explicit_point )
+        (*ppachData)[1] = '2';
+    else if( _data_type_code == dtc_explicit_point_scaled )
+        (*ppachData)[1] = '3';
+    else if( _data_type_code == dtc_char_bit_string )
+        (*ppachData)[1] = '4';
+    else if( _data_type_code == dtc_bit_string )
+        (*ppachData)[1] = '5';
+    else if( _data_type_code == dtc_mixed_data_type )
+        (*ppachData)[1] = '6';
+
+    (*ppachData)[2] = '0';
+    (*ppachData)[3] = '0';
+    (*ppachData)[4] = ';';
+    (*ppachData)[5] = '&';
+    (*ppachData)[6] = ' ';
+    (*ppachData)[7] = ' ';
+    (*ppachData)[8] = ' ';
+    sprintf( *ppachData + 9, "%s%c%s", 
+             _fieldName, DDF_UNIT_TERMINATOR, _arrayDescr );
+
+    if( strlen(_formatControls) > 0 )
+        sprintf( *ppachData + strlen(*ppachData), "%c%s",
+                 DDF_UNIT_TERMINATOR, _formatControls );
+    sprintf( *ppachData + strlen(*ppachData), "%c", DDF_FIELD_TERMINATOR );
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                             Initialize()                             */
+/*                                                                      */
+/*      Initialize the field definition from the information in the     */
+/*      DDR record.  This is called by DDFModule::Open().               */
+/************************************************************************/
+
+int DDFFieldDefn::Initialize( DDFModule * poModuleIn,
+                              const char * pszTagIn, 
+                              int nFieldEntrySize,
+                              const char * pachFieldArea )
+
+{
+    int         iFDOffset = poModuleIn->GetFieldControlLength();
+    int         nCharsConsumed;
+
+    poModule = poModuleIn;
+    
+    pszTag = CPLStrdup( pszTagIn );
+
+/* -------------------------------------------------------------------- */
+/*      Set the data struct and type codes.                             */
+/* -------------------------------------------------------------------- */
+    switch( pachFieldArea[0] )
+    {
+      case '0':
+        _data_struct_code = dsc_elementary;
+        break;
+
+      case '1':
+        _data_struct_code = dsc_vector;
+        break;
+
+      case '2':
+        _data_struct_code = dsc_array;
+        break;
+
+      case '3':
+        _data_struct_code = dsc_concatenated;
+        break;
+
+      default:
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Unrecognised data_struct_code value %c.\n"
+                  "Field %s initialization incorrect.",
+                  pachFieldArea[0], pszTag );
+        _data_struct_code = dsc_elementary;
+    }
+
+    switch( pachFieldArea[1] )
+    {
+      case '0':
+        _data_type_code = dtc_char_string;
+        break;
+        
+      case '1':
+        _data_type_code = dtc_implicit_point;
+        break;
+        
+      case '2':
+        _data_type_code = dtc_explicit_point;
+        break;
+        
+      case '3':
+        _data_type_code = dtc_explicit_point_scaled;
+        break;
+        
+      case '4':
+        _data_type_code = dtc_char_bit_string;
+        break;
+        
+      case '5':
+        _data_type_code = dtc_bit_string;
+        break;
+        
+      case '6':
+        _data_type_code = dtc_mixed_data_type;
+        break;
+
+      default:
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Unrecognised data_type_code value %c.\n"
+                  "Field %s initialization incorrect.",
+                  pachFieldArea[1], pszTag );
+        _data_type_code = dtc_char_string;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Capture the field name, description (sub field names), and      */
+/*      format statements.                                              */
+/* -------------------------------------------------------------------- */
+
+    _fieldName =
+        DDFFetchVariable( pachFieldArea + iFDOffset,
+                          nFieldEntrySize - iFDOffset,
+                          DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR,
+                          &nCharsConsumed );
+    iFDOffset += nCharsConsumed;
+    
+    _arrayDescr =
+        DDFFetchVariable( pachFieldArea + iFDOffset,
+                          nFieldEntrySize - iFDOffset,
+                          DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, 
+                          &nCharsConsumed );
+    iFDOffset += nCharsConsumed;
+    
+    _formatControls =
+        DDFFetchVariable( pachFieldArea + iFDOffset,
+                          nFieldEntrySize - iFDOffset,
+                          DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, 
+                          &nCharsConsumed );
+    
+/* -------------------------------------------------------------------- */
+/*      Parse the subfield info.                                        */
+/* -------------------------------------------------------------------- */
+    if( _data_struct_code != dsc_elementary )
+    {
+        if( !BuildSubfields() )
+            return FALSE;
+
+        if( !ApplyFormats() )
+            return FALSE;
+    }
+    
+    return TRUE;
+}
+
+/************************************************************************/
+/*                                Dump()                                */
+/************************************************************************/
+
+/**
+ * Write out field definition info to debugging file.
+ *
+ * A variety of information about this field definition, and all it's
+ * subfields is written to the give debugging file handle.
+ *
+ * @param fp The standard io file handle to write to.  ie. stderr
+ */
+
+void DDFFieldDefn::Dump( FILE * fp )
+
+{
+    const char  *pszValue = "";
+    
+    fprintf( fp, "  DDFFieldDefn:\n" );
+    fprintf( fp, "      Tag = `%s'\n", pszTag );
+    fprintf( fp, "      _fieldName = `%s'\n", _fieldName );
+    fprintf( fp, "      _arrayDescr = `%s'\n", _arrayDescr );
+    fprintf( fp, "      _formatControls = `%s'\n", _formatControls );
+
+    switch( _data_struct_code )
+    {
+      case dsc_elementary:
+        pszValue = "elementary";
+        break;
+        
+      case dsc_vector:
+        pszValue = "vector";
+        break;
+        
+      case dsc_array:
+        pszValue = "array";
+        break;
+        
+      case dsc_concatenated:
+        pszValue = "concatenated";
+        break;
+        
+      default:
+        CPLAssert( FALSE );
+        pszValue = "(unknown)";
+    }
+
+    fprintf( fp, "      _data_struct_code = %s\n", pszValue );
+
+    switch( _data_type_code )
+    {
+      case dtc_char_string:
+        pszValue = "char_string";
+        break;
+        
+      case dtc_implicit_point:
+        pszValue = "implicit_point";
+        break;
+        
+      case dtc_explicit_point:
+        pszValue = "explicit_point";
+        break;
+        
+      case dtc_explicit_point_scaled:
+        pszValue = "explicit_point_scaled";
+        break;
+        
+      case dtc_char_bit_string:
+        pszValue = "char_bit_string";
+        break;
+        
+      case dtc_bit_string:
+        pszValue = "bit_string";
+        break;
+        
+      case dtc_mixed_data_type:
+        pszValue = "mixed_data_type";
+        break;
+
+      default:
+        CPLAssert( FALSE );
+        pszValue = "(unknown)";
+        break;
+    }
+    
+    fprintf( fp, "      _data_type_code = %s\n", pszValue );
+
+    for( int i = 0; i < nSubfieldCount; i++ )
+        papoSubfields[i]->Dump( fp );
+}
+
+/************************************************************************/
+/*                           BuildSubfields()                           */
+/*                                                                      */
+/*      Based on the _arrayDescr build a set of subfields.              */
+/************************************************************************/
+
+int DDFFieldDefn::BuildSubfields()
+
+{
+    char        **papszSubfieldNames;
+    const char  *pszSublist = _arrayDescr;
+
+/* -------------------------------------------------------------------- */
+/*      It is valid to define a field with _arrayDesc                   */
+/*      '*STPT!CTPT!ENPT*YCOO!XCOO' and formatControls '(2b24)'.        */
+/*      This basically indicates that there are 3 (YCOO,XCOO)           */
+/*      structures named STPT, CTPT and ENPT.  But we can't handle      */
+/*      such a case gracefully here, so we just ignore the              */
+/*      "structure names" and treat such a thing as a repeating         */
+/*      YCOO/XCOO array.  This occurs with the AR2D field of some       */
+/*      AML S-57 files for instance.                                    */
+/*                                                                      */
+/*      We accomplish this by ignoring everything before the last       */
+/*      '*' in the subfield list.                                       */
+/* -------------------------------------------------------------------- */
+    if( strrchr(pszSublist, '*') != NULL )
+        pszSublist = strrchr(pszSublist,'*');
+
+/* -------------------------------------------------------------------- */
+/*      Strip off the repeating marker, when it occurs, but mark our    */
+/*      field as repeating.                                             */
+/* -------------------------------------------------------------------- */
+    if( pszSublist[0] == '*' )
+    {
+        bRepeatingSubfields = TRUE;
+        pszSublist++;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      split list of fields .                                          */
+/* -------------------------------------------------------------------- */
+    papszSubfieldNames = CSLTokenizeStringComplex( pszSublist, "!",
+                                                   FALSE, FALSE );
+
+/* -------------------------------------------------------------------- */
+/*      minimally initialize the subfields.  More will be done later.   */
+/* -------------------------------------------------------------------- */
+    int nSFCount = CSLCount( papszSubfieldNames );
+    for( int iSF = 0; iSF < nSFCount; iSF++ )
+    {
+        DDFSubfieldDefn *poSFDefn = new DDFSubfieldDefn;
+        
+        poSFDefn->SetName( papszSubfieldNames[iSF] );
+        AddSubfield( poSFDefn, TRUE );
+    }
+
+    CSLDestroy( papszSubfieldNames );
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                          ExtractSubstring()                          */
+/*                                                                      */
+/*      Extract a substring terminated by a comma (or end of            */
+/*      string).  Commas in brackets are ignored as terminated with     */
+/*      bracket nesting understood gracefully.  If the returned         */
+/*      string would being and end with a bracket then strip off the    */
+/*      brackets.                                                       */
+/*                                                                      */
+/*      Given a string like "(A,3(B,C),D),X,Y)" return "A,3(B,C),D".    */
+/*      Give a string like "3A,2C" return "3A".                         */
+/************************************************************************/
+
+char *DDFFieldDefn::ExtractSubstring( const char * pszSrc )
+
+{
+    int         nBracket=0, i;
+    char        *pszReturn;
+
+    for( i = 0;
+         pszSrc[i] != '\0' && (nBracket > 0 || pszSrc[i] != ',');
+         i++ )
+    {
+        if( pszSrc[i] == '(' )
+            nBracket++;
+        else if( pszSrc[i] == ')' )
+            nBracket--;
+    }
+
+    if( pszSrc[0] == '(' )
+    {
+        pszReturn = CPLStrdup( pszSrc + 1 );
+        pszReturn[i-2] = '\0';
+    }
+    else
+    {
+        pszReturn = CPLStrdup( pszSrc  );
+        pszReturn[i] = '\0';
+    }
+
+    return pszReturn;
+}
+
+/************************************************************************/
+/*                            ExpandFormat()                            */
+/************************************************************************/
+
+char *DDFFieldDefn::ExpandFormat( const char * pszSrc )
+
+{
+    int         nDestMax = 32;
+    char       *pszDest = (char *) CPLMalloc(nDestMax+1);
+    int         iSrc, iDst;
+    int         nRepeat = 0;
+
+    iSrc = 0;
+    iDst = 0;
+    pszDest[0] = '\0';
+
+    while( pszSrc[iSrc] != '\0' )
+    {
+        /* This is presumably an extra level of brackets around some
+           binary stuff related to rescaning which we don't care to do
+           (see 6.4.3.3 of the standard.  We just strip off the extra
+           layer of brackets */
+        if( (iSrc == 0 || pszSrc[iSrc-1] == ',') && pszSrc[iSrc] == '(' )
+        {
+            char       *pszContents = ExtractSubstring( pszSrc+iSrc );
+            char       *pszExpandedContents = ExpandFormat( pszContents );
+
+            if( (int) (strlen(pszExpandedContents) + strlen(pszDest) + 1)
+                > nDestMax )
+            {
+                nDestMax = 2 * (strlen(pszExpandedContents) + strlen(pszDest));
+                pszDest = (char *) CPLRealloc(pszDest,nDestMax+1);
+            }
+
+            strcat( pszDest, pszExpandedContents );
+            iDst = strlen(pszDest);
+            
+            iSrc = iSrc + strlen(pszContents) + 2;
+
+            CPLFree( pszContents );
+            CPLFree( pszExpandedContents );
+        }
+
+        /* this is a repeated subclause */
+        else if( (iSrc == 0 || pszSrc[iSrc-1] == ',')
+                 && isdigit(pszSrc[iSrc]) )
+        {
+            const char *pszNext;
+            nRepeat = atoi(pszSrc+iSrc);
+            
+            // skip over repeat count.
+            for( pszNext = pszSrc+iSrc; isdigit(*pszNext); pszNext++ )
+                iSrc++;
+
+            char       *pszContents = ExtractSubstring( pszNext );
+            char       *pszExpandedContents = ExpandFormat( pszContents );
+                
+            for( int i = 0; i < nRepeat; i++ )
+            {
+                if( (int) (strlen(pszExpandedContents) + strlen(pszDest) + 1)
+                    > nDestMax )
+                {
+                    nDestMax = 
+                        2 * (strlen(pszExpandedContents) + strlen(pszDest));
+                    pszDest = (char *) CPLRealloc(pszDest,nDestMax+1);
+                }
+
+                strcat( pszDest, pszExpandedContents );
+                if( i < nRepeat-1 )
+                    strcat( pszDest, "," );
+            }
+
+            iDst = strlen(pszDest);
+            
+            if( pszNext[0] == '(' )
+                iSrc = iSrc + strlen(pszContents) + 2;
+            else
+                iSrc = iSrc + strlen(pszContents);
+
+            CPLFree( pszContents );
+            CPLFree( pszExpandedContents );
+        }
+        else
+        {
+            if( iDst+1 >= nDestMax )
+            {
+                nDestMax = 2 * iDst;
+                pszDest = (char *) CPLRealloc(pszDest,nDestMax);
+            }
+
+            pszDest[iDst++] = pszSrc[iSrc++];
+            pszDest[iDst] = '\0';
+        }
+    }
+
+    return pszDest;
+}
+                                 
+/************************************************************************/
+/*                            ApplyFormats()                            */
+/*                                                                      */
+/*      This method parses the format string partially, and then        */
+/*      applies a subfield format string to each subfield object.       */
+/*      It in turn does final parsing of the subfield formats.          */
+/************************************************************************/
+
+int DDFFieldDefn::ApplyFormats()
+
+{
+    char        *pszFormatList;
+    char        **papszFormatItems;
+    
+/* -------------------------------------------------------------------- */
+/*      Verify that the format string is contained within brackets.     */
+/* -------------------------------------------------------------------- */
+    if( strlen(_formatControls) < 2
+        || _formatControls[0] != '('
+        || _formatControls[strlen(_formatControls)-1] != ')' )
+    {
+        CPLError( CE_Warning, CPLE_DiscardedFormat,
+                  "Format controls for `%s' field missing brackets:%s",
+                  pszTag, _formatControls );
+        
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Duplicate the string, and strip off the brackets.               */
+/* -------------------------------------------------------------------- */
+
+    pszFormatList = ExpandFormat( _formatControls );
+
+/* -------------------------------------------------------------------- */
+/*      Tokenize based on commas.                                       */
+/* -------------------------------------------------------------------- */
+    papszFormatItems =
+        CSLTokenizeStringComplex(pszFormatList, ",", FALSE, FALSE );
+
+    CPLFree( pszFormatList );
+
+/* -------------------------------------------------------------------- */
+/*      Apply the format items to subfields.                            */
+/* -------------------------------------------------------------------- */
+    int iFormatItem;
+    
+    for( iFormatItem = 0;
+         papszFormatItems[iFormatItem] != NULL;
+         iFormatItem++ )
+    {
+        const char      *pszPastPrefix;
+
+        pszPastPrefix = papszFormatItems[iFormatItem];
+        while( *pszPastPrefix >= '0' && *pszPastPrefix <= '9' )
+            pszPastPrefix++;
+
+        ///////////////////////////////////////////////////////////////
+        // Did we get too many formats for the subfields created
+        // by names?  This may be legal by the 8211 specification, but
+        // isn't encountered in any formats we care about so we just
+        // blow.
+        
+        if( iFormatItem >= nSubfieldCount )
+        {
+            CPLError( CE_Warning, CPLE_DiscardedFormat,
+                      "Got more formats than subfields for field `%s'.",
+                      pszTag );
+            break;
+        }
+        
+        if( !papoSubfields[iFormatItem]->SetFormat(pszPastPrefix) )
+            return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Verify that we got enough formats, cleanup and return.          */
+/* -------------------------------------------------------------------- */
+    CSLDestroy( papszFormatItems );
+
+    if( iFormatItem < nSubfieldCount )
+    {
+        CPLError( CE_Warning, CPLE_DiscardedFormat,
+                  "Got less formats than subfields for field `%s'.",
+                  pszTag );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If all the fields are fixed width, then we are fixed width      */
+/*      too.  This is important for repeating fields.                   */
+/* -------------------------------------------------------------------- */
+    nFixedWidth = 0;
+    for( int i = 0; i < nSubfieldCount; i++ )
+    {
+        if( papoSubfields[i]->GetWidth() == 0 )
+        {
+            nFixedWidth = 0;
+            break;
+        }
+        else
+            nFixedWidth += papoSubfields[i]->GetWidth();
+    }
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                          FindSubfieldDefn()                          */
+/************************************************************************/
+
+/**
+ * Find a subfield definition by it's mnemonic tag.  
+ *
+ * @param pszMnemonic The name of the field.
+ *
+ * @return The subfield pointer, or NULL if there isn't any such subfield.
+ */
+ 
+
+DDFSubfieldDefn *DDFFieldDefn::FindSubfieldDefn( const char * pszMnemonic )
+
+{
+    for( int i = 0; i < nSubfieldCount; i++ )
+    {
+        if( EQUAL(papoSubfields[i]->GetName(),pszMnemonic) )
+            return papoSubfields[i];
+    }
+
+    return NULL;
+}
+
+/************************************************************************/
+/*                            GetSubfield()                             */
+/*                                                                      */
+/*      Fetch a subfield by it's index.                                 */
+/************************************************************************/
+
+/**
+ * Fetch a subfield by index.
+ *
+ * @param i The index subfield index. (Between 0 and GetSubfieldCount()-1)
+ *
+ * @return The subfield pointer, or NULL if the index is out of range.
+ */
+
+DDFSubfieldDefn *DDFFieldDefn::GetSubfield( int i )
+
+{
+    if( i < 0 || i >= nSubfieldCount )
+    {
+        CPLAssert( FALSE );
+        return NULL;
+    }
+             
+    return papoSubfields[i];
+}
+
+/************************************************************************/
+/*                          GetDefaultValue()                           */
+/************************************************************************/
+
+/**
+ * Return default data for field instance.
+ */
+
+char *DDFFieldDefn::GetDefaultValue( int *pnSize )
+
+{                                                                       
+/* -------------------------------------------------------------------- */
+/*      Loop once collecting the sum of the subfield lengths.           */
+/* -------------------------------------------------------------------- */
+    int iSubfield;
+    int nTotalSize = 0;
+
+    for( iSubfield = 0; iSubfield < nSubfieldCount; iSubfield++ )
+    {
+        int nSubfieldSize;
+
+        if( !papoSubfields[iSubfield]->GetDefaultValue( NULL, 0, 
+                                                        &nSubfieldSize ) )
+            return NULL;
+        nTotalSize += nSubfieldSize;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Allocate buffer.                                                */
+/* -------------------------------------------------------------------- */
+    char *pachData = (char *) CPLMalloc( nTotalSize );
+
+    if( pnSize != NULL )
+        *pnSize = nTotalSize;
+
+/* -------------------------------------------------------------------- */
+/*      Loop again, collecting actual default values.                   */
+/* -------------------------------------------------------------------- */
+    int nOffset = 0;
+    for( iSubfield = 0; iSubfield < nSubfieldCount; iSubfield++ )
+    {
+        int nSubfieldSize;
+
+        if( !papoSubfields[iSubfield]->GetDefaultValue( 
+                pachData + nOffset, nTotalSize - nOffset, &nSubfieldSize ) )
+        {
+            CPLAssert( FALSE );
+            return NULL;
+        }
+
+        nOffset += nSubfieldSize;
+    }
+
+    CPLAssert( nOffset == nTotalSize );
+
+    return pachData;
+}
diff --git a/Utilities/GDAL/frmts/iso8211/ddfmodule.cpp b/Utilities/GDAL/frmts/iso8211/ddfmodule.cpp
new file mode 100644
index 0000000000..ba1ba97292
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/ddfmodule.cpp
@@ -0,0 +1,759 @@
+/******************************************************************************
+ * $Id: ddfmodule.cpp,v 1.16 2005/10/13 22:02:39 fwarmerdam Exp $
+ *
+ * Project:  ISO 8211 Access
+ * Purpose:  Implements the DDFModule class.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: ddfmodule.cpp,v $
+ * Revision 1.16  2005/10/13 22:02:39  fwarmerdam
+ * dont add fields that fail to initialize
+ *
+ * Revision 1.15  2004/02/18 14:09:42  warmerda
+ * doc fixups
+ *
+ * Revision 1.14  2003/07/03 15:38:46  warmerda
+ * some write capabilities added
+ *
+ * Revision 1.13  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.12  2000/09/19 14:08:51  warmerda
+ * keep and report _extendedCharSet
+ *
+ * Revision 1.11  1999/11/18 19:03:04  warmerda
+ * expanded tabs
+ *
+ * Revision 1.10  1999/11/18 19:02:38  warmerda
+ * added failquietly to open
+ *
+ * Revision 1.9  1999/09/03 14:14:39  warmerda
+ * fix cloning
+ *
+ * Revision 1.8  1999/09/02 03:10:01  warmerda
+ * fixed subtle problem with rewinding modules with reusable headers
+ *
+ * Revision 1.7  1999/08/16 15:44:29  warmerda
+ * Fixed bug in FindFieldDefn().
+ *
+ * Revision 1.6  1999/08/13 03:26:14  warmerda
+ * added Rewind()
+ *
+ * Revision 1.5  1999/05/08 20:15:59  warmerda
+ * added validity checking, and better cleanup on error
+ *
+ * Revision 1.4  1999/05/07 14:11:49  warmerda
+ * added support for tracking record clones
+ *
+ * Revision 1.3  1999/05/06 15:39:45  warmerda
+ * avoid redeclaring variable i
+ *
+ * Revision 1.2  1999/05/06 14:23:10  warmerda
+ * added Close(), and minor optimizations
+ *
+ * Revision 1.1  1999/04/27 18:45:05  warmerda
+ * New
+ *
+ */
+
+#include "iso8211.h"
+#include "cpl_conv.h"
+
+CPL_CVSID("$Id: ddfmodule.cpp,v 1.16 2005/10/13 22:02:39 fwarmerdam Exp $");
+
+/************************************************************************/
+/*                             DDFModule()                              */
+/************************************************************************/
+
+/**
+ * The constructor.
+ */
+
+DDFModule::DDFModule()
+
+{
+    nFieldDefnCount = 0;
+    papoFieldDefns = NULL;
+    poRecord = NULL;
+
+    papoClones = NULL;
+    nCloneCount = nMaxCloneCount = 0;
+
+    fpDDF = NULL;
+    bReadOnly = TRUE;
+
+    _interchangeLevel = '\0';
+    _inlineCodeExtensionIndicator = '\0';
+    _versionNumber = '\0';
+    _appIndicator = '\0';
+    _fieldControlLength = '\0';
+    strcpy( _extendedCharSet, " ! " );
+
+    _recLength = 0;
+    _leaderIden = 'L';
+    _fieldAreaStart = 0;
+    _sizeFieldLength = 0;
+    _sizeFieldPos = 0;
+    _sizeFieldTag = 0;
+}
+
+/************************************************************************/
+/*                             ~DDFModule()                             */
+/************************************************************************/
+
+/**
+ * The destructor.
+ */
+
+DDFModule::~DDFModule()
+
+{
+    Close();
+}
+
+/************************************************************************/
+/*                               Close()                                */
+/*                                                                      */
+/*      Note that closing a file also destroys essentially all other    */
+/*      module datastructures.                                          */
+/************************************************************************/
+
+/**
+ * Close an ISO 8211 file.
+ */
+
+void DDFModule::Close()
+
+{
+/* -------------------------------------------------------------------- */
+/*      Close the file.                                                 */
+/* -------------------------------------------------------------------- */
+    if( fpDDF != NULL )
+    {
+        VSIFClose( fpDDF );
+        fpDDF = NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup the working record.                                     */
+/* -------------------------------------------------------------------- */
+    if( poRecord != NULL )
+    {
+        delete poRecord;
+        poRecord = NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup the clones.  Deleting them will cause a callback to     */
+/*      remove them from the list.                                      */
+/* -------------------------------------------------------------------- */
+    while( nCloneCount > 0 )
+        delete papoClones[0];
+
+    nMaxCloneCount = 0;
+    CPLFree( papoClones );
+    papoClones = NULL;
+    
+/* -------------------------------------------------------------------- */
+/*      Cleanup the field definitions.                                  */
+/* -------------------------------------------------------------------- */
+    int i;
+
+    for( i = 0; i < nFieldDefnCount; i++ )
+        delete papoFieldDefns[i];
+    CPLFree( papoFieldDefns );
+    papoFieldDefns = NULL;
+    nFieldDefnCount = 0;
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/*                                                                      */
+/*      Open an ISO 8211 file, and read the DDR record to build the     */
+/*      field definitions.                                              */
+/************************************************************************/
+
+/**
+ * Open a ISO 8211 (DDF) file for reading.
+ *
+ * If the open succeeds the data descriptive record (DDR) will have been
+ * read, and all the field and subfield definitions will be available.
+ *
+ * @param pszFilename   The name of the file to open.
+ * @param bFailQuietly If FALSE a CPL Error is issued for non-8211 files, 
+ * otherwise quietly return NULL.
+ *
+ * @return FALSE if the open fails or TRUE if it succeeds.  Errors messages
+ * are issued internally with CPLError().
+ */
+
+int DDFModule::Open( const char * pszFilename, int bFailQuietly )
+
+{
+    static const size_t nLeaderSize = 24;
+
+/* -------------------------------------------------------------------- */
+/*      Close the existing file if there is one.                        */
+/* -------------------------------------------------------------------- */
+    if( fpDDF != NULL )
+        Close();
+    
+/* -------------------------------------------------------------------- */
+/*      Open the file.                                                  */
+/* -------------------------------------------------------------------- */
+    fpDDF = VSIFOpen( pszFilename, "rb" );
+
+    if( fpDDF == NULL )
+    {
+        if( !bFailQuietly )
+            CPLError( CE_Failure, CPLE_OpenFailed,
+                      "Unable to open DDF file `%s'.",
+                      pszFilename );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read the 24 byte leader.                                        */
+/* -------------------------------------------------------------------- */
+    char        achLeader[nLeaderSize];
+    
+    if( VSIFRead( achLeader, 1, nLeaderSize, fpDDF ) != nLeaderSize )
+    {
+        VSIFClose( fpDDF );
+        fpDDF = NULL;
+
+        if( !bFailQuietly )
+            CPLError( CE_Failure, CPLE_FileIO,
+                      "Leader is short on DDF file `%s'.",
+                      pszFilename );
+        
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Verify that this appears to be a valid DDF file.                */
+/* -------------------------------------------------------------------- */
+    int         i, bValid = TRUE;
+
+    for( i = 0; i < (int)nLeaderSize; i++ )
+    {
+        if( achLeader[i] < 32 || achLeader[i] > 126 )
+            bValid = FALSE;
+    }
+
+    if( achLeader[5] != '1' && achLeader[5] != '2' && achLeader[5] != '3' )
+        bValid = FALSE;
+
+    if( achLeader[6] != 'L' )
+        bValid = FALSE;
+    if( achLeader[8] != '1' && achLeader[8] != ' ' )
+        bValid = FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Extract information from leader.                                */
+/* -------------------------------------------------------------------- */
+
+    if( bValid )
+    {
+        _recLength                        = DDFScanInt( achLeader+0, 5 );
+        _interchangeLevel                 = achLeader[5];
+        _leaderIden                   = achLeader[6];
+        _inlineCodeExtensionIndicator = achLeader[7];
+        _versionNumber                = achLeader[8];
+        _appIndicator                 = achLeader[9];
+        _fieldControlLength           = DDFScanInt(achLeader+10,2);
+        _fieldAreaStart               = DDFScanInt(achLeader+12,5);
+        _extendedCharSet[0]           = achLeader[17];
+        _extendedCharSet[1]           = achLeader[18];
+        _extendedCharSet[2]           = achLeader[19];
+        _extendedCharSet[3]           = '\0';
+        _sizeFieldLength              = DDFScanInt(achLeader+20,1);
+        _sizeFieldPos                 = DDFScanInt(achLeader+21,1);
+        _sizeFieldTag                 = DDFScanInt(achLeader+23,1);
+
+        if( _recLength < 12 || _fieldControlLength == 0
+            || _fieldAreaStart < 24 || _sizeFieldLength == 0
+            || _sizeFieldPos == 0 || _sizeFieldTag == 0 )
+        {
+            bValid = FALSE;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If the header is invalid, then clean up, report the error       */
+/*      and return.                                                     */
+/* -------------------------------------------------------------------- */
+    if( !bValid )
+    {
+        VSIFClose( fpDDF );
+        fpDDF = NULL;
+
+        if( !bFailQuietly )
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "File `%s' does not appear to have\n"
+                      "a valid ISO 8211 header.\n",
+                      pszFilename );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read the whole record info memory.                              */
+/* -------------------------------------------------------------------- */
+    char        *pachRecord;
+
+    pachRecord = (char *) CPLMalloc(_recLength);
+    memcpy( pachRecord, achLeader, nLeaderSize );
+
+    if( VSIFRead( pachRecord+nLeaderSize, 1, _recLength-nLeaderSize, fpDDF )
+        != _recLength - nLeaderSize )
+    {
+        if( !bFailQuietly )
+            CPLError( CE_Failure, CPLE_FileIO,
+                      "Header record is short on DDF file `%s'.",
+                      pszFilename );
+        
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      First make a pass counting the directory entries.               */
+/* -------------------------------------------------------------------- */
+    int         nFieldEntryWidth, nFDCount = 0;
+
+    nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
+
+    for( i = nLeaderSize; i < _recLength; i += nFieldEntryWidth )
+    {
+        if( pachRecord[i] == DDF_FIELD_TERMINATOR )
+            break;
+
+        nFDCount++;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Allocate, and read field definitions.                           */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < nFDCount; i++ )
+    {
+        char    szTag[128];
+        int     nEntryOffset = nLeaderSize + i*nFieldEntryWidth;
+        int     nFieldLength, nFieldPos;
+        DDFFieldDefn *poFDefn;
+        
+        strncpy( szTag, pachRecord+nEntryOffset, _sizeFieldTag );
+        szTag[_sizeFieldTag] = '\0';
+
+        nEntryOffset += _sizeFieldTag;
+        nFieldLength = DDFScanInt( pachRecord+nEntryOffset, _sizeFieldLength );
+        
+        nEntryOffset += _sizeFieldLength;
+        nFieldPos = DDFScanInt( pachRecord+nEntryOffset, _sizeFieldPos );
+        
+        poFDefn = new DDFFieldDefn();
+        if( poFDefn->Initialize( this, szTag, nFieldLength,
+                                 pachRecord+_fieldAreaStart+nFieldPos ) )
+            AddField( poFDefn );
+        else
+            delete poFDefn;
+    }
+
+    CPLFree( pachRecord );
+    
+/* -------------------------------------------------------------------- */
+/*      Record the current file offset, the beginning of the first      */
+/*      data record.                                                    */
+/* -------------------------------------------------------------------- */
+    nFirstRecordOffset = VSIFTell( fpDDF );
+    
+    return TRUE;
+}
+
+/************************************************************************/
+/*                             Initialize()                             */
+/************************************************************************/
+
+int DDFModule::Initialize( char chInterchangeLevel,
+                           char chLeaderIden, 
+                           char chCodeExtensionIndicator,
+                           char chVersionNumber,
+                           char chAppIndicator,
+                           const char *pszExtendedCharSet,
+                           int nSizeFieldLength,
+                           int nSizeFieldPos,
+                           int nSizeFieldTag )
+
+{
+    _interchangeLevel = chInterchangeLevel;
+    _leaderIden = chLeaderIden;
+    _inlineCodeExtensionIndicator = chCodeExtensionIndicator;
+    _versionNumber = chVersionNumber;
+    _appIndicator = chAppIndicator;
+    strcpy( _extendedCharSet, pszExtendedCharSet );
+    _sizeFieldLength = nSizeFieldLength;
+    _sizeFieldPos = nSizeFieldPos;
+    _sizeFieldTag = nSizeFieldTag;
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                               Create()                               */
+/************************************************************************/
+
+int DDFModule::Create( const char *pszFilename )
+
+{
+    CPLAssert( fpDDF == NULL );
+    
+/* -------------------------------------------------------------------- */
+/*      Create the file on disk.                                        */
+/* -------------------------------------------------------------------- */
+    fpDDF = VSIFOpen( pszFilename, "wb+" );
+    if( fpDDF == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "Failed to create file %s, check path and permissions.",
+                  pszFilename );
+        return FALSE;
+    }
+    
+    bReadOnly = FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Prepare all the field definition information.                   */
+/* -------------------------------------------------------------------- */
+    int iField;
+
+    _fieldControlLength = 9;
+    _recLength = 24 
+        + nFieldDefnCount * (_sizeFieldLength+_sizeFieldPos+_sizeFieldTag) 
+        + 1;
+    
+    _fieldAreaStart = _recLength;
+
+    for( iField=0; iField < nFieldDefnCount; iField++ )
+    {
+        int nLength;
+
+        papoFieldDefns[iField]->GenerateDDREntry( NULL, &nLength );
+        _recLength += nLength;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Setup 24 byte leader.                                           */
+/* -------------------------------------------------------------------- */
+    char achLeader[25];
+
+    sprintf( achLeader+0, "%05d", (int) _recLength );
+    achLeader[5] = _interchangeLevel;
+    achLeader[6] = _leaderIden;
+    achLeader[7] = _inlineCodeExtensionIndicator;
+    achLeader[8] = _versionNumber;
+    achLeader[9] = _appIndicator;
+    sprintf( achLeader+10, "%02d", (int) _fieldControlLength );
+    sprintf( achLeader+12, "%05d", (int) _fieldAreaStart );
+    strncpy( achLeader+17, _extendedCharSet, 3 );
+    sprintf( achLeader+20, "%1d", (int) _sizeFieldLength );
+    sprintf( achLeader+21, "%1d", (int) _sizeFieldPos );
+    achLeader[22] = '0';
+    sprintf( achLeader+23, "%1d", (int) _sizeFieldTag );
+    VSIFWrite( achLeader, 24, 1, fpDDF );
+
+/* -------------------------------------------------------------------- */
+/*      Write out directory entries.                                    */
+/* -------------------------------------------------------------------- */
+    int nOffset = 0;
+    for( iField=0; iField < nFieldDefnCount; iField++ )
+    {
+        char achDirEntry[12];
+        int nLength;
+
+        papoFieldDefns[iField]->GenerateDDREntry( NULL, &nLength );
+
+        strcpy( achDirEntry, papoFieldDefns[iField]->GetName() );
+        sprintf( achDirEntry + _sizeFieldTag, "%03d", nLength );
+        sprintf( achDirEntry + _sizeFieldTag + _sizeFieldLength, 
+                 "%04d", nOffset );
+        nOffset += nLength;
+
+        VSIFWrite( achDirEntry, 11, 1, fpDDF );
+    }
+
+    char chUT = DDF_FIELD_TERMINATOR;
+    VSIFWrite( &chUT, 1, 1, fpDDF );
+
+/* -------------------------------------------------------------------- */
+/*      Write out the field descriptions themselves.                    */
+/* -------------------------------------------------------------------- */
+    for( iField=0; iField < nFieldDefnCount; iField++ )
+    {
+        char *pachData;
+        int nLength;
+
+        papoFieldDefns[iField]->GenerateDDREntry( &pachData, &nLength );
+        VSIFWrite( pachData, nLength, 1, fpDDF );
+        CPLFree( pachData );
+    }
+    
+    return TRUE;
+}
+
+/************************************************************************/
+/*                                Dump()                                */
+/************************************************************************/
+
+/**
+ * Write out module info to debugging file.
+ *
+ * A variety of information about the module is written to the debugging
+ * file.  This includes all the field and subfield definitions read from
+ * the header. 
+ *
+ * @param fp The standard io file handle to write to.  ie. stderr.
+ */
+
+void DDFModule::Dump( FILE * fp )
+
+{
+    fprintf( fp, "DDFModule:\n" );
+    fprintf( fp, "    _recLength = %ld\n", _recLength );
+    fprintf( fp, "    _interchangeLevel = %c\n", _interchangeLevel );
+    fprintf( fp, "    _leaderIden = %c\n", _leaderIden );
+    fprintf( fp, "    _inlineCodeExtensionIndicator = %c\n",
+             _inlineCodeExtensionIndicator );
+    fprintf( fp, "    _versionNumber = %c\n", _versionNumber );
+    fprintf( fp, "    _appIndicator = %c\n", _appIndicator );
+    fprintf( fp, "    _extendedCharSet = `%s'\n", _extendedCharSet );
+    fprintf( fp, "    _fieldControlLength = %d\n", _fieldControlLength );
+    fprintf( fp, "    _fieldAreaStart = %ld\n", _fieldAreaStart );
+    fprintf( fp, "    _sizeFieldLength = %ld\n", _sizeFieldLength );
+    fprintf( fp, "    _sizeFieldPos = %ld\n", _sizeFieldPos );
+    fprintf( fp, "    _sizeFieldTag = %ld\n", _sizeFieldTag );
+
+    for( int i = 0; i < nFieldDefnCount; i++ )
+    {
+        papoFieldDefns[i]->Dump( fp );
+    }
+}
+
+/************************************************************************/
+/*                           FindFieldDefn()                            */
+/************************************************************************/
+
+/**
+ * Fetch the definition of the named field.
+ *
+ * This function will scan the DDFFieldDefn's on this module, to find
+ * one with the indicated field name.
+ *
+ * @param pszFieldName The name of the field to search for.  The comparison is
+ *                     case insensitive.
+ *
+ * @return A pointer to the request DDFFieldDefn object is returned, or NULL
+ * if none matching the name are found.  The return object remains owned by
+ * the DDFModule, and should not be deleted by application code.
+ */
+
+DDFFieldDefn *DDFModule::FindFieldDefn( const char *pszFieldName )
+
+{
+    int         i;
+    
+/* -------------------------------------------------------------------- */
+/*      This pass tries to reduce the cost of comparing strings by      */
+/*      first checking the first character, and by using strcmp()       */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < nFieldDefnCount; i++ )
+    {
+        const char *pszThisName = papoFieldDefns[i]->GetName();
+        
+        if( *pszThisName == *pszFieldName
+            && strcmp( pszFieldName+1, pszThisName+1) == 0 )
+            return papoFieldDefns[i];
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Now do a more general check.  Application code may not          */
+/*      always use the correct name case.                               */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < nFieldDefnCount; i++ )
+    {
+        if( EQUAL(pszFieldName, papoFieldDefns[i]->GetName()) )
+            return papoFieldDefns[i];
+    }
+
+    return NULL;
+}
+
+/************************************************************************/
+/*                             ReadRecord()                             */
+/*                                                                      */
+/*      Read one record from the file, and return to the                */
+/*      application.  The returned record is owned by the module,       */
+/*      and is reused from call to call in order to preserve headers    */
+/*      when they aren't being re-read from record to record.           */
+/************************************************************************/
+
+/**
+ * Read one record from the file.
+ *
+ * @return A pointer to a DDFRecord object is returned, or NULL if a read
+ * error, or end of file occurs.  The returned record is owned by the
+ * module, and should not be deleted by the application.  The record is
+ * only valid untill the next ReadRecord() at which point it is overwritten.
+ */
+
+DDFRecord *DDFModule::ReadRecord()
+
+{
+    if( poRecord == NULL )
+        poRecord = new DDFRecord( this );
+
+    if( poRecord->Read() )
+        return poRecord;
+    else
+        return NULL;
+}
+
+/************************************************************************/
+/*                              AddField()                              */
+/************************************************************************/
+
+/**
+ * Add new field definition.
+ *
+ * Field definitions may only be added to DDFModules being used for 
+ * writing, not those being used for reading.  Ownership of the 
+ * DDFFieldDefn object is taken by the DDFModule.
+ *
+ * @param poNewFDefn definition to be added to the module. 
+ */
+
+void DDFModule::AddField( DDFFieldDefn *poNewFDefn )
+
+{
+    nFieldDefnCount++;
+    papoFieldDefns = (DDFFieldDefn **) 
+        CPLRealloc(papoFieldDefns, sizeof(void*)*nFieldDefnCount);
+    papoFieldDefns[nFieldDefnCount-1] = poNewFDefn;
+}
+
+/************************************************************************/
+/*                              GetField()                              */
+/************************************************************************/
+
+/**
+ * Fetch a field definition by index.
+ *
+ * @param i (from 0 to GetFieldCount() - 1.
+ * @return the returned field pointer or NULL if the index is out of range.
+ */
+
+DDFFieldDefn *DDFModule::GetField(int i)
+
+{
+    if( i < 0 || i >= nFieldDefnCount )
+        return NULL;
+    else
+        return papoFieldDefns[i];
+}
+    
+/************************************************************************/
+/*                           AddCloneRecord()                           */
+/*                                                                      */
+/*      We want to keep track of cloned records, so we can clean        */
+/*      them up when the module is destroyed.                           */
+/************************************************************************/
+
+void DDFModule::AddCloneRecord( DDFRecord * poRecord )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Do we need to grow the container array?                         */
+/* -------------------------------------------------------------------- */
+    if( nCloneCount == nMaxCloneCount )
+    {
+        nMaxCloneCount = nCloneCount*2 + 20;
+        papoClones = (DDFRecord **) CPLRealloc(papoClones,
+                                               nMaxCloneCount * sizeof(void*));
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Add to the list.                                                */
+/* -------------------------------------------------------------------- */
+    papoClones[nCloneCount++] = poRecord;
+}
+
+/************************************************************************/
+/*                         RemoveCloneRecord()                          */
+/************************************************************************/
+
+void DDFModule::RemoveCloneRecord( DDFRecord * poRecord )
+
+{
+    int         i;
+ 
+    for( i = 0; i < nCloneCount; i++ )
+    {
+        if( papoClones[i] == poRecord )
+        {
+            papoClones[i] = papoClones[nCloneCount-1];
+            nCloneCount--;
+            return;
+        }
+    }
+
+    CPLAssert( FALSE );
+}
+
+/************************************************************************/
+/*                               Rewind()                               */
+/************************************************************************/
+
+/**
+ * Return to first record.
+ * 
+ * The next call to ReadRecord() will read the first data record in the file.
+ *
+ * @param nOffset the offset in the file to return to.  By default this is
+ * -1, a special value indicating that reading should return to the first
+ * data record.  Otherwise it is an absolute byte offset in the file.
+ */
+
+void DDFModule::Rewind( long nOffset )
+
+{
+    if( nOffset == -1 )
+        nOffset = nFirstRecordOffset;
+
+    if( fpDDF == NULL )
+        return;
+    
+    VSIFSeek( fpDDF, nOffset, SEEK_SET );
+
+    if( nOffset == nFirstRecordOffset && poRecord != NULL )
+        poRecord->Clear();
+        
+}
diff --git a/Utilities/GDAL/frmts/iso8211/ddfrecord.cpp b/Utilities/GDAL/frmts/iso8211/ddfrecord.cpp
new file mode 100644
index 0000000000..278267a557
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/ddfrecord.cpp
@@ -0,0 +1,1941 @@
+/******************************************************************************
+ * $Id: ddfrecord.cpp,v 1.29 2006/02/24 15:59:24 fwarmerdam Exp $
+ *
+ * Project:  ISO 8211 Access
+ * Purpose:  Implements the DDFRecord class.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: ddfrecord.cpp,v $
+ * Revision 1.29  2006/02/24 15:59:24  fwarmerdam
+ * Added error checking on record buffer memory allocation.
+ *
+ * Revision 1.28  2005/10/16 04:09:20  fwarmerdam
+ * Avoid warnings in sprintf()s.
+ *
+ * Revision 1.27  2004/06/24 00:35:59  warmerda
+ * fixed bug in my last 'fix' for resizing records
+ *
+ * Revision 1.26  2004/06/23 19:03:59  warmerda
+ * fixed some serious problems in ResizeField(), especially with shrinking
+ *
+ * Revision 1.25  2004/03/10 18:09:35  warmerda
+ * Avoid warning on casting result of pointer subtraction.
+ *
+ * Revision 1.24  2004/02/18 14:09:42  warmerda
+ * doc fixups
+ *
+ * Revision 1.23  2003/12/08 20:32:48  warmerda
+ * Added some improved error checking in DDFRecord() on the leader values.
+ * Try reading extra bytes to find field terminator if the last byte in
+ * a record isn't the field terminator.
+ *
+ * Revision 1.22  2003/12/02 16:50:21  warmerda
+ * fixed problems with writing some kinds of records
+ *
+ * Revision 1.21  2003/11/12 21:21:55  warmerda
+ * fixed to include field terminators when creating fields
+ *
+ * Revision 1.20  2003/09/17 21:11:07  warmerda
+ * try to create new default field instance when needed
+ *
+ * Revision 1.19  2003/09/15 20:46:53  warmerda
+ * supressed some warnings in SetFieldRaw if field empty
+ *
+ * Revision 1.18  2003/09/11 19:56:35  warmerda
+ * avoid warnings
+ *
+ * Revision 1.17  2003/09/03 20:36:26  warmerda
+ * added subfield writing support
+ *
+ * Revision 1.16  2003/07/03 15:38:46  warmerda
+ * some write capabilities added
+ *
+ * Revision 1.15  2002/08/08 12:39:18  warmerda
+ * Added support for variable length records as per bugzilla bug 181.
+ *
+ * Revision 1.14  2001/08/30 21:08:19  warmerda
+ * expand tabs
+ *
+ * Revision 1.13  2001/08/27 19:09:00  warmerda
+ * added GetInstanceData() method on DDFField
+ *
+ * Revision 1.12  2001/08/24 19:41:19  warmerda
+ * fixed cloning problems
+ *
+ * Revision 1.11  2001/08/24 16:30:55  warmerda
+ * added DDFRecord update in place methods for S57 updating
+ *
+ * Revision 1.10  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.9  2000/02/15 17:55:53  warmerda
+ * Improved error message for corrupt DDF files.
+ *
+ * Revision 1.8  1999/11/18 19:03:04  warmerda
+ * expanded tabs
+ *
+ * Revision 1.7  1999/09/03 14:14:39  warmerda
+ * fix cloning
+ *
+ * Revision 1.6  1999/09/02 03:10:01  warmerda
+ * fixed subtle problem with rewinding modules with reusable headers
+ *
+ * Revision 1.5  1999/05/08 20:16:20  warmerda
+ * added some record validity testing
+ *
+ * Revision 1.4  1999/05/07 14:12:24  warmerda
+ * added record cloning, and subfield value fetches
+ *
+ * Revision 1.3  1999/05/06 14:48:28  warmerda
+ * Fixed EOF handling in files with reused headers
+ *
+ * Revision 1.2  1999/05/06 14:24:29  warmerda
+ * minor optimization, don't emit an error on EOF
+ *
+ * Revision 1.1  1999/04/27 18:45:05  warmerda
+ * New
+ *
+ */
+
+#include "iso8211.h"
+#include "cpl_conv.h"
+
+CPL_CVSID("$Id: ddfrecord.cpp,v 1.29 2006/02/24 15:59:24 fwarmerdam Exp $");
+
+static const size_t nLeaderSize = 24;
+
+/************************************************************************/
+/*                             DDFRecord()                              */
+/************************************************************************/
+
+DDFRecord::DDFRecord( DDFModule * poModuleIn )
+
+{
+    poModule = poModuleIn;
+
+    nReuseHeader = FALSE;
+
+    nFieldOffset = 0;
+
+    nDataSize = 0;
+    pachData = NULL;
+
+    nFieldCount = 0;
+    paoFields = NULL;
+
+    bIsClone = FALSE;
+
+    _sizeFieldTag = 4;
+    _sizeFieldPos = 0;
+    _sizeFieldLength = 0;
+}
+
+/************************************************************************/
+/*                             ~DDFRecord()                             */
+/************************************************************************/
+
+DDFRecord::~DDFRecord()
+
+{
+    Clear();
+
+    if( bIsClone )
+        poModule->RemoveCloneRecord( this );
+}
+
+/************************************************************************/
+/*                                Dump()                                */
+/************************************************************************/
+
+/**
+ * Write out record contents to debugging file.
+ *
+ * A variety of information about this record, and all it's fields and
+ * subfields is written to the given debugging file handle.  Note that
+ * field definition information (ala DDFFieldDefn) isn't written.
+ *
+ * @param fp The standard io file handle to write to.  ie. stderr
+ */
+
+void DDFRecord::Dump( FILE * fp )
+
+{
+    fprintf( fp, "DDFRecord:\n" );
+    fprintf( fp, "    nReuseHeader = %d\n", nReuseHeader );
+    fprintf( fp, "    nDataSize = %d\n", nDataSize );
+    fprintf( fp, 
+             "    _sizeFieldLength=%d, _sizeFieldPos=%d, _sizeFieldTag=%d\n",
+             _sizeFieldLength, _sizeFieldPos, _sizeFieldTag );
+
+    for( int i = 0; i < nFieldCount; i++ )
+    {
+        paoFields[i].Dump( fp );
+    }
+}
+
+/************************************************************************/
+/*                                Read()                                */
+/*                                                                      */
+/*      Read a record of data from the file, and parse the header to    */
+/*      build a field list for the record (or reuse the existing one    */
+/*      if reusing headers).  It is expected that the file pointer      */
+/*      will be positioned at the beginning of a data record.  It is    */
+/*      the DDFModule's responsibility to do so.                        */
+/*                                                                      */
+/*      This method should only be called by the DDFModule class.       */
+/************************************************************************/
+
+int DDFRecord::Read()
+
+{
+/* -------------------------------------------------------------------- */
+/*      Redefine the record on the basis of the header if needed.       */
+/*      As a side effect this will read the data for the record as well.*/
+/* -------------------------------------------------------------------- */
+    if( !nReuseHeader )
+    {
+        return( ReadHeader() );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Otherwise we read just the data and carefully overlay it on     */
+/*      the previous records data without disturbing the rest of the    */
+/*      record.                                                         */
+/* -------------------------------------------------------------------- */
+    size_t      nReadBytes;
+
+    nReadBytes = VSIFRead( pachData + nFieldOffset, 1,
+                           nDataSize - nFieldOffset,
+                           poModule->GetFP() );
+    if( nReadBytes != (size_t) (nDataSize - nFieldOffset)
+        && nReadBytes == 0
+        && VSIFEof( poModule->GetFP() ) )
+    {
+        return FALSE;
+    }
+    else if( nReadBytes != (size_t) (nDataSize - nFieldOffset) )
+    {
+        CPLError( CE_Failure, CPLE_FileIO,
+                  "Data record is short on DDF file.\n" );
+        
+        return FALSE;
+    }
+
+    // notdef: eventually we may have to do something at this point to 
+    // notify the DDFField's that their data values have changed. 
+    
+    return TRUE;
+}
+
+/************************************************************************/
+/*                               Write()                                */
+/************************************************************************/
+
+/**
+ * Write record out to module.
+ *
+ * This method writes the current record to the module to which it is 
+ * attached.  Normally this would be at the end of the file, and only used
+ * for modules newly created with DDFModule::Create().  Rewriting existing
+ * records is not supported at this time.  Calling Write() multiple times
+ * on a DDFRecord will result it multiple copies being written at the end of
+ * the module.
+ *
+ * @return TRUE on success or FALSE on failure.
+ */
+
+int DDFRecord::Write()
+
+{
+    if( !ResetDirectory() )
+        return FALSE;
+    
+/* -------------------------------------------------------------------- */
+/*      Prepare leader.                                                 */
+/* -------------------------------------------------------------------- */
+    char szLeader[nLeaderSize+1];
+
+    memset( szLeader, ' ', nLeaderSize );
+
+    sprintf( szLeader+0, "%05d", (int) (nDataSize + nLeaderSize) );
+    szLeader[5] = ' ';
+    szLeader[6] = 'D';
+    
+    sprintf( szLeader + 12, "%05d", (int) (nFieldOffset + nLeaderSize) );
+    szLeader[17] = ' ';
+
+    szLeader[20] = (char) ('0' + _sizeFieldLength);
+    szLeader[21] = (char) ('0' + _sizeFieldPos);
+    szLeader[22] = '0';
+    szLeader[23] = (char) ('0' + _sizeFieldTag);
+
+    /* notdef: lots of stuff missing */
+
+/* -------------------------------------------------------------------- */
+/*      Write the leader.                                               */
+/* -------------------------------------------------------------------- */
+    VSIFWrite( szLeader, nLeaderSize, 1, poModule->GetFP() );
+
+/* -------------------------------------------------------------------- */
+/*      Write the remainder of the record.                              */
+/* -------------------------------------------------------------------- */
+    VSIFWrite( pachData, nDataSize, 1, poModule->GetFP() );
+    
+    return TRUE;
+}
+
+/************************************************************************/
+/*                               Clear()                                */
+/*                                                                      */
+/*      Clear any information associated with the last header in        */
+/*      preparation for reading a new header.                           */
+/************************************************************************/
+
+void DDFRecord::Clear()
+
+{
+    if( paoFields != NULL )
+        delete[] paoFields;
+
+    paoFields = NULL;
+    nFieldCount = 0;
+
+    if( pachData != NULL )
+        CPLFree( pachData );
+
+    pachData = NULL;
+    nDataSize = 0;
+    nReuseHeader = FALSE;
+}
+
+/************************************************************************/
+/*                             ReadHeader()                             */
+/*                                                                      */
+/*      This perform the header reading and parsing job for the         */
+/*      Read() method.  It reads the header, and builds a field         */
+/*      list.                                                           */
+/************************************************************************/
+
+int DDFRecord::ReadHeader()
+
+{
+/* -------------------------------------------------------------------- */
+/*      Clear any existing information.                                 */
+/* -------------------------------------------------------------------- */
+    Clear();
+    
+/* -------------------------------------------------------------------- */
+/*      Read the 24 byte leader.                                        */
+/* -------------------------------------------------------------------- */
+    char        achLeader[nLeaderSize];
+    int         nReadBytes;
+
+    nReadBytes = VSIFRead(achLeader,1,nLeaderSize,poModule->GetFP());
+    if( nReadBytes == 0 && VSIFEof( poModule->GetFP() ) )
+    {
+        return FALSE;
+    }
+    else if( nReadBytes != (int) nLeaderSize )
+    {
+        CPLError( CE_Failure, CPLE_FileIO,
+                  "Leader is short on DDF file." );
+        
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Extract information from leader.                                */
+/* -------------------------------------------------------------------- */
+    int         _recLength, _fieldAreaStart;
+    char        _leaderIden;
+    
+    _recLength                    = DDFScanInt( achLeader+0, 5 );
+    _leaderIden                   = achLeader[6];
+    _fieldAreaStart               = DDFScanInt(achLeader+12,5);
+    
+    _sizeFieldLength = achLeader[20] - '0';
+    _sizeFieldPos = achLeader[21] - '0';
+    _sizeFieldTag = achLeader[23] - '0';
+
+    if( _sizeFieldLength < 0 || _sizeFieldLength > 9 
+        || _sizeFieldPos < 0 || _sizeFieldPos > 9
+        || _sizeFieldTag < 0 || _sizeFieldTag > 9 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "ISO8211 record leader appears to be corrupt." );
+        return FALSE;
+    }
+
+    if( _leaderIden == 'R' )
+        nReuseHeader = TRUE;
+
+    nFieldOffset = _fieldAreaStart - nLeaderSize;
+
+/* -------------------------------------------------------------------- */
+/*      Is there anything seemly screwy about this record?              */
+/* -------------------------------------------------------------------- */
+    if(( _recLength < 24 || _recLength > 100000000
+         || _fieldAreaStart < 24 || _fieldAreaStart > 100000 )
+       && (_recLength != 0))
+    {
+        CPLError( CE_Failure, CPLE_FileIO, 
+                  "Data record appears to be corrupt on DDF file.\n"
+                  " -- ensure that the files were uncompressed without modifying\n"
+                  "carriage return/linefeeds (by default WINZIP does this)." );
+        
+        return FALSE;
+    }
+
+/* ==================================================================== */
+/*      Handle the normal case with the record length available.        */
+/* ==================================================================== */
+    if(_recLength != 0) {
+/* -------------------------------------------------------------------- */
+/*      Read the remainder of the record.                               */
+/* -------------------------------------------------------------------- */
+        nDataSize = _recLength - nLeaderSize;
+        pachData = (char *) CPLMalloc(nDataSize);
+
+        if( VSIFRead( pachData, 1, nDataSize, poModule->GetFP()) !=
+            (size_t) nDataSize )
+        {
+            CPLError( CE_Failure, CPLE_FileIO, 
+                      "Data record is short on DDF file." );
+          
+            return FALSE;
+        }
+
+/* -------------------------------------------------------------------- */
+/*      If we don't find a field terminator at the end of the record    */
+/*      we will read extra bytes till we get to it.                     */
+/* -------------------------------------------------------------------- */
+        while( pachData[nDataSize-1] != DDF_FIELD_TERMINATOR )
+        {
+            nDataSize++;
+            pachData = (char *) CPLRealloc(pachData,nDataSize);
+            
+            if( VSIFRead( pachData + nDataSize - 1, 1, 1, poModule->GetFP() )
+                != 1 )
+            {
+                CPLError( CE_Failure, CPLE_FileIO, 
+                          "Data record is short on DDF file." );
+                
+                return FALSE;
+            }
+            CPLDebug( "ISO8211", 
+                      "Didn't find field terminator, read one more byte." );
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Loop over the directory entries, making a pass counting them.   */
+/* -------------------------------------------------------------------- */
+        int         i;
+        int         nFieldEntryWidth;
+      
+        nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
+        nFieldCount = 0;
+        for( i = 0; i < nDataSize; i += nFieldEntryWidth )
+        {
+            if( pachData[i] == DDF_FIELD_TERMINATOR )
+                break;
+          
+            nFieldCount++;
+        }
+    
+/* -------------------------------------------------------------------- */
+/*      Allocate, and read field definitions.                           */
+/* -------------------------------------------------------------------- */
+        paoFields = new DDFField[nFieldCount];
+    
+        for( i = 0; i < nFieldCount; i++ )
+        {
+            char    szTag[128];
+            int     nEntryOffset = i*nFieldEntryWidth;
+            int     nFieldLength, nFieldPos;
+          
+/* -------------------------------------------------------------------- */
+/*      Read the position information and tag.                          */
+/* -------------------------------------------------------------------- */
+            strncpy( szTag, pachData+nEntryOffset, _sizeFieldTag );
+            szTag[_sizeFieldTag] = '\0';
+          
+            nEntryOffset += _sizeFieldTag;
+            nFieldLength = DDFScanInt( pachData+nEntryOffset, _sizeFieldLength );
+          
+            nEntryOffset += _sizeFieldLength;
+            nFieldPos = DDFScanInt( pachData+nEntryOffset, _sizeFieldPos );
+          
+/* -------------------------------------------------------------------- */
+/*      Find the corresponding field in the module directory.           */
+/* -------------------------------------------------------------------- */
+            DDFFieldDefn    *poFieldDefn = poModule->FindFieldDefn( szTag );
+          
+            if( poFieldDefn == NULL )
+            {
+                CPLError( CE_Failure, CPLE_AppDefined,
+                          "Undefined field `%s' encountered in data record.",
+                          szTag );
+                return FALSE;
+            }
+
+/* -------------------------------------------------------------------- */
+/*      Assign info the DDFField.                                       */
+/* -------------------------------------------------------------------- */
+            paoFields[i].Initialize( poFieldDefn, 
+                                     pachData + _fieldAreaStart + nFieldPos - nLeaderSize,
+                                     nFieldLength );
+        }
+      
+        return TRUE;
+    }
+/* ==================================================================== */
+/*      Handle the exceptional case where the record length is          */
+/*      zero.  In this case we have to read all the data based on       */
+/*      the size of data items as per ISO8211 spec Annex C, 1.5.1.      */
+/*                                                                      */
+/*      See Bugzilla bug 181 and test with file US4CN21M.000.           */
+/* ==================================================================== */
+    else {
+        CPLDebug( "ISO8211", 
+                  "Record with zero length, use variant (C.1.5.1) logic." );
+
+        /* ----------------------------------------------------------------- */
+        /*   _recLength == 0, handle the large record.                       */
+        /*                                                                   */
+        /*   Read the remainder of the record.                               */
+        /* ----------------------------------------------------------------- */
+        nDataSize = 0;
+        pachData = NULL;
+
+        /* ----------------------------------------------------------------- */
+        /*   Loop over the directory entries, making a pass counting them.   */
+        /* ----------------------------------------------------------------- */
+        int nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
+        nFieldCount = 0;
+        int i=0;
+        char *tmpBuf = (char*)VSIMalloc(nFieldEntryWidth);
+
+        if( tmpBuf == NULL )
+        {
+            CPLError( CE_Failure, CPLE_OutOfMemory, 
+                      "Attempt to allocate %d byte ISO8211 record buffer failed.", 
+                      nFieldEntryWidth );
+            return FALSE;
+        }
+      
+        // while we're not at the end, store this entry,
+        // and keep on reading...
+        do {
+            // read an Entry:
+            if(nFieldEntryWidth != 
+               (int) VSIFRead(tmpBuf, 1, nFieldEntryWidth, poModule->GetFP())) {
+                CPLError(CE_Failure, CPLE_FileIO,
+                         "Data record is short on DDF file.");
+                return FALSE;
+            }
+      
+            // move this temp buffer into more permanent storage:
+            char *newBuf = (char*)CPLMalloc(nDataSize+nFieldEntryWidth);
+            if(pachData!=NULL) {
+                memcpy(newBuf, pachData, nDataSize);
+                CPLFree(pachData);
+            }
+            memcpy(&newBuf[nDataSize], tmpBuf, nFieldEntryWidth);
+            pachData = newBuf;
+            nDataSize += nFieldEntryWidth;
+
+            if(DDF_FIELD_TERMINATOR != tmpBuf[0]) {
+                nFieldCount++;
+            }
+        }
+        while(DDF_FIELD_TERMINATOR != tmpBuf[0]);
+
+        // Now, rewind a little.  Only the TERMINATOR should have been read:
+        int rewindSize = nFieldEntryWidth - 1;
+        FILE *fp = poModule->GetFP();
+        long pos = ftell(fp) - rewindSize;
+        fseek(fp, pos, SEEK_SET);
+        nDataSize -= rewindSize;
+
+        // --------------------------------------------------------------------
+        // Okay, now let's populate the heck out of pachData...
+        // --------------------------------------------------------------------
+        for(i=0; i<nFieldCount; i++) {
+            int nEntryOffset = (i*nFieldEntryWidth) + _sizeFieldTag;
+            int nFieldLength = DDFScanInt(pachData + nEntryOffset,
+                                          _sizeFieldLength);
+            char *tmpBuf = (char*)CPLMalloc(nFieldLength);
+
+            // read an Entry:
+            if(nFieldLength != 
+               (int) VSIFRead(tmpBuf, 1, nFieldLength, poModule->GetFP())) {
+                CPLError(CE_Failure, CPLE_FileIO,
+                         "Data record is short on DDF file.");
+                return FALSE;
+            }
+      
+            // move this temp buffer into more permanent storage:
+            char *newBuf = (char*)CPLMalloc(nDataSize+nFieldLength);
+            memcpy(newBuf, pachData, nDataSize);
+            CPLFree(pachData);
+            memcpy(&newBuf[nDataSize], tmpBuf, nFieldLength);
+            CPLFree(tmpBuf);
+            pachData = newBuf;
+            nDataSize += nFieldLength;
+        }
+    
+        /* ----------------------------------------------------------------- */
+        /*     Allocate, and read field definitions.                         */
+        /* ----------------------------------------------------------------- */
+        paoFields = new DDFField[nFieldCount];
+      
+        for( i = 0; i < nFieldCount; i++ )
+        {
+            char    szTag[128];
+            int     nEntryOffset = i*nFieldEntryWidth;
+            int     nFieldLength, nFieldPos;
+          
+            /* ------------------------------------------------------------- */
+            /* Read the position information and tag.                        */
+            /* ------------------------------------------------------------- */
+            strncpy( szTag, pachData+nEntryOffset, _sizeFieldTag );
+            szTag[_sizeFieldTag] = '\0';
+          
+            nEntryOffset += _sizeFieldTag;
+            nFieldLength = DDFScanInt( pachData+nEntryOffset, _sizeFieldLength );
+          
+            nEntryOffset += _sizeFieldLength;
+            nFieldPos = DDFScanInt( pachData+nEntryOffset, _sizeFieldPos );
+          
+            /* ------------------------------------------------------------- */
+            /* Find the corresponding field in the module directory.         */
+            /* ------------------------------------------------------------- */
+            DDFFieldDefn    *poFieldDefn = poModule->FindFieldDefn( szTag );
+          
+            if( poFieldDefn == NULL )
+            {
+                CPLError( CE_Failure, CPLE_AppDefined,
+                          "Undefined field `%s' encountered in data record.",
+                          szTag );
+                return FALSE;
+            }
+
+            /* ------------------------------------------------------------- */
+            /* Assign info the DDFField.                                     */
+            /* ------------------------------------------------------------- */
+
+            paoFields[i].Initialize( poFieldDefn, 
+                                     pachData + _fieldAreaStart
+                                     + nFieldPos - nLeaderSize,
+                                     nFieldLength );
+        }
+      
+        return TRUE;
+    }
+}
+
+/************************************************************************/
+/*                             FindField()                              */
+/************************************************************************/
+
+/**
+ * Find the named field within this record.
+ *
+ * @param pszName The name of the field to fetch.  The comparison is
+ * case insensitive.
+ * @param iFieldIndex The instance of this field to fetch.  Use zero (the
+ * default) for the first instance.
+ *
+ * @return Pointer to the requested DDFField.  This pointer is to an
+ * internal object, and should not be freed.  It remains valid until
+ * the next record read. 
+ */
+
+DDFField * DDFRecord::FindField( const char * pszName, int iFieldIndex )
+
+{
+    for( int i = 0; i < nFieldCount; i++ )
+    {
+        if( EQUAL(paoFields[i].GetFieldDefn()->GetName(),pszName) )
+        {
+            if( iFieldIndex == 0 )
+                return paoFields + i;
+            else
+                iFieldIndex--;
+        }
+    }
+
+    return NULL;
+}
+
+/************************************************************************/
+/*                              GetField()                              */
+/************************************************************************/
+
+/**
+ * Fetch field object based on index.
+ *
+ * @param i The index of the field to fetch.  Between 0 and GetFieldCount()-1.
+ *
+ * @return A DDFField pointer, or NULL if the index is out of range.
+ */
+
+DDFField *DDFRecord::GetField( int i )
+
+{
+    if( i < 0 || i >= nFieldCount )
+        return NULL;
+    else
+        return paoFields + i;
+}
+
+/************************************************************************/
+/*                           GetIntSubfield()                           */
+/************************************************************************/
+
+/**
+ * Fetch value of a subfield as an integer.  This is a convenience
+ * function for fetching a subfield of a field within this record.
+ *
+ * @param pszField The name of the field containing the subfield.
+ * @param iFieldIndex The instance of this field within the record.  Use
+ * zero for the first instance of this field.
+ * @param pszSubfield The name of the subfield within the selected field.
+ * @param iSubfieldIndex The instance of this subfield within the record.
+ * Use zero for the first instance.
+ * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
+ * succeeds, or FALSE if it fails.  Use NULL if you don't want to check
+ * success.
+ * @return The value of the subfield, or zero if it failed for some reason.
+ */
+
+int DDFRecord::GetIntSubfield( const char * pszField, int iFieldIndex,
+                               const char * pszSubfield, int iSubfieldIndex,
+                               int * pnSuccess )
+
+{
+    DDFField    *poField;
+    int         nDummyErr;
+
+    if( pnSuccess == NULL )
+        pnSuccess = &nDummyErr;
+
+    *pnSuccess = FALSE;
+            
+/* -------------------------------------------------------------------- */
+/*      Fetch the field. If this fails, return zero.                    */
+/* -------------------------------------------------------------------- */
+    poField = FindField( pszField, iFieldIndex );
+    if( poField == NULL )
+        return 0;
+
+/* -------------------------------------------------------------------- */
+/*      Get the subfield definition                                     */
+/* -------------------------------------------------------------------- */
+    DDFSubfieldDefn     *poSFDefn;
+
+    poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
+    if( poSFDefn == NULL )
+        return 0;
+
+/* -------------------------------------------------------------------- */
+/*      Get a pointer to the data.                                      */
+/* -------------------------------------------------------------------- */
+    int         nBytesRemaining;
+    
+    const char *pachData = poField->GetSubfieldData(poSFDefn,
+                                                    &nBytesRemaining,
+                                                    iSubfieldIndex);
+
+/* -------------------------------------------------------------------- */
+/*      Return the extracted value.                                     */
+/* -------------------------------------------------------------------- */
+    *pnSuccess = TRUE;
+
+    return( poSFDefn->ExtractIntData( pachData, nBytesRemaining, NULL ) );
+}
+
+/************************************************************************/
+/*                          GetFloatSubfield()                          */
+/************************************************************************/
+
+/**
+ * Fetch value of a subfield as a float (double).  This is a convenience
+ * function for fetching a subfield of a field within this record.
+ *
+ * @param pszField The name of the field containing the subfield.
+ * @param iFieldIndex The instance of this field within the record.  Use
+ * zero for the first instance of this field.
+ * @param pszSubfield The name of the subfield within the selected field.
+ * @param iSubfieldIndex The instance of this subfield within the record.
+ * Use zero for the first instance.
+ * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
+ * succeeds, or FALSE if it fails.  Use NULL if you don't want to check
+ * success.
+ * @return The value of the subfield, or zero if it failed for some reason.
+ */
+
+double DDFRecord::GetFloatSubfield( const char * pszField, int iFieldIndex,
+                                 const char * pszSubfield, int iSubfieldIndex,
+                                    int * pnSuccess )
+
+{
+    DDFField    *poField;
+    int         nDummyErr;
+
+    if( pnSuccess == NULL )
+        pnSuccess = &nDummyErr;
+
+    *pnSuccess = FALSE;
+            
+/* -------------------------------------------------------------------- */
+/*      Fetch the field. If this fails, return zero.                    */
+/* -------------------------------------------------------------------- */
+    poField = FindField( pszField, iFieldIndex );
+    if( poField == NULL )
+        return 0;
+
+/* -------------------------------------------------------------------- */
+/*      Get the subfield definition                                     */
+/* -------------------------------------------------------------------- */
+    DDFSubfieldDefn     *poSFDefn;
+
+    poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
+    if( poSFDefn == NULL )
+        return 0;
+
+/* -------------------------------------------------------------------- */
+/*      Get a pointer to the data.                                      */
+/* -------------------------------------------------------------------- */
+    int         nBytesRemaining;
+    
+    const char *pachData = poField->GetSubfieldData(poSFDefn,
+                                                    &nBytesRemaining,
+                                                    iSubfieldIndex);
+
+/* -------------------------------------------------------------------- */
+/*      Return the extracted value.                                     */
+/* -------------------------------------------------------------------- */
+    *pnSuccess = TRUE;
+
+    return( poSFDefn->ExtractFloatData( pachData, nBytesRemaining, NULL ) );
+}
+
+/************************************************************************/
+/*                         GetStringSubfield()                          */
+/************************************************************************/
+
+/**
+ * Fetch value of a subfield as a string.  This is a convenience
+ * function for fetching a subfield of a field within this record.
+ *
+ * @param pszField The name of the field containing the subfield.
+ * @param iFieldIndex The instance of this field within the record.  Use
+ * zero for the first instance of this field.
+ * @param pszSubfield The name of the subfield within the selected field.
+ * @param iSubfieldIndex The instance of this subfield within the record.
+ * Use zero for the first instance.
+ * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
+ * succeeds, or FALSE if it fails.  Use NULL if you don't want to check
+ * success.
+ * @return The value of the subfield, or NULL if it failed for some reason.
+ * The returned pointer is to internal data and should not be modified or
+ * freed by the application.
+ */
+
+const char *
+DDFRecord::GetStringSubfield( const char * pszField, int iFieldIndex,
+                              const char * pszSubfield, int iSubfieldIndex,
+                              int * pnSuccess )
+
+{
+    DDFField    *poField;
+    int         nDummyErr;
+
+    if( pnSuccess == NULL )
+        pnSuccess = &nDummyErr;
+
+    *pnSuccess = FALSE;
+            
+/* -------------------------------------------------------------------- */
+/*      Fetch the field. If this fails, return zero.                    */
+/* -------------------------------------------------------------------- */
+    poField = FindField( pszField, iFieldIndex );
+    if( poField == NULL )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Get the subfield definition                                     */
+/* -------------------------------------------------------------------- */
+    DDFSubfieldDefn     *poSFDefn;
+
+    poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
+    if( poSFDefn == NULL )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Get a pointer to the data.                                      */
+/* -------------------------------------------------------------------- */
+    int         nBytesRemaining;
+    
+    const char *pachData = poField->GetSubfieldData(poSFDefn,
+                                                    &nBytesRemaining,
+                                                    iSubfieldIndex);
+
+/* -------------------------------------------------------------------- */
+/*      Return the extracted value.                                     */
+/* -------------------------------------------------------------------- */
+    *pnSuccess = TRUE;
+
+    return( poSFDefn->ExtractStringData( pachData, nBytesRemaining, NULL ) );
+}
+
+/************************************************************************/
+/*                               Clone()                                */
+/************************************************************************/
+
+/**
+ * Make a copy of a record.
+ *
+ * This method is used to make a copy of a record that will become (mostly)
+ * the properly of application.  However, it is automatically destroyed if
+ * the DDFModule it was created relative to is destroyed, as it's field
+ * and subfield definitions relate to that DDFModule.  However, it does
+ * persist even when the record returned by DDFModule::ReadRecord() is
+ * invalidated, such as when reading a new record.  This allows an application
+ * to cache whole DDFRecords.
+ *
+ * @return A new copy of the DDFRecord.  This can be delete'd by the
+ * application when no longer needed, otherwise it will be cleaned up when
+ * the DDFModule it relates to is destroyed or closed.
+ */
+
+DDFRecord * DDFRecord::Clone()
+
+{
+    DDFRecord   *poNR;
+
+    poNR = new DDFRecord( poModule );
+
+    poNR->nReuseHeader = FALSE;
+    poNR->nFieldOffset = nFieldOffset;
+    
+    poNR->nDataSize = nDataSize;
+    poNR->pachData = (char *) CPLMalloc(nDataSize);
+    memcpy( poNR->pachData, pachData, nDataSize );
+    
+    poNR->nFieldCount = nFieldCount;
+    poNR->paoFields = new DDFField[nFieldCount];
+    for( int i = 0; i < nFieldCount; i++ )
+    {
+        int     nOffset;
+
+        nOffset = (paoFields[i].GetData() - pachData);
+        poNR->paoFields[i].Initialize( paoFields[i].GetFieldDefn(),
+                                       poNR->pachData + nOffset,
+                                       paoFields[i].GetDataSize() );
+    }
+    
+    poNR->bIsClone = TRUE;
+    poModule->AddCloneRecord( poNR );
+
+    return poNR;
+}
+
+/************************************************************************/
+/*                              CloneOn()                               */
+/************************************************************************/
+
+/**
+ * Recreate a record referencing another module.
+ *
+ * Works similarly to the DDFRecord::Clone() method, but creates the
+ * new record with reference to a different DDFModule.  All DDFFieldDefn
+ * references are transcribed onto the new module based on field names.
+ * If any fields don't have a similarly named field on the target module
+ * the operation will fail.  No validation of field types and properties
+ * is done, but this operation is intended only to be used between 
+ * modules with matching definitions of all affected fields. 
+ *
+ * The new record will be managed as a clone by the target module in
+ * a manner similar to regular clones. 
+ *
+ * @param poTargetModule the module on which the record copy should be
+ * created.
+ *
+ * @return NULL on failure or a pointer to the cloned record.
+ */
+
+DDFRecord *DDFRecord::CloneOn( DDFModule *poTargetModule )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Verify that all fields have a corresponding field definition    */
+/*      on the target module.                                           */
+/* -------------------------------------------------------------------- */
+    int         i;
+
+    for( i = 0; i < nFieldCount; i++ )
+    {
+        DDFFieldDefn    *poDefn = paoFields[i].GetFieldDefn();
+
+        if( poTargetModule->FindFieldDefn( poDefn->GetName() ) == NULL )
+            return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a clone.                                                 */
+/* -------------------------------------------------------------------- */
+    DDFRecord   *poClone;
+
+    poClone = Clone();
+
+/* -------------------------------------------------------------------- */
+/*      Update all internal information to reference other module.      */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < nFieldCount; i++ )
+    {
+        DDFField        *poField = poClone->paoFields+i;
+        DDFFieldDefn    *poDefn;
+
+        poDefn = poTargetModule->FindFieldDefn( 
+            poField->GetFieldDefn()->GetName() );
+        
+        poField->Initialize( poDefn, poField->GetData(), 
+                             poField->GetDataSize() );
+    }
+
+    poModule->RemoveCloneRecord( poClone );
+    poClone->poModule = poTargetModule;
+    poTargetModule->AddCloneRecord( poClone );
+
+    return poClone;
+}
+
+
+/************************************************************************/
+/*                            DeleteField()                             */
+/************************************************************************/
+
+/**
+ * Delete a field instance from a record.
+ *
+ * Remove a field from this record, cleaning up the data            
+ * portion and repacking the fields list.  We don't try to          
+ * reallocate the data area of the record to be smaller.            
+ *                                                                       
+ * NOTE: This method doesn't actually remove the header             
+ * information for this field from the record tag list yet.        
+ * This should be added if the resulting record is even to be      
+ * written back to disk!                                           
+ *
+ * @param poTarget the field instance on this record to delete.
+ *
+ * @return TRUE on success, or FALSE on failure.  Failure can occur if 
+ * poTarget isn't really a field on this record.
+ */
+
+int DDFRecord::DeleteField( DDFField *poTarget )
+
+{
+    int         iTarget, i;
+
+/* -------------------------------------------------------------------- */
+/*      Find which field we are to delete.                              */
+/* -------------------------------------------------------------------- */
+    for( iTarget = 0; iTarget < nFieldCount; iTarget++ )
+    {
+        if( paoFields + iTarget == poTarget )
+            break;
+    }
+
+    if( iTarget == nFieldCount )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Change the target fields data size to zero.  This takes care    */
+/*      of repacking the data array, and updating all the following     */
+/*      field data pointers.                                            */
+/* -------------------------------------------------------------------- */
+    ResizeField( poTarget, 0 );
+
+/* -------------------------------------------------------------------- */
+/*      remove the target field, moving down all the other fields       */
+/*      one step in the field list.                                     */
+/* -------------------------------------------------------------------- */
+    for( i = iTarget; i < nFieldCount-1; i++ )
+    {
+        paoFields[i] = paoFields[i+1];
+    }
+
+    nFieldCount--;
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                            ResizeField()                             */
+/************************************************************************/
+
+/**
+ * Alter field data size within record.
+ *
+ * This method will rearrange a DDFRecord altering the amount of space
+ * reserved for one of the existing fields.  All following fields will
+ * be shifted accordingly.  This includes updating the DDFField infos, 
+ * and actually moving stuff within the data array after reallocating
+ * to the desired size.
+ *
+ * @param poField the field to alter.
+ * @param nNewDataSize the number of data bytes to be reserved for the field.
+ *
+ * @return TRUE on success or FALSE on failure. 
+ */
+
+int DDFRecord::ResizeField( DDFField *poField, int nNewDataSize )
+
+{
+    int         iTarget, i;
+    int         nBytesToMove;
+
+/* -------------------------------------------------------------------- */
+/*      Find which field we are to resize.                              */
+/* -------------------------------------------------------------------- */
+    for( iTarget = 0; iTarget < nFieldCount; iTarget++ )
+    {
+        if( paoFields + iTarget == poField )
+            break;
+    }
+
+    if( iTarget == nFieldCount )
+    {
+        CPLAssert( FALSE );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Reallocate the data buffer accordingly.                         */
+/* -------------------------------------------------------------------- */
+    int nBytesToAdd = nNewDataSize - poField->GetDataSize();
+    const char *pachOldData = pachData;
+
+    // Don't realloc things smaller ... we will cut off some data.
+    if( nBytesToAdd > 0 )
+        pachData = (char *) CPLRealloc(pachData, nDataSize + nBytesToAdd );
+
+    nDataSize += nBytesToAdd;
+
+/* -------------------------------------------------------------------- */
+/*      How much data needs to be shifted up or down after this field?  */
+/* -------------------------------------------------------------------- */
+    nBytesToMove = nDataSize 
+        - (poField->GetData()+poField->GetDataSize()-pachOldData+nBytesToAdd);
+
+/* -------------------------------------------------------------------- */
+/*      Update fields to point into newly allocated buffer.             */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < nFieldCount; i++ )
+    {
+        int     nOffset;
+
+        nOffset = paoFields[i].GetData() - pachOldData;
+        paoFields[i].Initialize( paoFields[i].GetFieldDefn(), 
+                                 pachData + nOffset, 
+                                 paoFields[i].GetDataSize() );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Shift the data beyond this field up or down as needed.          */
+/* -------------------------------------------------------------------- */
+    if( nBytesToMove > 0 )
+        memmove( (char *)poField->GetData()+poField->GetDataSize()+nBytesToAdd,
+                 (char *)poField->GetData()+poField->GetDataSize(), 
+                 nBytesToMove );
+             
+/* -------------------------------------------------------------------- */
+/*      Update the target fields info.                                  */
+/* -------------------------------------------------------------------- */
+    poField->Initialize( poField->GetFieldDefn(),
+                         poField->GetData(), 
+                         poField->GetDataSize() + nBytesToAdd );
+
+/* -------------------------------------------------------------------- */
+/*      Shift all following fields down, and update their data          */
+/*      locations.                                                      */
+/* -------------------------------------------------------------------- */
+    if( nBytesToAdd < 0 )
+    {
+        for( i = iTarget+1; i < nFieldCount; i++ )
+        {
+            char *pszOldDataLocation;
+
+            pszOldDataLocation = (char *) paoFields[i].GetData();
+
+            paoFields[i].Initialize( paoFields[i].GetFieldDefn(), 
+                                     pszOldDataLocation + nBytesToAdd,
+                                     paoFields[i].GetDataSize() ); 
+        }
+    }
+    else
+    {
+        for( i = nFieldCount-1; i > iTarget; i-- )
+        {
+            char *pszOldDataLocation;
+
+            pszOldDataLocation = (char *) paoFields[i].GetData();
+
+            paoFields[i].Initialize( paoFields[i].GetFieldDefn(), 
+                                     pszOldDataLocation + nBytesToAdd,
+                                     paoFields[i].GetDataSize() ); 
+        }
+    }
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                              AddField()                              */
+/************************************************************************/
+
+/**
+ * Add a new field to record.
+ *
+ * Add a new zero sized field to the record.  The new field is always
+ * added at the end of the record. 
+ *
+ * NOTE: This method doesn't currently update the header information for
+ * the record to include the field information for this field, so the
+ * resulting record image isn't suitable for writing to disk.  However, 
+ * everything else about the record state should be updated properly to 
+ * reflect the new field.
+ *
+ * @param poDefn the definition of the field to be added.
+ *
+ * @return the field object on success, or NULL on failure.
+ */
+
+DDFField *DDFRecord::AddField( DDFFieldDefn *poDefn )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Reallocate the fields array larger by one, and initialize       */
+/*      the new field.                                                  */
+/* -------------------------------------------------------------------- */
+    DDFField    *paoNewFields;
+
+    paoNewFields = new DDFField[nFieldCount+1];
+    if( nFieldCount > 0 )
+    {
+        memcpy( paoNewFields, paoFields, sizeof(DDFField) * nFieldCount );
+        delete[] paoFields;
+    }
+    paoFields = paoNewFields;
+    nFieldCount++;
+
+/* -------------------------------------------------------------------- */
+/*      Initialize the new field properly.                              */
+/* -------------------------------------------------------------------- */
+    if( nFieldCount == 1 )
+    {
+        paoFields[0].Initialize( poDefn, GetData(), 0 );
+    }
+    else
+    {
+        paoFields[nFieldCount-1].Initialize( 
+            poDefn, 
+            paoFields[nFieldCount-2].GetData()
+            + paoFields[nFieldCount-2].GetDataSize(), 
+            0 );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize field.                                               */
+/* -------------------------------------------------------------------- */
+    CreateDefaultFieldInstance( paoFields + nFieldCount-1, 0 );
+
+    return paoFields + (nFieldCount - 1);
+}
+
+/************************************************************************/
+/*                            SetFieldRaw()                             */
+/************************************************************************/
+
+/**
+ * Set the raw contents of a field instance.
+ *
+ * @param poField the field to set data within. 
+ * @param iIndexWithinField The instance of this field to replace.  Must
+ * be a value between 0 and GetRepeatCount().  If GetRepeatCount() is used, a
+ * new instance of the field is appeneded.
+ * @param pachRawData the raw data to replace this field instance with.
+ * @param nRawDataSize the number of bytes pointed to by pachRawData.
+ *
+ * @return TRUE on success or FALSE on failure.
+ */
+
+int
+DDFRecord::SetFieldRaw( DDFField *poField, int iIndexWithinField,
+                        const char *pachRawData, int nRawDataSize )
+
+{
+    int         iTarget, nRepeatCount;
+
+/* -------------------------------------------------------------------- */
+/*      Find which field we are to update.                              */
+/* -------------------------------------------------------------------- */
+    for( iTarget = 0; iTarget < nFieldCount; iTarget++ )
+    {
+        if( paoFields + iTarget == poField )
+            break;
+    }
+
+    if( iTarget == nFieldCount )
+        return FALSE;
+
+    nRepeatCount = poField->GetRepeatCount();
+
+    if( iIndexWithinField < 0 || iIndexWithinField > nRepeatCount )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Are we adding an instance?  This is easier and different        */
+/*      than replacing an existing instance.                            */
+/* -------------------------------------------------------------------- */
+    if( iIndexWithinField == nRepeatCount 
+        || !poField->GetFieldDefn()->IsRepeating() )
+    {
+        char    *pachFieldData;
+        int     nOldSize;
+
+        if( !poField->GetFieldDefn()->IsRepeating() && iIndexWithinField != 0 )
+            return FALSE;
+
+        nOldSize = poField->GetDataSize();
+        if( nOldSize == 0 )
+            nOldSize++; // for added DDF_FIELD_TERMINATOR. 
+
+        if( !ResizeField( poField, nOldSize + nRawDataSize ) )
+            return FALSE;
+
+        pachFieldData = (char *) poField->GetData();
+        memcpy( pachFieldData + nOldSize - 1, 
+                pachRawData, nRawDataSize );
+        pachFieldData[nOldSize+nRawDataSize-1] = DDF_FIELD_TERMINATOR;
+
+        return TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get a pointer to the start of the existing data for this        */
+/*      iteration of the field.                                         */
+/* -------------------------------------------------------------------- */
+    const char *pachWrkData;
+    int         nInstanceSize;
+
+    // We special case this to avoid alot of warnings when initializing 
+    // the field the first time.
+    if( poField->GetDataSize() == 0 )
+    {
+        pachWrkData = poField->GetData();
+        nInstanceSize = 0;
+    }
+    else
+    {
+        pachWrkData = poField->GetInstanceData( iIndexWithinField, 
+                                                &nInstanceSize );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create new image of this whole field.                           */
+/* -------------------------------------------------------------------- */
+    char        *pachNewImage;
+    int         nPreBytes, nPostBytes, nNewFieldSize;
+
+    nNewFieldSize = poField->GetDataSize() - nInstanceSize + nRawDataSize;
+
+    pachNewImage = (char *) CPLMalloc(nNewFieldSize);
+
+    nPreBytes = pachWrkData - poField->GetData();
+    nPostBytes = poField->GetDataSize() - nPreBytes - nInstanceSize;
+
+    memcpy( pachNewImage, poField->GetData(), nPreBytes );
+    memcpy( pachNewImage + nPreBytes + nRawDataSize, 
+            poField->GetData() + nPreBytes + nInstanceSize,
+            nPostBytes );
+    memcpy( pachNewImage + nPreBytes, pachRawData, nRawDataSize );
+
+/* -------------------------------------------------------------------- */
+/*      Resize the field to the desired new size.                       */
+/* -------------------------------------------------------------------- */
+    ResizeField( poField, nNewFieldSize );
+
+    memcpy( (void *) poField->GetData(), pachNewImage, nNewFieldSize );
+    CPLFree( pachNewImage );
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                           UpdateFieldRaw()                           */
+/************************************************************************/
+
+int
+DDFRecord::UpdateFieldRaw( DDFField *poField, int iIndexWithinField,
+                           int nStartOffset, int nOldSize, 
+                           const char *pachRawData, int nRawDataSize )
+
+{
+    int         iTarget, nRepeatCount;
+
+/* -------------------------------------------------------------------- */
+/*      Find which field we are to update.                              */
+/* -------------------------------------------------------------------- */
+    for( iTarget = 0; iTarget < nFieldCount; iTarget++ )
+    {
+        if( paoFields + iTarget == poField )
+            break;
+    }
+
+    if( iTarget == nFieldCount )
+        return FALSE;
+
+    nRepeatCount = poField->GetRepeatCount();
+
+    if( iIndexWithinField < 0 || iIndexWithinField >= nRepeatCount )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Figure out how much pre and post data there is.                 */
+/* -------------------------------------------------------------------- */
+    char *pachWrkData;
+    int         nInstanceSize, nPostBytes, nPreBytes;
+        
+    pachWrkData = (char *) poField->GetInstanceData( iIndexWithinField, 
+                                                     &nInstanceSize );
+    nPreBytes = pachWrkData - poField->GetData() + nStartOffset;
+    nPostBytes = poField->GetDataSize() - nPreBytes - nOldSize;
+
+/* -------------------------------------------------------------------- */
+/*      If we aren't changing the size, just copy over the existing     */
+/*      data.                                                           */
+/* -------------------------------------------------------------------- */
+    if( nOldSize == nRawDataSize )
+    {
+        memcpy( pachWrkData + nStartOffset, pachRawData, nRawDataSize );
+        return TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If we are shrinking, move in the new data, and shuffle down     */
+/*      the old before resizing.                                        */
+/* -------------------------------------------------------------------- */
+    if( nRawDataSize < nOldSize )
+    {
+        memcpy( ((char*) poField->GetData()) + nPreBytes, 
+                pachRawData, nRawDataSize );
+        memmove( ((char *) poField->GetData()) + nPreBytes + nRawDataSize, 
+                 ((char *) poField->GetData()) + nPreBytes + nOldSize, 
+                 nPostBytes );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Resize the whole buffer.                                        */
+/* -------------------------------------------------------------------- */
+    if( !ResizeField( poField, 
+                      poField->GetDataSize() - nOldSize + nRawDataSize ) )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      If we growing the buffer, shuffle up the post data, and         */
+/*      move in our new values.                                         */
+/* -------------------------------------------------------------------- */
+    if( nRawDataSize >= nOldSize )
+    {
+        memmove( ((char *) poField->GetData()) + nPreBytes + nRawDataSize, 
+                 ((char *) poField->GetData()) + nPreBytes + nOldSize, 
+                 nPostBytes );
+        memcpy( ((char*) poField->GetData()) + nPreBytes, 
+                pachRawData, nRawDataSize );
+    }
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                           ResetDirectory()                           */
+/*                                                                      */
+/*      Re-prepares the directory information for the record.           */
+/************************************************************************/
+
+int DDFRecord::ResetDirectory()
+
+{
+    int iField;
+
+/* -------------------------------------------------------------------- */
+/*      Eventually we should try to optimize the size of offset and     */
+/*      field length.  For now we will use 5 for each which is          */
+/*      pretty big.                                                     */
+/* -------------------------------------------------------------------- */
+    _sizeFieldPos = 5;
+    _sizeFieldLength = 5;
+
+/* -------------------------------------------------------------------- */
+/*      Compute how large the directory needs to be.                    */
+/* -------------------------------------------------------------------- */
+    int nEntrySize, nDirSize;
+
+    nEntrySize = _sizeFieldPos + _sizeFieldLength + _sizeFieldTag;
+    nDirSize = nEntrySize * nFieldCount + 1;
+
+/* -------------------------------------------------------------------- */
+/*      If the directory size is different than what is currently       */
+/*      reserved for it, we must resize.                                */
+/* -------------------------------------------------------------------- */
+    if( nDirSize != nFieldOffset )
+    {
+        char *pachNewData;
+        int  nNewDataSize;
+
+        nNewDataSize = nDataSize - nFieldOffset + nDirSize;
+        pachNewData = (char *) CPLMalloc(nNewDataSize);
+        memcpy( pachNewData + nDirSize, 
+                pachData + nFieldOffset, 
+                nNewDataSize - nDirSize );
+     
+        for( iField = 0; iField < nFieldCount; iField++ )
+        {
+            int nOffset;
+            DDFField *poField = GetField( iField );
+
+            nOffset = poField->GetData() - pachData - nFieldOffset + nDirSize;
+            poField->Initialize( poField->GetFieldDefn(), 
+                                 pachNewData + nOffset, 
+                                 poField->GetDataSize() );
+        }
+
+        CPLFree( pachData );
+        pachData = pachNewData;
+        nDataSize = nNewDataSize;
+        nFieldOffset = nDirSize;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Now set each directory entry.                                   */
+/* -------------------------------------------------------------------- */
+    for( iField = 0; iField < nFieldCount; iField++ )
+    {
+        DDFField *poField = GetField( iField );
+        DDFFieldDefn *poDefn = poField->GetFieldDefn();
+        char      szFormat[128];
+
+        sprintf( szFormat, "%%%ds%%0%dd%%0%dd", 
+                 _sizeFieldTag, _sizeFieldLength, _sizeFieldPos );
+
+        sprintf( pachData + nEntrySize * iField, szFormat, 
+                 poDefn->GetName(), poField->GetDataSize(),
+                 poField->GetData() - pachData - nFieldOffset );
+    }
+
+    pachData[nEntrySize * nFieldCount] = DDF_FIELD_TERMINATOR;
+        
+    return TRUE;
+}
+
+/************************************************************************/
+/*                     CreateDefaultFieldInstance()                     */
+/************************************************************************/
+
+/**
+ * Initialize default instance.
+ *
+ * This method is normally only used internally by the AddField() method
+ * to initialize the new field instance with default subfield values.  It
+ * installs default data for one instance of the field in the record
+ * using the DDFFieldDefn::GetDefaultValue() method and 
+ * DDFRecord::SetFieldRaw(). 
+ *
+ * @param poField the field within the record to be assign a default 
+ * instance. 
+ * @param iIndexWithinField the instance to set (may not have been tested with
+ * values other than 0). 
+ *
+ * @return TRUE on success or FALSE on failure. 
+ */
+
+int DDFRecord::CreateDefaultFieldInstance( DDFField *poField, 
+                                           int iIndexWithinField )
+
+{
+    int nRawSize, nSuccess;
+    char *pachRawData;
+
+    pachRawData = poField->GetFieldDefn()->GetDefaultValue( &nRawSize );
+    if( pachRawData == NULL )
+        return FALSE;
+
+    nSuccess = SetFieldRaw( poField, iIndexWithinField, pachRawData, nRawSize);
+
+    CPLFree( pachRawData );
+
+    return nSuccess;
+}
+
+/************************************************************************/
+/*                         SetStringSubfield()                          */
+/************************************************************************/
+
+/**
+ * Set a string subfield in record.
+ *
+ * The value of a given subfield is replaced with a new string value 
+ * formatted appropriately. 
+ *
+ * @param pszField the field name to operate on.
+ * @param iFieldIndex the field index to operate on (zero based).
+ * @param pszSubfield the subfield name to operate on. 
+ * @param iSubfieldIndex the subfield index to operate on (zero based). 
+ * @param pszValue the new string to place in the subfield.  This may be 
+ * arbitrary binary bytes if nValueLength is specified. 
+ * @param nValueLength the number of valid bytes in pszValue, may be -1 to 
+ * internally fetch with strlen(). 
+ * 
+ * @return TRUE if successful, and FALSE if not. 
+ */
+
+int DDFRecord::SetStringSubfield( const char *pszField, int iFieldIndex, 
+                                  const char *pszSubfield, int iSubfieldIndex,
+                                  const char *pszValue, int nValueLength )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Fetch the field. If this fails, return zero.                    */
+/* -------------------------------------------------------------------- */
+    DDFField *poField; 
+
+    poField = FindField( pszField, iFieldIndex );
+    if( poField == NULL )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Get the subfield definition                                     */
+/* -------------------------------------------------------------------- */
+    DDFSubfieldDefn     *poSFDefn;
+
+    poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
+    if( poSFDefn == NULL )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      How long will the formatted value be?                           */
+/* -------------------------------------------------------------------- */
+    int nFormattedLen;
+
+    if( !poSFDefn->FormatStringValue( NULL, 0, &nFormattedLen, pszValue,
+                                      nValueLength ) )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Get a pointer to the data.                                      */
+/* -------------------------------------------------------------------- */
+    int         nMaxBytes;
+    char *pachSubfieldData = (char *) 
+        poField->GetSubfieldData(poSFDefn, &nMaxBytes,
+                                 iSubfieldIndex);
+
+/* -------------------------------------------------------------------- */
+/*      Add new instance if we have run out of data.                    */
+/* -------------------------------------------------------------------- */
+    if( nMaxBytes == 0 
+        || (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR) )
+    {
+        CreateDefaultFieldInstance( poField, iSubfieldIndex );
+
+        // Refetch.
+        pachSubfieldData = (char *) 
+            poField->GetSubfieldData(poSFDefn, &nMaxBytes,
+                                     iSubfieldIndex);
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If the new length matches the existing length, just overlay     */
+/*      and return.                                                     */
+/* -------------------------------------------------------------------- */
+    int nExistingLength;
+
+    poSFDefn->GetDataLength( pachSubfieldData, nMaxBytes, &nExistingLength );
+
+    if( nExistingLength == nFormattedLen )
+    {
+        return poSFDefn->FormatStringValue( pachSubfieldData, nFormattedLen,
+                                            NULL, pszValue, nValueLength );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      We will need to resize the raw data.                            */
+/* -------------------------------------------------------------------- */
+    const char *pachFieldInstData;
+    int         nInstanceSize, nStartOffset, nSuccess;
+    char *pachNewData;
+
+    pachFieldInstData = poField->GetInstanceData( iFieldIndex,
+                                                  &nInstanceSize );
+
+    nStartOffset = pachSubfieldData - pachFieldInstData;
+
+    pachNewData = (char *) CPLMalloc(nFormattedLen);
+    poSFDefn->FormatStringValue( pachNewData, nFormattedLen, NULL, 
+                                 pszValue, nValueLength );
+    
+    nSuccess = UpdateFieldRaw( poField, iFieldIndex,
+                               nStartOffset, nExistingLength, 
+                               pachNewData, nFormattedLen );
+
+    CPLFree( pachNewData );
+
+    return nSuccess;
+}
+
+/************************************************************************/
+/*                           SetIntSubfield()                           */
+/************************************************************************/
+
+/**
+ * Set an integer subfield in record.
+ *
+ * The value of a given subfield is replaced with a new integer value 
+ * formatted appropriately. 
+ *
+ * @param pszField the field name to operate on.
+ * @param iFieldIndex the field index to operate on (zero based).
+ * @param pszSubfield the subfield name to operate on. 
+ * @param iSubfieldIndex the subfield index to operate on (zero based). 
+ * @param nNewValue the new value to place in the subfield. 
+ * 
+ * @return TRUE if successful, and FALSE if not. 
+ */
+
+int DDFRecord::SetIntSubfield( const char *pszField, int iFieldIndex, 
+                               const char *pszSubfield, int iSubfieldIndex,
+                               int nNewValue )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Fetch the field. If this fails, return zero.                    */
+/* -------------------------------------------------------------------- */
+    DDFField *poField; 
+
+    poField = FindField( pszField, iFieldIndex );
+    if( poField == NULL )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Get the subfield definition                                     */
+/* -------------------------------------------------------------------- */
+    DDFSubfieldDefn     *poSFDefn;
+
+    poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
+    if( poSFDefn == NULL )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      How long will the formatted value be?                           */
+/* -------------------------------------------------------------------- */
+    int nFormattedLen;
+
+    if( !poSFDefn->FormatIntValue( NULL, 0, &nFormattedLen, nNewValue ) )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Get a pointer to the data.                                      */
+/* -------------------------------------------------------------------- */
+    int         nMaxBytes;
+    char *pachSubfieldData = (char *) 
+        poField->GetSubfieldData(poSFDefn, &nMaxBytes,
+                                 iSubfieldIndex);
+
+/* -------------------------------------------------------------------- */
+/*      Add new instance if we have run out of data.                    */
+/* -------------------------------------------------------------------- */
+    if( nMaxBytes == 0 
+        || (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR) )
+    {
+        CreateDefaultFieldInstance( poField, iSubfieldIndex );
+
+        // Refetch.
+        pachSubfieldData = (char *) 
+            poField->GetSubfieldData(poSFDefn, &nMaxBytes,
+                                     iSubfieldIndex);
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If the new length matches the existing length, just overlay     */
+/*      and return.                                                     */
+/* -------------------------------------------------------------------- */
+    int nExistingLength;
+
+    poSFDefn->GetDataLength( pachSubfieldData, nMaxBytes, &nExistingLength );
+
+    if( nExistingLength == nFormattedLen )
+    {
+        return poSFDefn->FormatIntValue( pachSubfieldData, nFormattedLen,
+                                         NULL, nNewValue );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      We will need to resize the raw data.                            */
+/* -------------------------------------------------------------------- */
+    const char *pachFieldInstData;
+    int         nInstanceSize, nStartOffset, nSuccess;
+    char *pachNewData;
+
+    pachFieldInstData = poField->GetInstanceData( iFieldIndex,
+                                                  &nInstanceSize );
+
+    nStartOffset = pachSubfieldData - pachFieldInstData;
+
+    pachNewData = (char *) CPLMalloc(nFormattedLen);
+    poSFDefn->FormatIntValue( pachNewData, nFormattedLen, NULL, 
+                              nNewValue );
+    
+    nSuccess = UpdateFieldRaw( poField, iFieldIndex,
+                               nStartOffset, nExistingLength, 
+                               pachNewData, nFormattedLen );
+
+    CPLFree( pachNewData );
+
+    return nSuccess;
+}
+
+/************************************************************************/
+/*                          SetFloatSubfield()                          */
+/************************************************************************/
+
+/**
+ * Set a float subfield in record.
+ *
+ * The value of a given subfield is replaced with a new float value 
+ * formatted appropriately. 
+ *
+ * @param pszField the field name to operate on.
+ * @param iFieldIndex the field index to operate on (zero based).
+ * @param pszSubfield the subfield name to operate on. 
+ * @param iSubfieldIndex the subfield index to operate on (zero based). 
+ * @param dfNewValue the new value to place in the subfield. 
+ * 
+ * @return TRUE if successful, and FALSE if not. 
+ */
+
+int DDFRecord::SetFloatSubfield( const char *pszField, int iFieldIndex, 
+                                 const char *pszSubfield, int iSubfieldIndex,
+                                 double dfNewValue )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Fetch the field. If this fails, return zero.                    */
+/* -------------------------------------------------------------------- */
+    DDFField *poField; 
+
+    poField = FindField( pszField, iFieldIndex );
+    if( poField == NULL )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Get the subfield definition                                     */
+/* -------------------------------------------------------------------- */
+    DDFSubfieldDefn     *poSFDefn;
+
+    poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
+    if( poSFDefn == NULL )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      How long will the formatted value be?                           */
+/* -------------------------------------------------------------------- */
+    int nFormattedLen;
+
+    if( !poSFDefn->FormatFloatValue( NULL, 0, &nFormattedLen, dfNewValue ) )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Get a pointer to the data.                                      */
+/* -------------------------------------------------------------------- */
+    int         nMaxBytes;
+    char *pachSubfieldData = (char *) 
+        poField->GetSubfieldData(poSFDefn, &nMaxBytes,
+                                 iSubfieldIndex);
+
+/* -------------------------------------------------------------------- */
+/*      Add new instance if we have run out of data.                    */
+/* -------------------------------------------------------------------- */
+    if( nMaxBytes == 0 
+        || (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR) )
+    {
+        CreateDefaultFieldInstance( poField, iSubfieldIndex );
+
+        // Refetch.
+        pachSubfieldData = (char *) 
+            poField->GetSubfieldData(poSFDefn, &nMaxBytes,
+                                     iSubfieldIndex);
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If the new length matches the existing length, just overlay     */
+/*      and return.                                                     */
+/* -------------------------------------------------------------------- */
+    int nExistingLength;
+
+    poSFDefn->GetDataLength( pachSubfieldData, nMaxBytes, &nExistingLength );
+
+    if( nExistingLength == nFormattedLen )
+    {
+        return poSFDefn->FormatFloatValue( pachSubfieldData, nFormattedLen,
+                                         NULL, dfNewValue );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      We will need to resize the raw data.                            */
+/* -------------------------------------------------------------------- */
+    const char *pachFieldInstData;
+    int         nInstanceSize, nStartOffset, nSuccess;
+    char *pachNewData;
+
+    pachFieldInstData = poField->GetInstanceData( iFieldIndex,
+                                                  &nInstanceSize );
+
+    nStartOffset = (int) (pachSubfieldData - pachFieldInstData);
+
+    pachNewData = (char *) CPLMalloc(nFormattedLen);
+    poSFDefn->FormatFloatValue( pachNewData, nFormattedLen, NULL, 
+                              dfNewValue );
+    
+    nSuccess = UpdateFieldRaw( poField, iFieldIndex,
+                               nStartOffset, nExistingLength, 
+                               pachNewData, nFormattedLen );
+
+    CPLFree( pachNewData );
+
+    return nSuccess;
+}
diff --git a/Utilities/GDAL/frmts/iso8211/ddfsubfielddefn.cpp b/Utilities/GDAL/frmts/iso8211/ddfsubfielddefn.cpp
new file mode 100644
index 0000000000..e2c136c9a4
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/ddfsubfielddefn.cpp
@@ -0,0 +1,994 @@
+/******************************************************************************
+ * $Id: ddfsubfielddefn.cpp,v 1.14 2006/04/04 04:24:07 fwarmerdam Exp $
+ *
+ * Project:  ISO 8211 Access
+ * Purpose:  Implements the DDFSubfieldDefn class.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: ddfsubfielddefn.cpp,v $
+ * Revision 1.14  2006/04/04 04:24:07  fwarmerdam
+ * update contact info
+ *
+ * Revision 1.13  2004/01/06 19:07:14  warmerda
+ * Added braces within complex case in switch for HP/UX compatibility.
+ *
+ * Revision 1.12  2003/12/15 20:24:58  warmerda
+ * expand tabs
+ *
+ * Revision 1.11  2003/11/12 21:22:14  warmerda
+ * fixed some docs
+ *
+ * Revision 1.10  2003/09/05 19:13:45  warmerda
+ * added format support for binary ints
+ *
+ * Revision 1.9  2003/09/03 20:36:26  warmerda
+ * added subfield writing support
+ *
+ * Revision 1.8  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.7  2000/09/19 14:09:34  warmerda
+ * avoid checking for field terminators in multi-byte strings
+ *
+ * Revision 1.6  2000/06/13 13:39:27  warmerda
+ * added warnings, and better handlng of short data for subfields
+ *
+ * Revision 1.5  1999/11/18 19:03:04  warmerda
+ * expanded tabs
+ *
+ * Revision 1.4  1999/05/10 17:36:23  warmerda
+ * Strip trailing spaces off subfield names.
+ *
+ * Revision 1.3  1999/05/06 15:15:07  warmerda
+ * Removed extra break;
+ *
+ * Revision 1.2  1999/05/06 14:25:43  warmerda
+ * added DDFBinaryString, and a bit of optimization
+ *
+ * Revision 1.1  1999/04/27 18:45:05  warmerda
+ * New
+ *
+ */
+
+#include "iso8211.h"
+#include "cpl_conv.h"
+
+CPL_CVSID("$Id: ddfsubfielddefn.cpp,v 1.14 2006/04/04 04:24:07 fwarmerdam Exp $");
+
+/************************************************************************/
+/*                          DDFSubfieldDefn()                           */
+/************************************************************************/
+
+DDFSubfieldDefn::DDFSubfieldDefn()
+
+{
+    pszName = NULL;
+    
+    bIsVariable = TRUE;
+    nFormatWidth = 0;
+    chFormatDelimeter = DDF_UNIT_TERMINATOR;
+    eBinaryFormat = NotBinary;
+    eType = DDFString;
+    
+    pszFormatString = CPLStrdup("");
+
+    nMaxBufChars = 0;
+    pachBuffer = NULL;
+}
+
+/************************************************************************/
+/*                          ~DDFSubfieldDefn()                          */
+/************************************************************************/
+
+DDFSubfieldDefn::~DDFSubfieldDefn()
+
+{
+    CPLFree( pszName );
+    CPLFree( pszFormatString );
+    CPLFree( pachBuffer );
+}
+
+/************************************************************************/
+/*                              SetName()                               */
+/************************************************************************/
+
+void DDFSubfieldDefn::SetName( const char * pszNewName )
+
+{
+    int         i;
+    
+    CPLFree( pszName );
+
+    pszName = CPLStrdup( pszNewName );
+
+    for( i = strlen(pszName)-1; i > 0 && pszName[i] == ' '; i-- )
+        pszName[i] = '\0';
+}
+
+/************************************************************************/
+/*                             SetFormat()                              */
+/*                                                                      */
+/*      While interpreting the format string we don't support:          */
+/*                                                                      */
+/*       o Passing an explicit terminator for variable length field.    */
+/*       o 'X' for unused data ... this should really be filtered       */
+/*         out by DDFFieldDefn::ApplyFormats(), but isn't.              */
+/*       o 'B' bitstrings that aren't a multiple of eight.              */
+/************************************************************************/
+
+int DDFSubfieldDefn::SetFormat( const char * pszFormat )
+
+{
+    CPLFree( pszFormatString );
+    pszFormatString = CPLStrdup( pszFormat );
+
+/* -------------------------------------------------------------------- */
+/*      These values will likely be used.                               */
+/* -------------------------------------------------------------------- */
+    if( pszFormatString[1] == '(' )
+    {
+        nFormatWidth = atoi(pszFormatString+2);
+        bIsVariable = nFormatWidth == 0;
+    }
+    else
+        bIsVariable = TRUE;
+    
+/* -------------------------------------------------------------------- */
+/*      Interpret the format string.                                    */
+/* -------------------------------------------------------------------- */
+    switch( pszFormatString[0] )
+    {
+      case 'A':
+      case 'C':         // It isn't clear to me how this is different than 'A'
+        eType = DDFString;
+        break;
+
+      case 'R':
+        eType = DDFFloat;
+        break;
+        
+      case 'I':
+      case 'S':
+        eType = DDFInt;
+        break;
+
+      case 'B':
+      case 'b':
+        // Is the width expressed in bits? (is it a bitstring)
+        bIsVariable = FALSE;
+        if( pszFormatString[1] == '(' )
+        {
+            CPLAssert( atoi(pszFormatString+2) % 8 == 0 );
+            
+            nFormatWidth = atoi(pszFormatString+2) / 8;
+            eBinaryFormat = SInt; // good default, works for SDTS.
+
+            if( nFormatWidth < 5 )
+                eType = DDFInt;
+            else
+                eType = DDFBinaryString;
+        }
+        
+        // or do we have a binary type indicator? (is it binary)
+        else
+        {
+            eBinaryFormat = (DDFBinaryFormat) (pszFormatString[1] - '0');
+            nFormatWidth = atoi(pszFormatString+2);
+
+            if( eBinaryFormat == SInt || eBinaryFormat == UInt )
+                eType = DDFInt;
+            else
+                eType = DDFFloat;
+        }
+        break;
+
+      case 'X':
+        // 'X' is extra space, and shouldn't be directly assigned to a
+        // subfield ... I haven't encountered it in use yet though.
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Format type of `%c' not supported.\n",
+                  pszFormatString[0] );
+        
+        CPLAssert( FALSE );
+        return FALSE;
+        
+      default:
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Format type of `%c' not recognised.\n",
+                  pszFormatString[0] );
+        
+        CPLAssert( FALSE );
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+/************************************************************************/
+/*                                Dump()                                */
+/************************************************************************/
+
+/**
+ * Write out subfield definition info to debugging file.
+ *
+ * A variety of information about this field definition is written to the
+ * give debugging file handle.
+ *
+ * @param fp The standard io file handle to write to.  ie. stderr
+ */
+
+void DDFSubfieldDefn::Dump( FILE * fp )
+
+{
+    fprintf( fp, "    DDFSubfieldDefn:\n" );
+    fprintf( fp, "        Label = `%s'\n", pszName );
+    fprintf( fp, "        FormatString = `%s'\n", pszFormatString );
+}
+
+/************************************************************************/
+/*                           GetDataLength()                            */
+/*                                                                      */
+/*      This method will scan for the end of a variable field.          */
+/************************************************************************/
+
+/**
+ * Scan for the end of variable length data.  Given a pointer to the data
+ * for this subfield (from within a DDFRecord) this method will return the
+ * number of bytes which are data for this subfield.  The number of bytes
+ * consumed as part of this field can also be fetched.  This number may
+ * be one longer than the length if there is a terminator character
+ * used.<p>
+ *
+ * This method is mainly for internal use, or for applications which
+ * want the raw binary data to interpret themselves.  Otherwise use one
+ * of ExtractStringData(), ExtractIntData() or ExtractFloatData().
+ *
+ * @param pachSourceData The pointer to the raw data for this field.  This
+ * may have come from DDFRecord::GetData(), taking into account skip factors
+ * over previous subfields data.
+ * @param nMaxBytes The maximum number of bytes that are accessable after
+ * pachSourceData.
+ * @param pnConsumedBytes Pointer to an integer into which the number of
+ * bytes consumed by this field should be written.  May be NULL to ignore.
+ *
+ * @return The number of bytes at pachSourceData which are actual data for
+ * this record (not including unit, or field terminator).  
+ */
+
+int DDFSubfieldDefn::GetDataLength( const char * pachSourceData,
+                                    int nMaxBytes, int * pnConsumedBytes )
+
+{
+    if( !bIsVariable )
+    {
+        if( nFormatWidth > nMaxBytes )
+        {
+            CPLError( CE_Warning, CPLE_AppDefined, 
+                      "Only %d bytes available for subfield %s with\n"
+                      "format string %s ... returning shortened data.",
+                      nMaxBytes, pszName, pszFormatString );
+
+            if( pnConsumedBytes != NULL )
+                *pnConsumedBytes = nMaxBytes;
+
+            return nMaxBytes;
+        }
+        else
+        {
+            if( pnConsumedBytes != NULL )
+                *pnConsumedBytes = nFormatWidth;
+
+            return nFormatWidth;
+        }
+    }
+    else
+    {
+        int     nLength = 0;
+        int     bCheckFieldTerminator = TRUE;
+
+        /* We only check for the field terminator because of some buggy 
+         * datasets with missing format terminators.  However, we have found
+         * the field terminator is a legal character within the fields of
+         * some extended datasets (such as JP34NC94.000).  So we don't check
+         * for the field terminator if the field appears to be multi-byte
+         * which we established by the first character being out of the 
+         * ASCII printable range (32-127). 
+         */
+
+        if( pachSourceData[0] < 32 || pachSourceData[0] >= 127 )
+            bCheckFieldTerminator = FALSE;
+        
+        while( nLength < nMaxBytes
+               && pachSourceData[nLength] != chFormatDelimeter )
+        {
+            if( bCheckFieldTerminator 
+                && pachSourceData[nLength] == DDF_FIELD_TERMINATOR )
+                break;
+
+            nLength++;
+        }
+
+        if( pnConsumedBytes != NULL )
+        {
+            if( nMaxBytes == 0 )
+                *pnConsumedBytes = nLength;
+            else
+                *pnConsumedBytes = nLength+1;
+        }
+        
+        return nLength;
+    }
+}
+
+/************************************************************************/
+/*                         ExtractStringData()                          */
+/************************************************************************/
+
+/**
+ * Extract a zero terminated string containing the data for this subfield.
+ * Given a pointer to the data
+ * for this subfield (from within a DDFRecord) this method will return the
+ * data for this subfield.  The number of bytes
+ * consumed as part of this field can also be fetched.  This number may
+ * be one longer than the string length if there is a terminator character
+ * used.<p>
+ *
+ * This function will return the raw binary data of a subfield for
+ * types other than DDFString, including data past zero chars.  This is
+ * the standard way of extracting DDFBinaryString subfields for instance.<p>
+ *
+ * @param pachSourceData The pointer to the raw data for this field.  This
+ * may have come from DDFRecord::GetData(), taking into account skip factors
+ * over previous subfields data.
+ * @param nMaxBytes The maximum number of bytes that are accessable after
+ * pachSourceData.
+ * @param pnConsumedBytes Pointer to an integer into which the number of
+ * bytes consumed by this field should be written.  May be NULL to ignore.
+ * This is used as a skip factor to increment pachSourceData to point to the
+ * next subfields data.
+ *
+ * @return A pointer to a buffer containing the data for this field.  The
+ * returned pointer is to an internal buffer which is invalidated on the
+ * next ExtractStringData() call on this DDFSubfieldDefn().  It should not
+ * be freed by the application.
+ *
+ * @see ExtractIntData(), ExtractFloatData()
+ */
+
+const char *
+DDFSubfieldDefn::ExtractStringData( const char * pachSourceData,
+                                    int nMaxBytes, int * pnConsumedBytes )
+
+{
+    int         nLength = GetDataLength( pachSourceData, nMaxBytes,
+                                         pnConsumedBytes );
+
+/* -------------------------------------------------------------------- */
+/*      Do we need to grow the buffer.                                  */
+/* -------------------------------------------------------------------- */
+    if( nMaxBufChars < nLength+1 )
+    {
+        CPLFree( pachBuffer );
+        
+        nMaxBufChars = nLength+1;
+        pachBuffer = (char *) CPLMalloc(nMaxBufChars);
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Copy the data to the buffer.  We use memcpy() so that it        */
+/*      will work for binary data.                                      */
+/* -------------------------------------------------------------------- */
+    memcpy( pachBuffer, pachSourceData, nLength );
+    pachBuffer[nLength] = '\0';
+
+    return pachBuffer;
+}
+
+/************************************************************************/
+/*                          ExtractFloatData()                          */
+/************************************************************************/
+
+/**
+ * Extract a subfield value as a float.  Given a pointer to the data
+ * for this subfield (from within a DDFRecord) this method will return the
+ * floating point data for this subfield.  The number of bytes
+ * consumed as part of this field can also be fetched.  This method may be
+ * called for any type of subfield, and will return zero if the subfield is
+ * not numeric.
+ *
+ * @param pachSourceData The pointer to the raw data for this field.  This
+ * may have come from DDFRecord::GetData(), taking into account skip factors
+ * over previous subfields data.
+ * @param nMaxBytes The maximum number of bytes that are accessable after
+ * pachSourceData.
+ * @param pnConsumedBytes Pointer to an integer into which the number of
+ * bytes consumed by this field should be written.  May be NULL to ignore.
+ * This is used as a skip factor to increment pachSourceData to point to the
+ * next subfields data.
+ *
+ * @return The subfield's numeric value (or zero if it isn't numeric).
+ *
+ * @see ExtractIntData(), ExtractStringData()
+ */
+
+double
+DDFSubfieldDefn::ExtractFloatData( const char * pachSourceData,
+                                   int nMaxBytes, int * pnConsumedBytes )
+
+{
+    switch( pszFormatString[0] )
+    {
+      case 'A':
+      case 'I':
+      case 'R':
+      case 'S':
+      case 'C':
+        return atof(ExtractStringData(pachSourceData, nMaxBytes,
+                                      pnConsumedBytes));
+
+      case 'B':
+      case 'b':
+      {
+          unsigned char   abyData[8];
+
+          CPLAssert( nFormatWidth <= nMaxBytes );
+          if( pnConsumedBytes != NULL )
+              *pnConsumedBytes = nFormatWidth;
+
+          // Byte swap the data if it isn't in machine native format.
+          // In any event we copy it into our buffer to ensure it is
+          // word aligned.
+#ifdef CPL_LSB
+          if( pszFormatString[0] == 'B' )
+#else            
+              if( pszFormatString[0] == 'b' )
+#endif            
+              {
+                  for( int i = 0; i < nFormatWidth; i++ )
+                      abyData[nFormatWidth-i-1] = pachSourceData[i];
+              }
+              else
+              {
+                  memcpy( abyData, pachSourceData, nFormatWidth );
+              }
+
+          // Interpret the bytes of data.
+          switch( eBinaryFormat )
+          {
+            case UInt:
+              if( nFormatWidth == 1 )
+                  return( abyData[0] );
+              else if( nFormatWidth == 2 )
+                  return( *((GUInt16 *) abyData) );
+              else if( nFormatWidth == 4 )
+                  return( *((GUInt32 *) abyData) );
+              else
+              {
+                  CPLAssert( FALSE );
+                  return 0.0;
+              }
+            
+            case SInt:
+              if( nFormatWidth == 1 )
+                  return( *((signed char *) abyData) );
+              else if( nFormatWidth == 2 )
+                  return( *((GInt16 *) abyData) );
+              else if( nFormatWidth == 4 )
+                  return( *((GInt32 *) abyData) );
+              else
+              {
+                  CPLAssert( FALSE );
+                  return 0.0;
+              }
+            
+            case FloatReal:
+              if( nFormatWidth == 4 )
+                  return( *((float *) abyData) );
+              else if( nFormatWidth == 8 )
+                  return( *((double *) abyData) );
+              else
+              {
+                  CPLAssert( FALSE );
+                  return 0.0;
+              }
+
+            case NotBinary:            
+            case FPReal:
+            case FloatComplex:
+              CPLAssert( FALSE );
+              return 0.0;
+          }
+          break;
+          // end of 'b'/'B' case.
+      }
+
+      default:
+        CPLAssert( FALSE );
+        return 0.0;
+    }
+
+    CPLAssert( FALSE );
+    return 0.0;
+}
+
+/************************************************************************/
+/*                           ExtractIntData()                           */
+/************************************************************************/
+
+/**
+ * Extract a subfield value as an integer.  Given a pointer to the data
+ * for this subfield (from within a DDFRecord) this method will return the
+ * int data for this subfield.  The number of bytes
+ * consumed as part of this field can also be fetched.  This method may be
+ * called for any type of subfield, and will return zero if the subfield is
+ * not numeric.
+ *
+ * @param pachSourceData The pointer to the raw data for this field.  This
+ * may have come from DDFRecord::GetData(), taking into account skip factors
+ * over previous subfields data.
+ * @param nMaxBytes The maximum number of bytes that are accessable after
+ * pachSourceData.
+ * @param pnConsumedBytes Pointer to an integer into which the number of
+ * bytes consumed by this field should be written.  May be NULL to ignore.
+ * This is used as a skip factor to increment pachSourceData to point to the
+ * next subfields data.
+ *
+ * @return The subfield's numeric value (or zero if it isn't numeric).
+ *
+ * @see ExtractFloatData(), ExtractStringData()
+ */
+
+int
+DDFSubfieldDefn::ExtractIntData( const char * pachSourceData,
+                                 int nMaxBytes, int * pnConsumedBytes )
+
+{
+    switch( pszFormatString[0] )
+    {
+      case 'A':
+      case 'I':
+      case 'R':
+      case 'S':
+      case 'C':
+        return atoi(ExtractStringData(pachSourceData, nMaxBytes,
+                                      pnConsumedBytes));
+
+      case 'B':
+      case 'b':
+      {
+          unsigned char   abyData[8];
+
+          if( nFormatWidth > nMaxBytes )
+          {
+              CPLError( CE_Warning, CPLE_AppDefined, 
+                        "Attempt to extract int subfield %s with format %s\n"
+                        "failed as only %d bytes available.  Using zero.",
+                        pszName, pszFormatString, nMaxBytes );
+              return 0;
+          }
+
+          if( pnConsumedBytes != NULL )
+              *pnConsumedBytes = nFormatWidth;
+
+          // Byte swap the data if it isn't in machine native format.
+          // In any event we copy it into our buffer to ensure it is
+          // word aligned.
+#ifdef CPL_LSB
+          if( pszFormatString[0] == 'B' )
+#else            
+              if( pszFormatString[0] == 'b' )
+#endif            
+              {
+                  for( int i = 0; i < nFormatWidth; i++ )
+                      abyData[nFormatWidth-i-1] = pachSourceData[i];
+              }
+              else
+              {
+                  memcpy( abyData, pachSourceData, nFormatWidth );
+              }
+
+          // Interpret the bytes of data.
+          switch( eBinaryFormat )
+          {
+            case UInt:
+              if( nFormatWidth == 4 )
+                  return( (int) *((GUInt32 *) abyData) );
+              else if( nFormatWidth == 1 )
+                  return( abyData[0] );
+              else if( nFormatWidth == 2 )
+                  return( *((GUInt16 *) abyData) );
+              else
+              {
+                  CPLAssert( FALSE );
+                  return 0;
+              }
+            
+            case SInt:
+              if( nFormatWidth == 4 )
+                  return( *((GInt32 *) abyData) );
+              else if( nFormatWidth == 1 )
+                  return( *((signed char *) abyData) );
+              else if( nFormatWidth == 2 )
+                  return( *((GInt16 *) abyData) );
+              else
+              {
+                  CPLAssert( FALSE );
+                  return 0;
+              }
+            
+            case FloatReal:
+              if( nFormatWidth == 4 )
+                  return( (int) *((float *) abyData) );
+              else if( nFormatWidth == 8 )
+                  return( (int) *((double *) abyData) );
+              else
+              {
+                  CPLAssert( FALSE );
+                  return 0;
+              }
+
+            case NotBinary:            
+            case FPReal:
+            case FloatComplex:
+              CPLAssert( FALSE );
+              return 0;
+          }
+          break;
+          // end of 'b'/'B' case.
+      }
+
+      default:
+        CPLAssert( FALSE );
+        return 0;
+    }
+
+    CPLAssert( FALSE );
+    return 0;
+}
+
+/************************************************************************/
+/*                              DumpData()                              */
+/*                                                                      */
+/*      Dump the instance data for this subfield from a data            */
+/*      record.  This fits into the output dump stream of a DDFField.   */
+/************************************************************************/
+
+/**
+ * Dump subfield value to debugging file.
+ *
+ * @param pachData Pointer to data for this subfield.
+ * @param nMaxBytes Maximum number of bytes available in pachData.
+ * @param fp File to write report to.
+ */
+
+void DDFSubfieldDefn::DumpData( const char * pachData, int nMaxBytes,
+                                FILE * fp )
+
+{
+    if( eType == DDFFloat )
+        fprintf( fp, "      Subfield `%s' = %f\n",
+                 pszName,
+                 ExtractFloatData( pachData, nMaxBytes, NULL ) );
+    else if( eType == DDFInt )
+        fprintf( fp, "      Subfield `%s' = %d\n",
+                 pszName,
+                 ExtractIntData( pachData, nMaxBytes, NULL ) );
+    else if( eType == DDFBinaryString )
+    {
+        int     nBytes, i;
+        GByte   *pabyBString = (GByte *) ExtractStringData( pachData, nMaxBytes, &nBytes );
+
+        fprintf( fp, "      Subfield `%s' = 0x", pszName );
+        for( i = 0; i < MIN(nBytes,24); i++ )
+            fprintf( fp, "%02X", pabyBString[i] );
+
+        if( nBytes > 24 )
+            fprintf( fp, "%s", "..." );
+
+        fprintf( fp, "\n" );
+    }
+    else
+        fprintf( fp, "      Subfield `%s' = `%s'\n",
+                 pszName,
+                 ExtractStringData( pachData, nMaxBytes, NULL ) );
+}
+
+/************************************************************************/
+/*                          GetDefaultValue()                           */
+/************************************************************************/
+
+/**
+ * Get default data. 
+ *
+ * Returns the default subfield data contents for this subfield definition.
+ * For variable length numbers this will normally be "0<unit-terminator>". 
+ * For variable length strings it will be "<unit-terminator>".  For fixed
+ * length numbers it is zero filled.  For fixed length strings it is space
+ * filled.  For binary numbers it is binary zero filled. 
+ *
+ * @param pachData the buffer into which the returned default will be placed.
+ * May be NULL if just querying default size.
+ * @param nBytesAvailable the size of pachData in bytes. 
+ * @param pnBytesUsed will receive the size of the subfield default data in
+ * bytes.
+ *
+ * @return TRUE on success or FALSE on failure or if the passed buffer is too
+ * small to hold the default.
+ */
+
+int DDFSubfieldDefn::GetDefaultValue( char *pachData, int nBytesAvailable, 
+                                      int *pnBytesUsed )
+
+{
+    int nDefaultSize;
+
+    if( !bIsVariable )
+        nDefaultSize = nFormatWidth;
+    else
+        nDefaultSize = 1;
+
+    if( pnBytesUsed != NULL )
+        *pnBytesUsed = nDefaultSize;
+
+    if( pachData == NULL )
+        return TRUE;
+
+    if( nBytesAvailable < nDefaultSize )
+        return FALSE;
+
+    if( bIsVariable )
+    {
+        pachData[0] = DDF_UNIT_TERMINATOR;
+    }
+    else
+    {
+        if( GetBinaryFormat() == NotBinary )
+        {
+            if( GetType() == DDFInt || GetType() == DDFFloat )
+                memset( pachData, '0', nDefaultSize );
+            else
+                memset( pachData, ' ', nDefaultSize );
+        }
+        else
+            memset( pachData, 0, nDefaultSize );
+    }
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                         FormatStringValue()                          */
+/************************************************************************/
+
+/**
+ * Format string subfield value.
+ *
+ * Returns a buffer with the passed in string value reformatted in a way
+ * suitable for storage in a DDFField for this subfield.  
+ */
+
+int DDFSubfieldDefn::FormatStringValue( char *pachData, int nBytesAvailable, 
+                                        int *pnBytesUsed, 
+                                        const char *pszValue,
+                                        int nValueLength )
+
+{
+    int nSize;
+
+    if( nValueLength == -1 )
+        nValueLength = strlen(pszValue);
+
+    if( bIsVariable )
+    {
+        nSize = nValueLength + 1;
+    }
+    else
+    {                                                                  
+        nSize = nFormatWidth;
+    }
+
+    if( pnBytesUsed != NULL )
+        *pnBytesUsed = nSize;
+
+    if( pachData == NULL )
+        return TRUE;
+
+    if( nBytesAvailable < nSize )
+        return FALSE;
+
+    if( bIsVariable )
+    {
+        strncpy( pachData, pszValue, nSize-1 );
+        pachData[nSize-1] = DDF_UNIT_TERMINATOR;
+    }
+    else
+    {
+        if( GetBinaryFormat() == NotBinary )
+        {
+            memset( pachData, ' ', nSize );
+            memcpy( pachData, pszValue, MIN(nValueLength,nSize) );
+        }
+        else
+        {
+            memset( pachData, 0, nSize );
+            memcpy( pachData, pszValue, MIN(nValueLength,nSize) );
+        }
+    }
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                           FormatIntValue()                           */
+/************************************************************************/
+
+/**
+ * Format int subfield value.
+ *
+ * Returns a buffer with the passed in int value reformatted in a way
+ * suitable for storage in a DDFField for this subfield.  
+ */
+
+int DDFSubfieldDefn::FormatIntValue( char *pachData, int nBytesAvailable, 
+                                     int *pnBytesUsed, int nNewValue )
+
+{
+    int nSize;
+    char szWork[30];
+
+    sprintf( szWork, "%d", nNewValue );
+
+    if( bIsVariable )
+    {
+        nSize = strlen(szWork) + 1;
+    }
+    else
+    {                                                                  
+        nSize = nFormatWidth;
+
+        if( GetBinaryFormat() == NotBinary && (int) strlen(szWork) > nSize )
+            return FALSE;
+    }
+
+    if( pnBytesUsed != NULL )
+        *pnBytesUsed = nSize;
+
+    if( pachData == NULL )
+        return TRUE;
+
+    if( nBytesAvailable < nSize )
+        return FALSE;
+
+    if( bIsVariable )
+    {
+        strncpy( pachData, szWork, nSize-1 );
+        pachData[nSize-1] = DDF_UNIT_TERMINATOR;
+    }
+    else
+    {
+        GUInt32 nMask = 0xff;
+        int i;
+
+        switch( GetBinaryFormat() )
+        {
+          case NotBinary:
+            memset( pachData, '0', nSize );
+            strncpy( pachData + nSize - strlen(szWork), szWork,
+                     strlen(szWork) );
+            break;
+
+          case UInt:
+          case SInt:
+            for( i = 0; i < nFormatWidth; i++ )
+            {
+                int iOut;
+
+                // big endian required?
+                if( pszFormatString[0] == 'B' )
+                    iOut = nFormatWidth - i - 1;
+                else
+                    iOut = i;
+
+                pachData[iOut] = (nNewValue & nMask) >> (i*8);
+                nMask *= 256;
+            }
+            break;
+
+          case FloatReal:
+            CPLAssert( FALSE );
+            break;
+
+          default:
+            CPLAssert( FALSE );
+            break;
+        }
+    }
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                          FormatFloatValue()                          */
+/************************************************************************/
+
+/**
+ * Format float subfield value.
+ *
+ * Returns a buffer with the passed in float value reformatted in a way
+ * suitable for storage in a DDFField for this subfield.  
+ */
+
+int DDFSubfieldDefn::FormatFloatValue( char *pachData, int nBytesAvailable, 
+                                       int *pnBytesUsed, double dfNewValue )
+
+{
+    int nSize;
+    char szWork[120];
+
+    sprintf( szWork, "%.16g", dfNewValue );
+
+    if( bIsVariable )
+    {
+        nSize = strlen(szWork) + 1;
+    }
+    else
+    {
+        nSize = nFormatWidth;
+
+        if( GetBinaryFormat() == NotBinary && (int) strlen(szWork) > nSize )
+            return FALSE;
+    }
+
+    if( pnBytesUsed != NULL )
+        *pnBytesUsed = nSize;
+
+    if( pachData == NULL )
+        return TRUE;
+
+    if( nBytesAvailable < nSize )
+        return FALSE;
+
+    if( bIsVariable )
+    {
+        strncpy( pachData, szWork, nSize-1 );
+        pachData[nSize-1] = DDF_UNIT_TERMINATOR;
+    }
+    else
+    {
+        if( GetBinaryFormat() == NotBinary )
+        {
+            memset( pachData, '0', nSize );
+            strncpy( pachData + nSize - strlen(szWork), szWork,
+                     strlen(szWork) );
+        }
+        else
+        {
+            CPLAssert( FALSE );
+            /* implement me */
+        }
+    }
+
+    return TRUE;
+}
diff --git a/Utilities/GDAL/frmts/iso8211/ddfutils.cpp b/Utilities/GDAL/frmts/iso8211/ddfutils.cpp
new file mode 100644
index 0000000000..1400a2868b
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/ddfutils.cpp
@@ -0,0 +1,125 @@
+/******************************************************************************
+ * $Id: ddfutils.cpp,v 1.7 2006/04/04 04:24:07 fwarmerdam Exp $
+ *
+ * Project:  ISO 8211 Access
+ * Purpose:  Various utility functions.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: ddfutils.cpp,v $
+ * Revision 1.7  2006/04/04 04:24:07  fwarmerdam
+ * update contact info
+ *
+ * Revision 1.6  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.5  1999/11/18 19:03:04  warmerda
+ * expanded tabs
+ *
+ * Revision 1.4  1999/09/20 19:29:16  warmerda
+ * make forgiving of UNIT/FIELD terminator mixup in Tiger SDTS files
+ *
+ * Revision 1.3  1999/05/06 14:41:03  warmerda
+ * Removed unused variable.
+ *
+ * Revision 1.2  1999/05/06 14:23:49  warmerda
+ * optimised DDFScanInt()
+ *
+ * Revision 1.1  1999/04/27 18:45:05  warmerda
+ * New
+ *
+ */
+
+#include "iso8211.h"
+#include "cpl_conv.h"
+
+CPL_CVSID("$Id: ddfutils.cpp,v 1.7 2006/04/04 04:24:07 fwarmerdam Exp $");
+
+/************************************************************************/
+/*                             DDFScanInt()                             */
+/*                                                                      */
+/*      Read up to nMaxChars from the passed string, and interpret      */
+/*      as an integer.                                                  */
+/************************************************************************/
+
+long DDFScanInt( const char * pszString, int nMaxChars )
+
+{
+    char        szWorking[33];
+
+    if( nMaxChars > 32 || nMaxChars == 0 )
+        nMaxChars = 32;
+
+    memcpy( szWorking, pszString, nMaxChars );
+    szWorking[nMaxChars] = '\0';
+
+    return( atoi(szWorking) );
+}
+
+/************************************************************************/
+/*                          DDFScanVariable()                           */
+/*                                                                      */
+/*      Establish the length of a variable length string in a           */
+/*      record.                                                         */
+/************************************************************************/
+
+int DDFScanVariable( const char *pszRecord, int nMaxChars, int nDelimChar )
+
+{
+    int         i;
+    
+    for( i = 0; i < nMaxChars-1 && pszRecord[i] != nDelimChar; i++ ) {}
+
+    return i;
+}
+
+/************************************************************************/
+/*                          DDFFetchVariable()                          */
+/*                                                                      */
+/*      Fetch a variable length string from a record, and allocate      */
+/*      it as a new string (with CPLStrdup()).                          */
+/************************************************************************/
+
+char * DDFFetchVariable( const char *pszRecord, int nMaxChars,
+                         int nDelimChar1, int nDelimChar2,
+                         int *pnConsumedChars )
+
+{
+    int         i;
+    char        *pszReturn;
+
+    for( i = 0; i < nMaxChars-1 && pszRecord[i] != nDelimChar1
+                                && pszRecord[i] != nDelimChar2; i++ ) {}
+
+    *pnConsumedChars = i;
+    if( i < nMaxChars
+        && (pszRecord[i] == nDelimChar1 || pszRecord[i] == nDelimChar2) )
+        (*pnConsumedChars)++;
+
+    pszReturn = (char *) CPLMalloc(i+1);
+    pszReturn[i] = '\0';
+    strncpy( pszReturn, pszRecord, i );
+
+    return pszReturn;
+}
diff --git a/Utilities/GDAL/frmts/iso8211/intro.dox b/Utilities/GDAL/frmts/iso8211/intro.dox
new file mode 100644
index 0000000000..f73d334213
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/intro.dox
@@ -0,0 +1,219 @@
+/*! \page ISO8211Lib
+
+<center>
+<title>ISO8211Lib</title>
+</center>
+
+<h2>Introduction</h2>
+
+ISO8211Lib is intended to be a simple reader for ISO/IEC 8211 formatted files,
+particularly those that are part of SDTS and S-57 datasets.  It consists
+of open source, easy to compile and integrate C++ code.<p>
+
+<h2>ISO 8211 Background</h2>
+
+The <a href="http://starship.python.net/crew/tibs/iso8211/faq.html">ISO 8211
+FAQ</a> has some good background on ISO 8211 formatted files.  I will briefly
+introduce it here, with reference to the library classes representing
+the components.<p>
+
+An 8211 file (DDFModule) 
+consists of a series of logical records.  The first record
+is special, and is called the DDR (Data Description Record).  It basically
+contains definitions of all the data objects (fields or DDFFieldDefn objects) 
+that can occur on the following data records.<p>
+
+The remainder of the records are known as DRs (data records - DDFRecord).  They
+each contain one or more field (DDFField) instances.  What fields appear 
+on what records is not defined by ISO 8211, though more specific requirements 
+may be implied by a particular data standard such as SDTS or S-57.<p>
+
+Each field instance has a name, and consists of a series of subfields.  A 
+given 
+field always has the same subfields in each field instance, and these subfields
+are defined in the DDR (DDFSubfieldDefn), in association with their 
+field definition (DDFFieldDefn).  A field may appear 0, 1, or many times in a DR. <p>
+
+Each subfield has a name, format (from the DDFSubfieldDefn) and 
+actual subfield data for a particular DR.  
+Some fields contain an <i>array</i> of their group of
+subfields.  For instance a <i>coordinate field</i> may have X and Y 
+subfields, and they may repeat many times within one coordinate field 
+indicating a series of points.<p>
+
+<i>This would be a real good place for a UML diagram of ISO 8211, and 
+the corresponding library classes!</i><p>
+ 
+<h2>Development Information</h2>
+
+The <b>iso8211.h</b> contains the definitions for all public ISO8211Lib
+classes, enumerations and other services.<p>
+
+To establish access to an ISO 8211 dataset, instantiate a DDFModule object,
+and then use the DDFModule::Open() method.  This will read the DDR, and
+establish all the DDFFieldDefn, and DDFSubfieldDefn objects which can be
+queried off the DDFModule.<p>
+
+The use DDFModule::ReadRecord() to fetch data records (DDFRecord).  When 
+a record is read, a list of field objects (DDFField) on that record are 
+created.  They can be queried with various DDFRecord methods.<P>
+
+Data pointers for individual subfields of a DDFField can be fetched with 
+DDFField::GetSubfieldData().  The interpreted value can then be extracted
+with the appropriate one of DDFSubfieldDefn::ExtractIntValue(), 
+DDFSubfieldDefn::ExtractStringValue(), or DDFSubfieldDefn::ExtractFloatValue().
+Note that there is no object instantiated for individual subfields of a
+DDFField.  Instead the application extracts a pointer to the subfields
+raw data, and then uses the DDFSubfieldDefn for that subfield to extract
+a useable value from the raw data.<p>
+
+Once the end of the file has been encountered (DDFModule::ReadRecord() returns
+NULL), the DDFModule should be deleted, which will close the file, and
+cleanup all records, definitions and related objects.<p>
+
+<h3>Class APIs</h3>
+
+<ul>
+<li> DDFModule class.
+<li> DDFFieldDefn class.
+<li> DDFSubfieldDefn class.
+<li> DDFRecord class.
+<li> DDFField class.
+</ul>
+
+A complete <a href="example.html">Example Reader</a> should clarify 
+simple use of ISO8211Lib.<p>
+
+<h2>Related Information</h2>
+
+<ul>
+
+<li> The ISO 8211 standard can be ordered through 
+<a href="http://www.iso.ch/">ISO</a>.  It cost me about $200CDN.<p>
+
+<li> The <a href="http://user.icx.net/~brooks/iso8211.html">ISO/IEC 8211/DDFS 
+Home Page</a> contains tutorials and some code by Dr. Alfred A.
+Brooks, one of the originators of the 8211 standard.<p>
+
+<li> The <a href="http://starship.skyport.net/crew/tibs/iso8211/iso8211.html">
+ISO/IEC 8211 Home Page</a> has some python code for parsing 8211 files, and
+some other useful background.<p>
+
+<li> The <a href="http://mcmcweb.er.usgs.gov/sdts/sdtsxx/index.html">SDTS++</a>
+library from the USGS 
+includes support for ISO 8211.  It doesn't include some of the 
+1994 additions to ISO 8211, but it is sufficient for SDTS, and quite 
+elegantly done.  Also supports writing ISO 8211 files.<p>
+
+<li> The USGS also has an older 
+<a href="ftp://sdts.er.usgs.gov/pub/sdts/software/fips123/">FIPS123</a> 
+library which supports the older profile of ISO 8211 (to some extent).<p>
+
+</ul>
+
+<h2>Licensing</h2>
+
+This library is offered as <a href="http://www.opensource.org">Open Source</a>.
+In particular, it is offered under the X Consortium license which doesn't
+attempt to impose any copyleft, or credit requirements on users of the code.<p>
+
+The precise license text is:<p>
+
+<em>
+ Copyright (c) 1999, Frank Warmerdam
+<p>
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+<p>
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+<p>
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+<p>
+</em>
+
+<h2>Building the Source</h2>
+
+<ol>
+
+<li> First, fetch the source.  The most recent source should be accessable at
+an url such as 
+<a href="http://gdal.velocet.ca/projects/iso8211/iso8211lib-1.2.tar.gz">
+http://gdal.velocet.ca/projects/iso8211/iso8211lib-1.2.tar.gz</a>, or
+by ftp from 
+<a href="ftp://gdal.velocet.ca/pub/outgoing/iso8211lib-1.2.tar.gz">
+ftp://gdal.velocet.ca/pub/outgoing/iso8211lib-1.2.tar.gz</a>.<p>
+
+<li> Untar the source.<p>
+<code>
+% gzip -d iso8211lib-1.2.tar.gz
+% tar xzvf iso8211lib-1.2.tar.gz
+</code>
+
+If for some reason you don't have gzip, or tar (perhaps you work on Windows)
+just <a href="mailto:warmerdam@pobox.com">let me know</a>, 
+and I will prepare a zip or similar archive of the source.<p>
+
+<li> On unix you can now type ``configure'' to establish configuration 
+options.<p>
+
+<li> On unix you can now type make to build libiso8211.a, and the sample
+mainline 8211view.<p>
+
+</ol>
+
+Windows developers will have to create their own makefile/project but
+can base it on the very simple Makefile.in provided.  As well, you would
+need to copy cpl_config.h.in to cpl_config.h, and modify as needed.  The
+default will likely work OK, but may result in some compiler warnings.  
+Let me know if you are having difficulties, and I will prepare a VC++ 
+makefile.<p>
+
+<h2>Author and Acknowledgements</h2>
+
+The primary author of ISO8211Lib is <a href="http://pobox.com/~warmerdam">
+Frank Warmerdam</a>, and I can be reached at 
+<a href="mailto:warmerdam@pobox.com">warmerdam@pobox.com</a>.  I am eager to 
+receive bug reports, and also open to praise or suggestions.<p>
+
+I would like to thank:<p>
+
+<ul>
+<li> <a href="http://www.safe.com/">Safe Software</a>
+who funded development of this library, and agreed for it to be Open Source.<p>
+
+<li> Mark Colletti, a primary author of SDTS++ from
+which I derived most of what I know about ISO 8211 and who was very 
+supportive, answering a variety of questions.<p>
+
+<li> Tony J Ibbs, author of the ISO/IEC 8211 home page who answered
+a number of questions, and collected a variety of very useful information.
+<p>
+
+<li> Rodney Jenson, for a detailed bug report related to repeating variable
+length fields (from S-57).<p>
+
+</ul>
+
+I would also like to dedicate this library to the memory of Sol Katz.  
+Sol released a variety of SDTS (and hence ISO8211) translators, at substantial
+personal effort, to the GIS community along with the many other generous 
+contributions he made to the community.  His example has been an inspiration
+to me, and I hope similar efforts on my part will contribute to his memory.<p>
+
+*/
+
+/*!
+\page ISO8211_Example
+\include 8211view.cpp
+*/
diff --git a/Utilities/GDAL/frmts/iso8211/iso8211.h b/Utilities/GDAL/frmts/iso8211/iso8211.h
new file mode 100644
index 0000000000..1f0c35c434
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/iso8211.h
@@ -0,0 +1,582 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  ISO 8211 Access
+ * Purpose:  Main declarations for ISO 8211.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: iso8211.h,v $
+ * Revision 1.20  2005/12/08 20:22:24  fwarmerdam
+ * use CPL_ODLL to optionally export ISO8211lib api
+ *
+ * Revision 1.19  2004/01/06 18:59:18  warmerda
+ * make enum identifiers more unique
+ *
+ * Revision 1.18  2004/01/06 18:53:41  warmerda
+ * made data_type_code and data_struct_code global for HP C++ builds
+ *
+ * Revision 1.17  2003/09/03 20:36:26  warmerda
+ * added subfield writing support
+ *
+ * Revision 1.16  2003/08/21 21:21:44  warmerda
+ * expose the binary format type for a subfield defn
+ *
+ * Revision 1.15  2003/07/03 15:38:46  warmerda
+ * some write capabilities added
+ *
+ * Revision 1.14  2001/08/29 17:47:33  warmerda
+ * added GetInstanceData
+ *
+ * Revision 1.13  2001/08/24 19:41:19  warmerda
+ * fixed cloning problems
+ *
+ * Revision 1.12  2001/08/24 16:30:55  warmerda
+ * added DDFRecord update in place methods for S57 updating
+ *
+ * Revision 1.11  2000/09/19 14:08:51  warmerda
+ * keep and report _extendedCharSet
+ *
+ * Revision 1.10  2000/06/16 18:02:08  warmerda
+ * added SetRepeatingFlag hack support
+ *
+ * Revision 1.9  2000/01/31 18:03:39  warmerda
+ * completely rewrote format expansion to make more general
+ *
+ * Revision 1.8  1999/11/18 19:03:04  warmerda
+ * expanded tabs
+ *
+ * Revision 1.7  1999/11/18 19:02:38  warmerda
+ * added failquietly to open
+ *
+ * Revision 1.6  1999/09/20 19:29:30  warmerda
+ * make forgiving of UNIT/FIELD terminator mixup in Tiger SDTS files
+ *
+ * Revision 1.5  1999/08/13 03:26:29  warmerda
+ * added Rewind()
+ *
+ * Revision 1.4  1999/05/07 14:11:22  warmerda
+ * added subfield value fetches on record, and other odds and ends.
+ *
+ * Revision 1.3  1999/05/06 14:23:32  warmerda
+ * added DDFBinaryString
+ *
+ * Revision 1.2  1999/04/27 22:09:50  warmerda
+ * updated docs
+ *
+ * Revision 1.1  1999/04/27 18:45:09  warmerda
+ * New
+ *
+ */
+
+#ifndef _ISO8211_H_INCLUDED
+#define _ISO8211_H_INCLUDED
+
+#include "cpl_port.h"
+
+/**
+  General data type
+    */
+typedef enum {
+    DDFInt,
+    DDFFloat,
+    DDFString,
+    DDFBinaryString
+} DDFDataType;
+  
+/************************************************************************/
+/*      These should really be private to the library ... they are      */
+/*      mostly conveniences.                                            */
+/************************************************************************/
+
+long CPL_ODLL DDFScanInt( const char *pszString, int nMaxChars );
+int  CPL_ODLL DDFScanVariable( const char * pszString, int nMaxChars, int nDelimChar );
+char CPL_ODLL *DDFFetchVariable( const char *pszString, int nMaxChars,
+                        int nDelimChar1, int nDelimChar2,
+                        int *pnConsumedChars );
+
+#define DDF_FIELD_TERMINATOR    30
+#define DDF_UNIT_TERMINATOR     31
+
+/************************************************************************/
+/*                           Predeclarations                            */
+/************************************************************************/
+
+class DDFFieldDefn;
+class DDFSubfieldDefn;
+class DDFRecord;
+class DDFField;
+
+/************************************************************************/
+/*                              DDFModule                               */
+/************************************************************************/
+
+/**
+  The primary class for reading ISO 8211 files.  This class contains all
+  the information read from the DDR record, and is used to read records
+  from the file.           
+
+*/  
+
+class CPL_ODLL DDFModule
+{
+  public:
+                DDFModule();
+                ~DDFModule();
+                
+    int         Open( const char * pszFilename, int bFailQuietly = FALSE );
+    int         Create( const char *pszFilename );
+    void        Close();
+
+    int         Initialize( char chInterchangeLevel = '3',
+                            char chLeaderIden = 'L', 
+                            char chCodeExtensionIndicator = 'E',
+                            char chVersionNumber = '1',
+                            char chAppIndicator = ' ',
+                            const char *pszExtendedCharSet = " ! ",
+                            int nSizeFieldLength = 3,
+                            int nSizeFieldPos = 4,
+                            int nSizeFieldTag = 4 );
+
+    void        Dump( FILE * fp );
+
+    DDFRecord   *ReadRecord( void );
+    void        Rewind( long nOffset = -1 );
+
+    DDFFieldDefn *FindFieldDefn( const char * );
+
+    /** Fetch the number of defined fields. */
+
+    int         GetFieldCount() { return nFieldDefnCount; }
+    DDFFieldDefn *GetField(int);
+    void        AddField( DDFFieldDefn *poNewFDefn );
+    
+    // This is really just for internal use.
+    int         GetFieldControlLength() { return _fieldControlLength; }
+    void        AddCloneRecord( DDFRecord * );
+    void        RemoveCloneRecord( DDFRecord * );
+    
+    // This is just for DDFRecord.
+    FILE        *GetFP() { return fpDDF; }
+    
+  private:
+    FILE        *fpDDF;
+    int         bReadOnly;
+    long        nFirstRecordOffset;
+
+    char        _interchangeLevel;
+    char        _inlineCodeExtensionIndicator;
+    char        _versionNumber;
+    char        _appIndicator;
+    int         _fieldControlLength;
+    char        _extendedCharSet[4];
+
+    long _recLength;
+    char _leaderIden;
+    long _fieldAreaStart;
+    long _sizeFieldLength;
+    long _sizeFieldPos;
+    long _sizeFieldTag;
+
+    // One DirEntry per field.  
+    int         nFieldDefnCount;
+    DDFFieldDefn **papoFieldDefns;
+
+    DDFRecord   *poRecord;
+
+    int         nCloneCount;
+    int         nMaxCloneCount;
+    DDFRecord   **papoClones;
+};
+
+/************************************************************************/
+/*                             DDFFieldDefn                             */
+/************************************************************************/
+
+  typedef enum { dsc_elementary, dsc_vector, dsc_array, dsc_concatenated } DDF_data_struct_code;
+  typedef enum { dtc_char_string, 
+                 dtc_implicit_point, 
+                 dtc_explicit_point, 
+                 dtc_explicit_point_scaled, 
+                 dtc_char_bit_string, 
+                 dtc_bit_string, 
+                 dtc_mixed_data_type } DDF_data_type_code;
+
+/**
+ * Information from the DDR defining one field.  Note that just because
+ * a field is defined for a DDFModule doesn't mean that it actually occurs
+ * on any records in the module.  DDFFieldDefns are normally just significant
+ * as containers of the DDFSubfieldDefns.
+ */
+
+class CPL_ODLL DDFFieldDefn
+{
+  public:
+                DDFFieldDefn();
+                ~DDFFieldDefn();
+
+    int         Create( const char *pszTag, const char *pszFieldName,
+                        const char *pszDescription,
+                        DDF_data_struct_code eDataStructCode,
+                        DDF_data_type_code   eDataTypeCode,
+                        const char *pszFormat = NULL );
+    void        AddSubfield( DDFSubfieldDefn *poNewSFDefn,
+                             int bDontAddToFormat = FALSE );
+    void        AddSubfield( const char *pszName, const char *pszFormat );
+    int         GenerateDDREntry( char **ppachData, int *pnLength ); 
+                            
+    int         Initialize( DDFModule * poModule, const char *pszTag,
+                            int nSize, const char * pachRecord );
+    
+    void        Dump( FILE * fp );
+
+    /** Fetch a pointer to the field name (tag).
+     * @return this is an internal copy and shouldn't be freed.
+     */
+    const char  *GetName() { return pszTag; }
+
+    /** Fetch a longer descriptio of this field.
+     * @return this is an internal copy and shouldn't be freed.
+     */
+    const char  *GetDescription() { return _fieldName; }
+
+    /** Get the number of subfields. */
+    int         GetSubfieldCount() { return nSubfieldCount; }
+    
+    DDFSubfieldDefn *GetSubfield( int i );
+    DDFSubfieldDefn *FindSubfieldDefn( const char * );
+
+    /**
+     * Get the width of this field.  This function isn't normally used
+     * by applications.
+     *
+     * @return The width of the field in bytes, or zero if the field is not
+     * apparently of a fixed width.
+     */
+    int         GetFixedWidth() { return nFixedWidth; }
+
+    /**
+     * Fetch repeating flag.
+     * @see DDFField::GetRepeatCount()
+     * @return TRUE if the field is marked as repeating.
+     */
+    int         IsRepeating() { return bRepeatingSubfields; }
+
+    static char       *ExpandFormat( const char * );
+
+    /** this is just for an S-57 hack for swedish data */
+    void SetRepeatingFlag( int n ) { bRepeatingSubfields = n; }
+
+    char        *GetDefaultValue( int *pnSize );
+    
+  private:
+
+    static char       *ExtractSubstring( const char * );
+
+    DDFModule * poModule;
+    char *      pszTag;
+
+    char *      _fieldName;
+    char *      _arrayDescr;
+    char *      _formatControls;
+
+    int         bRepeatingSubfields;
+    int         nFixedWidth;    // zero if variable. 
+
+    int         BuildSubfields();
+    int         ApplyFormats();
+
+    DDF_data_struct_code _data_struct_code;
+
+    DDF_data_type_code   _data_type_code;
+
+    int         nSubfieldCount;
+    DDFSubfieldDefn **papoSubfields;
+};
+
+/************************************************************************/
+/*                           DDFSubfieldDefn                            */
+/*                                                                      */
+/*      Information from the DDR record for one subfield of a           */
+/*      particular field.                                               */
+/************************************************************************/
+
+/**
+ * Information from the DDR record describing one subfield of a DDFFieldDefn.
+ * All subfields of a field will occur in each occurance of that field
+ * (as a DDFField) in a DDFRecord.  Subfield's actually contain formatted
+ * data (as instances within a record).
+ */
+
+class CPL_ODLL DDFSubfieldDefn
+{
+public:
+
+                DDFSubfieldDefn();
+                ~DDFSubfieldDefn();
+
+    void        SetName( const char * pszName );
+
+    /** Get pointer to subfield name. */
+    const char  *GetName() { return pszName; }
+    
+    /** Get pointer to subfield format string */
+    const char  *GetFormat() { return pszFormatString; }
+    int         SetFormat( const char * pszFormat );
+
+    /**
+     * Get the general type of the subfield.  This can be used to
+     * determine which of ExtractFloatData(), ExtractIntData() or
+     * ExtractStringData() should be used.
+     * @return The subfield type.  One of DDFInt, DDFFloat, DDFString or
+     * DDFBinaryString.
+     */
+      
+    DDFDataType GetType() { return eType; }
+
+    double      ExtractFloatData( const char *pachData, int nMaxBytes,
+                                  int * pnConsumedBytes );
+    int         ExtractIntData( const char *pachData, int nMaxBytes,
+                                int * pnConsumedBytes );
+    const char  *ExtractStringData( const char *pachData, int nMaxBytes,
+                                    int * pnConsumedBytes );
+    int         GetDataLength( const char *, int, int * );
+    void        DumpData( const char *pachData, int nMaxBytes, FILE * fp );
+
+    int         FormatStringValue( char *pachData, int nBytesAvailable, 
+                                   int *pnBytesUsed, const char *pszValue, 
+                                   int nValueLength = -1 );
+
+    int         FormatIntValue( char *pachData, int nBytesAvailable, 
+                                int *pnBytesUsed, int nNewValue );
+
+    int         FormatFloatValue( char *pachData, int nBytesAvailable, 
+                                  int *pnBytesUsed, double dfNewValue );
+
+    /** Get the subfield width (zero for variable). */
+    int         GetWidth() { return nFormatWidth; } // zero for variable.
+
+    int         GetDefaultValue( char *pachData, int nBytesAvailable, 
+                                 int *pnBytesUsed );
+    
+    void        Dump( FILE * fp );
+
+/**
+  Binary format: this is the digit immediately following the B or b for
+  binary formats. 
+  */
+typedef enum {
+    NotBinary=0,
+    UInt=1,
+    SInt=2,
+    FPReal=3,
+    FloatReal=4,
+    FloatComplex=5
+} DDFBinaryFormat;
+
+    DDFBinaryFormat GetBinaryFormat(void) const { return eBinaryFormat; }
+    
+
+private:
+
+  char      *pszName;   // a.k.a. subfield mnemonic
+  char      *pszFormatString; 
+
+  DDFDataType           eType;
+  DDFBinaryFormat       eBinaryFormat;
+
+/* -------------------------------------------------------------------- */
+/*      bIsVariable determines whether we using the                     */
+/*      chFormatDelimeter (TRUE), or the fixed width (FALSE).           */
+/* -------------------------------------------------------------------- */
+  int        bIsVariable;
+  
+  char       chFormatDelimeter;
+  int        nFormatWidth;
+
+/* -------------------------------------------------------------------- */
+/*      Fetched string cache.  This is where we hold the values         */
+/*      returned from ExtractStringData().                              */
+/* -------------------------------------------------------------------- */
+  int        nMaxBufChars;
+  char       *pachBuffer;
+};
+
+/************************************************************************/
+/*                              DDFRecord                               */
+/*                                                                      */
+/*      Class that contains one DR record from a file.  We read into    */
+/*      the same record object repeatedly to ensure that repeated       */
+/*      leaders can be easily preserved.                                */
+/************************************************************************/
+
+/**
+ * Contains instance data from one data record (DR).  The data is contained
+ * as a list of DDFField instances partitioning the raw data into fields.
+ */
+
+class CPL_ODLL DDFRecord
+{
+  public:
+                DDFRecord( DDFModule * );
+                ~DDFRecord();
+
+    DDFRecord  *Clone();
+    DDFRecord  *CloneOn( DDFModule * );
+    
+    void        Dump( FILE * );
+
+    /** Get the number of DDFFields on this record. */
+    int         GetFieldCount() { return nFieldCount; }
+
+    DDFField    *FindField( const char *, int = 0 );
+    DDFField    *GetField( int );
+
+    int         GetIntSubfield( const char *, int, const char *, int,
+                                int * = NULL );
+    double      GetFloatSubfield( const char *, int, const char *, int,
+                                  int * = NULL );
+    const char *GetStringSubfield( const char *, int, const char *, int,
+                                   int * = NULL );
+
+    int         SetIntSubfield( const char *pszField, int iFieldIndex, 
+                                const char *pszSubfield, int iSubfieldIndex,
+                                int nValue );
+    int         SetStringSubfield( const char *pszField, int iFieldIndex, 
+                                   const char *pszSubfield, int iSubfieldIndex,
+                                   const char *pszValue, int nValueLength=-1 );
+    int         SetFloatSubfield( const char *pszField, int iFieldIndex, 
+                                  const char *pszSubfield, int iSubfieldIndex,
+                                  double dfNewValue );
+
+    /** Fetch size of records raw data (GetData()) in bytes. */
+    int         GetDataSize() { return nDataSize; }
+
+    /**
+     * Fetch the raw data for this record.  The returned pointer is effectively
+     * to the data for the first field of the record, and is of size 
+     * GetDataSize().
+     */
+    const char  *GetData() { return pachData; }
+
+    /**
+     * Fetch the DDFModule with which this record is associated.
+     */
+
+    DDFModule * GetModule() { return poModule; }
+
+    int ResizeField( DDFField *poField, int nNewDataSize );
+    int DeleteField( DDFField *poField );
+    DDFField* AddField( DDFFieldDefn * );
+
+    int CreateDefaultFieldInstance( DDFField *poField, int iIndexWithinField );
+
+    int SetFieldRaw( DDFField *poField, int iIndexWithinField, 
+                     const char *pachRawData, int nRawDataSize );
+    int UpdateFieldRaw( DDFField *poField, int iIndexWithinField, 
+                        int nStartOffset, int nOldSize,
+                        const char *pachRawData, int nRawDataSize );
+
+    int         Write();
+    
+    // This is really just for the DDFModule class.
+    int         Read();
+    void        Clear();
+    int         ResetDirectory();
+    
+  private:
+
+    int         ReadHeader();
+    
+    DDFModule   *poModule;
+
+    int         nReuseHeader;   
+
+    int         nFieldOffset;   // field data area, not dir entries.
+
+    int         _sizeFieldTag;
+    int         _sizeFieldPos;
+    int         _sizeFieldLength;
+
+    int         nDataSize;      // Whole record except leader with header
+    char        *pachData;
+
+    int         nFieldCount;
+    DDFField    *paoFields;
+
+    int         bIsClone;
+};
+
+/************************************************************************/
+/*                               DDFField                               */
+/*                                                                      */
+/*      This object represents one field in a DDFRecord.                */
+/************************************************************************/
+
+/**
+ * This object represents one field in a DDFRecord.  This
+ * models an instance of the fields data, rather than it's data definition
+ * which is handled by the DDFFieldDefn class.  Note that a DDFField
+ * doesn't have DDFSubfield children as you would expect.  To extract
+ * subfield values use GetSubfieldData() to find the right data pointer and
+ * then use ExtractIntData(), ExtractFloatData() or ExtractStringData().
+ */
+
+class CPL_ODLL DDFField
+{
+  public:
+    void                Initialize( DDFFieldDefn *, const char *pszData,
+                                    int nSize );
+
+    void                Dump( FILE * fp );
+
+    const char         *GetSubfieldData( DDFSubfieldDefn *,
+                                         int * = NULL, int = 0 );
+
+    const char         *GetInstanceData( int nInstance, int *pnSize );
+
+    /**
+     * Return the pointer to the entire data block for this record. This
+     * is an internal copy, and shouldn't be freed by the application.
+     */
+    const char         *GetData() { return pachData; }
+
+    /** Return the number of bytes in the data block returned by GetData(). */
+    int                 GetDataSize() { return nDataSize; }
+
+    int                 GetRepeatCount();
+
+    /** Fetch the corresponding DDFFieldDefn. */
+    DDFFieldDefn        *GetFieldDefn() { return poDefn; }
+    
+  private:
+    DDFFieldDefn        *poDefn;
+
+    int                 nDataSize;
+
+    const char          *pachData;
+};
+
+
+#endif /* ndef _ISO8211_H_INCLUDED */
diff --git a/Utilities/GDAL/frmts/iso8211/makefile.vc b/Utilities/GDAL/frmts/iso8211/makefile.vc
new file mode 100644
index 0000000000..07cc6f6d78
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/makefile.vc
@@ -0,0 +1,29 @@
+
+OBJ =	ddfmodule.obj ddfutils.obj ddffielddefn.obj ddfrecord.obj \
+	ddffield.obj ddfsubfielddefn.obj
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	lib /out:libiso8211.lib $(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+	-del *.exe
+	-del libiso8211.lib
+
+all:	default 8211view.exe 8211dump.exe
+
+iso8211.dll:
+	link /dll $(OBJ) $(GDAL_ROOT)\port\cpl.lib \
+		/out:iso8211.dll /implib:iso8211_i.lib
+
+8211view.exe:	8211view.obj $(OBJ)
+	$(CC) $(CFLAGS) 8211view.obj $(OBJ) $(CPLLIB) /link $(LINKER_FLAGS)
+
+8211dump.exe:	8211dump.obj $(OBJ)
+	$(CC) $(CFLAGS) 8211dump.obj $(OBJ) $(CPLLIB) /link $(LINKER_FLAGS)
+
diff --git a/Utilities/GDAL/frmts/iso8211/mkcatalog.cpp b/Utilities/GDAL/frmts/iso8211/mkcatalog.cpp
new file mode 100755
index 0000000000..dd532dfde9
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/mkcatalog.cpp
@@ -0,0 +1,438 @@
+/* ****************************************************************************
+ * $Id: mkcatalog.cpp,v 1.3 2003/09/15 20:50:04 warmerda Exp $
+ *
+ * Project:  ISO8211 Library
+ * Purpose:  Test ISO8211 writing capability.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: mkcatalog.cpp,v $
+ * Revision 1.3  2003/09/15 20:50:04  warmerda
+ * updated
+ *
+ * Revision 1.2  2003/09/03 20:36:26  warmerda
+ * added subfield writing support
+ *
+ * Revision 1.1  2003/07/03 15:40:13  warmerda
+ * new
+ *
+ */
+
+#include "iso8211.h"
+
+CPL_CVSID("$Id: mkcatalog.cpp,v 1.3 2003/09/15 20:50:04 warmerda Exp $");
+
+
+/************************************************************************/
+/*                               mk_s57()                               */
+/************************************************************************/
+
+void mk_s57()
+
+{
+    DDFModule  oModule;
+    DDFFieldDefn *poFDefn;
+
+    oModule.Initialize();
+
+/* -------------------------------------------------------------------- */
+/*      Create the '0000' definition.                                   */
+/* -------------------------------------------------------------------- */
+    poFDefn = new DDFFieldDefn();
+
+    poFDefn->Create( "0000", "", "0001DSIDDSIDDSSI0001DSPM0001VRIDVRIDATTVVRIDVRPCVRIDVRPTVRIDSGCCVRIDSG2DVRIDSG3D0001FRIDFRIDFOIDFRIDATTFFRIDNATFFRIDFFPCFRIDFFPTFRIDFSPCFRIDFSPT",
+                     DDFFieldDefn::elementary, 
+                     DDFFieldDefn::char_string );
+
+    oModule.AddField( poFDefn );
+
+/* -------------------------------------------------------------------- */
+/*      Create the '0001' definition.                                   */
+/* -------------------------------------------------------------------- */
+    poFDefn = new DDFFieldDefn();
+
+    poFDefn->Create( "0001", "ISO 8211 Record Identifier", "", 
+                     DDFFieldDefn::elementary, DDFFieldDefn::bit_string,
+                     "(b12)" );
+
+    oModule.AddField( poFDefn );
+
+/* -------------------------------------------------------------------- */
+/*      Create the DSID field.                                          */
+/* -------------------------------------------------------------------- */
+    poFDefn = new DDFFieldDefn();
+
+    poFDefn->Create( "DSID", "Data set identification field", "",
+                     DDFFieldDefn::vector, DDFFieldDefn::mixed_data_type );
+
+    poFDefn->AddSubfield( "RCNM", "b11" );
+    poFDefn->AddSubfield( "RCID", "b14" );
+    poFDefn->AddSubfield( "EXPP", "b11" );
+    poFDefn->AddSubfield( "INTU", "b11" );
+    poFDefn->AddSubfield( "DSNM", "A" );
+    poFDefn->AddSubfield( "EDTN", "A" );
+    poFDefn->AddSubfield( "UPDN", "A" );
+    poFDefn->AddSubfield( "UADT", "A(8)" );
+    poFDefn->AddSubfield( "ISDT", "A(8)" );
+    poFDefn->AddSubfield( "STED", "R(4)" );
+    poFDefn->AddSubfield( "PRSP", "b11" );
+    poFDefn->AddSubfield( "PSDN", "A" );
+    poFDefn->AddSubfield( "PRED", "A" );
+    poFDefn->AddSubfield( "PROF", "b11" );
+    poFDefn->AddSubfield( "AGEN", "b12" );
+    poFDefn->AddSubfield( "COMT", "A" );
+
+    oModule.AddField( poFDefn );
+
+/* -------------------------------------------------------------------- */
+/*      Create the DSSI field.                                          */
+/* -------------------------------------------------------------------- */
+    poFDefn = new DDFFieldDefn();
+
+    poFDefn->Create( "DSSI", "Data set structure information field", "",
+                     DDFFieldDefn::vector, DDFFieldDefn::mixed_data_type );
+
+    poFDefn->AddSubfield( "DSTR", "b11" );
+    poFDefn->AddSubfield( "AALL", "b11" );
+    poFDefn->AddSubfield( "NALL", "b11" );
+    poFDefn->AddSubfield( "NOMR", "b14" );
+    poFDefn->AddSubfield( "NOCR", "b14" );
+    poFDefn->AddSubfield( "NOGR", "b14" );
+    poFDefn->AddSubfield( "NOLR", "b14" );
+    poFDefn->AddSubfield( "NOIN", "b14" );
+    poFDefn->AddSubfield( "NOCN", "b14" );
+    poFDefn->AddSubfield( "NOED", "b14" );
+    poFDefn->AddSubfield( "NOFA", "b14" );
+
+    oModule.AddField( poFDefn );
+
+/* -------------------------------------------------------------------- */
+/*      Create the DSPM field.                                          */
+/* -------------------------------------------------------------------- */
+    poFDefn = new DDFFieldDefn();
+
+    poFDefn->Create( "DSPM", "Data set parameter field", "",
+                     DDFFieldDefn::vector, DDFFieldDefn::mixed_data_type );
+
+    poFDefn->AddSubfield( "RCNM", "b11" );
+    poFDefn->AddSubfield( "RCID", "b14" );
+    poFDefn->AddSubfield( "HDAT", "b11" );
+    poFDefn->AddSubfield( "VDAT", "b11" );
+    poFDefn->AddSubfield( "SDAT", "b11" );
+    poFDefn->AddSubfield( "CSCL", "b14" );
+    poFDefn->AddSubfield( "DUNI", "b11" );
+    poFDefn->AddSubfield( "HUNI", "b11" );
+    poFDefn->AddSubfield( "PUNI", "b11" );
+    poFDefn->AddSubfield( "COUN", "b11" );
+    poFDefn->AddSubfield( "COMF", "b14" );
+    poFDefn->AddSubfield( "SOMF", "b14" );
+    poFDefn->AddSubfield( "COMT", "A" );
+
+    oModule.AddField( poFDefn );
+
+/* -------------------------------------------------------------------- */
+/*      Create the VRID field.                                          */
+/* -------------------------------------------------------------------- */
+    poFDefn = new DDFFieldDefn();
+
+    poFDefn->Create( "VRID", "Vector record identifier field", "",
+                     DDFFieldDefn::vector, DDFFieldDefn::mixed_data_type );
+
+    poFDefn->AddSubfield( "RCNM", "b11" );
+    poFDefn->AddSubfield( "RCID", "b14" );
+    poFDefn->AddSubfield( "RVER", "b12" );
+    poFDefn->AddSubfield( "RUIN", "b11" );
+
+    oModule.AddField( poFDefn );
+
+/* -------------------------------------------------------------------- */
+/*      Create the ATTV field.                                          */
+/* -------------------------------------------------------------------- */
+    poFDefn = new DDFFieldDefn();
+
+    poFDefn->Create( "ATTV", "Vector record attribute field", "",
+                     DDFFieldDefn::vector, DDFFieldDefn::mixed_data_type );
+
+    /* how do I mark this as repeating? */
+    poFDefn->AddSubfield( "ATTL", "b12" );
+    poFDefn->AddSubfield( "ATVL", "A" );
+
+    oModule.AddField( poFDefn );
+
+/* -------------------------------------------------------------------- */
+/*      Create the SG2D field.                                          */
+/* -------------------------------------------------------------------- */
+    poFDefn = new DDFFieldDefn();
+
+    poFDefn->Create( "SG2D", "2-D coordinate field", "*",
+                     DDFFieldDefn::vector, DDFFieldDefn::mixed_data_type );
+
+    /* how do I mark this as repeating? */
+    poFDefn->AddSubfield( "YCOO", "b24" );
+    poFDefn->AddSubfield( "XCOO", "b24" );
+
+    oModule.AddField( poFDefn );
+
+/* -------------------------------------------------------------------- */
+/*      Create the SG3D field.                                          */
+/* -------------------------------------------------------------------- */
+    poFDefn = new DDFFieldDefn();
+
+    poFDefn->Create( "SG3D", "3-D coordinate (sounding array) field", "*",
+                     DDFFieldDefn::vector, DDFFieldDefn::mixed_data_type );
+
+    /* how do I mark this as repeating? */
+    poFDefn->AddSubfield( "YCOO", "b24" );
+    poFDefn->AddSubfield( "XCOO", "b24" );
+    poFDefn->AddSubfield( "VE3D", "b24" );
+
+    oModule.AddField( poFDefn );
+
+/* -------------------------------------------------------------------- */
+
+// need to add: VRPC, VRPT, SGCC, FRID, FOID, ATTF, NATF, FFPC, 
+// FFPT, FSPC, and FSPT
+
+/* -------------------------------------------------------------------- */
+/*      Create file.                                                    */
+/* -------------------------------------------------------------------- */
+    oModule.Dump( stdout );
+
+    oModule.Create( "out.000" );
+
+/* -------------------------------------------------------------------- */
+/*      Create a record.                                                */
+/* -------------------------------------------------------------------- */
+    DDFRecord *poRec = new DDFRecord( &oModule );
+    DDFField *poField;
+
+    poField = poRec->AddField( oModule.FindFieldDefn( "0001" ) );
+    poRec->SetFieldRaw( poField, 0, "\1\0\036", 3 );
+
+    poField = poRec->AddField( oModule.FindFieldDefn( "DSID" ) );
+
+    poRec->SetIntSubfield   ( "DSID", 0, "RCNM", 0, 10 );
+    poRec->SetIntSubfield   ( "DSID", 0, "RCID", 0, 1 );
+    poRec->SetIntSubfield   ( "DSID", 0, "EXPP", 0, 1 );
+    poRec->SetIntSubfield   ( "DSID", 0, "INTU", 0, 4 );
+    poRec->SetStringSubfield( "DSID", 0, "DSNM", 0, "GB4X0000.000" );
+    poRec->SetStringSubfield( "DSID", 0, "EDTN", 0, "2" );
+    poRec->SetStringSubfield( "DSID", 0, "UPDN", 0, "0" );
+    poRec->SetStringSubfield( "DSID", 0, "UADT", 0, "20010409" );
+    poRec->SetStringSubfield( "DSID", 0, "ISDT", 0, "20010409" );
+    poRec->SetFloatSubfield ( "DSID", 0, "STED", 0, 3.1 );
+    poRec->SetIntSubfield   ( "DSID", 0, "PRSP", 0, 1 );
+    poRec->SetStringSubfield( "DSID", 0, "PSDN", 0, "" );
+    poRec->SetStringSubfield( "DSID", 0, "PRED", 0, "2.0" );
+    poRec->SetIntSubfield   ( "DSID", 0, "PROF", 0, 1 );
+    poRec->SetIntSubfield   ( "DSID", 0, "AGEN", 0, 540 );
+    poRec->SetStringSubfield( "DSID", 0, "COMT", 0, "" );
+
+    poField = poRec->AddField( oModule.FindFieldDefn( "DSSI" ) );
+
+    poRec->SetIntSubfield   ( "DSSI", 0, "DSTR", 0, 2 );
+    poRec->SetIntSubfield   ( "DSSI", 0, "AALL", 0, 1 );
+    poRec->SetIntSubfield   ( "DSSI", 0, "NALL", 0, 1 );
+    poRec->SetIntSubfield   ( "DSSI", 0, "NOMR", 0, 22 );
+    poRec->SetIntSubfield   ( "DSSI", 0, "NOCR", 0, 0 );
+    poRec->SetIntSubfield   ( "DSSI", 0, "NOGR", 0, 2141 );
+    poRec->SetIntSubfield   ( "DSSI", 0, "NOLR", 0, 15 );
+    poRec->SetIntSubfield   ( "DSSI", 0, "NOIN", 0, 512 );
+    poRec->SetIntSubfield   ( "DSSI", 0, "NOCN", 0, 2181 );
+    poRec->SetIntSubfield   ( "DSSI", 0, "NOED", 0, 3192 );
+    poRec->SetIntSubfield   ( "DSSI", 0, "NOFA", 0, 0 );
+
+    poRec->Write();
+    delete poRec;
+
+/* -------------------------------------------------------------------- */
+/*      Create a record.                                                */
+/* -------------------------------------------------------------------- */
+    poRec = new DDFRecord( &oModule );
+
+    poField = poRec->AddField( oModule.FindFieldDefn( "0001" ) );
+    poRec->SetFieldRaw( poField, 0, "\2\0\036", 3 );
+
+    poField = poRec->AddField( oModule.FindFieldDefn( "DSPM" ) );
+
+    poRec->SetIntSubfield   ( "DSPM", 0, "RCNM", 0, 20 );
+    poRec->SetIntSubfield   ( "DSPM", 0, "RCID", 0, 1 );
+    poRec->SetIntSubfield   ( "DSPM", 0, "HDAT", 0, 2 );
+    poRec->SetIntSubfield   ( "DSPM", 0, "VDAT", 0, 17 );
+    poRec->SetIntSubfield   ( "DSPM", 0, "SDAT", 0, 23 );
+    poRec->SetIntSubfield   ( "DSPM", 0, "CSCL", 0, 52000 );
+    poRec->SetIntSubfield   ( "DSPM", 0, "DUNI", 0, 1 );
+    poRec->SetIntSubfield   ( "DSPM", 0, "HUNI", 0, 1 );
+    poRec->SetIntSubfield   ( "DSPM", 0, "PUNI", 0, 1 );
+    poRec->SetIntSubfield   ( "DSPM", 0, "COUN", 0, 1 );
+    poRec->SetIntSubfield   ( "DSPM", 0, "COMF", 0, 1000000 );
+    poRec->SetIntSubfield   ( "DSPM", 0, "SOMF", 0, 10 );
+
+    poRec->Write();
+    delete poRec;
+
+/* -------------------------------------------------------------------- */
+/*      Create a record.                                                */
+/* -------------------------------------------------------------------- */
+    poRec = new DDFRecord( &oModule );
+
+    poField = poRec->AddField( oModule.FindFieldDefn( "0001" ) );
+    poRec->SetFieldRaw( poField, 0, "\3\0\036", 3 );
+
+    poField = poRec->AddField( oModule.FindFieldDefn( "VRID" ) );
+
+    poRec->SetIntSubfield   ( "VRID", 0, "RCNM", 0, 110 );
+    poRec->SetIntSubfield   ( "VRID", 0, "RCID", 0, 518 );
+    poRec->SetIntSubfield   ( "VRID", 0, "RVER", 0, 1 );
+    poRec->SetIntSubfield   ( "VRID", 0, "RUIN", 0, 1 );
+
+    poField = poRec->AddField( oModule.FindFieldDefn( "SG3D" ) );
+
+    poRec->SetIntSubfield   ( "SG3D", 0, "YCOO", 0, -325998702 );
+    poRec->SetIntSubfield   ( "SG3D", 0, "XCOO", 0, 612175350 );
+    poRec->SetIntSubfield   ( "SG3D", 0, "VE3D", 0, 174 );
+
+    poRec->SetIntSubfield   ( "SG3D", 0, "YCOO", 1, -325995189 );
+    poRec->SetIntSubfield   ( "SG3D", 0, "XCOO", 1, 612228812 );
+    poRec->SetIntSubfield   ( "SG3D", 0, "VE3D", 1, 400 );
+
+    poRec->Write();
+
+    delete poRec;
+}
+
+/************************************************************************/
+/*                             mk_catalog()                             */
+/************************************************************************/
+
+void mk_catalog()
+
+{
+    DDFModule  oModule;
+    DDFFieldDefn *poFDefn;
+
+    oModule.Initialize();
+
+/* -------------------------------------------------------------------- */
+/*      Create the '0000' definition.                                   */
+/* -------------------------------------------------------------------- */
+    poFDefn = new DDFFieldDefn();
+
+    poFDefn->Create( "0000", "", "0001CATD", 
+                     DDFFieldDefn::elementary, 
+                     DDFFieldDefn::char_string );
+
+    oModule.AddField( poFDefn );
+
+/* -------------------------------------------------------------------- */
+/*      Create the '0000' definition.                                   */
+/* -------------------------------------------------------------------- */
+    poFDefn = new DDFFieldDefn();
+
+    poFDefn->Create( "0001", "ISO 8211 Record Identifier", "", 
+                     DDFFieldDefn::elementary, DDFFieldDefn::bit_string,
+                     "(b12)" );
+
+    oModule.AddField( poFDefn );
+
+/* -------------------------------------------------------------------- */
+/*      Create the CATD field.                                          */
+/* -------------------------------------------------------------------- */
+    poFDefn = new DDFFieldDefn();
+
+    poFDefn->Create( "CATD", "Catalog Directory field", "",
+                     DDFFieldDefn::vector, DDFFieldDefn::mixed_data_type );
+
+    poFDefn->AddSubfield( "RCNM", "A(2)" );
+    poFDefn->AddSubfield( "RCID", "I(10)" );
+    poFDefn->AddSubfield( "FILE", "A" );
+    poFDefn->AddSubfield( "LFIL", "A" );
+    poFDefn->AddSubfield( "VOLM", "A" );
+    poFDefn->AddSubfield( "IMPL", "A(3)" );
+    poFDefn->AddSubfield( "SLAT", "R" );
+    poFDefn->AddSubfield( "WLON", "R" );
+    poFDefn->AddSubfield( "NLAT", "R" );
+    poFDefn->AddSubfield( "ELON", "R" );
+    poFDefn->AddSubfield( "CRCS", "A" );
+    poFDefn->AddSubfield( "COMT", "A" );
+
+    oModule.AddField( poFDefn );
+
+    oModule.Dump( stdout );
+
+    oModule.Create( "out.ddf" );
+
+/* -------------------------------------------------------------------- */
+/*      Create a record.                                                */
+/* -------------------------------------------------------------------- */
+    DDFRecord *poRec = new DDFRecord( &oModule );
+    DDFField *poField;
+
+    poField = poRec->AddField( oModule.FindFieldDefn( "0001" ) );
+    poRec->SetFieldRaw( poField, 0, "\0\0\036", 3 );
+
+    poField = poRec->AddField( oModule.FindFieldDefn( "CATD" ) );
+    poRec->SetStringSubfield( "CATD", 0, "RCNM", 0, "CD" );
+    poRec->SetIntSubfield   ( "CATD", 0, "RCID", 0, 1 );
+    poRec->SetStringSubfield( "CATD", 0, "FILE", 0, "CATALOG.030" );
+    poRec->SetStringSubfield( "CATD", 0, "VOLM", 0, "V01X01" );
+    poRec->SetStringSubfield( "CATD", 0, "IMPL", 0, "ASC" );
+    poRec->SetStringSubfield( "CATD", 0, "COMT", 0, 
+                              "Exchange Set Catalog file" );
+    poRec->Write();
+    delete poRec;
+
+/* -------------------------------------------------------------------- */
+/*      Create a record.                                                */
+/* -------------------------------------------------------------------- */
+    poRec = new DDFRecord( &oModule );
+
+    poField = poRec->AddField( oModule.FindFieldDefn( "0001" ) );
+    poRec->SetFieldRaw( poField, 0, "\1\0\036", 3 );
+
+    poField = poRec->AddField( oModule.FindFieldDefn( "CATD" ) );
+    poRec->SetStringSubfield( "CATD", 0, "RCNM", 0, "CD" );
+    poRec->SetIntSubfield   ( "CATD", 0, "RCID", 0, 2 );
+    poRec->SetStringSubfield( "CATD", 0, "FILE", 0, "No410810.000" );
+    poRec->SetStringSubfield( "CATD", 0, "VOLM", 0, "V01X01" );
+    poRec->SetStringSubfield( "CATD", 0, "IMPL", 0, "BIN" );
+    poRec->SetFloatSubfield ( "CATD", 0, "SLAT", 0, 59.000005 );
+    poRec->SetFloatSubfield ( "CATD", 0, "WLON", 0, 4.999996 );
+    poRec->SetFloatSubfield ( "CATD", 0, "NLAT", 0, 59.500003 );
+    poRec->SetFloatSubfield ( "CATD", 0, "ELON", 0, 5.499997 );
+    poRec->SetStringSubfield( "CATD", 0, "CRCS", 0, "555C3AD4" );
+    poRec->Write();
+    delete poRec;
+}
+
+/* **********************************************************************/
+/*                                main()                                */
+/* **********************************************************************/
+
+int main( int nArgc, char ** papszArgv )
+
+{
+    mk_s57();
+}
+
diff --git a/Utilities/GDAL/frmts/iso8211/teststream.out b/Utilities/GDAL/frmts/iso8211/teststream.out
new file mode 100644
index 0000000000..ae326c3790
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/teststream.out
@@ -0,0 +1,2986 @@
+---------------------------------------------------------------------
+-- 1183CEL0.DDF
+---------------------------------------------------------------------
+DDFModule:
+    _recLength = 187
+    _interchangeLevel = 2
+    _leaderIden = L
+    _inlineCodeExtensionIndicator =  
+    _versionNumber =  
+    _appIndicator =  
+    _fieldControlLength = 6
+    _fieldAreaStart = 61
+    _sizeFieldLength = 2
+    _sizeFieldPos = 3
+    _sizeFieldTag = 4
+  DDFFieldDefn:
+      Tag = `0000'
+      _fieldName = `1183CEL0.DDF'
+      _arrayDescr = `'
+      _formatControls = `'
+      _data_struct_code = elementary
+      _data_type_code = char_string
+  DDFFieldDefn:
+      Tag = `0001'
+      _fieldName = `DDF RECORD IDENTIFIER'
+      _arrayDescr = `'
+      _formatControls = `'
+      _data_struct_code = elementary
+      _data_type_code = implicit_point
+  DDFFieldDefn:
+      Tag = `CELL'
+      _fieldName = `Cell'
+      _arrayDescr = `MODN!RCID!ROWI!COLI'
+      _formatControls = `(A,3I)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `MODN'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `I'
+    DDFSubfieldDefn:
+        Label = `ROWI'
+        FormatString = `I'
+    DDFSubfieldDefn:
+        Label = `COLI'
+        FormatString = `I'
+  DDFFieldDefn:
+      Tag = `CVLS'
+      _fieldName = `Cell Values'
+      _arrayDescr = `*ELEVATION'
+      _formatControls = `(B(16))'
+      _data_struct_code = array
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `ELEVATION'
+        FormatString = `B(16)'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 803
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `0\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 11
+      Data = `CEL0\1F1\1F1\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 1
+      Subfield `ROWI' = 1
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05=\05<\05<\05>\05F\05B\05?\05?\05E\05H\05V\05]\05e\05s\05~...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1341
+      Subfield `ELEVATION' = 1340
+      Subfield `ELEVATION' = 1340
+      Subfield `ELEVATION' = 1342
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 803
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `1\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 11
+      Data = `CEL0\1F2\1F2\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 2
+      Subfield `ROWI' = 2
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05K\05M\05N\05T\05V\05U\05S\05M\05X\05U\05U\05e\05q\05z\05\FFFFFF8B...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1355
+      Subfield `ELEVATION' = 1357
+      Subfield `ELEVATION' = 1358
+      Subfield `ELEVATION' = 1364
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 803
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `2\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 11
+      Data = `CEL0\1F3\1F3\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 3
+      Subfield `ROWI' = 3
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05[\05^\05_\05`\05g\05f\05c\05^\05e\05i\05o\05o\05w\05\FFFFFF88\05\FFFFFF91...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1371
+      Subfield `ELEVATION' = 1374
+      Subfield `ELEVATION' = 1375
+      Subfield `ELEVATION' = 1376
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 803
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `3\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 11
+      Data = `CEL0\1F4\1F4\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 4
+      Subfield `ROWI' = 4
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05k\05m\05n\05t\05{\05w\05t\05s\05r\05|\05}\05\FFFFFF89\05\FFFFFF84\05\FFFFFF8E\05\FFFFFFA2...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1387
+      Subfield `ELEVATION' = 1389
+      Subfield `ELEVATION' = 1390
+      Subfield `ELEVATION' = 1396
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 803
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `4\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 11
+      Data = `CEL0\1F5\1F5\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 5
+      Subfield `ROWI' = 5
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05\7F\05\FFFFFF81\05\FFFFFF83\05\FFFFFF87\05\FFFFFF8C\05\FFFFFF8C\05\FFFFFF8A\05\FFFFFF8B\05\FFFFFF8A\05\FFFFFF84\05\FFFFFF89\05\FFFFFF8F\05\FFFFFF98\05\FFFFFF9C\05\FFFFFFA3\05\FFFFFFAB...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1407
+      Subfield `ELEVATION' = 1409
+      Subfield `ELEVATION' = 1411
+      Subfield `ELEVATION' = 1415
+      Subfield `ELEVATION' = 1420
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 803
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `5\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 11
+      Data = `CEL0\1F6\1F6\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 6
+      Subfield `ROWI' = 6
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05\FFFFFF91\05\FFFFFF95\05\FFFFFF99\05\FFFFFF9C\05\FFFFFF9F\05\FFFFFFA0\05\FFFFFF9D\05\FFFFFF9F\05\FFFFFFA0\05\FFFFFF94\05\FFFFFF97\05\FFFFFF9D\05\FFFFFFA6\05\FFFFFFB0\05\FFFFFFB5\05\FFFFFFB6...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1425
+      Subfield `ELEVATION' = 1429
+      Subfield `ELEVATION' = 1433
+      Subfield `ELEVATION' = 1436
+      Subfield `ELEVATION' = 1439
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 803
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `6\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 11
+      Data = `CEL0\1F7\1F7\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 7
+      Subfield `ROWI' = 7
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05\FFFFFFAA\05\FFFFFFAD\05\FFFFFFAE\05\FFFFFFB1\05\FFFFFFB2\05\FFFFFFB1\05\FFFFFFB0\05\FFFFFFB2\05\FFFFFFAF\05\FFFFFFA2\05\FFFFFFA2\05\FFFFFFAD\05\FFFFFFB2\05\FFFFFFC0\05\FFFFFFC9\05\FFFFFFCA...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1450
+      Subfield `ELEVATION' = 1453
+      Subfield `ELEVATION' = 1454
+      Subfield `ELEVATION' = 1457
+      Subfield `ELEVATION' = 1458
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 803
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `7\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 11
+      Data = `CEL0\1F8\1F8\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 8
+      Subfield `ROWI' = 8
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05\FFFFFFBE\05\FFFFFFC4\05\FFFFFFC8\05\FFFFFFCB\05\FFFFFFCE\05\FFFFFFCE\05\FFFFFFCE\05\FFFFFFCF\05\FFFFFFC5\05\FFFFFFB2\05\FFFFFFAD\05\FFFFFFBC\05\FFFFFFC5\05\FFFFFFD0\05\FFFFFFE0\05\FFFFFFF4...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1470
+      Subfield `ELEVATION' = 1476
+      Subfield `ELEVATION' = 1480
+      Subfield `ELEVATION' = 1483
+      Subfield `ELEVATION' = 1486
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 803
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `8\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 11
+      Data = `CEL0\1F9\1F9\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 9
+      Subfield `ROWI' = 9
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05\FFFFFFD4\05\FFFFFFD8\05\FFFFFFDD\06\0A\06&\06%\06$\06\1E\05\FFFFFFD6\05\FFFFFFBF\05\FFFFFFB9\05\FFFFFFCD\05\FFFFFFE1\05\FFFFFFFC\06\08\06_...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1492
+      Subfield `ELEVATION' = 1496
+      Subfield `ELEVATION' = 1501
+      Subfield `ELEVATION' = 1546
+      Subfield `ELEVATION' = 1574
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 805
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `9\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 13
+      Data = `CEL0\1F10\1F10\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 10
+      Subfield `ROWI' = 10
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\06\08\06\13\06&\06\FFFFFF88\06\FFFFFF87\06\FFFFFF89\06\FFFFFF8B\06\7F\06\02\05\FFFFFFCF\05\FFFFFFCA\05\FFFFFFE4\06W\06\FFFFFF8D\06\FFFFFF9A\06\FFFFFFA1...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1544
+      Subfield `ELEVATION' = 1555
+      Subfield `ELEVATION' = 1574
+      Subfield `ELEVATION' = 1672
+      Subfield `ELEVATION' = 1671
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 806
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `10\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 13
+      Data = `CEL0\1F11\1F11\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 11
+      Subfield `ROWI' = 11
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\06(\06\7F\06\FFFFFF93\06\FFFFFF9B\06\FFFFFF9F\06\FFFFFF9C\06\FFFFFF99\06\FFFFFF91\06W\05\FFFFFFDB\05\FFFFFFE3\06\0B\06\FFFFFF94\06\FFFFFF9D\06\FFFFFFA5\06\FFFFFFAA...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1576
+      Subfield `ELEVATION' = 1663
+      Subfield `ELEVATION' = 1683
+      Subfield `ELEVATION' = 1691
+      Subfield `ELEVATION' = 1695
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 806
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `11\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 13
+      Data = `CEL0\1F12\1F12\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 12
+      Subfield `ROWI' = 12
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\06{\06\FFFFFF95\06\FFFFFF9A\06\FFFFFF9F\06\FFFFFFA1\06\FFFFFFA1\06\FFFFFF9C\06\FFFFFF94\06_\05\FFFFFFE7\06\08\06\FFFFFF93\06\FFFFFF9B\06\FFFFFFA4\06\FFFFFFAE\06\FFFFFFB7...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1659
+      Subfield `ELEVATION' = 1685
+      Subfield `ELEVATION' = 1690
+      Subfield `ELEVATION' = 1695
+      Subfield `ELEVATION' = 1697
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 806
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `12\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 13
+      Data = `CEL0\1F13\1F13\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 13
+      Subfield `ROWI' = 13
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\06%\06\FFFFFF8C\06\FFFFFF96\06\FFFFFF9A\06\FFFFFF9D\06\FFFFFF9E\06\FFFFFF9D\06\FFFFFF96\06\FFFFFF81\06\08\06\FFFFFF8E\06\FFFFFF9B\06\FFFFFFA2\06\FFFFFFAA\06\FFFFFFB9\06\FFFFFFC6...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1573
+      Subfield `ELEVATION' = 1676
+      Subfield `ELEVATION' = 1686
+      Subfield `ELEVATION' = 1690
+      Subfield `ELEVATION' = 1693
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 806
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `13\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 13
+      Data = `CEL0\1F14\1F14\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 14
+      Subfield `ROWI' = 14
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\06$\060\06\FFFFFF93\06\FFFFFF97\06\FFFFFF9C\06\FFFFFF9C\06\FFFFFF9A\06\FFFFFF9A\06\FFFFFF8C\06\FFFFFF87\06\FFFFFF98\06\FFFFFFA0\06\FFFFFFA6\06\FFFFFFAE\06\FFFFFFB9\06\FFFFFFCC...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1572
+      Subfield `ELEVATION' = 1584
+      Subfield `ELEVATION' = 1683
+      Subfield `ELEVATION' = 1687
+      Subfield `ELEVATION' = 1692
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 806
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `14\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 13
+      Data = `CEL0\1F15\1F15\1F1\1E'
+      Subfield `MODN' = `CEL0'
+      Subfield `RCID' = 15
+      Subfield `ROWI' = 15
+      Subfield `COLI' = 1
+  DDFField:
+      Tag = `CVLS'
+      DataSize = 759
+      Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\06$\06%\06\FFFFFF87\06\FFFFFF94\06\FFFFFF97\06\FFFFFF98\06\FFFFFF98\06\FFFFFF98\06\FFFFFF99\06\FFFFFF94\06\FFFFFF9C\06\FFFFFFA1\06\FFFFFFA5\06\FFFFFFAD\06\FFFFFFB7\06\FFFFFFCA...'
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = -32766
+      Subfield `ELEVATION' = 1572
+      Subfield `ELEVATION' = 1573
+      Subfield `ELEVATION' = 1671
+      Subfield `ELEVATION' = 1684
+      Subfield `ELEVATION' = 1687
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 806
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `15\1E'
+  DDFField:
+      Tag = `CELL'
+      DataSize = 13
+---------------------------------------------------------------------
+-- CA49995B.000
+---------------------------------------------------------------------
+DDFModule:
+    _recLength = 2048
+    _interchangeLevel = 3
+    _leaderIden = L
+    _inlineCodeExtensionIndicator = E
+    _versionNumber = 1
+    _appIndicator =  
+    _fieldControlLength = 9
+    _fieldAreaStart = 345
+    _sizeFieldLength = 6
+    _sizeFieldPos = 6
+    _sizeFieldTag = 4
+  DDFFieldDefn:
+      Tag = `0000'
+      _fieldName = `'
+      _arrayDescr = `0001DSIDDSIDDSSI0001DSPM0001FRIDFRIDFOIDFRIDATTFFRIDNATFFRIDFFPCFRIDFFPTFRIDFSPCFRIDFSPT0001VRIDVRIDATTVVRIDVRPCVRIDVRPTVRIDSGCCVRIDSG2DVRIDSG3D'
+      _formatControls = `'
+      _data_struct_code = elementary
+      _data_type_code = char_string
+  DDFFieldDefn:
+      Tag = `0001'
+      _fieldName = `ISO 8211 Record Identifier'
+      _arrayDescr = `'
+      _formatControls = `(b12)'
+      _data_struct_code = elementary
+      _data_type_code = bit_string
+  DDFFieldDefn:
+      Tag = `DSID'
+      _fieldName = `Data set identification field'
+      _arrayDescr = `RCNM!RCID!EXPP!INTU!DSNM!EDTN!UPDN!UADT!ISDT!STED!PRSP!PSDN!PRED!PROF!AGEN!COMT'
+      _formatControls = `(b11,b14,2b11,3A,2A(8),R(4),b11,2A,b11,b12,A)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `RCNM'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `EXPP'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `INTU'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `DSNM'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `EDTN'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `UPDN'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `UADT'
+        FormatString = `A(8)'
+    DDFSubfieldDefn:
+        Label = `ISDT'
+        FormatString = `A(8)'
+    DDFSubfieldDefn:
+        Label = `STED'
+        FormatString = `R(4)'
+    DDFSubfieldDefn:
+        Label = `PRSP'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `PSDN'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `PRED'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `PROF'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `AGEN'
+        FormatString = `b12'
+    DDFSubfieldDefn:
+        Label = `COMT'
+        FormatString = `A'
+  DDFFieldDefn:
+      Tag = `DSSI'
+      _fieldName = `Data set structure information field'
+      _arrayDescr = `DSTR!AALL!NALL!NOMR!NOCR!NOGR!NOLR!NOIN!NOCN!NOED!NOFA'
+      _formatControls = `(3b11,8b14)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `DSTR'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `AALL'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `NALL'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `NOMR'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `NOCR'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `NOGR'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `NOLR'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `NOIN'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `NOCN'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `NOED'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `NOFA'
+        FormatString = `b14'
+  DDFFieldDefn:
+      Tag = `DSPM'
+      _fieldName = `Data set parameter field'
+      _arrayDescr = `RCNM!RCID!HDAT!VDAT!SDAT!CSCL!DUNI!HUNI!PUNI!COUN!COMF!SOMF!COMT'
+      _formatControls = `(b11,b14,3b11,b14,4b11,2b14,A)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `RCNM'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `HDAT'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `VDAT'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `SDAT'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `CSCL'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `DUNI'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `HUNI'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `PUNI'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `COUN'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `COMF'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `SOMF'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `COMT'
+        FormatString = `A'
+  DDFFieldDefn:
+      Tag = `FRID'
+      _fieldName = `Feature record identifier field'
+      _arrayDescr = `RCNM!RCID!PRIM!GRUP!OBJL!RVER!RUIN'
+      _formatControls = `(b11,b14,2b11,2b12,b11)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `RCNM'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `PRIM'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `GRUP'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `OBJL'
+        FormatString = `b12'
+    DDFSubfieldDefn:
+        Label = `RVER'
+        FormatString = `b12'
+    DDFSubfieldDefn:
+        Label = `RUIN'
+        FormatString = `b11'
+  DDFFieldDefn:
+      Tag = `FOID'
+      _fieldName = `Feature object identifier field'
+      _arrayDescr = `AGEN!FIDN!FIDS'
+      _formatControls = `(b12,b14,b12)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `AGEN'
+        FormatString = `b12'
+    DDFSubfieldDefn:
+        Label = `FIDN'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `FIDS'
+        FormatString = `b12'
+  DDFFieldDefn:
+      Tag = `ATTF'
+      _fieldName = `Feature record attribute field'
+      _arrayDescr = `*ATTL!ATVL'
+      _formatControls = `(b12,A)'
+      _data_struct_code = array
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `ATTL'
+        FormatString = `b12'
+    DDFSubfieldDefn:
+        Label = `ATVL'
+        FormatString = `A'
+  DDFFieldDefn:
+      Tag = `NATF'
+      _fieldName = `Feature record national attribute field'
+      _arrayDescr = `*ATTL!ATVL'
+      _formatControls = `(b12,A)'
+      _data_struct_code = array
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `ATTL'
+        FormatString = `b12'
+    DDFSubfieldDefn:
+        Label = `ATVL'
+        FormatString = `A'
+  DDFFieldDefn:
+      Tag = `FFPC'
+      _fieldName = `Feature record to feature object pointer control field'
+      _arrayDescr = `FFUI!FFIX!NFPT'
+      _formatControls = `(b11,2b12)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `FFUI'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `FFIX'
+        FormatString = `b12'
+    DDFSubfieldDefn:
+        Label = `NFPT'
+        FormatString = `b12'
+  DDFFieldDefn:
+      Tag = `FFPT'
+      _fieldName = `Feature record to feature object pointer field'
+      _arrayDescr = `*LNAM!RIND!COMT'
+      _formatControls = `(B(64),b11,A)'
+      _data_struct_code = array
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `LNAM'
+        FormatString = `B(64)'
+    DDFSubfieldDefn:
+        Label = `RIND'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `COMT'
+        FormatString = `A'
+  DDFFieldDefn:
+      Tag = `FSPC'
+      _fieldName = `Feature record to spatial record pointer control field'
+      _arrayDescr = `FSUI!FSIX!NSPT'
+      _formatControls = `(b11,2b12)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `FSUI'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `FSIX'
+        FormatString = `b12'
+    DDFSubfieldDefn:
+        Label = `NSPT'
+        FormatString = `b12'
+  DDFFieldDefn:
+      Tag = `FSPT'
+      _fieldName = `Feature record to spatial record pointer field'
+      _arrayDescr = `*NAME!ORNT!USAG!MASK'
+      _formatControls = `(B(40),3b11)'
+      _data_struct_code = array
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `NAME'
+        FormatString = `B(40)'
+    DDFSubfieldDefn:
+        Label = `ORNT'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `USAG'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `MASK'
+        FormatString = `b11'
+  DDFFieldDefn:
+      Tag = `VRID'
+      _fieldName = `Vector record identifier field'
+      _arrayDescr = `RCNM!RCID!RVER!RUIN'
+      _formatControls = `(b11,b14,b12,b11)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `RCNM'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `b14'
+    DDFSubfieldDefn:
+        Label = `RVER'
+        FormatString = `b12'
+    DDFSubfieldDefn:
+        Label = `RUIN'
+        FormatString = `b11'
+  DDFFieldDefn:
+      Tag = `ATTV'
+      _fieldName = `Vector record attribute field'
+      _arrayDescr = `*ATTL!ATVL'
+      _formatControls = `(b12,A)'
+      _data_struct_code = array
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `ATTL'
+        FormatString = `b12'
+    DDFSubfieldDefn:
+        Label = `ATVL'
+        FormatString = `A'
+  DDFFieldDefn:
+      Tag = `VRPC'
+      _fieldName = `Vector record pointer control field'
+      _arrayDescr = `VPUI!VPIX!NVPT'
+      _formatControls = `(b11,2b12)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `VPUI'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `VPIX'
+        FormatString = `b12'
+    DDFSubfieldDefn:
+        Label = `NVPT'
+        FormatString = `b12'
+  DDFFieldDefn:
+      Tag = `VRPT'
+      _fieldName = `Vector record pointer field'
+      _arrayDescr = `*NAME!ORNT!USAG!TOPI!MASK'
+      _formatControls = `(B(40),4b11)'
+      _data_struct_code = array
+      _data_type_code = char_string
+    DDFSubfieldDefn:
+        Label = `NAME'
+        FormatString = `B(40)'
+    DDFSubfieldDefn:
+        Label = `ORNT'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `USAG'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `TOPI'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `MASK'
+        FormatString = `b11'
+  DDFFieldDefn:
+      Tag = `SGCC'
+      _fieldName = `Coordinate control field'
+      _arrayDescr = `CCUI!CCIX!CCNC'
+      _formatControls = `(b11,2b12)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `CCUI'
+        FormatString = `b11'
+    DDFSubfieldDefn:
+        Label = `CCIX'
+        FormatString = `b12'
+    DDFSubfieldDefn:
+        Label = `CCNC'
+        FormatString = `b12'
+  DDFFieldDefn:
+      Tag = `SG2D'
+      _fieldName = `2-D Coordinate field'
+      _arrayDescr = `*YCOO!XCOO'
+      _formatControls = `(2b24)'
+      _data_struct_code = array
+      _data_type_code = bit_string
+    DDFSubfieldDefn:
+        Label = `YCOO'
+        FormatString = `b24'
+    DDFSubfieldDefn:
+        Label = `XCOO'
+        FormatString = `b24'
+  DDFFieldDefn:
+      Tag = `SG3D'
+      _fieldName = `3-D Coordinate field'
+      _arrayDescr = `*YCOO!XCOO!VE3D'
+      _formatControls = `(3b24)'
+      _data_struct_code = array
+      _data_type_code = bit_string
+    DDFSubfieldDefn:
+        Label = `YCOO'
+        FormatString = `b24'
+    DDFSubfieldDefn:
+        Label = `XCOO'
+        FormatString = `b24'
+    DDFSubfieldDefn:
+        Label = `VE3D'
+        FormatString = `b24'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 189
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `\03\00\1E'
+  DDFField:
+      Tag = `DSID'
+      DataSize = 107
+      Data = `\0A\01\00\00\00\01\04CA49995B.000\1F1\1F0\1F1997091019970910...'
+      Subfield `RCNM' = 10
+      Subfield `RCID' = 1
+      Subfield `EXPP' = 1
+      Subfield `INTU' = 4
+      Subfield `DSNM' = `CA49995B.000'
+      Subfield `EDTN' = `1'
+      Subfield `UPDN' = `0'
+      Subfield `UADT' = `19970910'
+      Subfield `ISDT' = `19970910'
+      Subfield `STED' = 3.000000
+      Subfield `PRSP' = 1
+      Subfield `PSDN' = `'
+      Subfield `PRED' = `1.0'
+      Subfield `PROF' = 1
+      Subfield `AGEN' = 50
+      Subfield `COMT' = `DATASET CREATED BY NAUTICAL DATA INTERNATIONAL, INC.'
+  DDFField:
+      Tag = `DSSI'
+      DataSize = 36
+      Data = `\02\00\00\04\00\00\00\00\00\00\00\FFFFFFCA\00\00\00\01\00\00\009\00\00\00\FFFFFF90\00\00\00\FFFFFFAA\00\00\00\00\00\00\00\1E'
+      Subfield `DSTR' = 2
+      Subfield `AALL' = 0
+      Subfield `NALL' = 0
+      Subfield `NOMR' = 4
+      Subfield `NOCR' = 0
+      Subfield `NOGR' = 202
+      Subfield `NOLR' = 1
+      Subfield `NOIN' = 57
+      Subfield `NOCN' = 144
+      Subfield `NOED' = 170
+      Subfield `NOFA' = 0
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 58
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `\03\00\1E'
+  DDFField:
+      Tag = `DSPM'
+      DataSize = 26
+      Data = `\14\02\00\00\00\02\1C\04\FFFFFFA8a\00\00\01\01\01\01@B\0F\00\0A\00\00\00\1F\1E'
+      Subfield `RCNM' = 20
+      Subfield `RCID' = 2
+      Subfield `HDAT' = 2
+      Subfield `VDAT' = 28
+      Subfield `SDAT' = 4
+      Subfield `CSCL' = 25000
+      Subfield `DUNI' = 1
+      Subfield `HUNI' = 1
+      Subfield `PUNI' = 1
+      Subfield `COUN' = 1
+      Subfield `COMF' = 1000000
+      Subfield `SOMF' = 10
+      Subfield `COMT' = `'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 788
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `\03\00\1E'
+  DDFField:
+      Tag = `VRID'
+      DataSize = 9
+      Data = `n9\00\00\00\01\00\01\1E'
+      Subfield `RCNM' = 110
+      Subfield `RCID' = 57
+      Subfield `RVER' = 1
+      Subfield `RUIN' = 1
+---------------------------------------------------------------------
+-- TWFLA009.DDF
+---------------------------------------------------------------------
+DDFModule:
+    _recLength = 224
+    _interchangeLevel = 2
+    _leaderIden = L
+    _inlineCodeExtensionIndicator =  
+    _versionNumber =  
+    _appIndicator =  
+    _fieldControlLength = 6
+    _fieldAreaStart = 61
+    _sizeFieldLength = 2
+    _sizeFieldPos = 3
+    _sizeFieldTag = 4
+  DDFFieldDefn:
+      Tag = `0000'
+      _fieldName = `TWFLA009.DDF'
+      _arrayDescr = `'
+      _formatControls = `'
+      _data_struct_code = elementary
+      _data_type_code = char_string
+  DDFFieldDefn:
+      Tag = `0001'
+      _fieldName = `DDF RECORD IDENTIFIER'
+      _arrayDescr = `'
+      _formatControls = `'
+      _data_struct_code = elementary
+      _data_type_code = implicit_point
+  DDFFieldDefn:
+      Tag = `ATPR'
+      _fieldName = `Attribute Primary'
+      _arrayDescr = `MODN!RCID'
+      _formatControls = `(A,I)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `MODN'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `I'
+  DDFFieldDefn:
+      Tag = `ATTP'
+      _fieldName = `Primary Attributes'
+      _arrayDescr = `ENTITY_LABEL!FEATURE_ID!DIMENSION!PHC'
+      _formatControls = `(A,I,2A)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `ENTITY_LABEL'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `FEATURE_ID'
+        FormatString = `I'
+    DDFSubfieldDefn:
+        Label = `DIMENSION'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `PHC'
+        FormatString = `A'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `0\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 7
+      Data = `A009\1F1\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 1
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 53
+      Data = `POPULATED PLACE\1F401\1FTwo-Dimensional\1FNot ...'
+      Subfield `ENTITY_LABEL' = `POPULATED PLACE'
+      Subfield `FEATURE_ID' = 401
+      Subfield `DIMENSION' = `Two-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 81
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `1\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 7
+      Data = `A009\1F2\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 2
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `BUILDING\1F1309\1FTwo-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `BUILDING'
+      Subfield `FEATURE_ID' = 1309
+      Subfield `DIMENSION' = `Two-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 81
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `2\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 7
+      Data = `A009\1F3\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 3
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `BUILDING\1F1307\1FTwo-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `BUILDING'
+      Subfield `FEATURE_ID' = 1307
+      Subfield `DIMENSION' = `Two-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 81
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `3\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 7
+      Data = `A009\1F4\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 4
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `BUILDING\1F1308\1FTwo-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `BUILDING'
+      Subfield `FEATURE_ID' = 1308
+      Subfield `DIMENSION' = `Two-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 81
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `4\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 7
+      Data = `A009\1F5\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 5
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `BUILDING\1F1306\1FTwo-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `BUILDING'
+      Subfield `FEATURE_ID' = 1306
+      Subfield `DIMENSION' = `Two-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 81
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `5\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 7
+      Data = `A009\1F6\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 6
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `BUILDING\1F1305\1FTwo-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `BUILDING'
+      Subfield `FEATURE_ID' = 1305
+      Subfield `DIMENSION' = `Two-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 81
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `6\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 7
+      Data = `A009\1F7\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 7
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `BUILDING\1F1304\1FTwo-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `BUILDING'
+      Subfield `FEATURE_ID' = 1304
+      Subfield `DIMENSION' = `Two-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 80
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `7\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 7
+      Data = `A009\1F8\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 8
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 46
+      Data = `CEMETERY\1F101\1FTwo-Dimensional\1FNot Photore...'
+      Subfield `ENTITY_LABEL' = `CEMETERY'
+      Subfield `FEATURE_ID' = 101
+      Subfield `DIMENSION' = `Two-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 81
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `8\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 7
+      Data = `A009\1F9\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 9
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `BUILDING\1F1303\1FTwo-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `BUILDING'
+      Subfield `FEATURE_ID' = 1303
+      Subfield `DIMENSION' = `Two-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 82
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `9\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 8
+      Data = `A009\1F10\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 10
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `BUILDING\1F1301\1FTwo-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `BUILDING'
+      Subfield `FEATURE_ID' = 1301
+      Subfield `DIMENSION' = `Two-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 83
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `10\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 8
+      Data = `A009\1F11\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 11
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `BUILDING\1F1302\1FTwo-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `BUILDING'
+      Subfield `FEATURE_ID' = 1302
+      Subfield `DIMENSION' = `Two-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 83
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `11\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 8
+      Data = `A009\1F12\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 12
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `FENCE LINE\1F14\1FOne-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `FENCE LINE'
+      Subfield `FEATURE_ID' = 14
+      Subfield `DIMENSION' = `One-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 83
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `12\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 8
+      Data = `A009\1F13\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 13
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `FENCE LINE\1F15\1FOne-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `FENCE LINE'
+      Subfield `FEATURE_ID' = 15
+      Subfield `DIMENSION' = `One-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 83
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `13\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 8
+      Data = `A009\1F14\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 14
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `FENCE LINE\1F16\1FOne-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `FENCE LINE'
+      Subfield `FEATURE_ID' = 16
+      Subfield `DIMENSION' = `One-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 83
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `14\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 8
+      Data = `A009\1F15\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 15
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `FENCE LINE\1F13\1FOne-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `FENCE LINE'
+      Subfield `FEATURE_ID' = 13
+      Subfield `DIMENSION' = `One-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 82
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `15\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 8
+      Data = `A009\1F16\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 16
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 46
+      Data = `FENCE LINE\1F7\1FOne-Dimensional\1FNot Photore...'
+      Subfield `ENTITY_LABEL' = `FENCE LINE'
+      Subfield `FEATURE_ID' = 7
+      Subfield `DIMENSION' = `One-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 82
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `16\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 8
+      Data = `A009\1F17\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 17
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 46
+      Data = `FENCE LINE\1F6\1FOne-Dimensional\1FNot Photore...'
+      Subfield `ENTITY_LABEL' = `FENCE LINE'
+      Subfield `FEATURE_ID' = 6
+      Subfield `DIMENSION' = `One-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 82
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `17\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 8
+      Data = `A009\1F18\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 18
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 46
+      Data = `FENCE LINE\1F9\1FOne-Dimensional\1FNot Photore...'
+      Subfield `ENTITY_LABEL' = `FENCE LINE'
+      Subfield `FEATURE_ID' = 9
+      Subfield `DIMENSION' = `One-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 83
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `18\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 8
+      Data = `A009\1F19\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 19
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `FENCE LINE\1F10\1FOne-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `FENCE LINE'
+      Subfield `FEATURE_ID' = 10
+      Subfield `DIMENSION' = `One-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 83
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `19\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 8
+      Data = `A009\1F20\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 20
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 47
+      Data = `FENCE LINE\1F12\1FOne-Dimensional\1FNot Photor...'
+      Subfield `ENTITY_LABEL' = `FENCE LINE'
+      Subfield `FEATURE_ID' = 12
+      Subfield `DIMENSION' = `One-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 82
+  DDFField:
+      Tag = `0001'
+      DataSize = 3
+      Data = `20\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 8
+      Data = `A009\1F21\1E'
+      Subfield `MODN' = `A009'
+      Subfield `RCID' = 21
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 46
+      Data = `FENCE LINE\1F8\1FOne-Dimensional\1FNot Photore...'
+      Subfield `ENTITY_LABEL' = `FENCE LINE'
+      Subfield `FEATURE_ID' = 8
+      Subfield `DIMENSION' = `One-Dimensional'
+      Subfield `PHC' = `Not Photorevised'
+DDFRecord:
+---------------------------------------------------------------------
+-- SC01CATD.DDF
+---------------------------------------------------------------------
+DDFModule:
+    _recLength = 160
+    _interchangeLevel = 2
+    _leaderIden = L
+    _inlineCodeExtensionIndicator =  
+    _versionNumber =  
+    _appIndicator =  
+    _fieldControlLength = 6
+    _fieldAreaStart = 49
+    _sizeFieldLength = 2
+    _sizeFieldPos = 2
+    _sizeFieldTag = 4
+  DDFFieldDefn:
+      Tag = `0000'
+      _fieldName = `SC01CATD'
+      _arrayDescr = `'
+      _formatControls = `'
+      _data_struct_code = elementary
+      _data_type_code = char_string
+  DDFFieldDefn:
+      Tag = `0001'
+      _fieldName = `DDF RECORD IDENTIFIER'
+      _arrayDescr = `'
+      _formatControls = `'
+      _data_struct_code = elementary
+      _data_type_code = implicit_point
+  DDFFieldDefn:
+      Tag = `CATD'
+      _fieldName = `CATALOG/DIRECTORY'
+      _arrayDescr = `MODN!RCID!NAME!TYPE!FILE!EXTR!MVER'
+      _formatControls = `(A,I,5A)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `MODN'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `I'
+    DDFSubfieldDefn:
+        Label = `NAME'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `TYPE'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `FILE'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `EXTR'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `MVER'
+        FormatString = `A'
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     1\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F     1\1FIDEN\1FIdentification         ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 1
+      Subfield `NAME' = `IDEN'
+      Subfield `TYPE' = `Identification            '
+      Subfield `FILE' = `SC01IDEN.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     2\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F     2\1FCATD\1FCatalog/Directory      ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 2
+      Subfield `NAME' = `CATD'
+      Subfield `TYPE' = `Catalog/Directory         '
+      Subfield `FILE' = `SC01CATD.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     3\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F     3\1FCATX\1FCatalog/Cross-Reference...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 3
+      Subfield `NAME' = `CATX'
+      Subfield `TYPE' = `Catalog/Cross-Reference   '
+      Subfield `FILE' = `SC01CATX.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     4\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F     4\1FCATS\1FCatalog/Spatial Domain ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 4
+      Subfield `NAME' = `CATS'
+      Subfield `TYPE' = `Catalog/Spatial Domain    '
+      Subfield `FILE' = `SC01CATS.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     5\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F     5\1FIREF\1FInternal Spatial Refere...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 5
+      Subfield `NAME' = `IREF'
+      Subfield `TYPE' = `Internal Spatial Reference'
+      Subfield `FILE' = `SC01IREF.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     6\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F     6\1FXREF\1FExternal Spatial Refere...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 6
+      Subfield `NAME' = `XREF'
+      Subfield `TYPE' = `External Spatial Reference'
+      Subfield `FILE' = `SC01XREF.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     7\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F     7\1FMDEF\1FData Dictionary/Definit...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 7
+      Subfield `NAME' = `MDEF'
+      Subfield `TYPE' = `Data Dictionary/Definition'
+      Subfield `FILE' = `DLG3MDEF.DDF'
+      Subfield `EXTR' = `Y'
+      Subfield `MVER' = ` 3.00'
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     8\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F     8\1FMDOM\1FData Dictionary/Domain ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 8
+      Subfield `NAME' = `MDOM'
+      Subfield `TYPE' = `Data Dictionary/Domain    '
+      Subfield `FILE' = `DLG3MDOM.DDF'
+      Subfield `EXTR' = `Y'
+      Subfield `MVER' = ` 3.00'
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     9\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F     9\1FDDSH\1FData Dictionary/Schema ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 9
+      Subfield `NAME' = `DDSH'
+      Subfield `TYPE' = `Data Dictionary/Schema    '
+      Subfield `FILE' = `SC01DDSH.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `    10\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F    10\1FSTAT\1FTransfer Statistics    ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 10
+      Subfield `NAME' = `STAT'
+      Subfield `TYPE' = `Transfer Statistics       '
+      Subfield `FILE' = `SC01STAT.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `    11\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F    11\1FDQHL\1FLineage                ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 11
+      Subfield `NAME' = `DQHL'
+      Subfield `TYPE' = `Lineage                   '
+      Subfield `FILE' = `SC01DQHL.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `    12\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F    12\1FDQPA\1FPositional Accuracy    ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 12
+      Subfield `NAME' = `DQPA'
+      Subfield `TYPE' = `Positional Accuracy       '
+      Subfield `FILE' = `SC01DQPA.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `    13\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F    13\1FDQAA\1FAttribute Accuracy     ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 13
+      Subfield `NAME' = `DQAA'
+      Subfield `TYPE' = `Attribute Accuracy        '
+      Subfield `FILE' = `SC01DQAA.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `    14\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F    14\1FDQLC\1FLogical Consistency    ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 14
+      Subfield `NAME' = `DQLC'
+      Subfield `TYPE' = `Logical Consistency       '
+      Subfield `FILE' = `SC01DQLC.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `    15\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F    15\1FDQCG\1FCompleteness           ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 15
+      Subfield `NAME' = `DQCG'
+      Subfield `TYPE' = `Completeness              '
+      Subfield `FILE' = `SC01DQCG.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `    16\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F    16\1FASCF\1FAttribute Primary      ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 16
+      Subfield `NAME' = `ASCF'
+      Subfield `TYPE' = `Attribute Primary         '
+      Subfield `FILE' = `SC01ASCF.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `    17\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F    17\1FAHDR\1FAttribute Primary      ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 17
+      Subfield `NAME' = `AHDR'
+      Subfield `TYPE' = `Attribute Primary         '
+      Subfield `FILE' = `SC01AHDR.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `    18\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F    18\1FFF01\1FComposite              ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 18
+      Subfield `NAME' = `FF01'
+      Subfield `TYPE' = `Composite                 '
+      Subfield `FILE' = `SC01FF01.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `    19\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F    19\1FNP01\1FPoint-Node             ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 19
+      Subfield `NAME' = `NP01'
+      Subfield `TYPE' = `Point-Node                '
+      Subfield `FILE' = `SC01NP01.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `    20\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F    20\1FNA01\1FPoint-Node             ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 20
+      Subfield `NAME' = `NA01'
+      Subfield `TYPE' = `Point-Node                '
+      Subfield `FILE' = `SC01NA01.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `    21\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F    21\1FNO01\1FPoint-Node             ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 21
+      Subfield `NAME' = `NO01'
+      Subfield `TYPE' = `Point-Node                '
+      Subfield `FILE' = `SC01NO01.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `    22\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F    22\1FLE01\1FLine                   ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 22
+      Subfield `NAME' = `LE01'
+      Subfield `TYPE' = `Line                      '
+      Subfield `FILE' = `SC01LE01.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+DDFRecord:
+    nReuseHeader = 1
+    nDataSize = 87
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `    23\1E'
+  DDFField:
+      Tag = `CATD'
+      DataSize = 65
+      Data = `CATD\1F    23\1FPC01\1FPolygon                ...'
+      Subfield `MODN' = `CATD'
+      Subfield `RCID' = 23
+      Subfield `NAME' = `PC01'
+      Subfield `TYPE' = `Polygon                   '
+      Subfield `FILE' = `SC01PC01.DDF'
+      Subfield `EXTR' = `N'
+      Subfield `MVER' = `     '
+---------------------------------------------------------------------
+-- SC01LE01.DDF
+---------------------------------------------------------------------
+DDFModule:
+    _recLength = 441
+    _interchangeLevel = 2
+    _leaderIden = L
+    _inlineCodeExtensionIndicator =  
+    _versionNumber =  
+    _appIndicator =  
+    _fieldControlLength = 6
+    _fieldAreaStart = 106
+    _sizeFieldLength = 2
+    _sizeFieldPos = 3
+    _sizeFieldTag = 4
+  DDFFieldDefn:
+      Tag = `0000'
+      _fieldName = `SC01LE01'
+      _arrayDescr = `'
+      _formatControls = `'
+      _data_struct_code = elementary
+      _data_type_code = char_string
+  DDFFieldDefn:
+      Tag = `0001'
+      _fieldName = `DDF RECORD IDENTIFIER'
+      _arrayDescr = `'
+      _formatControls = `'
+      _data_struct_code = elementary
+      _data_type_code = implicit_point
+  DDFFieldDefn:
+      Tag = `LINE'
+      _fieldName = `LINE'
+      _arrayDescr = `MODN!RCID!OBRP'
+      _formatControls = `(A(4),I(6),A(2))'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `MODN'
+        FormatString = `A(4)'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `I(6)'
+    DDFSubfieldDefn:
+        Label = `OBRP'
+        FormatString = `A(2)'
+  DDFFieldDefn:
+      Tag = `ATID'
+      _fieldName = `ATTRIBUTE ID'
+      _arrayDescr = `*MODN!RCID'
+      _formatControls = `(A(4),I(6))'
+      _data_struct_code = array
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `MODN'
+        FormatString = `A(4)'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `I(6)'
+  DDFFieldDefn:
+      Tag = `PIDL'
+      _fieldName = `POLYGON ID LEFT'
+      _arrayDescr = `MODN!RCID'
+      _formatControls = `(A(4),I(6))'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `MODN'
+        FormatString = `A(4)'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `I(6)'
+  DDFFieldDefn:
+      Tag = `PIDR'
+      _fieldName = `POLYGON ID RIGHT'
+      _arrayDescr = `MODN!RCID'
+      _formatControls = `(A(4),I(6))'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `MODN'
+        FormatString = `A(4)'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `I(6)'
+  DDFFieldDefn:
+      Tag = `SNID'
+      _fieldName = `STARTNODE ID'
+      _arrayDescr = `MODN!RCID'
+      _formatControls = `(A(4),I(6))'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `MODN'
+        FormatString = `A(4)'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `I(6)'
+  DDFFieldDefn:
+      Tag = `ENID'
+      _fieldName = `ENDNODE ID'
+      _arrayDescr = `MODN!RCID'
+      _formatControls = `(A(4),I(6))'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `MODN'
+        FormatString = `A(4)'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `I(6)'
+  DDFFieldDefn:
+      Tag = `SADR'
+      _fieldName = `SPATIAL ADDRESS'
+      _arrayDescr = `*X!Y'
+      _formatControls = `((2B(32)))'
+      _data_struct_code = array
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `X'
+        FormatString = `B(32)'
+    DDFSubfieldDefn:
+        Label = `Y'
+        FormatString = `B(32)'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 321
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     1\1E'
+  DDFField:
+      Tag = `LINE'
+      DataSize = 13
+      Data = `LE01     1LE\1E'
+      Subfield `MODN' = `LE01'
+      Subfield `RCID' = 1
+      Subfield `OBRP' = `LE'
+  DDFField:
+      Tag = `PIDL'
+      DataSize = 11
+      Data = `PC01     1\1E'
+      Subfield `MODN' = `PC01'
+      Subfield `RCID' = 1
+  DDFField:
+      Tag = `PIDR'
+      DataSize = 11
+      Data = `PC01     3\1E'
+      Subfield `MODN' = `PC01'
+      Subfield `RCID' = 3
+  DDFField:
+      Tag = `SNID'
+      DataSize = 11
+      Data = `NO01     2\1E'
+      Subfield `MODN' = `NO01'
+      Subfield `RCID' = 2
+  DDFField:
+      Tag = `ENID'
+      DataSize = 11
+      Data = `NO01     1\1E'
+      Subfield `MODN' = `NO01'
+      Subfield `RCID' = 1
+  DDFField:
+      Tag = `SADR'
+      DataSize = 193
+      Data = `\03`\18\FFFFFFEA\18\FFFFFFBD\02~\03`\FFFFFFD4\FFFFFF8D\18\FFFFFFBD\03\FFFFFFDF\03a\FFFFFF900\18\FFFFFFBD\05|\03bK\FFFFFFD4\18\FFFFFFBD\06\FFFFFFDD\03c\07v\18\FFFFFFBD\08z...'
+      Subfield `X' = 56629482
+      Subfield `Y' = 415040126
+      Subfield `X' = 56677517
+      Subfield `Y' = 415040479
+      Subfield `X' = 56725552
+      Subfield `Y' = 415040892
+      Subfield `X' = 56773588
+      Subfield `Y' = 415041245
+      Subfield `X' = 56821622
+      Subfield `Y' = 415041658
+      Subfield `X' = 56869657
+      Subfield `Y' = 415042072
+      Subfield `X' = 56917692
+      Subfield `Y' = 415042424
+      Subfield `X' = 56965727
+      Subfield `Y' = 415042838
+      Subfield `X' = 57013823
+      Subfield `Y' = 415043252
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 273
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     2\1E'
+  DDFField:
+      Tag = `LINE'
+      DataSize = 13
+      Data = `LE01     2LE\1E'
+      Subfield `MODN' = `LE01'
+      Subfield `RCID' = 2
+      Subfield `OBRP' = `LE'
+  DDFField:
+      Tag = `PIDL'
+      DataSize = 11
+      Data = `PC01     2\1E'
+      Subfield `MODN' = `PC01'
+      Subfield `RCID' = 2
+  DDFField:
+      Tag = `PIDR'
+      DataSize = 11
+      Data = `PC01     3\1E'
+      Subfield `MODN' = `PC01'
+      Subfield `RCID' = 3
+  DDFField:
+      Tag = `SNID'
+      DataSize = 11
+      Data = `NO01     3\1E'
+      Subfield `MODN' = `NO01'
+      Subfield `RCID' = 3
+  DDFField:
+      Tag = `ENID'
+      DataSize = 11
+      Data = `NO01     3\1E'
+      Subfield `MODN' = `NO01'
+      Subfield `RCID' = 3
+  DDFField:
+      Tag = `SADR'
+      DataSize = 145
+      Data = `\03a\1E\0A\18\FFFFFFBBp&\03a\17-\18\FFFFFFBBk\17\03a\11\FFFFFFBE\18\FFFFFFBBfH\03a\0E\FFFFFFEB\18\FFFFFFBBb5\03a\0Fv\18\FFFFFFBBZ\FFFFFF98...'
+      Subfield `X' = 56696330
+      Subfield `Y' = 414937126
+      Subfield `X' = 56694573
+      Subfield `Y' = 414935831
+      Subfield `X' = 56693182
+      Subfield `Y' = 414934600
+      Subfield `X' = 56692459
+      Subfield `Y' = 414933557
+      Subfield `X' = 56692598
+      Subfield `Y' = 414931608
+      Subfield `X' = 56692974
+      Subfield `Y' = 414930453
+      Subfield `X' = 56693957
+      Subfield `Y' = 414929547
+      Subfield `X' = 56695241
+      Subfield `Y' = 414929131
+      Subfield `X' = 56696765
+      Subfield `Y' = 414929083
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 521
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     3\1E'
+  DDFField:
+      Tag = `LINE'
+      DataSize = 13
+      Data = `LE01     3LE\1E'
+      Subfield `MODN' = `LE01'
+      Subfield `RCID' = 3
+      Subfield `OBRP' = `LE'
+  DDFField:
+      Tag = `PIDL'
+      DataSize = 11
+      Data = `PC01     4\1E'
+      Subfield `MODN' = `PC01'
+      Subfield `RCID' = 4
+  DDFField:
+      Tag = `PIDR'
+      DataSize = 11
+      Data = `PC01     3\1E'
+      Subfield `MODN' = `PC01'
+      Subfield `RCID' = 3
+  DDFField:
+      Tag = `SNID'
+      DataSize = 11
+      Data = `NO01     4\1E'
+      Subfield `MODN' = `NO01'
+      Subfield `RCID' = 4
+  DDFField:
+      Tag = `ENID'
+      DataSize = 11
+      Data = `NO01     4\1E'
+      Subfield `MODN' = `NO01'
+      Subfield `RCID' = 4
+  DDFField:
+      Tag = `SADR'
+      DataSize = 393
+      Data = `\03k\0F\11\18\FFFFFFB8\FFFFFF96\FFFFFFFC\03k\16\FFFFFF80\18\FFFFFFB8\FFFFFFAC{\03k\19\FFFFFFBD\18\FFFFFFB8\FFFFFFB7\FFFFFFF0\03k\1D7\18\FFFFFFB8\FFFFFFC2\FFFFFFEC\03k!f\18\FFFFFFB8\FFFFFFCE\FFFFFFA1...'
+      Subfield `X' = 57347857
+      Subfield `Y' = 414750460
+      Subfield `X' = 57349760
+      Subfield `Y' = 414755963
+      Subfield `X' = 57350589
+      Subfield `Y' = 414758896
+      Subfield `X' = 57351479
+      Subfield `Y' = 414761708
+      Subfield `X' = 57352550
+      Subfield `Y' = 414764705
+      Subfield `X' = 57352418
+      Subfield `Y' = 414765984
+      Subfield `X' = 57351435
+      Subfield `Y' = 414766829
+      Subfield `X' = 57348441
+      Subfield `Y' = 414767656
+      Subfield `X' = 57343968
+      Subfield `Y' = 414770239
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 146
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     4\1E'
+  DDFField:
+      Tag = `LINE'
+      DataSize = 13
+      Data = `LE01     4LE\1E'
+      Subfield `MODN' = `LE01'
+      Subfield `RCID' = 4
+      Subfield `OBRP' = `LE'
+  DDFField:
+      Tag = `PIDL'
+      DataSize = 11
+      Data = `PC01     3\1E'
+      Subfield `MODN' = `PC01'
+      Subfield `RCID' = 3
+  DDFField:
+      Tag = `PIDR'
+      DataSize = 11
+      Data = `PC01     1\1E'
+      Subfield `MODN' = `PC01'
+      Subfield `RCID' = 1
+  DDFField:
+      Tag = `SNID'
+      DataSize = 11
+      Data = `NO01     2\1E'
+      Subfield `MODN' = `NO01'
+      Subfield `RCID' = 2
+  DDFField:
+      Tag = `ENID'
+      DataSize = 11
+      Data = `NO01     5\1E'
+      Subfield `MODN' = `NO01'
+      Subfield `RCID' = 5
+  DDFField:
+      Tag = `SADR'
+      DataSize = 25
+      Data = `\03`\18\FFFFFFEA\18\FFFFFFBD\02~\03`\1BN\18\FFFFFFBB\FFFFFFD0\FFFFFFFD\03`#\FFFFFFEB\18\FFFFFFB7\FFFFFF95\FFFFFF8D\1E'
+      Subfield `X' = 56629482
+      Subfield `Y' = 415040126
+      Subfield `X' = 56630094
+      Subfield `Y' = 414961917
+      Subfield `X' = 56632299
+      Subfield `Y' = 414684557
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 138
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     5\1E'
+  DDFField:
+      Tag = `LINE'
+      DataSize = 13
+      Data = `LE01     5LE\1E'
+      Subfield `MODN' = `LE01'
+      Subfield `RCID' = 5
+      Subfield `OBRP' = `LE'
+  DDFField:
+      Tag = `PIDL'
+      DataSize = 11
+      Data = `PC01     5\1E'
+      Subfield `MODN' = `PC01'
+      Subfield `RCID' = 5
+  DDFField:
+      Tag = `PIDR'
+      DataSize = 11
+      Data = `PC01     1\1E'
+      Subfield `MODN' = `PC01'
+      Subfield `RCID' = 1
+  DDFField:
+      Tag = `SNID'
+      DataSize = 11
+      Data = `NO01     5\1E'
+      Subfield `MODN' = `NO01'
+      Subfield `RCID' = 5
+  DDFField:
+      Tag = `ENID'
+      DataSize = 11
+      Data = `NO01     7\1E'
+      Subfield `MODN' = `NO01'
+      Subfield `RCID' = 7
+  DDFField:
+      Tag = `SADR'
+      DataSize = 17
+      Data = `\03`#\FFFFFFEB\18\FFFFFFB7\FFFFFF95\FFFFFF8D\03`%c\18\FFFFFFB6\FFFFFFEBL\1E'
+      Subfield `X' = 56632299
+      Subfield `Y' = 414684557
+      Subfield `X' = 56632675
+      Subfield `Y' = 414640972
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 377
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     6\1E'
+  DDFField:
+      Tag = `LINE'
+      DataSize = 13
+      Data = `LE01     6LE\1E'
+      Subfield `MODN' = `LE01'
+      Subfield `RCID' = 6
+      Subfield `OBRP' = `LE'
+  DDFField:
+      Tag = `PIDL'
+      DataSize = 11
+      Data = `PC01     6\1E'
+      Subfield `MODN' = `PC01'
+      Subfield `RCID' = 6
+  DDFField:
+      Tag = `PIDR'
+      DataSize = 11
+      Data = `PC01     3\1E'
+      Subfield `MODN' = `PC01'
+      Subfield `RCID' = 3
+  DDFField:
+      Tag = `SNID'
+      DataSize = 11
+      Data = `NO01     6\1E'
+      Subfield `MODN' = `NO01'
+      Subfield `RCID' = 6
+  DDFField:
+      Tag = `ENID'
+      DataSize = 11
+      Data = `NO01     6\1E'
+      Subfield `MODN' = `NO01'
+      Subfield `RCID' = 6
+  DDFField:
+      Tag = `SADR'
+      DataSize = 249
+      Data = `\03h\0CM\18\FFFFFFB6\FFFFFFEF#\03h\0C\FFFFFFCC\18\FFFFFFB6\FFFFFFED\00\03h\11\\18\FFFFFFB6\FFFFFFE8\FFFFFFC0\03h\15/\18\FFFFFFB6\FFFFFFE6\FFFFFFE1\03h+\FFFFFFE1\18\FFFFFFB6\FFFFFFDE\FFFFFF80...'
+      Subfield `X' = 57150541
+      Subfield `Y' = 414641955
+      Subfield `X' = 57150668
+      Subfield `Y' = 414641408
+      Subfield `X' = 57151836
+      Subfield `Y' = 414640320
+      Subfield `X' = 57152815
+      Subfield `Y' = 414639841
+      Subfield `X' = 57158625
+      Subfield `Y' = 414637696
+      Subfield `X' = 57159664
+      Subfield `Y' = 414637462
+      Subfield `X' = 57160944
+      Subfield `Y' = 414637473
+      Subfield `X' = 57162828
+      Subfield `Y' = 414638098
+      Subfield `X' = 57165074
+      Subfield `Y' = 414639215
+      ...
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 138
+  DDFField:
+      Tag = `0001'
+      DataSize = 7
+      Data = `     7\1E'
+  DDFField:
+      Tag = `LINE'
+      DataSize = 13
+      Data = `LE01     7LE\1E'
+      Subfield `MODN' = `LE01'
+      Subfield `RCID' = 7
+      Subfield `OBRP' = `LE'
+  DDFField:
+      Tag = `PIDL'
+      DataSize = 11
+      Data = `PC01     7\1E'
+      Subfield `MODN' = `PC01'
+      Subfield `RCID' = 7
+  DDFField:
+      Tag = `PIDR'
+      DataSize = 11
+      Data = `PC01     1\1E'
+      Subfield `MODN' = `PC01'
+      Subfield `RCID' = 1
+  DDFField:
+      Tag = `SNID'
+      DataSize = 11
+      Data = `NO01     7\1E'
+      Subfield `MODN' = `NO01'
+      Subfield `RCID' = 7
+  DDFField:
+      Tag = `ENID'
+      DataSize = 11
+      Data = `NO01     8\1E'
+      Subfield `MODN' = `NO01'
+      Subfield `RCID' = 8
+  DDFField:
+      Tag = `SADR'
+      DataSize = 17
+      Data = `\03`%c\18\FFFFFFB6\FFFFFFEBL\03`%\FFFFFF8A\18\FFFFFFB6\FFFFFFD9p\1E'
+---------------------------------------------------------------------
+-- TSTPAGEO.DDF
+---------------------------------------------------------------------
+DDFModule:
+    _recLength = 682
+    _interchangeLevel = 2
+    _leaderIden = L
+    _inlineCodeExtensionIndicator =  
+    _versionNumber =  
+    _appIndicator =  
+    _fieldControlLength = 6
+    _fieldAreaStart = 75
+    _sizeFieldLength = 3
+    _sizeFieldPos = 3
+    _sizeFieldTag = 4
+  DDFFieldDefn:
+      Tag = `0000'
+      _fieldName = `TSTPAGEO'
+      _arrayDescr = `'
+      _formatControls = `'
+      _data_struct_code = elementary
+      _data_type_code = char_string
+  DDFFieldDefn:
+      Tag = `0001'
+      _fieldName = `DDF RECORD IDENTIFIER'
+      _arrayDescr = `'
+      _formatControls = `'
+      _data_struct_code = elementary
+      _data_type_code = implicit_point
+  DDFFieldDefn:
+      Tag = `ATPR'
+      _fieldName = `ATTRIBUTE PRIMARY'
+      _arrayDescr = `MODN!RCID'
+      _formatControls = `(A,I)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `MODN'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `I'
+  DDFFieldDefn:
+      Tag = `OBID'
+      _fieldName = `SPATIAL OBJECT ID'
+      _arrayDescr = `MODN!RCID'
+      _formatControls = `(A,I)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `MODN'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `RCID'
+        FormatString = `I'
+  DDFFieldDefn:
+      Tag = `ATTP'
+      _fieldName = `PRIMARY ATTRIBUTES'
+      _arrayDescr = `PUB_DATE!DESIG!PID!STATE!COUNTY!QUAD!HORZ_DATUM!LATITUDE!LONGITUDE!HORZ_CTRL!HORZ_ORDER!VERT_DATUM!HEIGHT_M!HEIGHT_FT!VERT_CTRL!VERT_ORDER!VERT_CLASS!EPOCH_DATE!X_GEO_COOR!Y_GEO_COOR!Z_GEO_COOR!LAPLAC_COR!LAPLAC_COR_DET!ELP_HT!ELP_HT_DET!ELP_ORDER!ELP_CLASS!GEOID_HT!GEOID_HT_DET!DYN_HT_M!DYN_HT_FT!DYN_HT_DET!MOD_GRAV!MOD_GR_DET!MON_MARK!MON_SETNG!MON_PROJ!MON_STAMP!MON_STABIL!SATT_OBS!SATT_DATE!ROD_PIPE_D!SLEEVE_DEP'
+      _formatControls = `(12A,2R,3A,4R,2(R,A),2A,R,A,2R,10A,2R)'
+      _data_struct_code = vector
+      _data_type_code = mixed_data_type
+    DDFSubfieldDefn:
+        Label = `PUB_DATE'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `DESIG'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `PID'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `STATE'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `COUNTY'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `QUAD'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `HORZ_DATUM'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `LATITUDE'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `LONGITUDE'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `HORZ_CTRL'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `HORZ_ORDER'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `VERT_DATUM'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `HEIGHT_M'
+        FormatString = `R'
+    DDFSubfieldDefn:
+        Label = `HEIGHT_FT'
+        FormatString = `R'
+    DDFSubfieldDefn:
+        Label = `VERT_CTRL'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `VERT_ORDER'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `VERT_CLASS'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `EPOCH_DATE'
+        FormatString = `R'
+    DDFSubfieldDefn:
+        Label = `X_GEO_COOR'
+        FormatString = `R'
+    DDFSubfieldDefn:
+        Label = `Y_GEO_COOR'
+        FormatString = `R'
+    DDFSubfieldDefn:
+        Label = `Z_GEO_COOR'
+        FormatString = `R'
+    DDFSubfieldDefn:
+        Label = `LAPLAC_COR'
+        FormatString = `R'
+    DDFSubfieldDefn:
+        Label = `LAPLAC_COR_DET'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `ELP_HT'
+        FormatString = `R'
+    DDFSubfieldDefn:
+        Label = `ELP_HT_DET'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `ELP_ORDER'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `ELP_CLASS'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `GEOID_HT'
+        FormatString = `R'
+    DDFSubfieldDefn:
+        Label = `GEOID_HT_DET'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `DYN_HT_M'
+        FormatString = `R'
+    DDFSubfieldDefn:
+        Label = `DYN_HT_FT'
+        FormatString = `R'
+    DDFSubfieldDefn:
+        Label = `DYN_HT_DET'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `MOD_GRAV'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `MOD_GR_DET'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `MON_MARK'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `MON_SETNG'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `MON_PROJ'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `MON_STAMP'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `MON_STABIL'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `SATT_OBS'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `SATT_DATE'
+        FormatString = `A'
+    DDFSubfieldDefn:
+        Label = `ROD_PIPE_D'
+        FormatString = `R'
+    DDFSubfieldDefn:
+        Label = `SLEEVE_DEP'
+        FormatString = `R'
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 367
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `1\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 7
+      Data = `AGEO\1F1\1E'
+      Subfield `MODN' = `AGEO'
+      Subfield `RCID' = 1
+  DDFField:
+      Tag = `OBID'
+      DataSize = 7
+      Data = `NE01\1F1\1E'
+      Subfield `MODN' = `NE01'
+      Subfield `RCID' = 1
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 314
+      Data = `JANUARY 31, 2000\1FG 687\1FEV0052\1FCA\1FSAN BER...'
+      Subfield `PUB_DATE' = `JANUARY 31, 2000'
+      Subfield `DESIG' = `G 687'
+      Subfield `PID' = `EV0052'
+      Subfield `STATE' = `CA'
+      Subfield `COUNTY' = `SAN BERNARDINO'
+      Subfield `QUAD' = `BARSTOW (1974)'
+      Subfield `HORZ_DATUM' = `NAD 83(1986)'
+      Subfield `LATITUDE' = `34 58 52.      N'
+      Subfield `LONGITUDE' = `117 00 03.      W'
+      Subfield `HORZ_CTRL' = `SCALED'
+      Subfield `HORZ_ORDER' = `'
+      Subfield `VERT_DATUM' = `NAVD 88'
+      Subfield `HEIGHT_M' = 815.146000
+      Subfield `HEIGHT_FT' = 2674.360000
+      Subfield `VERT_CTRL' = `ADJUSTED'
+      Subfield `VERT_ORDER' = `FIRST    '
+      Subfield `VERT_CLASS' = `I'
+      Subfield `EPOCH_DATE' = 0.000000
+      Subfield `X_GEO_COOR' = 0.000000
+      Subfield `Y_GEO_COOR' = 0.000000
+      Subfield `Z_GEO_COOR' = 0.000000
+      Subfield `LAPLAC_COR' = 0.000000
+      Subfield `LAPLAC_COR_DET' = `'
+      Subfield `ELP_HT' = 0.000000
+      Subfield `ELP_HT_DET' = `'
+      Subfield `ELP_ORDER' = `'
+      Subfield `ELP_CLASS' = `'
+      Subfield `GEOID_HT' = -31.200000
+      Subfield `GEOID_HT_DET' = `GEOID99'
+      Subfield `DYN_HT_M' = 814.215000
+      Subfield `DYN_HT_FT' = 2671.300000
+      Subfield `DYN_HT_DET' = `COMP'
+      Subfield `MOD_GRAV' = `979466.3'
+      Subfield `MOD_GR_DET' = `NAVD 88'
+      Subfield `MON_MARK' = `DB = BENCH MARK DISK'
+      Subfield `MON_SETNG' = `7 = SET IN TOP OF CONCRETE MONUMENT'
+      Subfield `MON_PROJ' = `'
+      Subfield `MON_STAMP' = `G 687 1943'
+      Subfield `MON_STABIL' = `C'
+      Subfield `SATT_OBS' = `'
+      Subfield `SATT_DATE' = `'
+      Subfield `ROD_PIPE_D' = 0.000000
+      Subfield `SLEEVE_DEP' = 0.000000
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 407
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `2\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 7
+      Data = `AGEO\1F2\1E'
+      Subfield `MODN' = `AGEO'
+      Subfield `RCID' = 2
+  DDFField:
+      Tag = `OBID'
+      DataSize = 7
+      Data = `NE01\1F2\1E'
+      Subfield `MODN' = `NE01'
+      Subfield `RCID' = 2
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 354
+      Data = `JANUARY 31, 2000\1FC 1297\1FEV3524\1FCA\1FSAN BE...'
+      Subfield `PUB_DATE' = `JANUARY 31, 2000'
+      Subfield `DESIG' = `C 1297'
+      Subfield `PID' = `EV3524'
+      Subfield `STATE' = `CA'
+      Subfield `COUNTY' = `SAN BERNARDINO'
+      Subfield `QUAD' = `NEBO (1971)'
+      Subfield `HORZ_DATUM' = `NAD 83(1986)'
+      Subfield `LATITUDE' = `34 59 23.      N'
+      Subfield `LONGITUDE' = `116 59 07.      W'
+      Subfield `HORZ_CTRL' = `SCALED'
+      Subfield `HORZ_ORDER' = `'
+      Subfield `VERT_DATUM' = `NAVD 88'
+      Subfield `HEIGHT_M' = 810.587000
+      Subfield `HEIGHT_FT' = 2659.400000
+      Subfield `VERT_CTRL' = `ADJUSTED'
+      Subfield `VERT_ORDER' = `FIRST    '
+      Subfield `VERT_CLASS' = `I'
+      Subfield `EPOCH_DATE' = 0.000000
+      Subfield `X_GEO_COOR' = 0.000000
+      Subfield `Y_GEO_COOR' = 0.000000
+      Subfield `Z_GEO_COOR' = 0.000000
+      Subfield `LAPLAC_COR' = 0.000000
+      Subfield `LAPLAC_COR_DET' = `'
+      Subfield `ELP_HT' = 0.000000
+      Subfield `ELP_HT_DET' = `'
+      Subfield `ELP_ORDER' = `'
+      Subfield `ELP_CLASS' = `'
+      Subfield `GEOID_HT' = -31.190000
+      Subfield `GEOID_HT_DET' = `GEOID99'
+      Subfield `DYN_HT_M' = 809.663000
+      Subfield `DYN_HT_FT' = 2656.370000
+      Subfield `DYN_HT_DET' = `COMP'
+      Subfield `MOD_GRAV' = `979467.8'
+      Subfield `MOD_GR_DET' = `NAVD 88'
+      Subfield `MON_MARK' = `DB = BENCH MARK DISK'
+      Subfield `MON_SETNG' = `16 = (FASTENED TO) A METAL ROD WITH BASE PLATE BURIED/SCREWED G: INTO GROUND'
+      Subfield `MON_PROJ' = `'
+      Subfield `MON_STAMP' = `C 1297 1977'
+      Subfield `MON_STABIL' = `C'
+      Subfield `SATT_OBS' = `'
+      Subfield `SATT_DATE' = `'
+      Subfield `ROD_PIPE_D' = 0.000000
+      Subfield `SLEEVE_DEP' = 0.000000
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 373
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `3\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 7
+      Data = `AGEO\1F3\1E'
+      Subfield `MODN' = `AGEO'
+      Subfield `RCID' = 3
+  DDFField:
+      Tag = `OBID'
+      DataSize = 7
+      Data = `NE01\1F3\1E'
+      Subfield `MODN' = `NE01'
+      Subfield `RCID' = 3
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 320
+      Data = `JANUARY 31, 2000\1FJ 687\1FFT0044\1FCA\1FSAN BER...'
+      Subfield `PUB_DATE' = `JANUARY 31, 2000'
+      Subfield `DESIG' = `J 687'
+      Subfield `PID' = `FT0044'
+      Subfield `STATE' = `CA'
+      Subfield `COUNTY' = `SAN BERNARDINO'
+      Subfield `QUAD' = `LANE MOUNTAIN (1986)'
+      Subfield `HORZ_DATUM' = `NAD 83(1986)'
+      Subfield `LATITUDE' = `35 00 07.      N'
+      Subfield `LONGITUDE' = `116 58 39.      W'
+      Subfield `HORZ_CTRL' = `SCALED'
+      Subfield `HORZ_ORDER' = `'
+      Subfield `VERT_DATUM' = `NAVD 88'
+      Subfield `HEIGHT_M' = 838.565000
+      Subfield `HEIGHT_FT' = 2751.190000
+      Subfield `VERT_CTRL' = `ADJUSTED'
+      Subfield `VERT_ORDER' = `FIRST    '
+      Subfield `VERT_CLASS' = `I'
+      Subfield `EPOCH_DATE' = 0.000000
+      Subfield `X_GEO_COOR' = 0.000000
+      Subfield `Y_GEO_COOR' = 0.000000
+      Subfield `Z_GEO_COOR' = 0.000000
+      Subfield `LAPLAC_COR' = 0.000000
+      Subfield `LAPLAC_COR_DET' = `'
+      Subfield `ELP_HT' = 0.000000
+      Subfield `ELP_HT_DET' = `'
+      Subfield `ELP_ORDER' = `'
+      Subfield `ELP_CLASS' = `'
+      Subfield `GEOID_HT' = -31.160000
+      Subfield `GEOID_HT_DET' = `GEOID99'
+      Subfield `DYN_HT_M' = 837.601000
+      Subfield `DYN_HT_FT' = 2748.030000
+      Subfield `DYN_HT_DET' = `COMP'
+      Subfield `MOD_GRAV' = `979457.7'
+      Subfield `MOD_GR_DET' = `NAVD 88'
+      Subfield `MON_MARK' = `DB = BENCH MARK DISK'
+      Subfield `MON_SETNG' = `7 = SET IN TOP OF CONCRETE MONUMENT'
+      Subfield `MON_PROJ' = `'
+      Subfield `MON_STAMP' = `J 687 1943'
+      Subfield `MON_STABIL' = `C'
+      Subfield `SATT_OBS' = `'
+      Subfield `SATT_DATE' = `'
+      Subfield `ROD_PIPE_D' = 0.000000
+      Subfield `SLEEVE_DEP' = 0.000000
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 387
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `4\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 7
+      Data = `AGEO\1F4\1E'
+      Subfield `MODN' = `AGEO'
+      Subfield `RCID' = 4
+  DDFField:
+      Tag = `OBID'
+      DataSize = 7
+      Data = `NE01\1F4\1E'
+      Subfield `MODN' = `NE01'
+      Subfield `RCID' = 4
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 334
+      Data = `JANUARY 31, 2000\1F2892\1FFT0043\1FCA\1FSAN BERN...'
+      Subfield `PUB_DATE' = `JANUARY 31, 2000'
+      Subfield `DESIG' = `2892'
+      Subfield `PID' = `FT0043'
+      Subfield `STATE' = `CA'
+      Subfield `COUNTY' = `SAN BERNARDINO'
+      Subfield `QUAD' = `LANE MOUNTAIN (1986)'
+      Subfield `HORZ_DATUM' = `NAD 83(1986)'
+      Subfield `LATITUDE' = `35 00 44.      N'
+      Subfield `LONGITUDE' = `116 58 07.      W'
+      Subfield `HORZ_CTRL' = `SCALED'
+      Subfield `HORZ_ORDER' = `'
+      Subfield `VERT_DATUM' = `NAVD 88'
+      Subfield `HEIGHT_M' = 882.390000
+      Subfield `HEIGHT_FT' = 2894.970000
+      Subfield `VERT_CTRL' = `ADJUSTED'
+      Subfield `VERT_ORDER' = `FIRST    '
+      Subfield `VERT_CLASS' = `I'
+      Subfield `EPOCH_DATE' = 0.000000
+      Subfield `X_GEO_COOR' = 0.000000
+      Subfield `Y_GEO_COOR' = 0.000000
+      Subfield `Z_GEO_COOR' = 0.000000
+      Subfield `LAPLAC_COR' = 0.000000
+      Subfield `LAPLAC_COR_DET' = `'
+      Subfield `ELP_HT' = 0.000000
+      Subfield `ELP_HT_DET' = `'
+      Subfield `ELP_ORDER' = `'
+      Subfield `ELP_CLASS' = `'
+      Subfield `GEOID_HT' = -31.140000
+      Subfield `GEOID_HT_DET' = `GEOID99'
+      Subfield `DYN_HT_M' = 881.369000
+      Subfield `DYN_HT_FT' = 2891.620000
+      Subfield `DYN_HT_DET' = `COMP'
+      Subfield `MOD_GRAV' = `979447.3'
+      Subfield `MOD_GR_DET' = `NAVD 88'
+      Subfield `MON_MARK' = `DD = SURVEY DISK'
+      Subfield `MON_SETNG' = `17 = SET INTO TOP OF METAL PIPE DRIVEN INTO GROUND'
+      Subfield `MON_PROJ' = `'
+      Subfield `MON_STAMP' = `2892 B 26 1906'
+      Subfield `MON_STABIL' = `D'
+      Subfield `SATT_OBS' = `'
+      Subfield `SATT_DATE' = `'
+      Subfield `ROD_PIPE_D' = 0.000000
+      Subfield `SLEEVE_DEP' = 0.000000
+DDFRecord:
+    nReuseHeader = 0
+    nDataSize = 373
+  DDFField:
+      Tag = `0001'
+      DataSize = 2
+      Data = `5\1E'
+  DDFField:
+      Tag = `ATPR'
+      DataSize = 7
+      Data = `AGEO\1F5\1E'
+      Subfield `MODN' = `AGEO'
+      Subfield `RCID' = 5
+  DDFField:
+      Tag = `OBID'
+      DataSize = 7
+      Data = `NE01\1F5\1E'
+      Subfield `MODN' = `NE01'
+      Subfield `RCID' = 5
+  DDFField:
+      Tag = `ATTP'
+      DataSize = 320
+      Data = `JANUARY 31, 2000\1FK 687\1FFT0042\1FCA\1FSAN BER...'
+      Subfield `PUB_DATE' = `JANUARY 31, 2000'
+      Subfield `DESIG' = `K 687'
+      Subfield `PID' = `FT0042'
+      Subfield `STATE' = `CA'
+      Subfield `COUNTY' = `SAN BERNARDINO'
+      Subfield `QUAD' = `LANE MOUNTAIN (1986)'
+      Subfield `HORZ_DATUM' = `NAD 83(1986)'
+      Subfield `LATITUDE' = `35 00 51.      N'
+      Subfield `LONGITUDE' = `116 58 07.      W'
+      Subfield `HORZ_CTRL' = `SCALED'
+      Subfield `HORZ_ORDER' = `'
+      Subfield `VERT_DATUM' = `NAVD 88'
+      Subfield `HEIGHT_M' = 889.871000
+      Subfield `HEIGHT_FT' = 2919.520000
+      Subfield `VERT_CTRL' = `ADJUSTED'
+      Subfield `VERT_ORDER' = `FIRST    '
+      Subfield `VERT_CLASS' = `I'
+      Subfield `EPOCH_DATE' = 0.000000
+      Subfield `X_GEO_COOR' = 0.000000
+      Subfield `Y_GEO_COOR' = 0.000000
+      Subfield `Z_GEO_COOR' = 0.000000
+      Subfield `LAPLAC_COR' = 0.000000
+      Subfield `LAPLAC_COR_DET' = `'
+      Subfield `ELP_HT' = 0.000000
+      Subfield `ELP_HT_DET' = `'
diff --git a/Utilities/GDAL/frmts/iso8211/teststream.sh b/Utilities/GDAL/frmts/iso8211/teststream.sh
new file mode 100755
index 0000000000..c77c517a6b
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/teststream.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+DATADIR=testdata
+
+for file in 1183CEL0.DDF CA49995B.000 TWFLA009.DDF SC01CATD.DDF SC01LE01.DDF TSTPAGEO.DDF ; \
+  do
+
+  echo "---------------------------------------------------------------------"
+  echo "-- $file"
+  echo "---------------------------------------------------------------------"
+  8211dump $DATADIR/$file | head -500
+
+done
+
+
+
diff --git a/Utilities/GDAL/frmts/iso8211/timetest.cpp b/Utilities/GDAL/frmts/iso8211/timetest.cpp
new file mode 100644
index 0000000000..87532cc315
--- /dev/null
+++ b/Utilities/GDAL/frmts/iso8211/timetest.cpp
@@ -0,0 +1,198 @@
+/* ****************************************************************************
+ * $Id: timetest.cpp,v 1.2 1999/11/18 19:03:04 warmerda Exp $
+ *
+ * Project:  SDTS Translator
+ * Purpose:  Example program dumping data in 8211 data to stdout.
+ * Author:   Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: timetest.cpp,v $
+ * Revision 1.2  1999/11/18 19:03:04  warmerda
+ * expanded tabs
+ *
+ * Revision 1.1  1999/05/06 14:26:33  warmerda
+ * New
+ *
+ * Revision 1.2  1999/04/28 05:16:47  warmerda
+ * added usage
+ *
+ * Revision 1.1  1999/04/27 22:09:30  warmerda
+ * New
+ *
+ */
+
+#include <stdio.h>
+#include "iso8211.h"
+
+static void ViewRecordField( DDFField * poField );
+static int ViewSubfield( DDFSubfieldDefn *poSFDefn,
+                         const char * pachFieldData,
+                         int nBytesRemaining );
+
+/* **********************************************************************/
+/*                                main()                                */
+/* **********************************************************************/
+
+int main( int nArgc, char ** papszArgv )
+
+{
+    DDFModule   oModule;
+    const char  *pszFilename;
+    int         i;
+
+    if( nArgc > 1 )
+        pszFilename = papszArgv[1];
+    else
+    {
+        printf( "Usage: 8211view filename\n" );
+        exit( 1 );
+    }
+
+    for( i = 0; i < 40; i++ )
+    {
+/* -------------------------------------------------------------------- */
+/*      Open the file.  Note that by default errors are reported to     */
+/*      stderr, so we don't bother doing it ourselves.                  */
+/* -------------------------------------------------------------------- */
+        if( !oModule.Open( pszFilename ) )
+        {
+            exit( 1 );
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Loop reading records till there are none left.                  */
+/* -------------------------------------------------------------------- */
+        DDFRecord       *poRecord;
+        int             nRecordCount = 0;
+        int             nFieldCount = 0;
+            
+    
+        while( (poRecord = oModule.ReadRecord()) != NULL )
+        {
+            /* ------------------------------------------------------------ */
+            /*      Loop over each field in this particular record.         */
+            /* ------------------------------------------------------------ */
+            for( int iField = 0; iField < poRecord->GetFieldCount(); iField++ )
+            {
+                DDFField        *poField = poRecord->GetField( iField );
+
+                ViewRecordField( poField );
+
+                nFieldCount++;
+            }
+
+            nRecordCount++;
+        }
+
+        oModule.Close();
+
+        printf( "Read %d records, %d fields.\n", nRecordCount, nFieldCount );
+    }
+}
+
+/* **********************************************************************/
+/*                          ViewRecordField()                           */
+/*                                                                      */
+/*      Dump the contents of a field instance in a record.              */
+/* **********************************************************************/
+
+static void ViewRecordField( DDFField * poField )
+
+{
+    int         nBytesRemaining;
+    const char  *pachFieldData;
+    DDFFieldDefn *poFieldDefn = poField->GetFieldDefn();
+    
+    // Get pointer to this fields raw data.  We will move through
+    // it consuming data as we report subfield values.
+            
+    pachFieldData = poField->GetData();
+    nBytesRemaining = poField->GetDataSize();
+                
+    /* -------------------------------------------------------- */
+    /*      Loop over the repeat count for this fields          */
+    /*      subfields.  The repeat count will almost            */
+    /*      always be one.                                      */
+    /* -------------------------------------------------------- */
+    int         iRepeat, nRepeatCount;
+
+    nRepeatCount = poField->GetRepeatCount();
+            
+    for( iRepeat = 0; iRepeat < nRepeatCount; iRepeat++ )
+    {
+
+        /* -------------------------------------------------------- */
+        /*   Loop over all the subfields of this field, advancing   */
+        /*   the data pointer as we consume data.                   */
+        /* -------------------------------------------------------- */
+        int     iSF;
+        
+        for( iSF = 0; iSF < poFieldDefn->GetSubfieldCount(); iSF++ )
+        {
+            DDFSubfieldDefn *poSFDefn = poFieldDefn->GetSubfield( iSF );
+            int         nBytesConsumed;
+
+            nBytesConsumed = ViewSubfield( poSFDefn, pachFieldData,
+                                           nBytesRemaining );
+
+            nBytesRemaining -= nBytesConsumed;
+            pachFieldData += nBytesConsumed;
+        }
+    }
+}
+
+/* **********************************************************************/
+/*                            ViewSubfield()                            */
+/* **********************************************************************/
+
+static int ViewSubfield( DDFSubfieldDefn *poSFDefn,
+                         const char * pachFieldData,
+                         int nBytesRemaining )
+
+{
+    int         nBytesConsumed = 0;
+    
+    switch( poSFDefn->GetType() )
+    {
+      case DDFInt:
+        poSFDefn->ExtractIntData( pachFieldData, nBytesRemaining,
+                                  &nBytesConsumed );
+        break;
+
+      case DDFFloat:
+        poSFDefn->ExtractFloatData( pachFieldData, nBytesRemaining,
+                                    &nBytesConsumed );
+        break;
+
+      case DDFString:
+        poSFDefn->ExtractStringData( pachFieldData, nBytesRemaining,
+                                     &nBytesConsumed );
+        break;
+
+      default:
+        break;
+    }
+
+    return nBytesConsumed;
+}
diff --git a/Utilities/GDAL/frmts/jdem/GNUmakefile b/Utilities/GDAL/frmts/jdem/GNUmakefile
new file mode 100644
index 0000000000..1953dfe75b
--- /dev/null
+++ b/Utilities/GDAL/frmts/jdem/GNUmakefile
@@ -0,0 +1,13 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	jdemdataset.o
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/jdem/jdemdataset.cpp b/Utilities/GDAL/frmts/jdem/jdemdataset.cpp
new file mode 100644
index 0000000000..e2da216be4
--- /dev/null
+++ b/Utilities/GDAL/frmts/jdem/jdemdataset.cpp
@@ -0,0 +1,346 @@
+/******************************************************************************
+ * $Id: jdemdataset.cpp,v 1.9 2005/05/05 15:54:48 fwarmerdam Exp $
+ *
+ * Project:  JDEM Reader
+ * Purpose:  All code for Japanese DEM Reader
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: jdemdataset.cpp,v $
+ * Revision 1.9  2005/05/05 15:54:48  fwarmerdam
+ * PAM Enabled
+ *
+ * Revision 1.8  2003/07/08 21:21:56  warmerda
+ * avoid warnings
+ *
+ * Revision 1.7  2002/09/04 06:50:37  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.6  2002/06/12 21:12:25  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.5  2001/11/11 23:51:00  warmerda
+ * added required class keyword to friend declarations
+ *
+ * Revision 1.4  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.3  2001/06/21 19:59:51  warmerda
+ * added help link
+ *
+ * Revision 1.2  2000/11/28 02:28:54  warmerda
+ * Added error checks, GetGeoTransform and GetProjection
+ *
+ * Revision 1.1  2000/11/27 19:03:26  warmerda
+ * New
+ *
+ */
+
+#include "gdal_pam.h"
+
+CPL_CVSID("$Id: jdemdataset.cpp,v 1.9 2005/05/05 15:54:48 fwarmerdam Exp $");
+
+CPL_C_START
+void	GDALRegister_JDEM(void);
+CPL_C_END
+
+/************************************************************************/
+/*                            JDEMGetField()                            */
+/************************************************************************/
+
+static int JDEMGetField( char *pszField, int nWidth )
+
+{
+    char	szWork[32];
+
+    CPLAssert( nWidth < (int) sizeof(szWork) );
+
+    strncpy( szWork, pszField, nWidth );
+    szWork[nWidth] = '\0';
+
+    return atoi(szWork);
+}
+
+/************************************************************************/
+/*                            JDEMGetAngle()                            */
+/************************************************************************/
+
+static double JDEMGetAngle( char *pszField )
+
+{
+    int		nAngle = JDEMGetField( pszField, 7 );
+    int		nDegree, nMin, nSec;
+
+    // Note, this isn't very general purpose, but it would appear
+    // from the field widths that angles are never negative.  Nice
+    // to be a country in the "first quadrant". 
+
+    nDegree = nAngle / 10000;
+    nMin = (nAngle / 100) % 100;
+    nSec = nAngle % 100;
+    
+    return nDegree + nMin / 60.0 + nSec / 3600.0;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*				JDEMDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class JDEMRasterBand;
+
+class JDEMDataset : public GDALPamDataset
+{
+    friend class JDEMRasterBand;
+
+    FILE	*fp;
+    GByte	abyHeader[1012];
+
+  public:
+		~JDEMDataset();
+    
+    static GDALDataset *Open( GDALOpenInfo * );
+
+    CPLErr 	GetGeoTransform( double * padfTransform );
+    const char *GetProjectionRef();
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            JDEMRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class JDEMRasterBand : public GDALPamRasterBand
+{
+    friend class JDEMDataset;
+    
+  public:
+
+    		JDEMRasterBand( JDEMDataset *, int );
+    
+    virtual CPLErr IReadBlock( int, int, void * );
+};
+
+
+/************************************************************************/
+/*                           JDEMRasterBand()                            */
+/************************************************************************/
+
+JDEMRasterBand::JDEMRasterBand( JDEMDataset *poDS, int nBand )
+
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+    
+    eDataType = GDT_Float32;
+
+    nBlockXSize = poDS->GetRasterXSize();
+    nBlockYSize = 1;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr JDEMRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+
+{
+    JDEMDataset *poGDS = (JDEMDataset *) poDS;
+    char	*pszRecord;
+    int		nRecordSize = nBlockXSize*5 + 9 + 2;
+    int		i;
+
+    VSIFSeek( poGDS->fp, 1011 + nRecordSize*nBlockYOff, SEEK_SET );
+
+    pszRecord = (char *) CPLMalloc(nRecordSize);
+    VSIFRead( pszRecord, 1, nRecordSize, poGDS->fp );
+
+    if( !EQUALN((char *) poGDS->abyHeader,pszRecord,6) )
+    {
+        CPLFree( pszRecord );
+
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "JDEM Scanline corrupt.  Perhaps file was not transferred\n"
+                  "in binary mode?" );
+        return CE_Failure;
+    }
+    
+    if( JDEMGetField( pszRecord + 6, 3 ) != nBlockYOff + 1 )
+    {
+        CPLFree( pszRecord );
+
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "JDEM scanline out of order, JDEM driver does not\n"
+                  "currently support partial datasets." );
+        return CE_Failure;
+    }
+
+    for( i = 0; i < nBlockXSize; i++ )
+        ((float *) pImage)[i] = (float)
+            (JDEMGetField( pszRecord + 9 + 5 * i, 5) * 0.1);
+
+    return CE_None;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*				JDEMDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                            ~JDEMDataset()                             */
+/************************************************************************/
+
+JDEMDataset::~JDEMDataset()
+
+{
+    FlushCache();
+    if( fp != NULL )
+        VSIFClose( fp );
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr JDEMDataset::GetGeoTransform( double * padfTransform )
+
+{
+    double	dfLLLat, dfLLLong, dfURLat, dfURLong;
+
+    dfLLLat = JDEMGetAngle( (char *) abyHeader + 29 );
+    dfLLLong = JDEMGetAngle( (char *) abyHeader + 36 );
+    dfURLat = JDEMGetAngle( (char *) abyHeader + 43 );
+    dfURLong = JDEMGetAngle( (char *) abyHeader + 50 );
+    
+    padfTransform[0] = dfLLLong;
+    padfTransform[3] = dfURLat;
+    padfTransform[1] = (dfURLong - dfLLLong) / GetRasterXSize();
+    padfTransform[2] = 0.0;
+        
+    padfTransform[4] = 0.0;
+    padfTransform[5] = -1 * (dfURLat - dfLLLat) / GetRasterYSize();
+
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *JDEMDataset::GetProjectionRef()
+
+{
+    return( "GEOGCS[\"Tokyo\",DATUM[\"Tokyo\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",7004]],TOWGS84[-148,507,685,0,0,0,0],AUTHORITY[\"EPSG\",6301]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"DMSH\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",4301]]" );
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *JDEMDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Before trying JDEMOpen() we first verify that there is at        */
+/*      least one "\n#keyword" type signature in the first chunk of     */
+/*      the file.                                                       */
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->fp == NULL || poOpenInfo->nHeaderBytes < 50 )
+        return NULL;
+
+    /* check if century values seem reasonable */
+    if( (!EQUALN((char *)poOpenInfo->pabyHeader+11,"19",2)
+          && !EQUALN((char *)poOpenInfo->pabyHeader+11,"20",2))
+        || (!EQUALN((char *)poOpenInfo->pabyHeader+15,"19",2)
+             && !EQUALN((char *)poOpenInfo->pabyHeader+15,"20",2))
+        || (!EQUALN((char *)poOpenInfo->pabyHeader+19,"19",2)
+             && !EQUALN((char *)poOpenInfo->pabyHeader+19,"20",2)) )
+    {
+        return NULL;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    JDEMDataset 	*poDS;
+
+    poDS = new JDEMDataset();
+
+    poDS->fp = poOpenInfo->fp;
+    poOpenInfo->fp = NULL;
+    
+/* -------------------------------------------------------------------- */
+/*      Read the header.                                                */
+/* -------------------------------------------------------------------- */
+    VSIFSeek( poDS->fp, 0, SEEK_SET );
+    VSIFRead( poDS->abyHeader, 1, 1012, poDS->fp );
+
+    poDS->nRasterXSize = JDEMGetField( (char *) poDS->abyHeader + 23, 3 );
+    poDS->nRasterYSize = JDEMGetField( (char *) poDS->abyHeader + 26, 3 );
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    poDS->SetBand( 1, new JDEMRasterBand( poDS, 1 ));
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                          GDALRegister_JDEM()                          */
+/************************************************************************/
+
+void GDALRegister_JDEM()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "JDEM" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "JDEM" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "Japanese DEM (.mem)" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_various.html#JDEM" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "mem" );
+
+        poDriver->pfnOpen = JDEMDataset::Open;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
diff --git a/Utilities/GDAL/frmts/jdem/makefile.vc b/Utilities/GDAL/frmts/jdem/makefile.vc
new file mode 100644
index 0000000000..fc7eeeeb01
--- /dev/null
+++ b/Utilities/GDAL/frmts/jdem/makefile.vc
@@ -0,0 +1,15 @@
+
+OBJ	=	jdemdataset.obj
+
+EXTRAFLAGS = 	-I..\iso8211
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/jpeg/GNUmakefile b/Utilities/GDAL/frmts/jpeg/GNUmakefile
new file mode 100644
index 0000000000..6cabfec3cd
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/GNUmakefile
@@ -0,0 +1,34 @@
+
+include ../../GDALmake.opt
+
+ifeq ($(JPEG_SETTING),internal)
+OBJ	=	\
+	jcapimin.o jcapistd.o jccoefct.o jccolor.o jcdctmgr.o jchuff.o \
+        jcinit.o jcmainct.o jcmarker.o jcmaster.o jcomapi.o jcparam.o \
+        jcphuff.o jcprepct.o jcsample.o jctrans.o jdapimin.o jdapistd.o \
+        jdatadst.o jdatasrc.o jdcoefct.o jdcolor.o jddctmgr.o jdhuff.o \
+        jdinput.o jdmainct.o jdmarker.o jdmaster.o jdmerge.o jdphuff.o \
+        jdpostct.o jdsample.o jdtrans.o jerror.o jfdctflt.o jfdctfst.o \
+        jfdctint.o jidctflt.o jidctfst.o jidctint.o jidctred.o jquant1.o \
+        jquant2.o jutils.o jmemmgr.o jmemansi.o \
+	\
+	jpgdataset.o vsidataio.o
+XTRA_OPT	=	-Ilibjpeg
+else
+OBJ	=	jpgdataset.o vsidataio.o
+XTRA_OPT	=	
+endif
+
+CPPFLAGS	:=	$(XTRA_OPT) $(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+../o/%.o:	libjpeg/%.c
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@
+
+all:	$(OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/jpeg/frmt_jpeg.html b/Utilities/GDAL/frmts/jpeg/frmt_jpeg.html
new file mode 100644
index 0000000000..15d3301563
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/frmt_jpeg.html
@@ -0,0 +1,55 @@
+<html>
+<head>
+<title>JPEG -- JPEG JFIF File Format</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>JPEG -- JPEG JFIF File Format</h1>
+
+The JPEG JFIF format is supported for reading, and batch writing, but not
+update in place.  JPEG files are represented as one band (greyscale) or three 
+band (RGB) datasets with Byte valued bands.<p>
+
+There is currently no support for georeferencing information or metadata for
+JPEG files. But if an ESRI world file exists with the .wld or .jgw suffixes,
+it will be read and used to establish the geotransform for the image. Overviews
+can be built for JPEG files as an external .ovr file.<p>
+
+The GDAL JPEG Driver is built using the Independent JPEG Group's jpeg
+library.  Also note that the GeoTIFF driver supports tiled TIFF with JPEG
+compressed tiles.<p>
+
+<h2>Creation Options</h2>
+
+JPEG files are created using the "JPEG" driver code.  Only Byte band types
+are supported, and only 1 and 3 band configurations.  JPEG file creation
+is implemented by the batch (CreateCopy) method.  
+
+<ul>
+
+<li> <b>WORLDFILE=ON</b>: Force the generation of an associated ESRI world
+file (.wld).<p>
+
+<li> <b>QUALITY=n</b>: By default the quality flag is set to 75, but this
+option can be used to select other values.  Values must be in the
+range 10-100.  Low values result in higher compression ratios, but poorer
+image quality.  A value of 100 will result in lossless compression.<p>
+
+<li> <b>PROGRESSIVE=ON</b>: Enabled generation of progressive jpegs.  In some
+cases these will display a reduced resolution image in viewers such as
+Netscape, and Internet Explorer, before the full file has been downloaded.
+However, some applications cannot read progressive jpegs at all. GDAL can
+read progressive jpegs, but takes no advantage of their progressive nature.<p>
+
+</ul>
+
+See Also:<p>
+
+<ul>
+<li> <a href="http://www.ijg.org/">Independent JPEG Group</a>
+</ul>
+
+</body>
+</html>
+
diff --git a/Utilities/GDAL/frmts/jpeg/gdalexif.h b/Utilities/GDAL/frmts/jpeg/gdalexif.h
new file mode 100755
index 0000000000..cb4b257575
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/gdalexif.h
@@ -0,0 +1,306 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  JPEG JFIF Driver
+ * Purpose:  Implement GDAL JPEG Support based on IJG libjpeg.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: gdalexif.h,v $
+ * Revision 1.7  2006/02/02 01:08:02  fwarmerdam
+ * Removed TIFFHEADER value ... it isn't a constant.
+ *
+ * Revision 1.6  2005/09/29 01:00:48  fwarmerdam
+ * enable MakerNote
+ *
+ * Revision 1.5  2005/09/22 15:00:28  fwarmerdam
+ * Reformat things more neatly.
+ *
+ * Revision 1.4  2005/09/21 00:19:04  fwarmerdam
+ * set names to be more similar to the specification
+ *
+ * Revision 1.3  2005/08/10 20:54:01  dnadeau
+ * do not print EXIF Offset tags
+ *
+ * Revision 1.2  2005/08/09 20:19:02  dnadeau
+ * add EXIF GPS IFD Tags
+ *
+ * Revision 1.1  2005/07/19 19:35:32  fwarmerdam
+ * New
+ *
+ *
+ */
+
+#define	ord(e)	((int)e)
+#define EXIFOFFSETTAG 0x8769
+#define INTEROPERABILITYOFFSET 0xA005
+#define GPSOFFSETTAG     0x8825
+#define MAXSTRINGLENGTH 65535
+
+
+static struct gpsname {
+    GUInt16 tag;
+    char*   name;
+} gpstags [] = {
+    { 0x00, "EXIF_GPSVersionID" },
+    { 0x01, "EXIF_GPSLatitudeRef" },
+    { 0x02, "EXIF_GPSLatitude" },
+    { 0x03, "EXIF_GPSLongitudeRef" },
+    { 0x04, "EXIF_GPSLongitude" },
+    { 0x05, "EXIF_GPSAltitudeRef" },
+    { 0x06, "EXIF_GPSAltitude" },
+    { 0x07, "EXIF_GPSTimeStamp" }, 
+    { 0x08, "EXIF_GPSSatellites" }, 
+    { 0x09, "EXIF_GPSStatus" }, 
+    { 0x0a, "EXIF_GPSMeasureMode" }, 
+    { 0x0b, "EXIF_GPSDOP" },
+    { 0x0c, "EXIF_GPSSpeedRef"},
+    { 0x0d, "EXIF_GPSSpeed"},
+    { 0x0e, "EXIF_GPSTrackRef"},
+    { 0x0f, "EXIF_GPSTrack"},
+    { 0x10, "EXIF_GPSImgDirectionRef"},
+    { 0x11, "EXIF_GPSImgDirection"},
+    { 0x12, "EXIF_GPSMapDatum"},
+    { 0x13, "EXIF_GPSDestLatitudeRef"},
+    { 0x14, "EXIF_GPSDestLatitude"},
+    { 0x15, "EXIF_GPSDestLongitudeRef"},
+    { 0x16, "EXIF_GPSDestLongitude"},
+    { 0x17, "EXIF_GPSDestBearingRef"},
+    { 0x18, "EXIF_GPSDestBearing"},
+    { 0x19, "EXIF_GPSDestDistanceRef"},
+    { 0x1a, "EXIF_GPSDestDistance"},
+    { 0x1b, "EXIF_GPSProcessingMethod"},
+    { 0x1c, "EXIF_GPSAreaInformation"},
+    { 0x1d, "EXIF_GPSDateStamp"},
+    { 0x1e, "EXIF_GPSDifferential"},  
+    { 0xffff,       ""}
+}; 
+
+static struct tagname {
+  GUInt16 tag;
+  char*  name;
+} tagnames [] = {
+
+//    { 0x100,	"EXIF_Image_Width"},
+//    { 0x101,	"EXIF_Image_Length"},
+    { 0x102,	"EXIF_BitsPerSample"},
+    { 0x103,	"EXIF_Compression"},
+    { 0x106,	"EXIF_PhotometricInterpretation"},
+    { 0x10A,	"EXIF_Fill_Order"},
+    { 0x10D,	"EXIF_Document_Name"},
+    { 0x10E,	"EXIF_ImageDescription"},
+    { 0x10F,	"EXIF_Make"},
+    { 0x110,	"EXIF_Model"},
+    { 0x111,	"EXIF_StripOffsets"},
+    { 0x112,	"EXIF_Orientation"},
+    { 0x115,	"EXIF_SamplesPerPixel"},
+    { 0x116,	"EXIF_RowsPerStrip"},
+    { 0x117,	"EXIF_StripByteCounts"},
+    { 0x11A,	"EXIF_XResolution"},
+    { 0x11B,	"EXIF_YResolution"},
+    { 0x11C,	"EXIF_PlanarConfiguration"},
+    { 0x128,	"EXIF_ResolutionUnit"},
+    { 0x12D,	"EXIF_TransferFunction"},
+    { 0x131,	"EXIF_Software"},
+    { 0x132,	"EXIF_DateTime"},
+    { 0x13B,	"EXIF_Artist"},
+    { 0x13E,	"EXIF_WhitePoint"},
+    { 0x13F,	"EXIF_PrimaryChromaticities"},
+    { 0x156,	"EXIF_Transfer_Range"},
+    { 0x200,	"EXIF_JPEG_Proc"},
+    { 0x201,	"EXIF_JPEGInterchangeFormat"},
+    { 0x202,	"EXIF_JPEGInterchangeFormatLength"},
+    { 0x211,	"EXIF_YCbCrCoefficients"},
+    { 0x212,	"EXIF_YCbCrSubSampling"},
+    { 0x213,	"EXIF_YCbCrPositioning"},
+    { 0x214,	"EXIF_ReferenceBlackWhite"},
+    { 0x828D,	"EXIF_CFA_Repeat_Pattern_Dim"},
+    { 0x828E,	"EXIF_CFA_Pattern"},
+    { 0x828F,	"EXIF_Battery_Level"},
+    { 0x8298,	"EXIF_Copyright"},
+    { 0x829A,	"EXIF_ExposureTime"},
+    { 0x829D,	"EXIF_FNumber"},
+    { 0x83BB,	"EXIF_IPTC/NAA"},
+//	{ 0x8769,	"EXIF_Offset"},
+    { 0x8773,	"EXIF_Inter_Color_Profile"},
+    { 0x8822,	"EXIF_ExposureProgram"},
+    { 0x8824,	"EXIF_SpectralSensitivity"},
+//	{ 0x8825,	"EXIF_GPSOffset"},
+    { 0x8827,	"EXIF_ISOSpeedRatings"},
+    { 0x8828,	"EXIF_OECF"},
+    { 0x9000,	"EXIF_ExifVersion"},
+    { 0x9003,	"EXIF_DateTimeOriginal"},
+    { 0x9004,	"EXIF_DateTimeDigitized"},
+    { 0x9101,	"EXIF_ComponentsConfiguration"},
+    { 0x9102,	"EXIF_CompressedBitsPerPixel"},
+    { 0x9201,	"EXIF_ShutterSpeedValue"},
+    { 0x9202,	"EXIF_ApertureValue"},
+    { 0x9203,	"EXIF_BrightnessValue"},
+    { 0x9204,	"EXIF_ExposureBiasValue"},
+    { 0x9205,	"EXIF_MaxApertureValue"},
+    { 0x9206,	"EXIF_SubjectDistance"},
+    { 0x9207,	"EXIF_MeteringMode"},
+    { 0x9208,	"EXIF_LightSource"},
+    { 0x9209,	"EXIF_Flash"},
+    { 0x920A,	"EXIF_FocalLength"},
+    { 0x9214,   "EXIF_SubjectArea"},
+    { 0x927C,	"EXIF_MakerNote"},
+    { 0x9286,	"EXIF_UserComment"},
+    { 0x9290,	"EXIF_SubSecTime"},
+    { 0x9291,	"EXIF_SubSecTime_Original"},
+    { 0x9292,	"EXIF_SubSecTime_Digitized"},
+    { 0xA000,	"EXIF_FlashpixVersion"},
+    { 0xA001,	"EXIF_ColorSpace"},
+    { 0xA002,	"EXIF_PixelXDimension"},
+    { 0xA003,	"EXIF_PixelYDimension"},
+    { 0xA004,       "EXIF_RelatedSoundFile"},
+//	{ 0xA005,	"EXIF_InteroperabilityOffset"},
+    { 0xA20B,	"EXIF_FlashEnergy"},	  // 0x920B in TIFF/EP
+    { 0xA20C,	"EXIF_SpatialFrequencyResponse"},   // 0x920C    -  -
+    { 0xA20E,	"EXIF_FocalPlaneXResolution"},     // 0x920E    -  -
+    { 0xA20F,	"EXIF_FocalPlaneYResolution"},     // 0x920F    -  -
+    { 0xA210,	"EXIF_FocalPlaneResolutionUnit"},  // 0x9210    -  -
+    { 0xA214,	"EXIF_SubjectLocation"},	// 0x9214    -  -
+    { 0xA215,	"EXIF_ExposureIndex"},		// 0x9215    -  -
+    { 0xA217,	"EXIF_SensingMethod"},		// 0x9217    -  -
+    { 0xA300,	"EXIF_FileSource"},
+    { 0xA301,	"EXIF_SceneType"},
+    { 0xA302,   "EXIF_CFAPattern"},
+    { 0xA401,   "EXIF_CustomRendered"},
+    { 0xA402,   "EXIF_ExposureMode"},
+    { 0XA403,   "EXIF_WhiteBalance"},
+    { 0xA404,   "EXIF_DigitalZoomRatio"},
+    { 0xA405,   "EXIF_FocalLengthIn35mmFilm"},
+    { 0xA406,   "EXIF_SceneCaptureType"},
+    { 0xA407,   "EXIF_GainControl"},
+    { 0xA408,   "EXIF_Contrast"},
+    { 0xA409,   "EXIF_Saturation"},
+    { 0xA40A,   "EXIF_Sharpness"},
+    { 0xA40B,   "EXIF_DeviceSettingDescription"},
+    { 0xA40C,   "EXIF_SubjectDistanceRange"},
+    { 0xA420,   "EXIF_ImageUniqueID"},
+    { 0x0000,       ""}
+};
+
+
+static struct intr_tag {
+  GInt16 tag;
+  char*  name;
+} intr_tags [] = {
+
+    { 0x1,	"EXIF_Interoperability_Index"},
+    { 0x2,	"EXIF_Interoperability_Version"},
+    { 0x1000,	"EXIF_Related_Image_File_Format"},
+    { 0x1001,	"EXIF_Related_Image_Width"},
+    { 0x1002,	"EXIF_Related_Image_Length"},
+    { 0x0000,       ""}
+};
+
+
+static int datawidth[] = {
+    0,	/* nothing */
+    1,	/* TIFF_BYTE */
+    1,	/* TIFF_ASCII */
+    2,	/* TIFF_SHORT */
+    4,	/* TIFF_LONG */
+    8,	/* TIFF_RATIONAL */
+    1,	/* TIFF_SBYTE */
+    1,	/* TIFF_UNDEFINED */
+    2,	/* TIFF_SSHORT */
+    4,	/* TIFF_SLONG */
+    8,	/* TIFF_SRATIONAL */
+    4,	/* TIFF_FLOAT */
+    8,	/* TIFF_DOUBLE */
+};
+
+#define TIFF_VERSION            42
+#define TIFF_BIGTIFF_VERSION    43
+
+#define TIFF_BIGENDIAN          0x4d4d
+#define TIFF_LITTLEENDIAN       0x4949
+
+/*
+ * TIFF header.
+ */
+typedef struct {
+        GUInt16  tiff_magic;     /* magic number (defines byte order) */
+#define TIFF_MAGIC_SIZE         2
+        GUInt16  tiff_version;   /* TIFF version number */
+#define TIFF_VERSION_SIZE       2
+        GUInt32  tiff_diroff;    /* byte offset to first directory */
+#define TIFF_DIROFFSET_SIZE     4
+} TIFFHeader;
+
+
+typedef enum {
+        TIFF_NOTYPE     = 0,    /* placeholder */
+        TIFF_BYTE       = 1,    /* 8-bit unsigned integer */
+        TIFF_ASCII      = 2,    /* 8-bit bytes w/ last byte null */
+        TIFF_SHORT      = 3,    /* 16-bit unsigned integer */
+        TIFF_LONG       = 4,    /* 32-bit unsigned integer */
+        TIFF_RATIONAL   = 5,    /* 64-bit unsigned fraction */
+        TIFF_SBYTE      = 6,    /* !8-bit signed integer */
+        TIFF_UNDEFINED  = 7,    /* !8-bit untyped data */
+        TIFF_SSHORT     = 8,    /* !16-bit signed integer */
+        TIFF_SLONG      = 9,    /* !32-bit signed integer */
+        TIFF_SRATIONAL  = 10,   /* !64-bit signed fraction */
+        TIFF_FLOAT      = 11,   /* !32-bit IEEE floating point */
+        TIFF_DOUBLE     = 12,   /* !64-bit IEEE floating point */
+        TIFF_IFD        = 13    /* %32-bit unsigned integer (offset) */
+} TIFFDataType;
+
+/*
+ * TIFF Image File Directories are comprised of a table of field
+ * descriptors of the form shown below.  The table is sorted in
+ * ascending order by tag.  The values associated with each entry are
+ * disjoint and may appear anywhere in the file (so long as they are
+ * placed on a word boundary).
+ *
+ * If the value is 4 bytes or less, then it is placed in the offset
+ * field to save space.  If the value is less than 4 bytes, it is
+ * left-justified in the offset field.
+ */
+typedef struct {
+        GUInt16          tdir_tag;       /* see below */
+        GUInt16          tdir_type;      /* data type; see below */
+        GUInt32          tdir_count;     /* number of items; length in spec */
+        GUInt32          tdir_offset;    /* byte offset to field data */
+} TIFFDirEntry;
+
+typedef GUInt32 tsize_t;          /* i/o size in bytes */
+
+#define	NWIDTHS	(sizeof (datawidth) / sizeof (datawidth[0]))
+
+
+CPL_C_START
+extern	int TIFFDataWidth(TIFFDataType);    /* table of tag datatype widths */
+extern	void TIFFSwabShort(GUInt16*);
+extern	void TIFFSwabLong(GUInt32*);
+extern	void TIFFSwabDouble(double*);
+extern	void TIFFSwabArrayOfShort(GUInt16*, unsigned long);
+extern	void TIFFSwabArrayOfTriples(GByte*, unsigned long);
+extern	void TIFFSwabArrayOfLong(GUInt32*, unsigned long);
+extern	void TIFFSwabArrayOfDouble(double*, unsigned long);
+CPL_C_END
+
diff --git a/Utilities/GDAL/frmts/jpeg/jpgdataset.cpp b/Utilities/GDAL/frmts/jpeg/jpgdataset.cpp
new file mode 100644
index 0000000000..86f86bf684
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/jpgdataset.cpp
@@ -0,0 +1,1448 @@
+/******************************************************************************
+ * $Id: jpgdataset.cpp,v 1.41 2006/03/21 19:46:37 fwarmerdam Exp $
+ *
+ * Project:  JPEG JFIF Driver
+ * Purpose:  Implement GDAL JPEG Support based on IJG libjpeg.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: jpgdataset.cpp,v $
+ * Revision 1.41  2006/03/21 19:46:37  fwarmerdam
+ * Avoid calling jpeg_finish_compress() if there was an error or
+ * libjpeg is likely to issue an error and call exit()!
+ *
+ * Revision 1.40  2006/02/26 14:32:32  fwarmerdam
+ * Added accelerated dataset rasterio for common case c/o Mike Mazzella.
+ * http://bugzilla.remotesensing.org/show_bug.cgi?id=1046
+ *
+ * Revision 1.39  2006/02/02 01:07:36  fwarmerdam
+ * Added support for finding EXIF information when APP1 chunk appears
+ * after an APP0 JFIF chunk.  ie. albania.jpg
+ *
+ * Revision 1.38  2005/10/14 21:52:26  fwarmerdam
+ * Slightly safer version of last fix, retaining ASCII setting for ucomments.
+ *
+ * Revision 1.37  2005/10/14 21:50:03  fwarmerdam
+ * Don't flip out of usercomment is less than 8 characters.
+ *
+ * Revision 1.36  2005/09/26 14:52:57  fwarmerdam
+ * Fixed UserComments to skip language encoding.  Force some new tags
+ * to ASCII.
+ *
+ * Revision 1.35  2005/09/23 20:01:56  fwarmerdam
+ * Fixed progress reporting.
+ *
+ * Revision 1.34  2005/09/22 14:59:47  fwarmerdam
+ * Fixed bug with unrecognised exif tags sometimes being written over
+ * metadata of a previous tag.
+ * Hack so that UserComments is treated as ASCII.
+ *
+ * Revision 1.33  2005/09/11 17:15:33  fwarmerdam
+ * direct io through VSI, use large file API
+ *
+ * Revision 1.32  2005/09/11 16:36:54  fwarmerdam
+ * Turn raw EXIF printf() into a debug statement.
+ *
+ * Revision 1.31  2005/09/05 22:37:52  fwarmerdam
+ * Added progress monitor support in createcopy.
+ *
+ * Revision 1.30  2005/08/10 20:53:24  dnadeau
+ * correct GPS and logic problems for EXIF
+ *
+ * Revision 1.29  2005/08/09 20:19:19  dnadeau
+ * add EXIF GPS IFD Tags
+ *
+ * Revision 1.28  2005/07/27 02:00:39  dnadeau
+ * correct leak problem. Free poTIFFDir.
+ *
+ * Revision 1.27  2005/07/19 19:36:15  fwarmerdam
+ * fixed libtiff conflicts
+ *
+ * Revision 1.26  2005/07/19 18:06:28  dnadeau
+ * fix loop problem finding tags
+ *
+ * Revision 1.25  2005/07/19 15:38:33  fwarmerdam
+ * Disable a few less interesting tags.
+ *
+ * Revision 1.24  2005/07/19 15:33:53  fwarmerdam
+ * Removed /Exif in longname.
+ *
+ * Revision 1.23  2005/07/19 15:21:57  dnadeau
+ * added exif support
+ *
+ * Revision 1.22  2005/05/23 06:57:12  fwarmerdam
+ * Use lockedblockrefs
+ *
+ * Revision 1.21  2005/04/27 16:35:58  fwarmerdam
+ * PAM enable
+ *
+ * Revision 1.20  2005/03/23 00:27:32  fwarmerdam
+ * First pass try at support for the libjpeg Mk1 library.
+ *
+ * Revision 1.19  2005/03/22 21:48:11  fwarmerdam
+ * Added preliminary Mk1 libjpeg support.  Compress still not working.
+ *
+ * Revision 1.18  2005/02/25 15:17:35  fwarmerdam
+ * Use dataset io to fetch to provide (potentially) faster interleaved
+ * reads.
+ *
+ * Revision 1.17  2003/10/31 00:58:55  warmerda
+ * Added support for .jpgw as per request from Markus.
+ *
+ * Revision 1.16  2003/04/04 13:45:12  warmerda
+ * Made casting to JSAMPLE explicit.
+ *
+ * Revision 1.15  2002/11/23 18:57:06  warmerda
+ * added CREATIONOPTIONS metadata on driver
+ *
+ * Revision 1.14  2002/09/04 06:50:37  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.13  2002/07/13 04:16:39  warmerda
+ * added WORLDFILE support
+ *
+ * Revision 1.12  2002/06/20 19:57:04  warmerda
+ * ensure GetGeoTransform always sets geotransform.
+ *
+ * Revision 1.11  2002/06/18 02:50:20  warmerda
+ * fixed multiline string constants
+ *
+ * Revision 1.10  2002/06/12 21:12:25  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.9  2001/12/11 16:50:16  warmerda
+ * try to push green and blue values into cache when reading red
+ *
+ * Revision 1.8  2001/11/11 23:51:00  warmerda
+ * added required class keyword to friend declarations
+ *
+ * Revision 1.7  2001/08/22 17:11:30  warmerda
+ * added support for .wld world files
+ *
+ * Revision 1.6  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.5  2001/06/01 14:13:12  warmerda
+ * Improved magic number testing.
+ *
+ * Revision 1.4  2001/05/01 18:18:28  warmerda
+ * added world file support
+ *
+ * Revision 1.3  2001/01/12 21:19:25  warmerda
+ * added progressive support
+ *
+ * Revision 1.2  2000/07/07 15:11:01  warmerda
+ * added QUALITY=n creation option
+ *
+ * Revision 1.1  2000/04/28 20:57:57  warmerda
+ * New
+ *
+ */
+
+#include "gdal_pam.h"
+#include "cpl_string.h"
+#include "gdalexif.h"
+
+
+CPL_CVSID("$Id: jpgdataset.cpp,v 1.41 2006/03/21 19:46:37 fwarmerdam Exp $");
+
+CPL_C_START
+#include "jpeglib.h"
+CPL_C_END
+
+CPL_C_START
+void	GDALRegister_JPEG(void);
+CPL_C_END
+
+void jpeg_vsiio_src (j_decompress_ptr cinfo, FILE * infile);
+void jpeg_vsiio_dest (j_compress_ptr cinfo, FILE * outfile);
+
+/************************************************************************/
+/* ==================================================================== */
+/*				JPGDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class JPGRasterBand;
+
+class JPGDataset : public GDALPamDataset
+{
+    friend class JPGRasterBand;
+
+    struct jpeg_decompress_struct sDInfo;
+    struct jpeg_error_mgr sJErr;
+
+    int	   bGeoTransformValid;
+    double adfGeoTransform[6];
+
+    FILE   *fpImage;
+    int    nLoadedScanline;
+    GByte  *pabyScanline;
+
+    char   **papszMetadata;
+    char   **papszSubDatasets;
+    int	   bigendian;
+    int    nExifOffset;
+    int    nInterOffset;
+    int    nGPSOffset;
+    int	   bSwabflag;
+    int    nTiffDirStart;
+    int    nTIFFHEADER;
+
+    CPLErr LoadScanline(int);
+    void   Restart();
+    
+    CPLErr EXIFExtractMetadata(FILE *, int);
+    int    EXIFInit(FILE *);
+    void   EXIFPrintByte(char *, const char*, TIFFDirEntry* );
+    void   EXIFPrintShort(char *, const char*, TIFFDirEntry*);
+    void   EXIFPrintData(char *, GUInt16, GUInt32, unsigned char* );
+
+    
+
+  public:
+                 JPGDataset();
+                 ~JPGDataset();
+
+    virtual CPLErr      IRasterIO( GDALRWFlag, int, int, int, int,
+                                   void *, int, int, GDALDataType,
+                                   int, int *, int, int, int );
+
+    virtual CPLErr GetGeoTransform( double * );
+    static GDALDataset *Open( GDALOpenInfo * );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            JPGRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class JPGRasterBand : public GDALPamRasterBand
+{
+    friend class JPGDataset;
+
+  public:
+
+                   JPGRasterBand( JPGDataset *, int );
+
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual GDALColorInterp GetColorInterpretation();
+};
+
+
+/************************************************************************/
+/*                         EXIFPrintByte()                              */
+/************************************************************************/
+void JPGDataset::EXIFPrintByte(char *pszData, 
+			       const char* fmt, TIFFDirEntry* dp)
+{
+  char* sep = "";
+  
+  if (bSwabflag) {
+    switch ((int)dp->tdir_count) {
+    case 4: sprintf(pszData, fmt, sep, dp->tdir_offset&0xff);
+      sep = " ";
+    case 3: sprintf(pszData, fmt, sep, (dp->tdir_offset>>8)&0xff);
+      sep = " ";
+    case 2: sprintf(pszData, fmt, sep, (dp->tdir_offset>>16)&0xff);
+      sep = " ";
+    case 1: sprintf(pszData, fmt, sep, dp->tdir_offset>>24);
+    }
+  } else {
+    switch ((int)dp->tdir_count) {
+    case 4: sprintf(pszData, fmt, sep, dp->tdir_offset>>24);
+      sep = " ";
+    case 3: sprintf(pszData, fmt, sep, (dp->tdir_offset>>16)&0xff);
+      sep = " ";
+    case 2: sprintf(pszData, fmt, sep, (dp->tdir_offset>>8)&0xff);
+      sep = " ";
+    case 1: sprintf(pszData, fmt, sep, dp->tdir_offset&0xff);
+    }
+  }
+}
+
+/************************************************************************/
+/*                         EXIFPrintShort()                             */
+/************************************************************************/
+void JPGDataset::EXIFPrintShort(char *pszData, const char* fmt, 
+			     TIFFDirEntry* dp)
+{
+  char *sep = "";
+  if (bSwabflag) {
+    switch (dp->tdir_count) {
+    case 2: sprintf(pszData, fmt, sep, dp->tdir_offset&0xffff);
+      sep = " ";
+    case 1: sprintf(pszData, fmt, sep, dp->tdir_offset>>16);
+    }
+  } else {
+    switch (dp->tdir_count) {
+    case 2: sprintf(pszData, fmt, sep, dp->tdir_offset>>16);
+      sep = " ";
+    case 1: sprintf(pszData, fmt, sep, dp->tdir_offset&0xffff);
+    }
+  }
+}
+
+/************************************************************************/
+/*                         EXIFPrintData()                              */
+/************************************************************************/
+void JPGDataset::EXIFPrintData(char* pszData, GUInt16 type, 
+			    GUInt32 count, unsigned char* data)
+{
+  char* sep = "";
+  char  pszTemp[MAXSTRINGLENGTH];
+
+  pszData[0]='\0';
+
+  switch (type) {
+
+  case TIFF_UNDEFINED:
+  case TIFF_BYTE:
+    while (count-- > 0){
+      sprintf(pszTemp, "%s%#02x", sep, *data++), sep = " ";
+      strcat(pszData,pszTemp);
+    }
+    break;
+
+  case TIFF_SBYTE:
+    while (count-- > 0){
+      sprintf(pszTemp, "%s%d", sep, *(char *)data++), sep = " ";
+      strcat(pszData,pszTemp);
+    }
+    break;
+	  
+  case TIFF_ASCII:
+    sprintf(pszData, "%s", data);
+    break;
+
+  case TIFF_SHORT: {
+    register GUInt16 *wp = (GUInt16*)data;
+    while (count-- > 0) {
+      sprintf(pszTemp, "%s%u", sep, *wp++), sep = " ";
+      strcat(pszData,pszTemp);
+    }
+    break;
+  }
+  case TIFF_SSHORT: {
+    register GInt16 *wp = (GInt16*)data;
+    while (count-- > 0) {
+      sprintf(pszTemp, "%s%d", sep, *wp++), sep = " ";
+      strcat(pszData,pszTemp);
+    }
+    break;
+  }
+  case TIFF_LONG: {
+    register GUInt32 *lp = (GUInt32*)data;
+    while (count-- > 0) {
+      sprintf(pszTemp, "%s%lu", sep, (unsigned long) *lp++);
+      sep = " ";
+      strcat(pszData,pszTemp);
+    }
+    break;
+  }
+  case TIFF_SLONG: {
+    register GInt32 *lp = (GInt32*)data;
+    while (count-- > 0){
+      sprintf(pszTemp, "%s%ld", sep, (long) *lp++), sep = " ";
+      strcat(pszData,pszTemp);
+    }
+    break;
+  }
+  case TIFF_RATIONAL: {
+      register GUInt32 *lp = (GUInt32*)data;
+      //      if(bSwabflag)
+      //	  TIFFSwabArrayOfLong((GUInt32*) data, 2*count);
+      while (count-- > 0) {
+	  if( (lp[0]==0) && (lp[1] == 0) ) {
+	      sprintf(pszTemp,"%s(0)",sep);
+	  }
+	  else{
+	      sprintf(pszTemp, "%s(%g)", sep,
+		      (double) lp[0]/ (double)lp[1]);
+	  }
+	  sep = " ";
+	  lp += 2;
+	  strcat(pszData,pszTemp);
+      }
+      break;
+  }
+  case TIFF_SRATIONAL: {
+    register GInt32 *lp = (GInt32*)data;
+    while (count-- > 0) {
+      sprintf(pszTemp, "%s(%g)", sep,
+	      (float) lp[0]/ (float) lp[1]);
+      sep = " ";
+      lp += 2;
+      strcat(pszData,pszTemp);
+    }
+    break;
+  }
+  case TIFF_FLOAT: {
+    register float *fp = (float *)data;
+    while (count-- > 0){
+      sprintf(pszTemp, "%s%g", sep, *fp++), sep = " ";
+      strcat(pszData,pszTemp);
+    }
+    break;
+  }
+  case TIFF_DOUBLE: {
+    register double *dp = (double *)data;
+    while (count-- > 0) {
+      sprintf(pszTemp, "%s%g", sep, *dp++), sep = " ";
+      strcat(pszData,pszTemp);
+    }
+    break;
+  }
+  }
+}
+
+
+
+/************************************************************************/
+/*                        EXIFInit()                                    */
+/*                                                                      */
+/*           Create Metadata from Information file directory APP1       */
+/************************************************************************/
+int JPGDataset::EXIFInit(FILE *fp)
+{
+    int           one = 1;
+    TIFFHeader    hdr;
+  
+    bigendian = (*(char *)&one == 0);
+
+/* -------------------------------------------------------------------- */
+/*      Search for APP1 chunk.                                          */
+/* -------------------------------------------------------------------- */
+    GByte abyChunkHeader[10];
+    int nChunkLoc = 2;
+
+    for( ; TRUE; ) 
+    {
+        if( VSIFSeekL( fp, nChunkLoc, SEEK_SET ) != 0 )
+            return FALSE;
+
+        if( VSIFReadL( abyChunkHeader, sizeof(abyChunkHeader), 1, fp ) != 1 )
+            return FALSE;
+
+        if( abyChunkHeader[0] != 0xFF 
+            || (abyChunkHeader[1] & 0xf0) != 0xe0 )
+            return FALSE; // Not an APP chunk.
+
+        if( abyChunkHeader[1] == 0xe1 
+            && strncmp((const char *) abyChunkHeader + 4,"Exif",4) == 0 )
+        {
+            nTIFFHEADER = nChunkLoc + 10;
+            break; // APP1 - Exif
+        }
+
+        nChunkLoc += 2 + abyChunkHeader[2] * 256 + abyChunkHeader[3];
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read TIFF header                                                */
+/* -------------------------------------------------------------------- */
+    VSIFSeekL(fp, nTIFFHEADER, SEEK_SET);
+    if(VSIFReadL(&hdr,1,sizeof(hdr),fp) != sizeof(hdr)) 
+        CPLError( CE_Failure, CPLE_FileIO,
+                  "Failed to read %d byte from image header.",
+                  sizeof(hdr));
+
+    if (hdr.tiff_magic != TIFF_BIGENDIAN && hdr.tiff_magic != TIFF_LITTLEENDIAN)
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Not a TIFF file, bad magic number %u (%#x)",
+                  hdr.tiff_magic, hdr.tiff_magic);
+
+    if (hdr.tiff_magic == TIFF_BIGENDIAN)    bSwabflag = !bigendian;
+    if (hdr.tiff_magic == TIFF_LITTLEENDIAN) bSwabflag = bigendian;
+
+
+    if (bSwabflag) {
+        TIFFSwabShort(&hdr.tiff_version);
+        TIFFSwabLong(&hdr.tiff_diroff);
+    }
+
+
+    if (hdr.tiff_version != TIFF_VERSION)
+        CPLError(CE_Failure, CPLE_AppDefined,
+                 "Not a TIFF file, bad version number %u (%#x)",
+                 hdr.tiff_version, hdr.tiff_version); 
+    nTiffDirStart = hdr.tiff_diroff;
+
+    CPLDebug( "JPEG", "Magic: %#x <%s-endian> Version: %#x\n",
+              hdr.tiff_magic,
+              hdr.tiff_magic == TIFF_BIGENDIAN ? "big" : "little",
+              hdr.tiff_version );
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                        EXIFExtractMetadata()                         */
+/*                                                                      */
+/*      Extract all entry from a IFD                                    */
+/************************************************************************/
+CPLErr JPGDataset::EXIFExtractMetadata(FILE *fp, int nOffset)
+{
+    GUInt16        nEntryCount;
+    int space;
+    unsigned int           n,i;
+    char          pszTemp[MAXSTRINGLENGTH];
+    char          pszName[MAXSTRINGLENGTH];
+
+    TIFFDirEntry *poTIFFDirEntry;
+    TIFFDirEntry *poTIFFDir;
+    struct tagname *poExifTags ;
+    struct intr_tag *poInterTags = intr_tags;
+    struct gpsname *poGPSTags;
+
+/* -------------------------------------------------------------------- */
+/*      Read number of entry in directory                               */
+/* -------------------------------------------------------------------- */
+    VSIFSeekL(fp, nOffset+nTIFFHEADER, SEEK_SET);
+
+    if(VSIFReadL(&nEntryCount,1,sizeof(GUInt16),fp) != sizeof(GUInt16)) 
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Error directory count");
+
+    if (bSwabflag)
+        TIFFSwabShort(&nEntryCount);
+
+    poTIFFDir = (TIFFDirEntry *)CPLMalloc(nEntryCount * sizeof(TIFFDirEntry));
+
+    if (poTIFFDir == NULL) 
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "No space for TIFF directory");
+  
+/* -------------------------------------------------------------------- */
+/*      Read all directory entries                                      */
+/* -------------------------------------------------------------------- */
+    n = VSIFReadL(poTIFFDir, 1,nEntryCount*sizeof(TIFFDirEntry),fp);
+    if (n != nEntryCount*sizeof(TIFFDirEntry)) 
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Could not read all directories");
+
+/* -------------------------------------------------------------------- */
+/*      Parse all entry information in this directory                   */
+/* -------------------------------------------------------------------- */
+    for(poTIFFDirEntry = poTIFFDir,i=nEntryCount; i > 0; i--,poTIFFDirEntry++) {
+        if (bSwabflag) {
+            TIFFSwabShort(&poTIFFDirEntry->tdir_tag);
+            TIFFSwabShort(&poTIFFDirEntry->tdir_type);
+            TIFFSwabLong (&poTIFFDirEntry->tdir_count);
+            TIFFSwabLong (&poTIFFDirEntry->tdir_offset);
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Find Tag name in table                                          */
+/* -------------------------------------------------------------------- */
+        pszName[0] = '\0';
+
+        for (poExifTags = tagnames; poExifTags->tag; poExifTags++)
+            if(poExifTags->tag == poTIFFDirEntry->tdir_tag){
+                strcpy(pszName, poExifTags->name);
+                break;
+            }
+
+    
+        if( nOffset == nGPSOffset) {
+            for( poGPSTags = gpstags; poGPSTags->tag != 0xffff; poGPSTags++ ) 
+                if( poGPSTags->tag == poTIFFDirEntry->tdir_tag ) {
+                    strcpy(pszName, poGPSTags->name);
+                    break;
+                }
+        }
+/* -------------------------------------------------------------------- */
+/*      If the tag was not found, look into the interoperability table  */
+/* -------------------------------------------------------------------- */
+        if( nOffset == nInterOffset ) {
+            for(poInterTags = intr_tags; poInterTags->tag; poInterTags++)
+                if(poInterTags->tag == poTIFFDirEntry->tdir_tag) {
+                    strcpy(pszName, poInterTags->name);
+                    break;
+                }
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Save important directory tag offset                             */
+/* -------------------------------------------------------------------- */
+        if( poTIFFDirEntry->tdir_tag == EXIFOFFSETTAG )
+            nExifOffset=poTIFFDirEntry->tdir_offset;
+        if( poTIFFDirEntry->tdir_tag == INTEROPERABILITYOFFSET )
+            nInterOffset=poTIFFDirEntry->tdir_offset;
+        if( poTIFFDirEntry->tdir_tag == GPSOFFSETTAG ) {
+            nGPSOffset=poTIFFDirEntry->tdir_offset;
+        }
+
+/* -------------------------------------------------------------------- */
+/*      If we didn't recognise the tag just ignore it.  To see all      */
+/*      tags comment out the continue.                                  */
+/* -------------------------------------------------------------------- */
+        if( pszName[0] == '\0' )
+        {
+            sprintf( pszName, "EXIF_%d", poTIFFDirEntry->tdir_tag );
+            continue;
+        }
+
+/* -------------------------------------------------------------------- */
+/*      For UserComment we need to ignore the language binding and      */
+/*      just return the actual contents.                                */
+/* -------------------------------------------------------------------- */
+        if( EQUAL(pszName,"EXIF_UserComment")  )
+        {
+            poTIFFDirEntry->tdir_type = TIFF_ASCII;
+            
+            if( poTIFFDirEntry->tdir_count >= 8 )
+            {
+                poTIFFDirEntry->tdir_count -= 8;
+                poTIFFDirEntry->tdir_offset += 8;
+            }
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Make some UNDEFINED or BYTE fields ASCII for readability.       */
+/* -------------------------------------------------------------------- */
+        if( EQUAL(pszName,"EXIF_ExifVersion")
+            || EQUAL(pszName,"EXIF_FlashPixVersion")
+            || EQUAL(pszName,"EXIF_MakerNote")
+            || EQUAL(pszName,"GPSProcessingMethod") )
+            poTIFFDirEntry->tdir_type = TIFF_ASCII;
+
+/* -------------------------------------------------------------------- */
+/*      Print tags                                                      */
+/* -------------------------------------------------------------------- */
+        space = poTIFFDirEntry->tdir_count * 
+            datawidth[poTIFFDirEntry->tdir_type];
+
+/* -------------------------------------------------------------------- */
+/*      This is at most 4 byte data so we can read it from tdir_offset  */
+/* -------------------------------------------------------------------- */
+        if (space >= 0 && space <= 4) {
+            switch (poTIFFDirEntry->tdir_type) {
+              case TIFF_FLOAT:
+              case TIFF_UNDEFINED:
+              case TIFF_ASCII: {
+                  unsigned char data[4];
+                  memcpy(data, &poTIFFDirEntry->tdir_offset, 4);
+                  if (bSwabflag)
+                      TIFFSwabLong((GUInt32*) data);
+
+                  EXIFPrintData(pszTemp,
+                                poTIFFDirEntry->tdir_type, 
+                                poTIFFDirEntry->tdir_count, data);
+                  break;
+              }
+
+              case TIFF_BYTE:
+                EXIFPrintByte(pszTemp, "%s%#02x", poTIFFDirEntry);
+                break;
+              case TIFF_SBYTE:
+                EXIFPrintByte(pszTemp, "%s%d", poTIFFDirEntry);
+                break;
+              case TIFF_SHORT:
+                EXIFPrintShort(pszTemp, "%s%u", poTIFFDirEntry);
+                break;
+              case TIFF_SSHORT:
+                EXIFPrintShort(pszTemp, "%s%d", poTIFFDirEntry);
+                break;
+              case TIFF_LONG:
+                sprintf(pszTemp, "%lu",(long) poTIFFDirEntry->tdir_offset);	
+                break;
+              case TIFF_SLONG:
+                sprintf(pszTemp, "%lu",(long) poTIFFDirEntry->tdir_offset);
+                break;
+            }
+	
+        }
+/* -------------------------------------------------------------------- */
+/*      The data is being read where tdir_offset point to in the file   */
+/* -------------------------------------------------------------------- */
+        else {
+
+            unsigned char *data = (unsigned char *)CPLMalloc(space);
+
+            if (data) {
+                int width = TIFFDataWidth((TIFFDataType) poTIFFDirEntry->tdir_type);
+                tsize_t cc = poTIFFDirEntry->tdir_count * width;
+                VSIFSeekL(fp,poTIFFDirEntry->tdir_offset+nTIFFHEADER,SEEK_SET);
+                VSIFReadL(data, 1, cc, fp);
+
+                if (bSwabflag) {
+                    switch (poTIFFDirEntry->tdir_type) {
+                      case TIFF_SHORT:
+                      case TIFF_SSHORT:
+                        TIFFSwabArrayOfShort((GUInt16*) data, 
+                                             poTIFFDirEntry->tdir_count);
+                        break;
+                      case TIFF_LONG:
+                      case TIFF_SLONG:
+                      case TIFF_FLOAT:
+                        TIFFSwabArrayOfLong((GUInt32*) data, 
+                                            poTIFFDirEntry->tdir_count);
+                        break;
+                      case TIFF_RATIONAL:
+                      case TIFF_SRATIONAL:
+                        TIFFSwabArrayOfLong((GUInt32*) data, 
+                                            2*poTIFFDirEntry->tdir_count);
+                        break;
+                      case TIFF_DOUBLE:
+                        TIFFSwabArrayOfDouble((double*) data, 
+                                              poTIFFDirEntry->tdir_count);
+                        break;
+                      default:
+                        break;
+                    }
+                }
+
+                EXIFPrintData(pszTemp, poTIFFDirEntry->tdir_type,
+                              poTIFFDirEntry->tdir_count, data);
+                if (data) CPLFree(data);
+            }
+        }
+        papszMetadata = CSLSetNameValue(papszMetadata, pszName, pszTemp);
+    }
+    CPLFree(poTIFFDir);
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           JPGRasterBand()                            */
+/************************************************************************/
+
+JPGRasterBand::JPGRasterBand( JPGDataset *poDS, int nBand )
+
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+    if( poDS->sDInfo.data_precision == 12 )
+        eDataType = GDT_UInt16;
+    else
+        eDataType = GDT_Byte;
+
+    nBlockXSize = poDS->nRasterXSize;;
+    nBlockYSize = 1;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr JPGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+
+{
+    JPGDataset	*poGDS = (JPGDataset *) poDS;
+    CPLErr      eErr;
+    int         nXSize = GetXSize();
+    int         nWordSize = GDALGetDataTypeSize(eDataType) / 8;
+    
+    CPLAssert( nBlockXOff == 0 );
+
+/* -------------------------------------------------------------------- */
+/*      Load the desired scanline into the working buffer.              */
+/* -------------------------------------------------------------------- */
+    eErr = poGDS->LoadScanline( nBlockYOff );
+    if( eErr != CE_None )
+        return eErr;
+
+/* -------------------------------------------------------------------- */
+/*      Transfer between the working buffer the the callers buffer.     */
+/* -------------------------------------------------------------------- */
+    if( poGDS->GetRasterCount() == 1 )
+    {
+#ifdef JPEG_LIB_MK1
+        GDALCopyWords( poGDS->pabyScanline, GDT_UInt16, 2, 
+                       pImage, eDataType, nWordSize, 
+                       nXSize );
+#else
+        memcpy( pImage, poGDS->pabyScanline, nXSize * nWordSize );
+#endif
+    }
+    else
+    {
+#ifdef JPEG_LIB_MK1
+        GDALCopyWords( poGDS->pabyScanline + (nBand-1) * 2, 
+                       GDT_UInt16, 6, 
+                       pImage, eDataType, nWordSize, 
+                       nXSize );
+#else
+        GDALCopyWords( poGDS->pabyScanline + (nBand-1) * nWordSize, 
+                       eDataType, nWordSize * 3, 
+                       pImage, eDataType, nWordSize, 
+                       nXSize );
+#endif
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Forceably load the other bands associated with this scanline.   */
+/* -------------------------------------------------------------------- */
+    if( poGDS->GetRasterCount() == 3 && nBand == 1 )
+    {
+        GDALRasterBlock *poBlock;
+
+        poBlock = 
+            poGDS->GetRasterBand(2)->GetLockedBlockRef(nBlockXOff,nBlockYOff);
+        poBlock->DropLock();
+
+        poBlock = 
+            poGDS->GetRasterBand(3)->GetLockedBlockRef(nBlockXOff,nBlockYOff);
+        poBlock->DropLock();
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp JPGRasterBand::GetColorInterpretation()
+
+{
+    JPGDataset	*poGDS = (JPGDataset *) poDS;
+
+    if( poGDS->nBands == 1 )
+        return GCI_GrayIndex;
+
+    else if( nBand == 1 )
+        return GCI_RedBand;
+
+    else if( nBand == 2 )
+        return GCI_GreenBand;
+
+    else 
+        return GCI_BlueBand;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             JPGDataset                               */
+/* ==================================================================== */
+/************************************************************************/
+
+
+/************************************************************************/
+/*                            JPGDataset()                              */
+/************************************************************************/
+
+JPGDataset::JPGDataset()
+
+{
+    pabyScanline = NULL;
+    nLoadedScanline = -1;
+
+    papszMetadata   = NULL;						
+    papszSubDatasets= NULL;
+    nExifOffset     = -1;
+    nInterOffset    = -1;
+    nGPSOffset      = -1;
+    bGeoTransformValid = FALSE;
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+}
+
+/************************************************************************/
+/*                           ~JPGDataset()                            */
+/************************************************************************/
+
+JPGDataset::~JPGDataset()
+
+{
+    FlushCache();
+
+    jpeg_abort_decompress( &sDInfo );
+    jpeg_destroy_decompress( &sDInfo );
+
+    if( fpImage != NULL )
+        VSIFCloseL( fpImage );
+
+    if( pabyScanline != NULL )
+        CPLFree( pabyScanline );
+    if( papszMetadata != NULL )
+      CSLDestroy( papszMetadata );
+}
+/************************************************************************/
+/*                            LoadScanline()                            */
+/************************************************************************/
+
+CPLErr JPGDataset::LoadScanline( int iLine )
+
+{
+    if( nLoadedScanline == iLine )
+        return CE_None;
+
+    if( pabyScanline == NULL )
+        pabyScanline = (GByte *)
+            CPLMalloc(GetRasterCount() * GetRasterXSize() * 2);
+
+    if( iLine < nLoadedScanline )
+        Restart();
+        
+    while( nLoadedScanline < iLine )
+    {
+        JSAMPLE	*ppSamples;
+            
+        ppSamples = (JSAMPLE *) pabyScanline;
+        jpeg_read_scanlines( &sDInfo, &ppSamples, 1 );
+        nLoadedScanline++;
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                              Restart()                               */
+/*                                                                      */
+/*      Restart compressor at the beginning of the file.                */
+/************************************************************************/
+
+void JPGDataset::Restart()
+
+{
+    jpeg_abort_decompress( &sDInfo );
+    jpeg_destroy_decompress( &sDInfo );
+    jpeg_create_decompress( &sDInfo );
+
+    VSIRewindL( fpImage );
+
+    jpeg_vsiio_src( &sDInfo, fpImage );
+    jpeg_read_header( &sDInfo, TRUE );
+    
+    if( GetRasterCount() == 1 )
+        sDInfo.out_color_space = JCS_GRAYSCALE;
+    else
+        sDInfo.out_color_space = JCS_RGB;
+    nLoadedScanline = -1;
+    jpeg_start_decompress( &sDInfo );
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr JPGDataset::GetGeoTransform( double * padfTransform )
+
+{
+    if( bGeoTransformValid )
+    {
+        memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
+        
+        return CE_None;
+    }
+    else 
+        return GDALPamDataset::GetGeoTransform( padfTransform );
+}
+
+/************************************************************************/
+/*                             IRasterIO()                              */
+/*                                                                      */
+/*      Checks for what might be the most common read case              */
+/*      (reading an entire interleaved, 8bit, RGB JPEG), and            */
+/*      optimizes for that case                                         */
+/************************************************************************/
+
+CPLErr JPGDataset::IRasterIO( GDALRWFlag eRWFlag, 
+                              int nXOff, int nYOff, int nXSize, int nYSize,
+                              void *pData, int nBufXSize, int nBufYSize, 
+                              GDALDataType eBufType,
+                              int nBandCount, int *panBandMap, 
+                              int nPixelSpace, int nLineSpace, int nBandSpace )
+
+{
+    if((eRWFlag == GF_Read) &&
+       (nBandCount == 3) &&
+       (nBands == 3) &&
+       (nXOff == 0) && (nXOff == 0) &&
+       (nXSize == nBufXSize) && (nXSize == nRasterXSize) &&
+       (nYSize == nBufYSize) && (nYSize == nRasterYSize) &&
+       (eBufType == GDT_Byte) && (sDInfo.data_precision != 12) &&
+       /*(nPixelSpace >= 3)*/(nPixelSpace > 3) &&
+       (nLineSpace == (nPixelSpace*nXSize)) &&
+       (nBandSpace == 1) &&
+       (pData != NULL) &&
+       (panBandMap != NULL) &&
+       (panBandMap[0] == 1) && (panBandMap[1] == 2) && (panBandMap[2] == 3))
+    {
+        Restart();
+        int y;
+        CPLErr tmpError;
+        int x;
+
+        // handles copy with padding case
+        for(y = 0; y < nYSize; ++y)
+        {
+            tmpError = LoadScanline(y);
+            if(tmpError != CE_None) return tmpError;
+
+            for(x = 0; x < nXSize; ++x)
+            {
+                tmpError = LoadScanline(y);
+                if(tmpError != CE_None) return tmpError;
+                memcpy(&(((GByte*)pData)[(y*nLineSpace) + (x*nPixelSpace)]), 
+                       (const GByte*)&(pabyScanline[x*3]), 3);
+            }
+        }
+
+        return CE_None;
+    }
+
+    return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
+                                     pData, nBufXSize, nBufYSize, eBufType, 
+                                     nBandCount, panBandMap, 
+                                     nPixelSpace, nLineSpace, nBandSpace);
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *JPGDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+/* -------------------------------------------------------------------- */
+/*	First we check to see if the file has the expected header	*/
+/*	bytes.								*/    
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->nHeaderBytes < 10 )
+        return NULL;
+
+    if( poOpenInfo->pabyHeader[0] != 0xff
+        || poOpenInfo->pabyHeader[1] != 0xd8
+        || poOpenInfo->pabyHeader[2] != 0xff )
+        return NULL;
+
+    if( poOpenInfo->pabyHeader[3] == 0xe0
+        && poOpenInfo->pabyHeader[6] == 'J'
+        && poOpenInfo->pabyHeader[7] == 'F'
+        && poOpenInfo->pabyHeader[8] == 'I'
+        && poOpenInfo->pabyHeader[9] == 'F' )
+        /* OK */;
+    else if( poOpenInfo->pabyHeader[3] == 0xe1
+             && poOpenInfo->pabyHeader[6] == 'E'
+             && poOpenInfo->pabyHeader[7] == 'x'
+             && poOpenInfo->pabyHeader[8] == 'i'
+             && poOpenInfo->pabyHeader[9] == 'f' )
+        /* OK */;
+    else
+        return NULL;
+
+    if( poOpenInfo->eAccess == GA_Update )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "The JPEG driver does not support update access to existing"
+                  " datasets.\n" );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    JPGDataset 	*poDS;
+
+    poDS = new JPGDataset();
+
+/* -------------------------------------------------------------------- */
+/*      Open the file using the large file api.                         */
+/* -------------------------------------------------------------------- */
+    poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
+    if( poDS->fpImage == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "VSIFOpenL(%s) failed unexpectedly in jpgdataset.cpp", 
+                  poOpenInfo->pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Take care of EXIF Metadata                                      */
+/* -------------------------------------------------------------------- */
+    if( poDS->EXIFInit(poDS->fpImage) )
+    {
+        poDS->EXIFExtractMetadata(poDS->fpImage,poDS->nTiffDirStart);
+
+        if(poDS->nExifOffset  > 0){ 
+            poDS->EXIFExtractMetadata(poDS->fpImage,poDS->nExifOffset);
+        }
+        if(poDS->nInterOffset > 0) {
+            poDS->EXIFExtractMetadata(poDS->fpImage,poDS->nInterOffset);
+        }
+        if(poDS->nGPSOffset > 0) {
+            poDS->EXIFExtractMetadata(poDS->fpImage,poDS->nGPSOffset);
+        }
+        poDS->SetMetadata( poDS->papszMetadata );
+    }
+
+    poDS->eAccess = GA_ReadOnly;
+
+    poDS->sDInfo.err = jpeg_std_error( &(poDS->sJErr) );
+
+    jpeg_create_decompress( &(poDS->sDInfo) );
+
+/* -------------------------------------------------------------------- */
+/*	Read pre-image data after ensuring the file is rewound.         */
+/* -------------------------------------------------------------------- */
+    VSIFSeekL( poDS->fpImage, 0, SEEK_SET );
+
+    jpeg_vsiio_src( &(poDS->sDInfo), poDS->fpImage );
+    jpeg_read_header( &(poDS->sDInfo), TRUE );
+
+    if( poDS->sDInfo.data_precision != 8
+        && poDS->sDInfo.data_precision != 12 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "GDAL JPEG Driver doesn't support files with precision of"
+                  " other than 8 or 12 bits." );
+        delete poDS;
+        return NULL;
+    }
+
+    jpeg_start_decompress( &(poDS->sDInfo) );
+
+/* -------------------------------------------------------------------- */
+/*      Capture some information from the file that is of interest.     */
+/* -------------------------------------------------------------------- */
+    poDS->nRasterXSize = poDS->sDInfo.image_width;
+    poDS->nRasterYSize = poDS->sDInfo.image_height;
+
+    if( poDS->sDInfo.jpeg_color_space == JCS_GRAYSCALE )
+    {
+        poDS->nBands = 1;
+        poDS->sDInfo.out_color_space = JCS_GRAYSCALE;
+    }
+    else if( poDS->sDInfo.jpeg_color_space == JCS_RGB 
+             || poDS->sDInfo.jpeg_color_space == JCS_YCbCr )
+    {
+        poDS->nBands = 3;
+        poDS->sDInfo.out_color_space = JCS_RGB;
+    }
+    else
+    {
+        delete poDS;
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "Unrecognised jpeg_color_space value of %d.\n", 
+                  poDS->sDInfo.jpeg_color_space );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    for( int iBand = 0; iBand < poDS->nBands; iBand++ )
+        poDS->SetBand( iBand+1, new JPGRasterBand( poDS, iBand+1 ) );
+
+/* -------------------------------------------------------------------- */
+/*      Open overviews.                                                 */
+/* -------------------------------------------------------------------- */
+    poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+/* -------------------------------------------------------------------- */
+/*      Check for world file.                                           */
+/* -------------------------------------------------------------------- */
+    poDS->bGeoTransformValid = 
+        GDALReadWorldFile( poOpenInfo->pszFilename, ".jgw", 
+                           poDS->adfGeoTransform )
+        || GDALReadWorldFile( poOpenInfo->pszFilename, ".jpgw", 
+                              poDS->adfGeoTransform )
+        || GDALReadWorldFile( poOpenInfo->pszFilename, ".wld", 
+                              poDS->adfGeoTransform );
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                           JPEGCreateCopy()                           */
+/************************************************************************/
+
+static GDALDataset *
+JPEGCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, 
+                int bStrict, char ** papszOptions, 
+                GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    int  nBands = poSrcDS->GetRasterCount();
+    int  nXSize = poSrcDS->GetRasterXSize();
+    int  nYSize = poSrcDS->GetRasterYSize();
+    int  anBandList[3] = {1,2,3};
+    int  nQuality = 75;
+    int  bProgressive = FALSE;
+
+    if( !pfnProgress( 0.0, NULL, pProgressData ) )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Some some rudimentary checks                                    */
+/* -------------------------------------------------------------------- */
+    if( nBands != 1 && nBands != 3 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "JPEG driver doesn't support %d bands.  Must be 1 (grey) "
+                  "or 3 (RGB) bands.\n", nBands );
+
+        return NULL;
+    }
+
+    GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
+
+#ifdef JPEG_LIB_MK1
+    if( eDT != GDT_Byte && eDT != GDT_UInt16 && bStrict )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "JPEG driver doesn't support data type %s. "
+                  "Only eight and twelve bit bands supported (Mk1 libjpeg).\n",
+                  GDALGetDataTypeName( 
+                      poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
+
+        return NULL;
+    }
+
+    if( eDT == GDT_UInt16 || eDT == GDT_Int16 )
+        eDT = GDT_UInt16;
+    else
+        eDT = GDT_Byte;
+
+#else
+    if( eDT != GDT_Byte && bStrict )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "JPEG driver doesn't support data type %s. "
+                  "Only eight bit byte bands supported.\n", 
+                  GDALGetDataTypeName( 
+                      poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
+
+        return NULL;
+    }
+    
+    eDT = GDT_Byte; // force to 8bit. 
+#endif
+
+/* -------------------------------------------------------------------- */
+/*      What options has the user selected?                             */
+/* -------------------------------------------------------------------- */
+    if( CSLFetchNameValue(papszOptions,"QUALITY") != NULL )
+    {
+        nQuality = atoi(CSLFetchNameValue(papszOptions,"QUALITY"));
+        if( nQuality < 10 || nQuality > 100 )
+        {
+            CPLError( CE_Failure, CPLE_IllegalArg,
+                      "QUALITY=%s is not a legal value in the range 10-100.",
+                      CSLFetchNameValue(papszOptions,"QUALITY") );
+            return NULL;
+        }
+    }
+
+    bProgressive = CSLFetchBoolean( papszOptions, "PROGRESSIVE", FALSE );
+
+/* -------------------------------------------------------------------- */
+/*      Create the dataset.                                             */
+/* -------------------------------------------------------------------- */
+    FILE	*fpImage;
+
+    fpImage = VSIFOpenL( pszFilename, "wb" );
+    if( fpImage == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "Unable to create jpeg file %s.\n", 
+                  pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize JPG access to the file.                              */
+/* -------------------------------------------------------------------- */
+    struct jpeg_compress_struct sCInfo;
+    struct jpeg_error_mgr sJErr;
+    
+    sCInfo.err = jpeg_std_error( &sJErr );
+    jpeg_create_compress( &sCInfo );
+    
+    jpeg_vsiio_dest( &sCInfo, fpImage );
+    
+    sCInfo.image_width = nXSize;
+    sCInfo.image_height = nYSize;
+    sCInfo.input_components = nBands;
+
+    if( nBands == 1 )
+    {
+        sCInfo.in_color_space = JCS_GRAYSCALE;
+    }
+    else
+    {
+        sCInfo.in_color_space = JCS_RGB;
+    }
+
+    jpeg_set_defaults( &sCInfo );
+    
+#ifdef JPEG_LIB_MK1
+    if( eDT == GDT_UInt16 )
+    {
+        sCInfo.data_precision = 12;
+        sCInfo.bits_in_jsample = 12;
+    }
+    else
+    {
+        sCInfo.data_precision = 8;
+        sCInfo.bits_in_jsample = 8;
+    }
+#endif
+
+    jpeg_set_quality( &sCInfo, nQuality, TRUE );
+
+    if( bProgressive )
+        jpeg_simple_progression( &sCInfo );
+
+    jpeg_start_compress( &sCInfo, TRUE );
+
+/* -------------------------------------------------------------------- */
+/*      Loop over image, copying image data.                            */
+/* -------------------------------------------------------------------- */
+    GByte 	*pabyScanline;
+    CPLErr      eErr = CE_None;
+
+    pabyScanline = (GByte *) CPLMalloc( nBands * nXSize * 2 );
+
+    for( int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ )
+    {
+        JSAMPLE      *ppSamples;
+
+#ifdef JPEG_LIB_MK1
+        eErr = poSrcDS->RasterIO( GF_Read, 0, iLine, nXSize, 1, 
+                                  pabyScanline, nXSize, 1, GDT_UInt16,
+                                  nBands, anBandList, 
+                                  nBands*2, nBands * nXSize * 2, 2 );
+#else
+        eErr = poSrcDS->RasterIO( GF_Read, 0, iLine, nXSize, 1, 
+                                  pabyScanline, nXSize, 1, GDT_Byte,
+                                  nBands, anBandList, 
+                                  nBands, nBands * nXSize, 1 );
+#endif
+
+        // Should we clip values over 4095 (12bit)? 
+
+        ppSamples = (JSAMPLE *) pabyScanline;
+
+        if( eErr == CE_None )
+            jpeg_write_scanlines( &sCInfo, &ppSamples, 1 );
+
+        if( eErr == CE_None 
+            && !pfnProgress( (iLine+1) / (double) nYSize,
+                             NULL, pProgressData ) )
+        {
+            eErr = CE_Failure;
+            CPLError( CE_Failure, CPLE_UserInterrupt, 
+                      "User terminated CreateCopy()" );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup and close.                                              */
+/* -------------------------------------------------------------------- */
+    CPLFree( pabyScanline );
+
+    if( eErr == CE_None )
+        jpeg_finish_compress( &sCInfo );
+    jpeg_destroy_compress( &sCInfo );
+
+    VSIFCloseL( fpImage );
+
+    if( eErr != CE_None )
+    {
+        VSIUnlink( pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Do we need a world file?                                          */
+/* -------------------------------------------------------------------- */
+    if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) )
+    {
+    	double      adfGeoTransform[6];
+	
+	poSrcDS->GetGeoTransform( adfGeoTransform );
+	GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Re-open dataset, and copy any auxilary pam information.         */
+/* -------------------------------------------------------------------- */
+    JPGDataset *poDS = (JPGDataset *) GDALOpen( pszFilename, GA_ReadOnly );
+
+    if( poDS )
+        poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                         GDALRegister_JPEG()                          */
+/************************************************************************/
+
+void GDALRegister_JPEG()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "JPEG" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "JPEG" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "JPEG JFIF" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_jpeg.html" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "jpg" );
+        poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/jpeg" );
+
+#ifdef JPEG_LIB_MK1
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte UInt16" );
+#else
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte" );
+#endif
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, 
+"<CreationOptionList>\n"
+"   <Option name='PROGRESSIVE' type='boolean'/>\n"
+"   <Option name='QUALITY' type='int' description='good=100, bad=0, default=75'/>\n"
+"   <Option name='WORLDFILE' type='boolean'/>\n"
+"</CreationOptionList>\n" );
+
+        poDriver->pfnOpen = JPGDataset::Open;
+        poDriver->pfnCreateCopy = JPEGCreateCopy;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/README b/Utilities/GDAL/frmts/jpeg/libjpeg/README
new file mode 100644
index 0000000000..86cc20669d
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/README
@@ -0,0 +1,385 @@
+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/Utilities/GDAL/frmts/jpeg/libjpeg/jcapimin.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jcapimin.c
new file mode 100644
index 0000000000..54fb8c58c5
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jcapimin.c
@@ -0,0 +1,280 @@
+/*
+ * jcapimin.c
+ *
+ * Copyright (C) 1994-1998, 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 application interface code for the compression half
+ * of the JPEG library.  These are the "minimum" API routines that may be
+ * needed in either the normal full-compression case or the transcoding-only
+ * case.
+ *
+ * Most of the routines intended to be called directly by an application
+ * are in this file or in jcapistd.c.  But also see jcparam.c for
+ * parameter-setup helper routines, jcomapi.c for routines shared by
+ * compression and decompression, and jctrans.c for the transcoding case.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/*
+ * Initialization of a JPEG compression object.
+ * The error manager must already be set up (in case memory manager fails).
+ */
+
+GLOBAL(void)
+jpeg_CreateCompress (j_compress_ptr cinfo, int version, size_t structsize)
+{
+  int i;
+
+  /* Guard against version mismatches between library and caller. */
+  cinfo->mem = NULL;		/* so jpeg_destroy knows mem mgr not called */
+  if (version != JPEG_LIB_VERSION)
+    ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version);
+  if (structsize != SIZEOF(struct jpeg_compress_struct))
+    ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, 
+	     (int) SIZEOF(struct jpeg_compress_struct), (int) structsize);
+
+  /* For debugging purposes, we zero the whole master structure.
+   * But the application has already set the err pointer, and may have set
+   * client_data, so we have to save and restore those fields.
+   * Note: if application hasn't set client_data, tools like Purify may
+   * complain here.
+   */
+  {
+    struct jpeg_error_mgr * err = cinfo->err;
+    void * client_data = cinfo->client_data; /* ignore Purify complaint here */
+    MEMZERO(cinfo, SIZEOF(struct jpeg_compress_struct));
+    cinfo->err = err;
+    cinfo->client_data = client_data;
+  }
+  cinfo->is_decompressor = FALSE;
+
+  /* Initialize a memory manager instance for this object */
+  jinit_memory_mgr((j_common_ptr) cinfo);
+
+  /* Zero out pointers to permanent structures. */
+  cinfo->progress = NULL;
+  cinfo->dest = NULL;
+
+  cinfo->comp_info = NULL;
+
+  for (i = 0; i < NUM_QUANT_TBLS; i++)
+    cinfo->quant_tbl_ptrs[i] = NULL;
+
+  for (i = 0; i < NUM_HUFF_TBLS; i++) {
+    cinfo->dc_huff_tbl_ptrs[i] = NULL;
+    cinfo->ac_huff_tbl_ptrs[i] = NULL;
+  }
+
+  cinfo->script_space = NULL;
+
+  cinfo->input_gamma = 1.0;	/* in case application forgets */
+
+  /* OK, I'm ready */
+  cinfo->global_state = CSTATE_START;
+}
+
+
+/*
+ * Destruction of a JPEG compression object
+ */
+
+GLOBAL(void)
+jpeg_destroy_compress (j_compress_ptr cinfo)
+{
+  jpeg_destroy((j_common_ptr) cinfo); /* use common routine */
+}
+
+
+/*
+ * Abort processing of a JPEG compression operation,
+ * but don't destroy the object itself.
+ */
+
+GLOBAL(void)
+jpeg_abort_compress (j_compress_ptr cinfo)
+{
+  jpeg_abort((j_common_ptr) cinfo); /* use common routine */
+}
+
+
+/*
+ * Forcibly suppress or un-suppress all quantization and Huffman tables.
+ * Marks all currently defined tables as already written (if suppress)
+ * or not written (if !suppress).  This will control whether they get emitted
+ * by a subsequent jpeg_start_compress call.
+ *
+ * This routine is exported for use by applications that want to produce
+ * abbreviated JPEG datastreams.  It logically belongs in jcparam.c, but
+ * since it is called by jpeg_start_compress, we put it here --- otherwise
+ * jcparam.o would be linked whether the application used it or not.
+ */
+
+GLOBAL(void)
+jpeg_suppress_tables (j_compress_ptr cinfo, boolean suppress)
+{
+  int i;
+  JQUANT_TBL * qtbl;
+  JHUFF_TBL * htbl;
+
+  for (i = 0; i < NUM_QUANT_TBLS; i++) {
+    if ((qtbl = cinfo->quant_tbl_ptrs[i]) != NULL)
+      qtbl->sent_table = suppress;
+  }
+
+  for (i = 0; i < NUM_HUFF_TBLS; i++) {
+    if ((htbl = cinfo->dc_huff_tbl_ptrs[i]) != NULL)
+      htbl->sent_table = suppress;
+    if ((htbl = cinfo->ac_huff_tbl_ptrs[i]) != NULL)
+      htbl->sent_table = suppress;
+  }
+}
+
+
+/*
+ * Finish JPEG compression.
+ *
+ * If a multipass operating mode was selected, this may do a great deal of
+ * work including most of the actual output.
+ */
+
+GLOBAL(void)
+jpeg_finish_compress (j_compress_ptr cinfo)
+{
+  JDIMENSION iMCU_row;
+
+  if (cinfo->global_state == CSTATE_SCANNING ||
+      cinfo->global_state == CSTATE_RAW_OK) {
+    /* Terminate first pass */
+    if (cinfo->next_scanline < cinfo->image_height)
+      ERREXIT(cinfo, JERR_TOO_LITTLE_DATA);
+    (*cinfo->master->finish_pass) (cinfo);
+  } else if (cinfo->global_state != CSTATE_WRCOEFS)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+  /* Perform any remaining passes */
+  while (! cinfo->master->is_last_pass) {
+    (*cinfo->master->prepare_for_pass) (cinfo);
+    for (iMCU_row = 0; iMCU_row < cinfo->total_iMCU_rows; iMCU_row++) {
+      if (cinfo->progress != NULL) {
+	cinfo->progress->pass_counter = (long) iMCU_row;
+	cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows;
+	(*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
+      }
+      /* We bypass the main controller and invoke coef controller directly;
+       * all work is being done from the coefficient buffer.
+       */
+      if (! (*cinfo->coef->compress_data) (cinfo, (JSAMPIMAGE) NULL))
+	ERREXIT(cinfo, JERR_CANT_SUSPEND);
+    }
+    (*cinfo->master->finish_pass) (cinfo);
+  }
+  /* Write EOI, do final cleanup */
+  (*cinfo->marker->write_file_trailer) (cinfo);
+  (*cinfo->dest->term_destination) (cinfo);
+  /* We can use jpeg_abort to release memory and reset global_state */
+  jpeg_abort((j_common_ptr) cinfo);
+}
+
+
+/*
+ * Write a special marker.
+ * This is only recommended for writing COM or APPn markers.
+ * Must be called after jpeg_start_compress() and before
+ * first call to jpeg_write_scanlines() or jpeg_write_raw_data().
+ */
+
+GLOBAL(void)
+jpeg_write_marker (j_compress_ptr cinfo, int marker,
+		   const JOCTET *dataptr, unsigned int datalen)
+{
+  JMETHOD(void, write_marker_byte, (j_compress_ptr info, int val));
+
+  if (cinfo->next_scanline != 0 ||
+      (cinfo->global_state != CSTATE_SCANNING &&
+       cinfo->global_state != CSTATE_RAW_OK &&
+       cinfo->global_state != CSTATE_WRCOEFS))
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+
+  (*cinfo->marker->write_marker_header) (cinfo, marker, datalen);
+  write_marker_byte = cinfo->marker->write_marker_byte;	/* copy for speed */
+  while (datalen--) {
+    (*write_marker_byte) (cinfo, *dataptr);
+    dataptr++;
+  }
+}
+
+/* Same, but piecemeal. */
+
+GLOBAL(void)
+jpeg_write_m_header (j_compress_ptr cinfo, int marker, unsigned int datalen)
+{
+  if (cinfo->next_scanline != 0 ||
+      (cinfo->global_state != CSTATE_SCANNING &&
+       cinfo->global_state != CSTATE_RAW_OK &&
+       cinfo->global_state != CSTATE_WRCOEFS))
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+
+  (*cinfo->marker->write_marker_header) (cinfo, marker, datalen);
+}
+
+GLOBAL(void)
+jpeg_write_m_byte (j_compress_ptr cinfo, int val)
+{
+  (*cinfo->marker->write_marker_byte) (cinfo, val);
+}
+
+
+/*
+ * Alternate compression function: just write an abbreviated table file.
+ * Before calling this, all parameters and a data destination must be set up.
+ *
+ * To produce a pair of files containing abbreviated tables and abbreviated
+ * image data, one would proceed as follows:
+ *
+ *		initialize JPEG object
+ *		set JPEG parameters
+ *		set destination to table file
+ *		jpeg_write_tables(cinfo);
+ *		set destination to image file
+ *		jpeg_start_compress(cinfo, FALSE);
+ *		write data...
+ *		jpeg_finish_compress(cinfo);
+ *
+ * jpeg_write_tables has the side effect of marking all tables written
+ * (same as jpeg_suppress_tables(..., TRUE)).  Thus a subsequent start_compress
+ * will not re-emit the tables unless it is passed write_all_tables=TRUE.
+ */
+
+GLOBAL(void)
+jpeg_write_tables (j_compress_ptr cinfo)
+{
+  if (cinfo->global_state != CSTATE_START)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+
+  /* (Re)initialize error mgr and destination modules */
+  (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo);
+  (*cinfo->dest->init_destination) (cinfo);
+  /* Initialize the marker writer ... bit of a crock to do it here. */
+  jinit_marker_writer(cinfo);
+  /* Write them tables! */
+  (*cinfo->marker->write_tables_only) (cinfo);
+  /* And clean up. */
+  (*cinfo->dest->term_destination) (cinfo);
+  /*
+   * In library releases up through v6a, we called jpeg_abort() here to free
+   * any working memory allocated by the destination manager and marker
+   * writer.  Some applications had a problem with that: they allocated space
+   * of their own from the library memory manager, and didn't want it to go
+   * away during write_tables.  So now we do nothing.  This will cause a
+   * memory leak if an app calls write_tables repeatedly without doing a full
+   * compression cycle or otherwise resetting the JPEG object.  However, that
+   * seems less bad than unexpectedly freeing memory in the normal case.
+   * An app that prefers the old behavior can call jpeg_abort for itself after
+   * each call to jpeg_write_tables().
+   */
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jcapistd.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jcapistd.c
new file mode 100644
index 0000000000..c0320b1b19
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jcapistd.c
@@ -0,0 +1,161 @@
+/*
+ * jcapistd.c
+ *
+ * Copyright (C) 1994-1996, 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 application interface code for the compression half
+ * of the JPEG library.  These are the "standard" API routines that are
+ * used in the normal full-compression case.  They are not used by a
+ * transcoding-only application.  Note that if an application links in
+ * jpeg_start_compress, it will end up linking in the entire compressor.
+ * We thus must separate this file from jcapimin.c to avoid linking the
+ * whole compression library into a transcoder.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/*
+ * Compression initialization.
+ * Before calling this, all parameters and a data destination must be set up.
+ *
+ * We require a write_all_tables parameter as a failsafe check when writing
+ * multiple datastreams from the same compression object.  Since prior runs
+ * will have left all the tables marked sent_table=TRUE, a subsequent run
+ * would emit an abbreviated stream (no tables) by default.  This may be what
+ * is wanted, but for safety's sake it should not be the default behavior:
+ * programmers should have to make a deliberate choice to emit abbreviated
+ * images.  Therefore the documentation and examples should encourage people
+ * to pass write_all_tables=TRUE; then it will take active thought to do the
+ * wrong thing.
+ */
+
+GLOBAL(void)
+jpeg_start_compress (j_compress_ptr cinfo, boolean write_all_tables)
+{
+  if (cinfo->global_state != CSTATE_START)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+
+  if (write_all_tables)
+    jpeg_suppress_tables(cinfo, FALSE);	/* mark all tables to be written */
+
+  /* (Re)initialize error mgr and destination modules */
+  (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo);
+  (*cinfo->dest->init_destination) (cinfo);
+  /* Perform master selection of active modules */
+  jinit_compress_master(cinfo);
+  /* Set up for the first pass */
+  (*cinfo->master->prepare_for_pass) (cinfo);
+  /* Ready for application to drive first pass through jpeg_write_scanlines
+   * or jpeg_write_raw_data.
+   */
+  cinfo->next_scanline = 0;
+  cinfo->global_state = (cinfo->raw_data_in ? CSTATE_RAW_OK : CSTATE_SCANNING);
+}
+
+
+/*
+ * Write some scanlines of data to the JPEG compressor.
+ *
+ * The return value will be the number of lines actually written.
+ * This should be less than the supplied num_lines only in case that
+ * the data destination module has requested suspension of the compressor,
+ * or if more than image_height scanlines are passed in.
+ *
+ * Note: we warn about excess calls to jpeg_write_scanlines() since
+ * this likely signals an application programmer error.  However,
+ * excess scanlines passed in the last valid call are *silently* ignored,
+ * so that the application need not adjust num_lines for end-of-image
+ * when using a multiple-scanline buffer.
+ */
+
+GLOBAL(JDIMENSION)
+jpeg_write_scanlines (j_compress_ptr cinfo, JSAMPARRAY scanlines,
+		      JDIMENSION num_lines)
+{
+  JDIMENSION row_ctr, rows_left;
+
+  if (cinfo->global_state != CSTATE_SCANNING)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+  if (cinfo->next_scanline >= cinfo->image_height)
+    WARNMS(cinfo, JWRN_TOO_MUCH_DATA);
+
+  /* Call progress monitor hook if present */
+  if (cinfo->progress != NULL) {
+    cinfo->progress->pass_counter = (long) cinfo->next_scanline;
+    cinfo->progress->pass_limit = (long) cinfo->image_height;
+    (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
+  }
+
+  /* Give master control module another chance if this is first call to
+   * jpeg_write_scanlines.  This lets output of the frame/scan headers be
+   * delayed so that application can write COM, etc, markers between
+   * jpeg_start_compress and jpeg_write_scanlines.
+   */
+  if (cinfo->master->call_pass_startup)
+    (*cinfo->master->pass_startup) (cinfo);
+
+  /* Ignore any extra scanlines at bottom of image. */
+  rows_left = cinfo->image_height - cinfo->next_scanline;
+  if (num_lines > rows_left)
+    num_lines = rows_left;
+
+  row_ctr = 0;
+  (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, num_lines);
+  cinfo->next_scanline += row_ctr;
+  return row_ctr;
+}
+
+
+/*
+ * Alternate entry point to write raw data.
+ * Processes exactly one iMCU row per call, unless suspended.
+ */
+
+GLOBAL(JDIMENSION)
+jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE data,
+		     JDIMENSION num_lines)
+{
+  JDIMENSION lines_per_iMCU_row;
+
+  if (cinfo->global_state != CSTATE_RAW_OK)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+  if (cinfo->next_scanline >= cinfo->image_height) {
+    WARNMS(cinfo, JWRN_TOO_MUCH_DATA);
+    return 0;
+  }
+
+  /* Call progress monitor hook if present */
+  if (cinfo->progress != NULL) {
+    cinfo->progress->pass_counter = (long) cinfo->next_scanline;
+    cinfo->progress->pass_limit = (long) cinfo->image_height;
+    (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
+  }
+
+  /* Give master control module another chance if this is first call to
+   * jpeg_write_raw_data.  This lets output of the frame/scan headers be
+   * delayed so that application can write COM, etc, markers between
+   * jpeg_start_compress and jpeg_write_raw_data.
+   */
+  if (cinfo->master->call_pass_startup)
+    (*cinfo->master->pass_startup) (cinfo);
+
+  /* Verify that at least one iMCU row has been passed. */
+  lines_per_iMCU_row = cinfo->max_v_samp_factor * DCTSIZE;
+  if (num_lines < lines_per_iMCU_row)
+    ERREXIT(cinfo, JERR_BUFFER_SIZE);
+
+  /* Directly compress the row. */
+  if (! (*cinfo->coef->compress_data) (cinfo, data)) {
+    /* If compressor did not consume the whole row, suspend processing. */
+    return 0;
+  }
+
+  /* OK, we processed one iMCU row. */
+  cinfo->next_scanline += lines_per_iMCU_row;
+  return lines_per_iMCU_row;
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jccoefct.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jccoefct.c
new file mode 100644
index 0000000000..1963ddb61b
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jccoefct.c
@@ -0,0 +1,449 @@
+/*
+ * jccoefct.c
+ *
+ * Copyright (C) 1994-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.
+ *
+ * This file contains the coefficient buffer controller for compression.
+ * This controller is the top level of the JPEG compressor proper.
+ * The coefficient buffer lies between forward-DCT and entropy encoding steps.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/* We use a full-image coefficient buffer when doing Huffman optimization,
+ * and also for writing multiple-scan JPEG files.  In all cases, the DCT
+ * step is run during the first pass, and subsequent passes need only read
+ * the buffered coefficients.
+ */
+#ifdef ENTROPY_OPT_SUPPORTED
+#define FULL_COEF_BUFFER_SUPPORTED
+#else
+#ifdef C_MULTISCAN_FILES_SUPPORTED
+#define FULL_COEF_BUFFER_SUPPORTED
+#endif
+#endif
+
+
+/* Private buffer controller object */
+
+typedef struct {
+  struct jpeg_c_coef_controller pub; /* public fields */
+
+  JDIMENSION iMCU_row_num;	/* iMCU row # within image */
+  JDIMENSION mcu_ctr;		/* counts MCUs processed in current row */
+  int MCU_vert_offset;		/* counts MCU rows within iMCU row */
+  int MCU_rows_per_iMCU_row;	/* number of such rows needed */
+
+  /* For single-pass compression, it's sufficient to buffer just one MCU
+   * (although this may prove a bit slow in practice).  We allocate a
+   * workspace of C_MAX_BLOCKS_IN_MCU coefficient blocks, and reuse it for each
+   * MCU constructed and sent.  (On 80x86, the workspace is FAR even though
+   * it's not really very big; this is to keep the module interfaces unchanged
+   * when a large coefficient buffer is necessary.)
+   * In multi-pass modes, this array points to the current MCU's blocks
+   * within the virtual arrays.
+   */
+  JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU];
+
+  /* In multi-pass modes, we need a virtual block array for each component. */
+  jvirt_barray_ptr whole_image[MAX_COMPONENTS];
+} my_coef_controller;
+
+typedef my_coef_controller * my_coef_ptr;
+
+
+/* Forward declarations */
+METHODDEF(boolean) compress_data
+    JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf));
+#ifdef FULL_COEF_BUFFER_SUPPORTED
+METHODDEF(boolean) compress_first_pass
+    JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf));
+METHODDEF(boolean) compress_output
+    JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf));
+#endif
+
+
+LOCAL(void)
+start_iMCU_row (j_compress_ptr cinfo)
+/* Reset within-iMCU-row counters for a new row */
+{
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+
+  /* In an interleaved scan, an MCU row is the same as an iMCU row.
+   * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows.
+   * But at the bottom of the image, process only what's left.
+   */
+  if (cinfo->comps_in_scan > 1) {
+    coef->MCU_rows_per_iMCU_row = 1;
+  } else {
+    if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1))
+      coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor;
+    else
+      coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height;
+  }
+
+  coef->mcu_ctr = 0;
+  coef->MCU_vert_offset = 0;
+}
+
+
+/*
+ * Initialize for a processing pass.
+ */
+
+METHODDEF(void)
+start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode)
+{
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+
+  coef->iMCU_row_num = 0;
+  start_iMCU_row(cinfo);
+
+  switch (pass_mode) {
+  case JBUF_PASS_THRU:
+    if (coef->whole_image[0] != NULL)
+      ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+    coef->pub.compress_data = compress_data;
+    break;
+#ifdef FULL_COEF_BUFFER_SUPPORTED
+  case JBUF_SAVE_AND_PASS:
+    if (coef->whole_image[0] == NULL)
+      ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+    coef->pub.compress_data = compress_first_pass;
+    break;
+  case JBUF_CRANK_DEST:
+    if (coef->whole_image[0] == NULL)
+      ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+    coef->pub.compress_data = compress_output;
+    break;
+#endif
+  default:
+    ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+    break;
+  }
+}
+
+
+/*
+ * Process some data in the single-pass case.
+ * We process the equivalent of one fully interleaved MCU row ("iMCU" row)
+ * per call, ie, v_samp_factor block rows for each component in the image.
+ * Returns TRUE if the iMCU row is completed, FALSE if suspended.
+ *
+ * NB: input_buf contains a plane for each component in image,
+ * which we index according to the component's SOF position.
+ */
+
+METHODDEF(boolean)
+compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
+{
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+  JDIMENSION MCU_col_num;	/* index of current MCU within row */
+  JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1;
+  JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1;
+  int blkn, bi, ci, yindex, yoffset, blockcnt;
+  JDIMENSION ypos, xpos;
+  jpeg_component_info *compptr;
+
+  /* Loop to write as much as one whole iMCU row */
+  for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row;
+       yoffset++) {
+    for (MCU_col_num = coef->mcu_ctr; MCU_col_num <= last_MCU_col;
+	 MCU_col_num++) {
+      /* Determine where data comes from in input_buf and do the DCT thing.
+       * Each call on forward_DCT processes a horizontal row of DCT blocks
+       * as wide as an MCU; we rely on having allocated the MCU_buffer[] blocks
+       * sequentially.  Dummy blocks at the right or bottom edge are filled in
+       * specially.  The data in them does not matter for image reconstruction,
+       * so we fill them with values that will encode to the smallest amount of
+       * data, viz: all zeroes in the AC entries, DC entries equal to previous
+       * block's DC value.  (Thanks to Thomas Kinsman for this idea.)
+       */
+      blkn = 0;
+      for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+	compptr = cinfo->cur_comp_info[ci];
+	blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width
+						: compptr->last_col_width;
+	xpos = MCU_col_num * compptr->MCU_sample_width;
+	ypos = yoffset * DCTSIZE; /* ypos == (yoffset+yindex) * DCTSIZE */
+	for (yindex = 0; yindex < compptr->MCU_height; yindex++) {
+	  if (coef->iMCU_row_num < last_iMCU_row ||
+	      yoffset+yindex < compptr->last_row_height) {
+	    (*cinfo->fdct->forward_DCT) (cinfo, compptr,
+					 input_buf[compptr->component_index],
+					 coef->MCU_buffer[blkn],
+					 ypos, xpos, (JDIMENSION) blockcnt);
+	    if (blockcnt < compptr->MCU_width) {
+	      /* Create some dummy blocks at the right edge of the image. */
+	      jzero_far((void FAR *) coef->MCU_buffer[blkn + blockcnt],
+			(compptr->MCU_width - blockcnt) * SIZEOF(JBLOCK));
+	      for (bi = blockcnt; bi < compptr->MCU_width; bi++) {
+		coef->MCU_buffer[blkn+bi][0][0] = coef->MCU_buffer[blkn+bi-1][0][0];
+	      }
+	    }
+	  } else {
+	    /* Create a row of dummy blocks at the bottom of the image. */
+	    jzero_far((void FAR *) coef->MCU_buffer[blkn],
+		      compptr->MCU_width * SIZEOF(JBLOCK));
+	    for (bi = 0; bi < compptr->MCU_width; bi++) {
+	      coef->MCU_buffer[blkn+bi][0][0] = coef->MCU_buffer[blkn-1][0][0];
+	    }
+	  }
+	  blkn += compptr->MCU_width;
+	  ypos += DCTSIZE;
+	}
+      }
+      /* Try to write the MCU.  In event of a suspension failure, we will
+       * re-DCT the MCU on restart (a bit inefficient, could be fixed...)
+       */
+      if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) {
+	/* Suspension forced; update state counters and exit */
+	coef->MCU_vert_offset = yoffset;
+	coef->mcu_ctr = MCU_col_num;
+	return FALSE;
+      }
+    }
+    /* Completed an MCU row, but perhaps not an iMCU row */
+    coef->mcu_ctr = 0;
+  }
+  /* Completed the iMCU row, advance counters for next one */
+  coef->iMCU_row_num++;
+  start_iMCU_row(cinfo);
+  return TRUE;
+}
+
+
+#ifdef FULL_COEF_BUFFER_SUPPORTED
+
+/*
+ * Process some data in the first pass of a multi-pass case.
+ * We process the equivalent of one fully interleaved MCU row ("iMCU" row)
+ * per call, ie, v_samp_factor block rows for each component in the image.
+ * This amount of data is read from the source buffer, DCT'd and quantized,
+ * and saved into the virtual arrays.  We also generate suitable dummy blocks
+ * as needed at the right and lower edges.  (The dummy blocks are constructed
+ * in the virtual arrays, which have been padded appropriately.)  This makes
+ * it possible for subsequent passes not to worry about real vs. dummy blocks.
+ *
+ * We must also emit the data to the entropy encoder.  This is conveniently
+ * done by calling compress_output() after we've loaded the current strip
+ * of the virtual arrays.
+ *
+ * NB: input_buf contains a plane for each component in image.  All
+ * components are DCT'd and loaded into the virtual arrays in this pass.
+ * However, it may be that only a subset of the components are emitted to
+ * the entropy encoder during this first pass; be careful about looking
+ * at the scan-dependent variables (MCU dimensions, etc).
+ */
+
+METHODDEF(boolean)
+compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
+{
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+  JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1;
+  JDIMENSION blocks_across, MCUs_across, MCUindex;
+  int bi, ci, h_samp_factor, block_row, block_rows, ndummy;
+  JCOEF lastDC;
+  jpeg_component_info *compptr;
+  JBLOCKARRAY buffer;
+  JBLOCKROW thisblockrow, lastblockrow;
+
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    /* Align the virtual buffer for this component. */
+    buffer = (*cinfo->mem->access_virt_barray)
+      ((j_common_ptr) cinfo, coef->whole_image[ci],
+       coef->iMCU_row_num * compptr->v_samp_factor,
+       (JDIMENSION) compptr->v_samp_factor, TRUE);
+    /* Count non-dummy DCT block rows in this iMCU row. */
+    if (coef->iMCU_row_num < last_iMCU_row)
+      block_rows = compptr->v_samp_factor;
+    else {
+      /* NB: can't use last_row_height here, since may not be set! */
+      block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor);
+      if (block_rows == 0) block_rows = compptr->v_samp_factor;
+    }
+    blocks_across = compptr->width_in_blocks;
+    h_samp_factor = compptr->h_samp_factor;
+    /* Count number of dummy blocks to be added at the right margin. */
+    ndummy = (int) (blocks_across % h_samp_factor);
+    if (ndummy > 0)
+      ndummy = h_samp_factor - ndummy;
+    /* Perform DCT for all non-dummy blocks in this iMCU row.  Each call
+     * on forward_DCT processes a complete horizontal row of DCT blocks.
+     */
+    for (block_row = 0; block_row < block_rows; block_row++) {
+      thisblockrow = buffer[block_row];
+      (*cinfo->fdct->forward_DCT) (cinfo, compptr,
+				   input_buf[ci], thisblockrow,
+				   (JDIMENSION) (block_row * DCTSIZE),
+				   (JDIMENSION) 0, blocks_across);
+      if (ndummy > 0) {
+	/* Create dummy blocks at the right edge of the image. */
+	thisblockrow += blocks_across; /* => first dummy block */
+	jzero_far((void FAR *) thisblockrow, ndummy * SIZEOF(JBLOCK));
+	lastDC = thisblockrow[-1][0];
+	for (bi = 0; bi < ndummy; bi++) {
+	  thisblockrow[bi][0] = lastDC;
+	}
+      }
+    }
+    /* If at end of image, create dummy block rows as needed.
+     * The tricky part here is that within each MCU, we want the DC values
+     * of the dummy blocks to match the last real block's DC value.
+     * This squeezes a few more bytes out of the resulting file...
+     */
+    if (coef->iMCU_row_num == last_iMCU_row) {
+      blocks_across += ndummy;	/* include lower right corner */
+      MCUs_across = blocks_across / h_samp_factor;
+      for (block_row = block_rows; block_row < compptr->v_samp_factor;
+	   block_row++) {
+	thisblockrow = buffer[block_row];
+	lastblockrow = buffer[block_row-1];
+	jzero_far((void FAR *) thisblockrow,
+		  (size_t) (blocks_across * SIZEOF(JBLOCK)));
+	for (MCUindex = 0; MCUindex < MCUs_across; MCUindex++) {
+	  lastDC = lastblockrow[h_samp_factor-1][0];
+	  for (bi = 0; bi < h_samp_factor; bi++) {
+	    thisblockrow[bi][0] = lastDC;
+	  }
+	  thisblockrow += h_samp_factor; /* advance to next MCU in row */
+	  lastblockrow += h_samp_factor;
+	}
+      }
+    }
+  }
+  /* NB: compress_output will increment iMCU_row_num if successful.
+   * A suspension return will result in redoing all the work above next time.
+   */
+
+  /* Emit data to the entropy encoder, sharing code with subsequent passes */
+  return compress_output(cinfo, input_buf);
+}
+
+
+/*
+ * Process some data in subsequent passes of a multi-pass case.
+ * We process the equivalent of one fully interleaved MCU row ("iMCU" row)
+ * per call, ie, v_samp_factor block rows for each component in the scan.
+ * The data is obtained from the virtual arrays and fed to the entropy coder.
+ * Returns TRUE if the iMCU row is completed, FALSE if suspended.
+ *
+ * NB: input_buf is ignored; it is likely to be a NULL pointer.
+ */
+
+METHODDEF(boolean)
+compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
+{
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+  JDIMENSION MCU_col_num;	/* index of current MCU within row */
+  int blkn, ci, xindex, yindex, yoffset;
+  JDIMENSION start_col;
+  JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN];
+  JBLOCKROW buffer_ptr;
+  jpeg_component_info *compptr;
+
+  /* Align the virtual buffers for the components used in this scan.
+   * NB: during first pass, this is safe only because the buffers will
+   * already be aligned properly, so jmemmgr.c won't need to do any I/O.
+   */
+  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+    compptr = cinfo->cur_comp_info[ci];
+    buffer[ci] = (*cinfo->mem->access_virt_barray)
+      ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index],
+       coef->iMCU_row_num * compptr->v_samp_factor,
+       (JDIMENSION) compptr->v_samp_factor, FALSE);
+  }
+
+  /* Loop to process one whole iMCU row */
+  for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row;
+       yoffset++) {
+    for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row;
+	 MCU_col_num++) {
+      /* Construct list of pointers to DCT blocks belonging to this MCU */
+      blkn = 0;			/* index of current DCT block within MCU */
+      for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+	compptr = cinfo->cur_comp_info[ci];
+	start_col = MCU_col_num * compptr->MCU_width;
+	for (yindex = 0; yindex < compptr->MCU_height; yindex++) {
+	  buffer_ptr = buffer[ci][yindex+yoffset] + start_col;
+	  for (xindex = 0; xindex < compptr->MCU_width; xindex++) {
+	    coef->MCU_buffer[blkn++] = buffer_ptr++;
+	  }
+	}
+      }
+      /* Try to write the MCU. */
+      if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) {
+	/* Suspension forced; update state counters and exit */
+	coef->MCU_vert_offset = yoffset;
+	coef->mcu_ctr = MCU_col_num;
+	return FALSE;
+      }
+    }
+    /* Completed an MCU row, but perhaps not an iMCU row */
+    coef->mcu_ctr = 0;
+  }
+  /* Completed the iMCU row, advance counters for next one */
+  coef->iMCU_row_num++;
+  start_iMCU_row(cinfo);
+  return TRUE;
+}
+
+#endif /* FULL_COEF_BUFFER_SUPPORTED */
+
+
+/*
+ * Initialize coefficient buffer controller.
+ */
+
+GLOBAL(void)
+jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer)
+{
+  my_coef_ptr coef;
+
+  coef = (my_coef_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_coef_controller));
+  cinfo->coef = (struct jpeg_c_coef_controller *) coef;
+  coef->pub.start_pass = start_pass_coef;
+
+  /* Create the coefficient buffer. */
+  if (need_full_buffer) {
+#ifdef FULL_COEF_BUFFER_SUPPORTED
+    /* Allocate a full-image virtual array for each component, */
+    /* padded to a multiple of samp_factor DCT blocks in each direction. */
+    int ci;
+    jpeg_component_info *compptr;
+
+    for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+	 ci++, compptr++) {
+      coef->whole_image[ci] = (*cinfo->mem->request_virt_barray)
+	((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
+	 (JDIMENSION) jround_up((long) compptr->width_in_blocks,
+				(long) compptr->h_samp_factor),
+	 (JDIMENSION) jround_up((long) compptr->height_in_blocks,
+				(long) compptr->v_samp_factor),
+	 (JDIMENSION) compptr->v_samp_factor);
+    }
+#else
+    ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+#endif
+  } else {
+    /* We only need a single-MCU buffer. */
+    JBLOCKROW buffer;
+    int i;
+
+    buffer = (JBLOCKROW)
+      (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				  C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK));
+    for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) {
+      coef->MCU_buffer[i] = buffer + i;
+    }
+    coef->whole_image[0] = NULL; /* flag for no virtual arrays */
+  }
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jccolor.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jccolor.c
new file mode 100644
index 0000000000..0a8a4b5d13
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jccolor.c
@@ -0,0 +1,459 @@
+/*
+ * jccolor.c
+ *
+ * Copyright (C) 1991-1996, 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 input colorspace conversion routines.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/* Private subobject */
+
+typedef struct {
+  struct jpeg_color_converter pub; /* public fields */
+
+  /* Private state for RGB->YCC conversion */
+  INT32 * rgb_ycc_tab;		/* => table for RGB to YCbCr conversion */
+} my_color_converter;
+
+typedef my_color_converter * my_cconvert_ptr;
+
+
+/**************** RGB -> YCbCr conversion: most common case **************/
+
+/*
+ * YCbCr is defined per CCIR 601-1, except that Cb and Cr are
+ * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5.
+ * 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  + CENTERJSAMPLE
+ *	Cr =  0.50000 * R - 0.41869 * G - 0.08131 * B  + CENTERJSAMPLE
+ * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.)
+ * Note: older versions of the IJG code used a zero offset of MAXJSAMPLE/2,
+ * rather than CENTERJSAMPLE, for Cb and Cr.  This gave equal positive and
+ * negative swings for Cb/Cr, but meant that grayscale values (Cb=Cr=0)
+ * were not represented exactly.  Now we sacrifice exact representation of
+ * maximum red and maximum blue in order to get exact grayscales.
+ *
+ * To avoid floating-point arithmetic, we represent the fractional constants
+ * as integers scaled up by 2^16 (about 4 digits precision); we have to divide
+ * the products by 2^16, with appropriate rounding, to get the correct answer.
+ *
+ * For even more speed, we avoid doing any multiplications in the inner loop
+ * by precalculating the constants times R,G,B for all possible values.
+ * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table);
+ * for 12-bit samples it is still acceptable.  It's not very reasonable for
+ * 16-bit samples, but if you want lossless storage you shouldn't be changing
+ * colorspace anyway.
+ * The CENTERJSAMPLE offsets and the rounding fudge-factor of 0.5 are included
+ * in the tables to save adding them separately in the inner loop.
+ */
+
+#define SCALEBITS	16	/* speediest right-shift on some machines */
+#define CBCR_OFFSET	((INT32) CENTERJSAMPLE << SCALEBITS)
+#define ONE_HALF	((INT32) 1 << (SCALEBITS-1))
+#define FIX(x)		((INT32) ((x) * (1L<<SCALEBITS) + 0.5))
+
+/* We allocate one big table and divide it up into eight parts, instead of
+ * doing eight alloc_small requests.  This lets us use a single table base
+ * address, which can be held in a register in the inner loops on many
+ * machines (more than can hold all eight addresses, anyway).
+ */
+
+#define R_Y_OFF		0			/* offset to R => Y section */
+#define G_Y_OFF		(1*(MAXJSAMPLE+1))	/* offset to G => Y section */
+#define B_Y_OFF		(2*(MAXJSAMPLE+1))	/* etc. */
+#define R_CB_OFF	(3*(MAXJSAMPLE+1))
+#define G_CB_OFF	(4*(MAXJSAMPLE+1))
+#define B_CB_OFF	(5*(MAXJSAMPLE+1))
+#define R_CR_OFF	B_CB_OFF		/* B=>Cb, R=>Cr are the same */
+#define G_CR_OFF	(6*(MAXJSAMPLE+1))
+#define B_CR_OFF	(7*(MAXJSAMPLE+1))
+#define TABLE_SIZE	(8*(MAXJSAMPLE+1))
+
+
+/*
+ * Initialize for RGB->YCC colorspace conversion.
+ */
+
+METHODDEF(void)
+rgb_ycc_start (j_compress_ptr cinfo)
+{
+  my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
+  INT32 * rgb_ycc_tab;
+  INT32 i;
+
+  /* Allocate and fill in the conversion tables. */
+  cconvert->rgb_ycc_tab = rgb_ycc_tab = (INT32 *)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				(TABLE_SIZE * SIZEOF(INT32)));
+
+  for (i = 0; i <= MAXJSAMPLE; i++) {
+    rgb_ycc_tab[i+R_Y_OFF] = FIX(0.29900) * i;
+    rgb_ycc_tab[i+G_Y_OFF] = FIX(0.58700) * i;
+    rgb_ycc_tab[i+B_Y_OFF] = FIX(0.11400) * i     + ONE_HALF;
+    rgb_ycc_tab[i+R_CB_OFF] = (-FIX(0.16874)) * i;
+    rgb_ycc_tab[i+G_CB_OFF] = (-FIX(0.33126)) * i;
+    /* We use a rounding fudge-factor of 0.5-epsilon for Cb and Cr.
+     * This ensures that the maximum output will round to MAXJSAMPLE
+     * not MAXJSAMPLE+1, and thus that we don't have to range-limit.
+     */
+    rgb_ycc_tab[i+B_CB_OFF] = FIX(0.50000) * i    + CBCR_OFFSET + ONE_HALF-1;
+/*  B=>Cb and R=>Cr tables are the same
+    rgb_ycc_tab[i+R_CR_OFF] = FIX(0.50000) * i    + CBCR_OFFSET + ONE_HALF-1;
+*/
+    rgb_ycc_tab[i+G_CR_OFF] = (-FIX(0.41869)) * i;
+    rgb_ycc_tab[i+B_CR_OFF] = (-FIX(0.08131)) * i;
+  }
+}
+
+
+/*
+ * Convert some rows of samples to the JPEG colorspace.
+ *
+ * Note that we change from the application's interleaved-pixel format
+ * to our internal noninterleaved, one-plane-per-component format.
+ * The input buffer is therefore three times as wide as the output buffer.
+ *
+ * A starting row offset is provided only for the output buffer.  The caller
+ * can easily adjust the passed input_buf value to accommodate any row
+ * offset required on that side.
+ */
+
+METHODDEF(void)
+rgb_ycc_convert (j_compress_ptr cinfo,
+		 JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+		 JDIMENSION output_row, int num_rows)
+{
+  my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
+  register int r, g, b;
+  register INT32 * ctab = cconvert->rgb_ycc_tab;
+  register JSAMPROW inptr;
+  register JSAMPROW outptr0, outptr1, outptr2;
+  register JDIMENSION col;
+  JDIMENSION num_cols = cinfo->image_width;
+
+  while (--num_rows >= 0) {
+    inptr = *input_buf++;
+    outptr0 = output_buf[0][output_row];
+    outptr1 = output_buf[1][output_row];
+    outptr2 = output_buf[2][output_row];
+    output_row++;
+    for (col = 0; col < num_cols; col++) {
+      r = GETJSAMPLE(inptr[RGB_RED]);
+      g = GETJSAMPLE(inptr[RGB_GREEN]);
+      b = GETJSAMPLE(inptr[RGB_BLUE]);
+      inptr += RGB_PIXELSIZE;
+      /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations
+       * must be too; we do not need an explicit range-limiting operation.
+       * Hence the value being shifted is never negative, and we don't
+       * need the general RIGHT_SHIFT macro.
+       */
+      /* Y */
+      outptr0[col] = (JSAMPLE)
+		((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF])
+		 >> SCALEBITS);
+      /* Cb */
+      outptr1[col] = (JSAMPLE)
+		((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF])
+		 >> SCALEBITS);
+      /* Cr */
+      outptr2[col] = (JSAMPLE)
+		((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF])
+		 >> SCALEBITS);
+    }
+  }
+}
+
+
+/**************** Cases other than RGB -> YCbCr **************/
+
+
+/*
+ * Convert some rows of samples to the JPEG colorspace.
+ * This version handles RGB->grayscale conversion, which is the same
+ * as the RGB->Y portion of RGB->YCbCr.
+ * We assume rgb_ycc_start has been called (we only use the Y tables).
+ */
+
+METHODDEF(void)
+rgb_gray_convert (j_compress_ptr cinfo,
+		  JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+		  JDIMENSION output_row, int num_rows)
+{
+  my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
+  register int r, g, b;
+  register INT32 * ctab = cconvert->rgb_ycc_tab;
+  register JSAMPROW inptr;
+  register JSAMPROW outptr;
+  register JDIMENSION col;
+  JDIMENSION num_cols = cinfo->image_width;
+
+  while (--num_rows >= 0) {
+    inptr = *input_buf++;
+    outptr = output_buf[0][output_row];
+    output_row++;
+    for (col = 0; col < num_cols; col++) {
+      r = GETJSAMPLE(inptr[RGB_RED]);
+      g = GETJSAMPLE(inptr[RGB_GREEN]);
+      b = GETJSAMPLE(inptr[RGB_BLUE]);
+      inptr += RGB_PIXELSIZE;
+      /* Y */
+      outptr[col] = (JSAMPLE)
+		((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF])
+		 >> SCALEBITS);
+    }
+  }
+}
+
+
+/*
+ * Convert some rows of samples to the JPEG colorspace.
+ * This version handles Adobe-style CMYK->YCCK conversion,
+ * where we convert R=1-C, G=1-M, and B=1-Y to YCbCr using the same
+ * conversion as above, while passing K (black) unchanged.
+ * We assume rgb_ycc_start has been called.
+ */
+
+METHODDEF(void)
+cmyk_ycck_convert (j_compress_ptr cinfo,
+		   JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+		   JDIMENSION output_row, int num_rows)
+{
+  my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
+  register int r, g, b;
+  register INT32 * ctab = cconvert->rgb_ycc_tab;
+  register JSAMPROW inptr;
+  register JSAMPROW outptr0, outptr1, outptr2, outptr3;
+  register JDIMENSION col;
+  JDIMENSION num_cols = cinfo->image_width;
+
+  while (--num_rows >= 0) {
+    inptr = *input_buf++;
+    outptr0 = output_buf[0][output_row];
+    outptr1 = output_buf[1][output_row];
+    outptr2 = output_buf[2][output_row];
+    outptr3 = output_buf[3][output_row];
+    output_row++;
+    for (col = 0; col < num_cols; col++) {
+      r = MAXJSAMPLE - GETJSAMPLE(inptr[0]);
+      g = MAXJSAMPLE - GETJSAMPLE(inptr[1]);
+      b = MAXJSAMPLE - GETJSAMPLE(inptr[2]);
+      /* K passes through as-is */
+      outptr3[col] = inptr[3];	/* don't need GETJSAMPLE here */
+      inptr += 4;
+      /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations
+       * must be too; we do not need an explicit range-limiting operation.
+       * Hence the value being shifted is never negative, and we don't
+       * need the general RIGHT_SHIFT macro.
+       */
+      /* Y */
+      outptr0[col] = (JSAMPLE)
+		((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF])
+		 >> SCALEBITS);
+      /* Cb */
+      outptr1[col] = (JSAMPLE)
+		((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF])
+		 >> SCALEBITS);
+      /* Cr */
+      outptr2[col] = (JSAMPLE)
+		((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF])
+		 >> SCALEBITS);
+    }
+  }
+}
+
+
+/*
+ * Convert some rows of samples to the JPEG colorspace.
+ * This version handles grayscale output with no conversion.
+ * The source can be either plain grayscale or YCbCr (since Y == gray).
+ */
+
+METHODDEF(void)
+grayscale_convert (j_compress_ptr cinfo,
+		   JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+		   JDIMENSION output_row, int num_rows)
+{
+  register JSAMPROW inptr;
+  register JSAMPROW outptr;
+  register JDIMENSION col;
+  JDIMENSION num_cols = cinfo->image_width;
+  int instride = cinfo->input_components;
+
+  while (--num_rows >= 0) {
+    inptr = *input_buf++;
+    outptr = output_buf[0][output_row];
+    output_row++;
+    for (col = 0; col < num_cols; col++) {
+      outptr[col] = inptr[0];	/* don't need GETJSAMPLE() here */
+      inptr += instride;
+    }
+  }
+}
+
+
+/*
+ * Convert some rows of samples to the JPEG colorspace.
+ * This version handles multi-component colorspaces without conversion.
+ * We assume input_components == num_components.
+ */
+
+METHODDEF(void)
+null_convert (j_compress_ptr cinfo,
+	      JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+	      JDIMENSION output_row, int num_rows)
+{
+  register JSAMPROW inptr;
+  register JSAMPROW outptr;
+  register JDIMENSION col;
+  register int ci;
+  int nc = cinfo->num_components;
+  JDIMENSION num_cols = cinfo->image_width;
+
+  while (--num_rows >= 0) {
+    /* It seems fastest to make a separate pass for each component. */
+    for (ci = 0; ci < nc; ci++) {
+      inptr = *input_buf;
+      outptr = output_buf[ci][output_row];
+      for (col = 0; col < num_cols; col++) {
+	outptr[col] = inptr[ci]; /* don't need GETJSAMPLE() here */
+	inptr += nc;
+      }
+    }
+    input_buf++;
+    output_row++;
+  }
+}
+
+
+/*
+ * Empty method for start_pass.
+ */
+
+METHODDEF(void)
+null_method (j_compress_ptr cinfo)
+{
+  /* no work needed */
+}
+
+
+/*
+ * Module initialization routine for input colorspace conversion.
+ */
+
+GLOBAL(void)
+jinit_color_converter (j_compress_ptr cinfo)
+{
+  my_cconvert_ptr cconvert;
+
+  cconvert = (my_cconvert_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_color_converter));
+  cinfo->cconvert = (struct jpeg_color_converter *) cconvert;
+  /* set start_pass to null method until we find out differently */
+  cconvert->pub.start_pass = null_method;
+
+  /* Make sure input_components agrees with in_color_space */
+  switch (cinfo->in_color_space) {
+  case JCS_GRAYSCALE:
+    if (cinfo->input_components != 1)
+      ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
+    break;
+
+  case JCS_RGB:
+#if RGB_PIXELSIZE != 3
+    if (cinfo->input_components != RGB_PIXELSIZE)
+      ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
+    break;
+#endif /* else share code with YCbCr */
+
+  case JCS_YCbCr:
+    if (cinfo->input_components != 3)
+      ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
+    break;
+
+  case JCS_CMYK:
+  case JCS_YCCK:
+    if (cinfo->input_components != 4)
+      ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
+    break;
+
+  default:			/* JCS_UNKNOWN can be anything */
+    if (cinfo->input_components < 1)
+      ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
+    break;
+  }
+
+  /* Check num_components, set conversion method based on requested space */
+  switch (cinfo->jpeg_color_space) {
+  case JCS_GRAYSCALE:
+    if (cinfo->num_components != 1)
+      ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+    if (cinfo->in_color_space == JCS_GRAYSCALE)
+      cconvert->pub.color_convert = grayscale_convert;
+    else if (cinfo->in_color_space == JCS_RGB) {
+      cconvert->pub.start_pass = rgb_ycc_start;
+      cconvert->pub.color_convert = rgb_gray_convert;
+    } else if (cinfo->in_color_space == JCS_YCbCr)
+      cconvert->pub.color_convert = grayscale_convert;
+    else
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+    break;
+
+  case JCS_RGB:
+    if (cinfo->num_components != 3)
+      ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+    if (cinfo->in_color_space == JCS_RGB && RGB_PIXELSIZE == 3)
+      cconvert->pub.color_convert = null_convert;
+    else
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+    break;
+
+  case JCS_YCbCr:
+    if (cinfo->num_components != 3)
+      ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+    if (cinfo->in_color_space == JCS_RGB) {
+      cconvert->pub.start_pass = rgb_ycc_start;
+      cconvert->pub.color_convert = rgb_ycc_convert;
+    } else if (cinfo->in_color_space == JCS_YCbCr)
+      cconvert->pub.color_convert = null_convert;
+    else
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+    break;
+
+  case JCS_CMYK:
+    if (cinfo->num_components != 4)
+      ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+    if (cinfo->in_color_space == JCS_CMYK)
+      cconvert->pub.color_convert = null_convert;
+    else
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+    break;
+
+  case JCS_YCCK:
+    if (cinfo->num_components != 4)
+      ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+    if (cinfo->in_color_space == JCS_CMYK) {
+      cconvert->pub.start_pass = rgb_ycc_start;
+      cconvert->pub.color_convert = cmyk_ycck_convert;
+    } else if (cinfo->in_color_space == JCS_YCCK)
+      cconvert->pub.color_convert = null_convert;
+    else
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+    break;
+
+  default:			/* allow null conversion of JCS_UNKNOWN */
+    if (cinfo->jpeg_color_space != cinfo->in_color_space ||
+	cinfo->num_components != cinfo->input_components)
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+    cconvert->pub.color_convert = null_convert;
+    break;
+  }
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jcdctmgr.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jcdctmgr.c
new file mode 100644
index 0000000000..61fa79b9e6
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jcdctmgr.c
@@ -0,0 +1,387 @@
+/*
+ * jcdctmgr.c
+ *
+ * Copyright (C) 1994-1996, 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 forward-DCT management logic.
+ * This code selects a particular DCT implementation to be used,
+ * and it performs related housekeeping chores including coefficient
+ * quantization.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jdct.h"		/* Private declarations for DCT subsystem */
+
+
+/* Private subobject for this module */
+
+typedef struct {
+  struct jpeg_forward_dct pub;	/* public fields */
+
+  /* Pointer to the DCT routine actually in use */
+  forward_DCT_method_ptr do_dct;
+
+  /* The actual post-DCT divisors --- not identical to the quant table
+   * entries, because of scaling (especially for an unnormalized DCT).
+   * Each table is given in normal array order.
+   */
+  DCTELEM * divisors[NUM_QUANT_TBLS];
+
+#ifdef DCT_FLOAT_SUPPORTED
+  /* Same as above for the floating-point case. */
+  float_DCT_method_ptr do_float_dct;
+  FAST_FLOAT * float_divisors[NUM_QUANT_TBLS];
+#endif
+} my_fdct_controller;
+
+typedef my_fdct_controller * my_fdct_ptr;
+
+
+/*
+ * Initialize for a processing pass.
+ * Verify that all referenced Q-tables are present, and set up
+ * the divisor table for each one.
+ * In the current implementation, DCT of all components is done during
+ * the first pass, even if only some components will be output in the
+ * first scan.  Hence all components should be examined here.
+ */
+
+METHODDEF(void)
+start_pass_fdctmgr (j_compress_ptr cinfo)
+{
+  my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct;
+  int ci, qtblno, i;
+  jpeg_component_info *compptr;
+  JQUANT_TBL * qtbl;
+  DCTELEM * dtbl;
+
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    qtblno = compptr->quant_tbl_no;
+    /* Make sure specified quantization table is present */
+    if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS ||
+	cinfo->quant_tbl_ptrs[qtblno] == NULL)
+      ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno);
+    qtbl = cinfo->quant_tbl_ptrs[qtblno];
+    /* Compute divisors for this quant table */
+    /* We may do this more than once for same table, but it's not a big deal */
+    switch (cinfo->dct_method) {
+#ifdef DCT_ISLOW_SUPPORTED
+    case JDCT_ISLOW:
+      /* For LL&M IDCT method, divisors are equal to raw quantization
+       * coefficients multiplied by 8 (to counteract scaling).
+       */
+      if (fdct->divisors[qtblno] == NULL) {
+	fdct->divisors[qtblno] = (DCTELEM *)
+	  (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				      DCTSIZE2 * SIZEOF(DCTELEM));
+      }
+      dtbl = fdct->divisors[qtblno];
+      for (i = 0; i < DCTSIZE2; i++) {
+	dtbl[i] = ((DCTELEM) qtbl->quantval[i]) << 3;
+      }
+      break;
+#endif
+#ifdef DCT_IFAST_SUPPORTED
+    case JDCT_IFAST:
+      {
+	/* For AA&N IDCT method, divisors are equal to quantization
+	 * coefficients scaled by scalefactor[row]*scalefactor[col], where
+	 *   scalefactor[0] = 1
+	 *   scalefactor[k] = cos(k*PI/16) * sqrt(2)    for k=1..7
+	 * We apply a further scale factor of 8.
+	 */
+#define CONST_BITS 14
+	static const INT16 aanscales[DCTSIZE2] = {
+	  /* precomputed values scaled up by 14 bits */
+	  16384, 22725, 21407, 19266, 16384, 12873,  8867,  4520,
+	  22725, 31521, 29692, 26722, 22725, 17855, 12299,  6270,
+	  21407, 29692, 27969, 25172, 21407, 16819, 11585,  5906,
+	  19266, 26722, 25172, 22654, 19266, 15137, 10426,  5315,
+	  16384, 22725, 21407, 19266, 16384, 12873,  8867,  4520,
+	  12873, 17855, 16819, 15137, 12873, 10114,  6967,  3552,
+	   8867, 12299, 11585, 10426,  8867,  6967,  4799,  2446,
+	   4520,  6270,  5906,  5315,  4520,  3552,  2446,  1247
+	};
+	SHIFT_TEMPS
+
+	if (fdct->divisors[qtblno] == NULL) {
+	  fdct->divisors[qtblno] = (DCTELEM *)
+	    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+					DCTSIZE2 * SIZEOF(DCTELEM));
+	}
+	dtbl = fdct->divisors[qtblno];
+	for (i = 0; i < DCTSIZE2; i++) {
+	  dtbl[i] = (DCTELEM)
+	    DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[i],
+				  (INT32) aanscales[i]),
+		    CONST_BITS-3);
+	}
+      }
+      break;
+#endif
+#ifdef DCT_FLOAT_SUPPORTED
+    case JDCT_FLOAT:
+      {
+	/* For float AA&N IDCT method, divisors are equal to quantization
+	 * coefficients scaled by scalefactor[row]*scalefactor[col], where
+	 *   scalefactor[0] = 1
+	 *   scalefactor[k] = cos(k*PI/16) * sqrt(2)    for k=1..7
+	 * We apply a further scale factor of 8.
+	 * What's actually stored is 1/divisor so that the inner loop can
+	 * use a multiplication rather than a division.
+	 */
+	FAST_FLOAT * fdtbl;
+	int row, col;
+	static const double aanscalefactor[DCTSIZE] = {
+	  1.0, 1.387039845, 1.306562965, 1.175875602,
+	  1.0, 0.785694958, 0.541196100, 0.275899379
+	};
+
+	if (fdct->float_divisors[qtblno] == NULL) {
+	  fdct->float_divisors[qtblno] = (FAST_FLOAT *)
+	    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+					DCTSIZE2 * SIZEOF(FAST_FLOAT));
+	}
+	fdtbl = fdct->float_divisors[qtblno];
+	i = 0;
+	for (row = 0; row < DCTSIZE; row++) {
+	  for (col = 0; col < DCTSIZE; col++) {
+	    fdtbl[i] = (FAST_FLOAT)
+	      (1.0 / (((double) qtbl->quantval[i] *
+		       aanscalefactor[row] * aanscalefactor[col] * 8.0)));
+	    i++;
+	  }
+	}
+      }
+      break;
+#endif
+    default:
+      ERREXIT(cinfo, JERR_NOT_COMPILED);
+      break;
+    }
+  }
+}
+
+
+/*
+ * Perform forward DCT on one or more blocks of a component.
+ *
+ * The input samples are taken from the sample_data[] array starting at
+ * position start_row/start_col, and moving to the right for any additional
+ * blocks. The quantized coefficients are returned in coef_blocks[].
+ */
+
+METHODDEF(void)
+forward_DCT (j_compress_ptr cinfo, jpeg_component_info * compptr,
+	     JSAMPARRAY sample_data, JBLOCKROW coef_blocks,
+	     JDIMENSION start_row, JDIMENSION start_col,
+	     JDIMENSION num_blocks)
+/* This version is used for integer DCT implementations. */
+{
+  /* This routine is heavily used, so it's worth coding it tightly. */
+  my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct;
+  forward_DCT_method_ptr do_dct = fdct->do_dct;
+  DCTELEM * divisors = fdct->divisors[compptr->quant_tbl_no];
+  DCTELEM workspace[DCTSIZE2];	/* work area for FDCT subroutine */
+  JDIMENSION bi;
+
+  sample_data += start_row;	/* fold in the vertical offset once */
+
+  for (bi = 0; bi < num_blocks; bi++, start_col += DCTSIZE) {
+    /* Load data into workspace, applying unsigned->signed conversion */
+    { register DCTELEM *workspaceptr;
+      register JSAMPROW elemptr;
+      register int elemr;
+
+      workspaceptr = workspace;
+      for (elemr = 0; elemr < DCTSIZE; elemr++) {
+	elemptr = sample_data[elemr] + start_col;
+#if DCTSIZE == 8		/* unroll the inner loop */
+	*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
+	*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
+	*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
+	*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
+	*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
+	*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
+	*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
+	*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
+#else
+	{ register int elemc;
+	  for (elemc = DCTSIZE; elemc > 0; elemc--) {
+	    *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
+	  }
+	}
+#endif
+      }
+    }
+
+    /* Perform the DCT */
+    (*do_dct) (workspace);
+
+    /* Quantize/descale the coefficients, and store into coef_blocks[] */
+    { register DCTELEM temp, qval;
+      register int i;
+      register JCOEFPTR output_ptr = coef_blocks[bi];
+
+      for (i = 0; i < DCTSIZE2; i++) {
+	qval = divisors[i];
+	temp = workspace[i];
+	/* Divide the coefficient value by qval, ensuring proper rounding.
+	 * Since C does not specify the direction of rounding for negative
+	 * quotients, we have to force the dividend positive for portability.
+	 *
+	 * In most files, at least half of the output values will be zero
+	 * (at default quantization settings, more like three-quarters...)
+	 * so we should ensure that this case is fast.  On many machines,
+	 * a comparison is enough cheaper than a divide to make a special test
+	 * a win.  Since both inputs will be nonnegative, we need only test
+	 * for a < b to discover whether a/b is 0.
+	 * If your machine's division is fast enough, define FAST_DIVIDE.
+	 */
+#ifdef FAST_DIVIDE
+#define DIVIDE_BY(a,b)	a /= b
+#else
+#define DIVIDE_BY(a,b)	if (a >= b) a /= b; else a = 0
+#endif
+	if (temp < 0) {
+	  temp = -temp;
+	  temp += qval>>1;	/* for rounding */
+	  DIVIDE_BY(temp, qval);
+	  temp = -temp;
+	} else {
+	  temp += qval>>1;	/* for rounding */
+	  DIVIDE_BY(temp, qval);
+	}
+	output_ptr[i] = (JCOEF) temp;
+      }
+    }
+  }
+}
+
+
+#ifdef DCT_FLOAT_SUPPORTED
+
+METHODDEF(void)
+forward_DCT_float (j_compress_ptr cinfo, jpeg_component_info * compptr,
+		   JSAMPARRAY sample_data, JBLOCKROW coef_blocks,
+		   JDIMENSION start_row, JDIMENSION start_col,
+		   JDIMENSION num_blocks)
+/* This version is used for floating-point DCT implementations. */
+{
+  /* This routine is heavily used, so it's worth coding it tightly. */
+  my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct;
+  float_DCT_method_ptr do_dct = fdct->do_float_dct;
+  FAST_FLOAT * divisors = fdct->float_divisors[compptr->quant_tbl_no];
+  FAST_FLOAT workspace[DCTSIZE2]; /* work area for FDCT subroutine */
+  JDIMENSION bi;
+
+  sample_data += start_row;	/* fold in the vertical offset once */
+
+  for (bi = 0; bi < num_blocks; bi++, start_col += DCTSIZE) {
+    /* Load data into workspace, applying unsigned->signed conversion */
+    { register FAST_FLOAT *workspaceptr;
+      register JSAMPROW elemptr;
+      register int elemr;
+
+      workspaceptr = workspace;
+      for (elemr = 0; elemr < DCTSIZE; elemr++) {
+	elemptr = sample_data[elemr] + start_col;
+#if DCTSIZE == 8		/* unroll the inner loop */
+	*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
+	*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
+	*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
+	*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
+	*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
+	*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
+	*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
+	*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
+#else
+	{ register int elemc;
+	  for (elemc = DCTSIZE; elemc > 0; elemc--) {
+	    *workspaceptr++ = (FAST_FLOAT)
+	      (GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
+	  }
+	}
+#endif
+      }
+    }
+
+    /* Perform the DCT */
+    (*do_dct) (workspace);
+
+    /* Quantize/descale the coefficients, and store into coef_blocks[] */
+    { register FAST_FLOAT temp;
+      register int i;
+      register JCOEFPTR output_ptr = coef_blocks[bi];
+
+      for (i = 0; i < DCTSIZE2; i++) {
+	/* Apply the quantization and scaling factor */
+	temp = workspace[i] * divisors[i];
+	/* Round to nearest integer.
+	 * Since C does not specify the direction of rounding for negative
+	 * quotients, we have to force the dividend positive for portability.
+	 * The maximum coefficient size is +-16K (for 12-bit data), so this
+	 * code should work for either 16-bit or 32-bit ints.
+	 */
+	output_ptr[i] = (JCOEF) ((int) (temp + (FAST_FLOAT) 16384.5) - 16384);
+      }
+    }
+  }
+}
+
+#endif /* DCT_FLOAT_SUPPORTED */
+
+
+/*
+ * Initialize FDCT manager.
+ */
+
+GLOBAL(void)
+jinit_forward_dct (j_compress_ptr cinfo)
+{
+  my_fdct_ptr fdct;
+  int i;
+
+  fdct = (my_fdct_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_fdct_controller));
+  cinfo->fdct = (struct jpeg_forward_dct *) fdct;
+  fdct->pub.start_pass = start_pass_fdctmgr;
+
+  switch (cinfo->dct_method) {
+#ifdef DCT_ISLOW_SUPPORTED
+  case JDCT_ISLOW:
+    fdct->pub.forward_DCT = forward_DCT;
+    fdct->do_dct = jpeg_fdct_islow;
+    break;
+#endif
+#ifdef DCT_IFAST_SUPPORTED
+  case JDCT_IFAST:
+    fdct->pub.forward_DCT = forward_DCT;
+    fdct->do_dct = jpeg_fdct_ifast;
+    break;
+#endif
+#ifdef DCT_FLOAT_SUPPORTED
+  case JDCT_FLOAT:
+    fdct->pub.forward_DCT = forward_DCT_float;
+    fdct->do_float_dct = jpeg_fdct_float;
+    break;
+#endif
+  default:
+    ERREXIT(cinfo, JERR_NOT_COMPILED);
+    break;
+  }
+
+  /* Mark divisor tables unallocated */
+  for (i = 0; i < NUM_QUANT_TBLS; i++) {
+    fdct->divisors[i] = NULL;
+#ifdef DCT_FLOAT_SUPPORTED
+    fdct->float_divisors[i] = NULL;
+#endif
+  }
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jchuff.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jchuff.c
new file mode 100644
index 0000000000..f235250548
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jchuff.c
@@ -0,0 +1,909 @@
+/*
+ * jchuff.c
+ *
+ * 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.
+ *
+ * This file contains Huffman entropy encoding routines.
+ *
+ * Much of the complexity here has to do with supporting output suspension.
+ * If the data destination module demands suspension, we want to be able to
+ * back up to the start of the current MCU.  To do this, we copy state
+ * variables into local working storage, and update them back to the
+ * permanent JPEG objects only upon successful completion of an MCU.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jchuff.h"		/* Declarations shared with jcphuff.c */
+
+
+/* Expanded entropy encoder object for Huffman encoding.
+ *
+ * The savable_state subrecord contains fields that change within an MCU,
+ * but must not be updated permanently until we complete the MCU.
+ */
+
+typedef struct {
+  INT32 put_buffer;		/* current bit-accumulation buffer */
+  int put_bits;			/* # of bits now in it */
+  int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */
+} savable_state;
+
+/* This macro is to work around compilers with missing or broken
+ * structure assignment.  You'll need to fix this code if you have
+ * such a compiler and you change MAX_COMPS_IN_SCAN.
+ */
+
+#ifndef NO_STRUCT_ASSIGN
+#define ASSIGN_STATE(dest,src)  ((dest) = (src))
+#else
+#if MAX_COMPS_IN_SCAN == 4
+#define ASSIGN_STATE(dest,src)  \
+	((dest).put_buffer = (src).put_buffer, \
+	 (dest).put_bits = (src).put_bits, \
+	 (dest).last_dc_val[0] = (src).last_dc_val[0], \
+	 (dest).last_dc_val[1] = (src).last_dc_val[1], \
+	 (dest).last_dc_val[2] = (src).last_dc_val[2], \
+	 (dest).last_dc_val[3] = (src).last_dc_val[3])
+#endif
+#endif
+
+
+typedef struct {
+  struct jpeg_entropy_encoder pub; /* public fields */
+
+  savable_state saved;		/* Bit buffer & DC state at start of MCU */
+
+  /* These fields are NOT loaded into local working state. */
+  unsigned int restarts_to_go;	/* MCUs left in this restart interval */
+  int next_restart_num;		/* next restart number to write (0-7) */
+
+  /* Pointers to derived tables (these workspaces have image lifespan) */
+  c_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS];
+  c_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS];
+
+#ifdef ENTROPY_OPT_SUPPORTED	/* Statistics tables for optimization */
+  long * dc_count_ptrs[NUM_HUFF_TBLS];
+  long * ac_count_ptrs[NUM_HUFF_TBLS];
+#endif
+} huff_entropy_encoder;
+
+typedef huff_entropy_encoder * huff_entropy_ptr;
+
+/* Working state while writing an MCU.
+ * This struct contains all the fields that are needed by subroutines.
+ */
+
+typedef struct {
+  JOCTET * next_output_byte;	/* => next byte to write in buffer */
+  size_t free_in_buffer;	/* # of byte spaces remaining in buffer */
+  savable_state cur;		/* Current bit buffer & DC state */
+  j_compress_ptr cinfo;		/* dump_buffer needs access to this */
+} working_state;
+
+
+/* Forward declarations */
+METHODDEF(boolean) encode_mcu_huff JPP((j_compress_ptr cinfo,
+					JBLOCKROW *MCU_data));
+METHODDEF(void) finish_pass_huff JPP((j_compress_ptr cinfo));
+#ifdef ENTROPY_OPT_SUPPORTED
+METHODDEF(boolean) encode_mcu_gather JPP((j_compress_ptr cinfo,
+					  JBLOCKROW *MCU_data));
+METHODDEF(void) finish_pass_gather JPP((j_compress_ptr cinfo));
+#endif
+
+
+/*
+ * Initialize for a Huffman-compressed scan.
+ * If gather_statistics is TRUE, we do not output anything during the scan,
+ * just count the Huffman symbols used and generate Huffman code tables.
+ */
+
+METHODDEF(void)
+start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics)
+{
+  huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
+  int ci, dctbl, actbl;
+  jpeg_component_info * compptr;
+
+  if (gather_statistics) {
+#ifdef ENTROPY_OPT_SUPPORTED
+    entropy->pub.encode_mcu = encode_mcu_gather;
+    entropy->pub.finish_pass = finish_pass_gather;
+#else
+    ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif
+  } else {
+    entropy->pub.encode_mcu = encode_mcu_huff;
+    entropy->pub.finish_pass = finish_pass_huff;
+  }
+
+  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+    compptr = cinfo->cur_comp_info[ci];
+    dctbl = compptr->dc_tbl_no;
+    actbl = compptr->ac_tbl_no;
+    if (gather_statistics) {
+#ifdef ENTROPY_OPT_SUPPORTED
+      /* Check for invalid table indexes */
+      /* (make_c_derived_tbl does this in the other path) */
+      if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS)
+	ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl);
+      if (actbl < 0 || actbl >= NUM_HUFF_TBLS)
+	ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl);
+      /* Allocate and zero the statistics tables */
+      /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */
+      if (entropy->dc_count_ptrs[dctbl] == NULL)
+	entropy->dc_count_ptrs[dctbl] = (long *)
+	  (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				      257 * SIZEOF(long));
+      MEMZERO(entropy->dc_count_ptrs[dctbl], 257 * SIZEOF(long));
+      if (entropy->ac_count_ptrs[actbl] == NULL)
+	entropy->ac_count_ptrs[actbl] = (long *)
+	  (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				      257 * SIZEOF(long));
+      MEMZERO(entropy->ac_count_ptrs[actbl], 257 * SIZEOF(long));
+#endif
+    } else {
+      /* Compute derived values for Huffman tables */
+      /* We may do this more than once for a table, but it's not expensive */
+      jpeg_make_c_derived_tbl(cinfo, TRUE, dctbl,
+			      & entropy->dc_derived_tbls[dctbl]);
+      jpeg_make_c_derived_tbl(cinfo, FALSE, actbl,
+			      & entropy->ac_derived_tbls[actbl]);
+    }
+    /* Initialize DC predictions to 0 */
+    entropy->saved.last_dc_val[ci] = 0;
+  }
+
+  /* Initialize bit buffer to empty */
+  entropy->saved.put_buffer = 0;
+  entropy->saved.put_bits = 0;
+
+  /* Initialize restart stuff */
+  entropy->restarts_to_go = cinfo->restart_interval;
+  entropy->next_restart_num = 0;
+}
+
+
+/*
+ * Compute the derived values for a Huffman table.
+ * This routine also performs some validation checks on the table.
+ *
+ * Note this is also used by jcphuff.c.
+ */
+
+GLOBAL(void)
+jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno,
+			 c_derived_tbl ** pdtbl)
+{
+  JHUFF_TBL *htbl;
+  c_derived_tbl *dtbl;
+  int p, i, l, lastp, si, maxsymbol;
+  char huffsize[257];
+  unsigned int huffcode[257];
+  unsigned int code;
+
+  /* Note that huffsize[] and huffcode[] are filled in code-length order,
+   * paralleling the order of the symbols themselves in htbl->huffval[].
+   */
+
+  /* Find the input Huffman table */
+  if (tblno < 0 || tblno >= NUM_HUFF_TBLS)
+    ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno);
+  htbl =
+    isDC ? cinfo->dc_huff_tbl_ptrs[tblno] : cinfo->ac_huff_tbl_ptrs[tblno];
+  if (htbl == NULL)
+    ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno);
+
+  /* Allocate a workspace if we haven't already done so. */
+  if (*pdtbl == NULL)
+    *pdtbl = (c_derived_tbl *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				  SIZEOF(c_derived_tbl));
+  dtbl = *pdtbl;
+  
+  /* Figure C.1: make table of Huffman code length for each symbol */
+
+  p = 0;
+  for (l = 1; l <= 16; l++) {
+    i = (int) htbl->bits[l];
+    if (i < 0 || p + i > 256)	/* protect against table overrun */
+      ERREXIT(cinfo, JERR_BAD_HUFF_TABLE);
+    while (i--)
+      huffsize[p++] = (char) l;
+  }
+  huffsize[p] = 0;
+  lastp = p;
+  
+  /* Figure C.2: generate the codes themselves */
+  /* We also validate that the counts represent a legal Huffman code tree. */
+
+  code = 0;
+  si = huffsize[0];
+  p = 0;
+  while (huffsize[p]) {
+    while (((int) huffsize[p]) == si) {
+      huffcode[p++] = code;
+      code++;
+    }
+    /* code is now 1 more than the last code used for codelength si; but
+     * it must still fit in si bits, since no code is allowed to be all ones.
+     */
+    if (((INT32) code) >= (((INT32) 1) << si))
+      ERREXIT(cinfo, JERR_BAD_HUFF_TABLE);
+    code <<= 1;
+    si++;
+  }
+  
+  /* Figure C.3: generate encoding tables */
+  /* These are code and size indexed by symbol value */
+
+  /* Set all codeless symbols to have code length 0;
+   * this lets us detect duplicate VAL entries here, and later
+   * allows emit_bits to detect any attempt to emit such symbols.
+   */
+  MEMZERO(dtbl->ehufsi, SIZEOF(dtbl->ehufsi));
+
+  /* This is also a convenient place to check for out-of-range
+   * and duplicated VAL entries.  We allow 0..255 for AC symbols
+   * but only 0..15 for DC.  (We could constrain them further
+   * based on data depth and mode, but this seems enough.)
+   */
+  maxsymbol = isDC ? 15 : 255;
+
+  for (p = 0; p < lastp; p++) {
+    i = htbl->huffval[p];
+    if (i < 0 || i > maxsymbol || dtbl->ehufsi[i])
+      ERREXIT(cinfo, JERR_BAD_HUFF_TABLE);
+    dtbl->ehufco[i] = huffcode[p];
+    dtbl->ehufsi[i] = huffsize[p];
+  }
+}
+
+
+/* Outputting bytes to the file */
+
+/* Emit a byte, taking 'action' if must suspend. */
+#define emit_byte(state,val,action)  \
+	{ *(state)->next_output_byte++ = (JOCTET) (val);  \
+	  if (--(state)->free_in_buffer == 0)  \
+	    if (! dump_buffer(state))  \
+	      { action; } }
+
+
+LOCAL(boolean)
+dump_buffer (working_state * state)
+/* Empty the output buffer; return TRUE if successful, FALSE if must suspend */
+{
+  struct jpeg_destination_mgr * dest = state->cinfo->dest;
+
+  if (! (*dest->empty_output_buffer) (state->cinfo))
+    return FALSE;
+  /* After a successful buffer dump, must reset buffer pointers */
+  state->next_output_byte = dest->next_output_byte;
+  state->free_in_buffer = dest->free_in_buffer;
+  return TRUE;
+}
+
+
+/* Outputting bits to the file */
+
+/* Only the right 24 bits of put_buffer are used; the valid bits are
+ * left-justified in this part.  At most 16 bits can be passed to emit_bits
+ * in one call, and we never retain more than 7 bits in put_buffer
+ * between calls, so 24 bits are sufficient.
+ */
+
+INLINE
+LOCAL(boolean)
+emit_bits (working_state * state, unsigned int code, int size)
+/* Emit some bits; return TRUE if successful, FALSE if must suspend */
+{
+  /* This routine is heavily used, so it's worth coding tightly. */
+  register INT32 put_buffer = (INT32) code;
+  register int put_bits = state->cur.put_bits;
+
+  /* if size is 0, caller used an invalid Huffman table entry */
+  if (size == 0)
+    ERREXIT(state->cinfo, JERR_HUFF_MISSING_CODE);
+
+  put_buffer &= (((INT32) 1)<<size) - 1; /* mask off any extra bits in code */
+  
+  put_bits += size;		/* new number of bits in buffer */
+  
+  put_buffer <<= 24 - put_bits; /* align incoming bits */
+
+  put_buffer |= state->cur.put_buffer; /* and merge with old buffer contents */
+  
+  while (put_bits >= 8) {
+    int c = (int) ((put_buffer >> 16) & 0xFF);
+    
+    emit_byte(state, c, return FALSE);
+    if (c == 0xFF) {		/* need to stuff a zero byte? */
+      emit_byte(state, 0, return FALSE);
+    }
+    put_buffer <<= 8;
+    put_bits -= 8;
+  }
+
+  state->cur.put_buffer = put_buffer; /* update state variables */
+  state->cur.put_bits = put_bits;
+
+  return TRUE;
+}
+
+
+LOCAL(boolean)
+flush_bits (working_state * state)
+{
+  if (! emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */
+    return FALSE;
+  state->cur.put_buffer = 0;	/* and reset bit-buffer to empty */
+  state->cur.put_bits = 0;
+  return TRUE;
+}
+
+
+/* Encode a single block's worth of coefficients */
+
+LOCAL(boolean)
+encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val,
+		  c_derived_tbl *dctbl, c_derived_tbl *actbl)
+{
+  register int temp, temp2;
+  register int nbits;
+  register int k, r, i;
+  
+  /* Encode the DC coefficient difference per section F.1.2.1 */
+  
+  temp = temp2 = block[0] - last_dc_val;
+
+  if (temp < 0) {
+    temp = -temp;		/* temp is abs value of input */
+    /* For a negative input, want temp2 = bitwise complement of abs(input) */
+    /* This code assumes we are on a two's complement machine */
+    temp2--;
+  }
+  
+  /* Find the number of bits needed for the magnitude of the coefficient */
+  nbits = 0;
+  while (temp) {
+    nbits++;
+    temp >>= 1;
+  }
+  /* Check for out-of-range coefficient values.
+   * Since we're encoding a difference, the range limit is twice as much.
+   */
+  if (nbits > MAX_COEF_BITS+1)
+    ERREXIT(state->cinfo, JERR_BAD_DCT_COEF);
+  
+  /* Emit the Huffman-coded symbol for the number of bits */
+  if (! emit_bits(state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits]))
+    return FALSE;
+
+  /* Emit that number of bits of the value, if positive, */
+  /* or the complement of its magnitude, if negative. */
+  if (nbits)			/* emit_bits rejects calls with size 0 */
+    if (! emit_bits(state, (unsigned int) temp2, nbits))
+      return FALSE;
+
+  /* Encode the AC coefficients per section F.1.2.2 */
+  
+  r = 0;			/* r = run length of zeros */
+  
+  for (k = 1; k < DCTSIZE2; k++) {
+    if ((temp = block[jpeg_natural_order[k]]) == 0) {
+      r++;
+    } else {
+      /* if run length > 15, must emit special run-length-16 codes (0xF0) */
+      while (r > 15) {
+	if (! emit_bits(state, actbl->ehufco[0xF0], actbl->ehufsi[0xF0]))
+	  return FALSE;
+	r -= 16;
+      }
+
+      temp2 = temp;
+      if (temp < 0) {
+	temp = -temp;		/* temp is abs value of input */
+	/* This code assumes we are on a two's complement machine */
+	temp2--;
+      }
+      
+      /* Find the number of bits needed for the magnitude of the coefficient */
+      nbits = 1;		/* there must be at least one 1 bit */
+      while ((temp >>= 1))
+	nbits++;
+      /* Check for out-of-range coefficient values */
+      if (nbits > MAX_COEF_BITS)
+	ERREXIT(state->cinfo, JERR_BAD_DCT_COEF);
+      
+      /* Emit Huffman symbol for run length / number of bits */
+      i = (r << 4) + nbits;
+      if (! emit_bits(state, actbl->ehufco[i], actbl->ehufsi[i]))
+	return FALSE;
+
+      /* Emit that number of bits of the value, if positive, */
+      /* or the complement of its magnitude, if negative. */
+      if (! emit_bits(state, (unsigned int) temp2, nbits))
+	return FALSE;
+      
+      r = 0;
+    }
+  }
+
+  /* If the last coef(s) were zero, emit an end-of-block code */
+  if (r > 0)
+    if (! emit_bits(state, actbl->ehufco[0], actbl->ehufsi[0]))
+      return FALSE;
+
+  return TRUE;
+}
+
+
+/*
+ * Emit a restart marker & resynchronize predictions.
+ */
+
+LOCAL(boolean)
+emit_restart (working_state * state, int restart_num)
+{
+  int ci;
+
+  if (! flush_bits(state))
+    return FALSE;
+
+  emit_byte(state, 0xFF, return FALSE);
+  emit_byte(state, JPEG_RST0 + restart_num, return FALSE);
+
+  /* Re-initialize DC predictions to 0 */
+  for (ci = 0; ci < state->cinfo->comps_in_scan; ci++)
+    state->cur.last_dc_val[ci] = 0;
+
+  /* The restart counter is not updated until we successfully write the MCU. */
+
+  return TRUE;
+}
+
+
+/*
+ * Encode and output one MCU's worth of Huffman-compressed coefficients.
+ */
+
+METHODDEF(boolean)
+encode_mcu_huff (j_compress_ptr cinfo, JBLOCKROW *MCU_data)
+{
+  huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
+  working_state state;
+  int blkn, ci;
+  jpeg_component_info * compptr;
+
+  /* Load up working state */
+  state.next_output_byte = cinfo->dest->next_output_byte;
+  state.free_in_buffer = cinfo->dest->free_in_buffer;
+  ASSIGN_STATE(state.cur, entropy->saved);
+  state.cinfo = cinfo;
+
+  /* Emit restart marker if needed */
+  if (cinfo->restart_interval) {
+    if (entropy->restarts_to_go == 0)
+      if (! emit_restart(&state, entropy->next_restart_num))
+	return FALSE;
+  }
+
+  /* Encode the MCU data blocks */
+  for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
+    ci = cinfo->MCU_membership[blkn];
+    compptr = cinfo->cur_comp_info[ci];
+    if (! encode_one_block(&state,
+			   MCU_data[blkn][0], state.cur.last_dc_val[ci],
+			   entropy->dc_derived_tbls[compptr->dc_tbl_no],
+			   entropy->ac_derived_tbls[compptr->ac_tbl_no]))
+      return FALSE;
+    /* Update last_dc_val */
+    state.cur.last_dc_val[ci] = MCU_data[blkn][0][0];
+  }
+
+  /* Completed MCU, so update state */
+  cinfo->dest->next_output_byte = state.next_output_byte;
+  cinfo->dest->free_in_buffer = state.free_in_buffer;
+  ASSIGN_STATE(entropy->saved, state.cur);
+
+  /* Update restart-interval state too */
+  if (cinfo->restart_interval) {
+    if (entropy->restarts_to_go == 0) {
+      entropy->restarts_to_go = cinfo->restart_interval;
+      entropy->next_restart_num++;
+      entropy->next_restart_num &= 7;
+    }
+    entropy->restarts_to_go--;
+  }
+
+  return TRUE;
+}
+
+
+/*
+ * Finish up at the end of a Huffman-compressed scan.
+ */
+
+METHODDEF(void)
+finish_pass_huff (j_compress_ptr cinfo)
+{
+  huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
+  working_state state;
+
+  /* Load up working state ... flush_bits needs it */
+  state.next_output_byte = cinfo->dest->next_output_byte;
+  state.free_in_buffer = cinfo->dest->free_in_buffer;
+  ASSIGN_STATE(state.cur, entropy->saved);
+  state.cinfo = cinfo;
+
+  /* Flush out the last data */
+  if (! flush_bits(&state))
+    ERREXIT(cinfo, JERR_CANT_SUSPEND);
+
+  /* Update state */
+  cinfo->dest->next_output_byte = state.next_output_byte;
+  cinfo->dest->free_in_buffer = state.free_in_buffer;
+  ASSIGN_STATE(entropy->saved, state.cur);
+}
+
+
+/*
+ * Huffman coding optimization.
+ *
+ * We first scan the supplied data and count the number of uses of each symbol
+ * that is to be Huffman-coded. (This process MUST agree with the code above.)
+ * Then we build a Huffman coding tree for the observed counts.
+ * Symbols which are not needed at all for the particular image are not
+ * assigned any code, which saves space in the DHT marker as well as in
+ * the compressed data.
+ */
+
+#ifdef ENTROPY_OPT_SUPPORTED
+
+
+/* Process a single block's worth of coefficients */
+
+LOCAL(void)
+htest_one_block (j_compress_ptr cinfo, JCOEFPTR block, int last_dc_val,
+		 long dc_counts[], long ac_counts[])
+{
+  register int temp;
+  register int nbits;
+  register int k, r;
+  
+  /* Encode the DC coefficient difference per section F.1.2.1 */
+  
+  temp = block[0] - last_dc_val;
+  if (temp < 0)
+    temp = -temp;
+  
+  /* Find the number of bits needed for the magnitude of the coefficient */
+  nbits = 0;
+  while (temp) {
+    nbits++;
+    temp >>= 1;
+  }
+  /* Check for out-of-range coefficient values.
+   * Since we're encoding a difference, the range limit is twice as much.
+   */
+  if (nbits > MAX_COEF_BITS+1)
+    ERREXIT(cinfo, JERR_BAD_DCT_COEF);
+
+  /* Count the Huffman symbol for the number of bits */
+  dc_counts[nbits]++;
+  
+  /* Encode the AC coefficients per section F.1.2.2 */
+  
+  r = 0;			/* r = run length of zeros */
+  
+  for (k = 1; k < DCTSIZE2; k++) {
+    if ((temp = block[jpeg_natural_order[k]]) == 0) {
+      r++;
+    } else {
+      /* if run length > 15, must emit special run-length-16 codes (0xF0) */
+      while (r > 15) {
+	ac_counts[0xF0]++;
+	r -= 16;
+      }
+      
+      /* Find the number of bits needed for the magnitude of the coefficient */
+      if (temp < 0)
+	temp = -temp;
+      
+      /* Find the number of bits needed for the magnitude of the coefficient */
+      nbits = 1;		/* there must be at least one 1 bit */
+      while ((temp >>= 1))
+	nbits++;
+      /* Check for out-of-range coefficient values */
+      if (nbits > MAX_COEF_BITS)
+	ERREXIT(cinfo, JERR_BAD_DCT_COEF);
+      
+      /* Count Huffman symbol for run length / number of bits */
+      ac_counts[(r << 4) + nbits]++;
+      
+      r = 0;
+    }
+  }
+
+  /* If the last coef(s) were zero, emit an end-of-block code */
+  if (r > 0)
+    ac_counts[0]++;
+}
+
+
+/*
+ * Trial-encode one MCU's worth of Huffman-compressed coefficients.
+ * No data is actually output, so no suspension return is possible.
+ */
+
+METHODDEF(boolean)
+encode_mcu_gather (j_compress_ptr cinfo, JBLOCKROW *MCU_data)
+{
+  huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
+  int blkn, ci;
+  jpeg_component_info * compptr;
+
+  /* Take care of restart intervals if needed */
+  if (cinfo->restart_interval) {
+    if (entropy->restarts_to_go == 0) {
+      /* Re-initialize DC predictions to 0 */
+      for (ci = 0; ci < cinfo->comps_in_scan; ci++)
+	entropy->saved.last_dc_val[ci] = 0;
+      /* Update restart state */
+      entropy->restarts_to_go = cinfo->restart_interval;
+    }
+    entropy->restarts_to_go--;
+  }
+
+  for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
+    ci = cinfo->MCU_membership[blkn];
+    compptr = cinfo->cur_comp_info[ci];
+    htest_one_block(cinfo, MCU_data[blkn][0], entropy->saved.last_dc_val[ci],
+		    entropy->dc_count_ptrs[compptr->dc_tbl_no],
+		    entropy->ac_count_ptrs[compptr->ac_tbl_no]);
+    entropy->saved.last_dc_val[ci] = MCU_data[blkn][0][0];
+  }
+
+  return TRUE;
+}
+
+
+/*
+ * Generate the best Huffman code table for the given counts, fill htbl.
+ * Note this is also used by jcphuff.c.
+ *
+ * The JPEG standard requires that no symbol be assigned a codeword of all
+ * one bits (so that padding bits added at the end of a compressed segment
+ * can't look like a valid code).  Because of the canonical ordering of
+ * codewords, this just means that there must be an unused slot in the
+ * longest codeword length category.  Section K.2 of the JPEG spec suggests
+ * reserving such a slot by pretending that symbol 256 is a valid symbol
+ * with count 1.  In theory that's not optimal; giving it count zero but
+ * including it in the symbol set anyway should give a better Huffman code.
+ * But the theoretically better code actually seems to come out worse in
+ * practice, because it produces more all-ones bytes (which incur stuffed
+ * zero bytes in the final file).  In any case the difference is tiny.
+ *
+ * The JPEG standard requires Huffman codes to be no more than 16 bits long.
+ * If some symbols have a very small but nonzero probability, the Huffman tree
+ * must be adjusted to meet the code length restriction.  We currently use
+ * the adjustment method suggested in JPEG section K.2.  This method is *not*
+ * optimal; it may not choose the best possible limited-length code.  But
+ * typically only very-low-frequency symbols will be given less-than-optimal
+ * lengths, so the code is almost optimal.  Experimental comparisons against
+ * an optimal limited-length-code algorithm indicate that the difference is
+ * microscopic --- usually less than a hundredth of a percent of total size.
+ * So the extra complexity of an optimal algorithm doesn't seem worthwhile.
+ */
+
+GLOBAL(void)
+jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[])
+{
+#define MAX_CLEN 32		/* assumed maximum initial code length */
+  UINT8 bits[MAX_CLEN+1];	/* bits[k] = # of symbols with code length k */
+  int codesize[257];		/* codesize[k] = code length of symbol k */
+  int others[257];		/* next symbol in current branch of tree */
+  int c1, c2;
+  int p, i, j;
+  long v;
+
+  /* This algorithm is explained in section K.2 of the JPEG standard */
+
+  MEMZERO(bits, SIZEOF(bits));
+  MEMZERO(codesize, SIZEOF(codesize));
+  for (i = 0; i < 257; i++)
+    others[i] = -1;		/* init links to empty */
+  
+  freq[256] = 1;		/* make sure 256 has a nonzero count */
+  /* Including the pseudo-symbol 256 in the Huffman procedure guarantees
+   * that no real symbol is given code-value of all ones, because 256
+   * will be placed last in the largest codeword category.
+   */
+
+  /* Huffman's basic algorithm to assign optimal code lengths to symbols */
+
+  for (;;) {
+    /* Find the smallest nonzero frequency, set c1 = its symbol */
+    /* In case of ties, take the larger symbol number */
+    c1 = -1;
+    v = 1000000000L;
+    for (i = 0; i <= 256; i++) {
+      if (freq[i] && freq[i] <= v) {
+	v = freq[i];
+	c1 = i;
+      }
+    }
+
+    /* Find the next smallest nonzero frequency, set c2 = its symbol */
+    /* In case of ties, take the larger symbol number */
+    c2 = -1;
+    v = 1000000000L;
+    for (i = 0; i <= 256; i++) {
+      if (freq[i] && freq[i] <= v && i != c1) {
+	v = freq[i];
+	c2 = i;
+      }
+    }
+
+    /* Done if we've merged everything into one frequency */
+    if (c2 < 0)
+      break;
+    
+    /* Else merge the two counts/trees */
+    freq[c1] += freq[c2];
+    freq[c2] = 0;
+
+    /* Increment the codesize of everything in c1's tree branch */
+    codesize[c1]++;
+    while (others[c1] >= 0) {
+      c1 = others[c1];
+      codesize[c1]++;
+    }
+    
+    others[c1] = c2;		/* chain c2 onto c1's tree branch */
+    
+    /* Increment the codesize of everything in c2's tree branch */
+    codesize[c2]++;
+    while (others[c2] >= 0) {
+      c2 = others[c2];
+      codesize[c2]++;
+    }
+  }
+
+  /* Now count the number of symbols of each code length */
+  for (i = 0; i <= 256; i++) {
+    if (codesize[i]) {
+      /* The JPEG standard seems to think that this can't happen, */
+      /* but I'm paranoid... */
+      if (codesize[i] > MAX_CLEN)
+	ERREXIT(cinfo, JERR_HUFF_CLEN_OVERFLOW);
+
+      bits[codesize[i]]++;
+    }
+  }
+
+  /* JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure
+   * Huffman procedure assigned any such lengths, we must adjust the coding.
+   * Here is what the JPEG spec says about how this next bit works:
+   * Since symbols are paired for the longest Huffman code, the symbols are
+   * removed from this length category two at a time.  The prefix for the pair
+   * (which is one bit shorter) is allocated to one of the pair; then,
+   * skipping the BITS entry for that prefix length, a code word from the next
+   * shortest nonzero BITS entry is converted into a prefix for two code words
+   * one bit longer.
+   */
+  
+  for (i = MAX_CLEN; i > 16; i--) {
+    while (bits[i] > 0) {
+      j = i - 2;		/* find length of new prefix to be used */
+      while (bits[j] == 0)
+	j--;
+      
+      bits[i] -= 2;		/* remove two symbols */
+      bits[i-1]++;		/* one goes in this length */
+      bits[j+1] += 2;		/* two new symbols in this length */
+      bits[j]--;		/* symbol of this length is now a prefix */
+    }
+  }
+
+  /* Remove the count for the pseudo-symbol 256 from the largest codelength */
+  while (bits[i] == 0)		/* find largest codelength still in use */
+    i--;
+  bits[i]--;
+  
+  /* Return final symbol counts (only for lengths 0..16) */
+  MEMCOPY(htbl->bits, bits, SIZEOF(htbl->bits));
+  
+  /* Return a list of the symbols sorted by code length */
+  /* It's not real clear to me why we don't need to consider the codelength
+   * changes made above, but the JPEG spec seems to think this works.
+   */
+  p = 0;
+  for (i = 1; i <= MAX_CLEN; i++) {
+    for (j = 0; j <= 255; j++) {
+      if (codesize[j] == i) {
+	htbl->huffval[p] = (UINT8) j;
+	p++;
+      }
+    }
+  }
+
+  /* Set sent_table FALSE so updated table will be written to JPEG file. */
+  htbl->sent_table = FALSE;
+}
+
+
+/*
+ * Finish up a statistics-gathering pass and create the new Huffman tables.
+ */
+
+METHODDEF(void)
+finish_pass_gather (j_compress_ptr cinfo)
+{
+  huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
+  int ci, dctbl, actbl;
+  jpeg_component_info * compptr;
+  JHUFF_TBL **htblptr;
+  boolean did_dc[NUM_HUFF_TBLS];
+  boolean did_ac[NUM_HUFF_TBLS];
+
+  /* It's important not to apply jpeg_gen_optimal_table more than once
+   * per table, because it clobbers the input frequency counts!
+   */
+  MEMZERO(did_dc, SIZEOF(did_dc));
+  MEMZERO(did_ac, SIZEOF(did_ac));
+
+  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+    compptr = cinfo->cur_comp_info[ci];
+    dctbl = compptr->dc_tbl_no;
+    actbl = compptr->ac_tbl_no;
+    if (! did_dc[dctbl]) {
+      htblptr = & cinfo->dc_huff_tbl_ptrs[dctbl];
+      if (*htblptr == NULL)
+	*htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo);
+      jpeg_gen_optimal_table(cinfo, *htblptr, entropy->dc_count_ptrs[dctbl]);
+      did_dc[dctbl] = TRUE;
+    }
+    if (! did_ac[actbl]) {
+      htblptr = & cinfo->ac_huff_tbl_ptrs[actbl];
+      if (*htblptr == NULL)
+	*htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo);
+      jpeg_gen_optimal_table(cinfo, *htblptr, entropy->ac_count_ptrs[actbl]);
+      did_ac[actbl] = TRUE;
+    }
+  }
+}
+
+
+#endif /* ENTROPY_OPT_SUPPORTED */
+
+
+/*
+ * Module initialization routine for Huffman entropy encoding.
+ */
+
+GLOBAL(void)
+jinit_huff_encoder (j_compress_ptr cinfo)
+{
+  huff_entropy_ptr entropy;
+  int i;
+
+  entropy = (huff_entropy_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(huff_entropy_encoder));
+  cinfo->entropy = (struct jpeg_entropy_encoder *) entropy;
+  entropy->pub.start_pass = start_pass_huff;
+
+  /* Mark tables unallocated */
+  for (i = 0; i < NUM_HUFF_TBLS; i++) {
+    entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL;
+#ifdef ENTROPY_OPT_SUPPORTED
+    entropy->dc_count_ptrs[i] = entropy->ac_count_ptrs[i] = NULL;
+#endif
+  }
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jchuff.h b/Utilities/GDAL/frmts/jpeg/libjpeg/jchuff.h
new file mode 100644
index 0000000000..a9599fc1e6
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jchuff.h
@@ -0,0 +1,47 @@
+/*
+ * jchuff.h
+ *
+ * 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.
+ *
+ * This file contains declarations for Huffman entropy encoding routines
+ * that are shared between the sequential encoder (jchuff.c) and the
+ * progressive encoder (jcphuff.c).  No other modules need to see these.
+ */
+
+/* The legal range of a DCT coefficient is
+ *  -1024 .. +1023  for 8-bit data;
+ * -16384 .. +16383 for 12-bit data.
+ * Hence the magnitude should always fit in 10 or 14 bits respectively.
+ */
+
+#if BITS_IN_JSAMPLE == 8
+#define MAX_COEF_BITS 10
+#else
+#define MAX_COEF_BITS 14
+#endif
+
+/* Derived data constructed for each Huffman table */
+
+typedef struct {
+  unsigned int ehufco[256];	/* code for each symbol */
+  char ehufsi[256];		/* length of code for each symbol */
+  /* If no code has been allocated for a symbol S, ehufsi[S] contains 0 */
+} c_derived_tbl;
+
+/* Short forms of external names for systems with brain-damaged linkers. */
+
+#ifdef NEED_SHORT_EXTERNAL_NAMES
+#define jpeg_make_c_derived_tbl	jMkCDerived
+#define jpeg_gen_optimal_table	jGenOptTbl
+#endif /* NEED_SHORT_EXTERNAL_NAMES */
+
+/* Expand a Huffman table definition into the derived format */
+EXTERN(void) jpeg_make_c_derived_tbl
+	JPP((j_compress_ptr cinfo, boolean isDC, int tblno,
+	     c_derived_tbl ** pdtbl));
+
+/* Generate an optimal table definition given the specified counts */
+EXTERN(void) jpeg_gen_optimal_table
+	JPP((j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]));
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jcinit.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jcinit.c
new file mode 100644
index 0000000000..5efffe3316
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jcinit.c
@@ -0,0 +1,72 @@
+/*
+ * jcinit.c
+ *
+ * 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.
+ *
+ * This file contains initialization logic for the JPEG compressor.
+ * This routine is in charge of selecting the modules to be executed and
+ * making an initialization call to each one.
+ *
+ * Logically, this code belongs in jcmaster.c.  It's split out because
+ * linking this routine implies linking the entire compression library.
+ * For a transcoding-only application, we want to be able to use jcmaster.c
+ * without linking in the whole library.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/*
+ * Master selection of compression modules.
+ * This is done once at the start of processing an image.  We determine
+ * which modules will be used and give them appropriate initialization calls.
+ */
+
+GLOBAL(void)
+jinit_compress_master (j_compress_ptr cinfo)
+{
+  /* Initialize master control (includes parameter checking/processing) */
+  jinit_c_master_control(cinfo, FALSE /* full compression */);
+
+  /* Preprocessing */
+  if (! cinfo->raw_data_in) {
+    jinit_color_converter(cinfo);
+    jinit_downsampler(cinfo);
+    jinit_c_prep_controller(cinfo, FALSE /* never need full buffer here */);
+  }
+  /* Forward DCT */
+  jinit_forward_dct(cinfo);
+  /* Entropy encoding: either Huffman or arithmetic coding. */
+  if (cinfo->arith_code) {
+    ERREXIT(cinfo, JERR_ARITH_NOTIMPL);
+  } else {
+    if (cinfo->progressive_mode) {
+#ifdef C_PROGRESSIVE_SUPPORTED
+      jinit_phuff_encoder(cinfo);
+#else
+      ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif
+    } else
+      jinit_huff_encoder(cinfo);
+  }
+
+  /* Need a full-image coefficient buffer in any multi-pass mode. */
+  jinit_c_coef_controller(cinfo,
+		(boolean) (cinfo->num_scans > 1 || cinfo->optimize_coding));
+  jinit_c_main_controller(cinfo, FALSE /* never need full buffer here */);
+
+  jinit_marker_writer(cinfo);
+
+  /* We can now tell the memory manager to allocate virtual arrays. */
+  (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo);
+
+  /* Write the datastream header (SOI) immediately.
+   * Frame and scan headers are postponed till later.
+   * This lets application insert special markers after the SOI.
+   */
+  (*cinfo->marker->write_file_header) (cinfo);
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jcmainct.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jcmainct.c
new file mode 100644
index 0000000000..e0279a7e01
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jcmainct.c
@@ -0,0 +1,293 @@
+/*
+ * jcmainct.c
+ *
+ * Copyright (C) 1994-1996, 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 main buffer controller for compression.
+ * The main buffer lies between the pre-processor and the JPEG
+ * compressor proper; it holds downsampled data in the JPEG colorspace.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/* Note: currently, there is no operating mode in which a full-image buffer
+ * is needed at this step.  If there were, that mode could not be used with
+ * "raw data" input, since this module is bypassed in that case.  However,
+ * we've left the code here for possible use in special applications.
+ */
+#undef FULL_MAIN_BUFFER_SUPPORTED
+
+
+/* Private buffer controller object */
+
+typedef struct {
+  struct jpeg_c_main_controller pub; /* public fields */
+
+  JDIMENSION cur_iMCU_row;	/* number of current iMCU row */
+  JDIMENSION rowgroup_ctr;	/* counts row groups received in iMCU row */
+  boolean suspended;		/* remember if we suspended output */
+  J_BUF_MODE pass_mode;		/* current operating mode */
+
+  /* If using just a strip buffer, this points to the entire set of buffers
+   * (we allocate one for each component).  In the full-image case, this
+   * points to the currently accessible strips of the virtual arrays.
+   */
+  JSAMPARRAY buffer[MAX_COMPONENTS];
+
+#ifdef FULL_MAIN_BUFFER_SUPPORTED
+  /* If using full-image storage, this array holds pointers to virtual-array
+   * control blocks for each component.  Unused if not full-image storage.
+   */
+  jvirt_sarray_ptr whole_image[MAX_COMPONENTS];
+#endif
+} my_main_controller;
+
+typedef my_main_controller * my_main_ptr;
+
+
+/* Forward declarations */
+METHODDEF(void) process_data_simple_main
+	JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf,
+	     JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail));
+#ifdef FULL_MAIN_BUFFER_SUPPORTED
+METHODDEF(void) process_data_buffer_main
+	JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf,
+	     JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail));
+#endif
+
+
+/*
+ * Initialize for a processing pass.
+ */
+
+METHODDEF(void)
+start_pass_main (j_compress_ptr cinfo, J_BUF_MODE pass_mode)
+{
+  my_main_ptr main = (my_main_ptr) cinfo->main;
+
+  /* Do nothing in raw-data mode. */
+  if (cinfo->raw_data_in)
+    return;
+
+  main->cur_iMCU_row = 0;	/* initialize counters */
+  main->rowgroup_ctr = 0;
+  main->suspended = FALSE;
+  main->pass_mode = pass_mode;	/* save mode for use by process_data */
+
+  switch (pass_mode) {
+  case JBUF_PASS_THRU:
+#ifdef FULL_MAIN_BUFFER_SUPPORTED
+    if (main->whole_image[0] != NULL)
+      ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+#endif
+    main->pub.process_data = process_data_simple_main;
+    break;
+#ifdef FULL_MAIN_BUFFER_SUPPORTED
+  case JBUF_SAVE_SOURCE:
+  case JBUF_CRANK_DEST:
+  case JBUF_SAVE_AND_PASS:
+    if (main->whole_image[0] == NULL)
+      ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+    main->pub.process_data = process_data_buffer_main;
+    break;
+#endif
+  default:
+    ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+    break;
+  }
+}
+
+
+/*
+ * Process some data.
+ * This routine handles the simple pass-through mode,
+ * where we have only a strip buffer.
+ */
+
+METHODDEF(void)
+process_data_simple_main (j_compress_ptr cinfo,
+			  JSAMPARRAY input_buf, JDIMENSION *in_row_ctr,
+			  JDIMENSION in_rows_avail)
+{
+  my_main_ptr main = (my_main_ptr) cinfo->main;
+
+  while (main->cur_iMCU_row < cinfo->total_iMCU_rows) {
+    /* Read input data if we haven't filled the main buffer yet */
+    if (main->rowgroup_ctr < DCTSIZE)
+      (*cinfo->prep->pre_process_data) (cinfo,
+					input_buf, in_row_ctr, in_rows_avail,
+					main->buffer, &main->rowgroup_ctr,
+					(JDIMENSION) DCTSIZE);
+
+    /* If we don't have a full iMCU row buffered, return to application for
+     * more data.  Note that preprocessor will always pad to fill the iMCU row
+     * at the bottom of the image.
+     */
+    if (main->rowgroup_ctr != DCTSIZE)
+      return;
+
+    /* Send the completed row to the compressor */
+    if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) {
+      /* If compressor did not consume the whole row, then we must need to
+       * suspend processing and return to the application.  In this situation
+       * we pretend we didn't yet consume the last input row; otherwise, if
+       * it happened to be the last row of the image, the application would
+       * think we were done.
+       */
+      if (! main->suspended) {
+	(*in_row_ctr)--;
+	main->suspended = TRUE;
+      }
+      return;
+    }
+    /* We did finish the row.  Undo our little suspension hack if a previous
+     * call suspended; then mark the main buffer empty.
+     */
+    if (main->suspended) {
+      (*in_row_ctr)++;
+      main->suspended = FALSE;
+    }
+    main->rowgroup_ctr = 0;
+    main->cur_iMCU_row++;
+  }
+}
+
+
+#ifdef FULL_MAIN_BUFFER_SUPPORTED
+
+/*
+ * Process some data.
+ * This routine handles all of the modes that use a full-size buffer.
+ */
+
+METHODDEF(void)
+process_data_buffer_main (j_compress_ptr cinfo,
+			  JSAMPARRAY input_buf, JDIMENSION *in_row_ctr,
+			  JDIMENSION in_rows_avail)
+{
+  my_main_ptr main = (my_main_ptr) cinfo->main;
+  int ci;
+  jpeg_component_info *compptr;
+  boolean writing = (main->pass_mode != JBUF_CRANK_DEST);
+
+  while (main->cur_iMCU_row < cinfo->total_iMCU_rows) {
+    /* Realign the virtual buffers if at the start of an iMCU row. */
+    if (main->rowgroup_ctr == 0) {
+      for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+	   ci++, compptr++) {
+	main->buffer[ci] = (*cinfo->mem->access_virt_sarray)
+	  ((j_common_ptr) cinfo, main->whole_image[ci],
+	   main->cur_iMCU_row * (compptr->v_samp_factor * DCTSIZE),
+	   (JDIMENSION) (compptr->v_samp_factor * DCTSIZE), writing);
+      }
+      /* In a read pass, pretend we just read some source data. */
+      if (! writing) {
+	*in_row_ctr += cinfo->max_v_samp_factor * DCTSIZE;
+	main->rowgroup_ctr = DCTSIZE;
+      }
+    }
+
+    /* If a write pass, read input data until the current iMCU row is full. */
+    /* Note: preprocessor will pad if necessary to fill the last iMCU row. */
+    if (writing) {
+      (*cinfo->prep->pre_process_data) (cinfo,
+					input_buf, in_row_ctr, in_rows_avail,
+					main->buffer, &main->rowgroup_ctr,
+					(JDIMENSION) DCTSIZE);
+      /* Return to application if we need more data to fill the iMCU row. */
+      if (main->rowgroup_ctr < DCTSIZE)
+	return;
+    }
+
+    /* Emit data, unless this is a sink-only pass. */
+    if (main->pass_mode != JBUF_SAVE_SOURCE) {
+      if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) {
+	/* If compressor did not consume the whole row, then we must need to
+	 * suspend processing and return to the application.  In this situation
+	 * we pretend we didn't yet consume the last input row; otherwise, if
+	 * it happened to be the last row of the image, the application would
+	 * think we were done.
+	 */
+	if (! main->suspended) {
+	  (*in_row_ctr)--;
+	  main->suspended = TRUE;
+	}
+	return;
+      }
+      /* We did finish the row.  Undo our little suspension hack if a previous
+       * call suspended; then mark the main buffer empty.
+       */
+      if (main->suspended) {
+	(*in_row_ctr)++;
+	main->suspended = FALSE;
+      }
+    }
+
+    /* If get here, we are done with this iMCU row.  Mark buffer empty. */
+    main->rowgroup_ctr = 0;
+    main->cur_iMCU_row++;
+  }
+}
+
+#endif /* FULL_MAIN_BUFFER_SUPPORTED */
+
+
+/*
+ * Initialize main buffer controller.
+ */
+
+GLOBAL(void)
+jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer)
+{
+  my_main_ptr main;
+  int ci;
+  jpeg_component_info *compptr;
+
+  main = (my_main_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_main_controller));
+  cinfo->main = (struct jpeg_c_main_controller *) main;
+  main->pub.start_pass = start_pass_main;
+
+  /* We don't need to create a buffer in raw-data mode. */
+  if (cinfo->raw_data_in)
+    return;
+
+  /* Create the buffer.  It holds downsampled data, so each component
+   * may be of a different size.
+   */
+  if (need_full_buffer) {
+#ifdef FULL_MAIN_BUFFER_SUPPORTED
+    /* Allocate a full-image virtual array for each component */
+    /* Note we pad the bottom to a multiple of the iMCU height */
+    for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+	 ci++, compptr++) {
+      main->whole_image[ci] = (*cinfo->mem->request_virt_sarray)
+	((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
+	 compptr->width_in_blocks * DCTSIZE,
+	 (JDIMENSION) jround_up((long) compptr->height_in_blocks,
+				(long) compptr->v_samp_factor) * DCTSIZE,
+	 (JDIMENSION) (compptr->v_samp_factor * DCTSIZE));
+    }
+#else
+    ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+#endif
+  } else {
+#ifdef FULL_MAIN_BUFFER_SUPPORTED
+    main->whole_image[0] = NULL; /* flag for no virtual arrays */
+#endif
+    /* Allocate a strip buffer for each component */
+    for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+	 ci++, compptr++) {
+      main->buffer[ci] = (*cinfo->mem->alloc_sarray)
+	((j_common_ptr) cinfo, JPOOL_IMAGE,
+	 compptr->width_in_blocks * DCTSIZE,
+	 (JDIMENSION) (compptr->v_samp_factor * DCTSIZE));
+    }
+  }
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jcmarker.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jcmarker.c
new file mode 100644
index 0000000000..3d1e6c6d52
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jcmarker.c
@@ -0,0 +1,664 @@
+/*
+ * jcmarker.c
+ *
+ * Copyright (C) 1991-1998, 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 routines to write JPEG datastream markers.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+typedef enum {			/* JPEG marker codes */
+  M_SOF0  = 0xc0,
+  M_SOF1  = 0xc1,
+  M_SOF2  = 0xc2,
+  M_SOF3  = 0xc3,
+  
+  M_SOF5  = 0xc5,
+  M_SOF6  = 0xc6,
+  M_SOF7  = 0xc7,
+  
+  M_JPG   = 0xc8,
+  M_SOF9  = 0xc9,
+  M_SOF10 = 0xca,
+  M_SOF11 = 0xcb,
+  
+  M_SOF13 = 0xcd,
+  M_SOF14 = 0xce,
+  M_SOF15 = 0xcf,
+  
+  M_DHT   = 0xc4,
+  
+  M_DAC   = 0xcc,
+  
+  M_RST0  = 0xd0,
+  M_RST1  = 0xd1,
+  M_RST2  = 0xd2,
+  M_RST3  = 0xd3,
+  M_RST4  = 0xd4,
+  M_RST5  = 0xd5,
+  M_RST6  = 0xd6,
+  M_RST7  = 0xd7,
+  
+  M_SOI   = 0xd8,
+  M_EOI   = 0xd9,
+  M_SOS   = 0xda,
+  M_DQT   = 0xdb,
+  M_DNL   = 0xdc,
+  M_DRI   = 0xdd,
+  M_DHP   = 0xde,
+  M_EXP   = 0xdf,
+  
+  M_APP0  = 0xe0,
+  M_APP1  = 0xe1,
+  M_APP2  = 0xe2,
+  M_APP3  = 0xe3,
+  M_APP4  = 0xe4,
+  M_APP5  = 0xe5,
+  M_APP6  = 0xe6,
+  M_APP7  = 0xe7,
+  M_APP8  = 0xe8,
+  M_APP9  = 0xe9,
+  M_APP10 = 0xea,
+  M_APP11 = 0xeb,
+  M_APP12 = 0xec,
+  M_APP13 = 0xed,
+  M_APP14 = 0xee,
+  M_APP15 = 0xef,
+  
+  M_JPG0  = 0xf0,
+  M_JPG13 = 0xfd,
+  M_COM   = 0xfe,
+  
+  M_TEM   = 0x01,
+  
+  M_ERROR = 0x100
+} JPEG_MARKER;
+
+
+/* Private state */
+
+typedef struct {
+  struct jpeg_marker_writer pub; /* public fields */
+
+  unsigned int last_restart_interval; /* last DRI value emitted; 0 after SOI */
+} my_marker_writer;
+
+typedef my_marker_writer * my_marker_ptr;
+
+
+/*
+ * Basic output routines.
+ *
+ * Note that we do not support suspension while writing a marker.
+ * Therefore, an application using suspension must ensure that there is
+ * enough buffer space for the initial markers (typ. 600-700 bytes) before
+ * calling jpeg_start_compress, and enough space to write the trailing EOI
+ * (a few bytes) before calling jpeg_finish_compress.  Multipass compression
+ * modes are not supported at all with suspension, so those two are the only
+ * points where markers will be written.
+ */
+
+LOCAL(void)
+emit_byte (j_compress_ptr cinfo, int val)
+/* Emit a byte */
+{
+  struct jpeg_destination_mgr * dest = cinfo->dest;
+
+  *(dest->next_output_byte)++ = (JOCTET) val;
+  if (--dest->free_in_buffer == 0) {
+    if (! (*dest->empty_output_buffer) (cinfo))
+      ERREXIT(cinfo, JERR_CANT_SUSPEND);
+  }
+}
+
+
+LOCAL(void)
+emit_marker (j_compress_ptr cinfo, JPEG_MARKER mark)
+/* Emit a marker code */
+{
+  emit_byte(cinfo, 0xFF);
+  emit_byte(cinfo, (int) mark);
+}
+
+
+LOCAL(void)
+emit_2bytes (j_compress_ptr cinfo, int value)
+/* Emit a 2-byte integer; these are always MSB first in JPEG files */
+{
+  emit_byte(cinfo, (value >> 8) & 0xFF);
+  emit_byte(cinfo, value & 0xFF);
+}
+
+
+/*
+ * Routines to write specific marker types.
+ */
+
+LOCAL(int)
+emit_dqt (j_compress_ptr cinfo, int index)
+/* Emit a DQT marker */
+/* Returns the precision used (0 = 8bits, 1 = 16bits) for baseline checking */
+{
+  JQUANT_TBL * qtbl = cinfo->quant_tbl_ptrs[index];
+  int prec;
+  int i;
+
+  if (qtbl == NULL)
+    ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, index);
+
+  prec = 0;
+  for (i = 0; i < DCTSIZE2; i++) {
+    if (qtbl->quantval[i] > 255)
+      prec = 1;
+  }
+
+  if (! qtbl->sent_table) {
+    emit_marker(cinfo, M_DQT);
+
+    emit_2bytes(cinfo, prec ? DCTSIZE2*2 + 1 + 2 : DCTSIZE2 + 1 + 2);
+
+    emit_byte(cinfo, index + (prec<<4));
+
+    for (i = 0; i < DCTSIZE2; i++) {
+      /* The table entries must be emitted in zigzag order. */
+      unsigned int qval = qtbl->quantval[jpeg_natural_order[i]];
+      if (prec)
+	emit_byte(cinfo, (int) (qval >> 8));
+      emit_byte(cinfo, (int) (qval & 0xFF));
+    }
+
+    qtbl->sent_table = TRUE;
+  }
+
+  return prec;
+}
+
+
+LOCAL(void)
+emit_dht (j_compress_ptr cinfo, int index, boolean is_ac)
+/* Emit a DHT marker */
+{
+  JHUFF_TBL * htbl;
+  int length, i;
+  
+  if (is_ac) {
+    htbl = cinfo->ac_huff_tbl_ptrs[index];
+    index += 0x10;		/* output index has AC bit set */
+  } else {
+    htbl = cinfo->dc_huff_tbl_ptrs[index];
+  }
+
+  if (htbl == NULL)
+    ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, index);
+  
+  if (! htbl->sent_table) {
+    emit_marker(cinfo, M_DHT);
+    
+    length = 0;
+    for (i = 1; i <= 16; i++)
+      length += htbl->bits[i];
+    
+    emit_2bytes(cinfo, length + 2 + 1 + 16);
+    emit_byte(cinfo, index);
+    
+    for (i = 1; i <= 16; i++)
+      emit_byte(cinfo, htbl->bits[i]);
+    
+    for (i = 0; i < length; i++)
+      emit_byte(cinfo, htbl->huffval[i]);
+    
+    htbl->sent_table = TRUE;
+  }
+}
+
+
+LOCAL(void)
+emit_dac (j_compress_ptr cinfo)
+/* Emit a DAC marker */
+/* Since the useful info is so small, we want to emit all the tables in */
+/* one DAC marker.  Therefore this routine does its own scan of the table. */
+{
+#ifdef C_ARITH_CODING_SUPPORTED
+  char dc_in_use[NUM_ARITH_TBLS];
+  char ac_in_use[NUM_ARITH_TBLS];
+  int length, i;
+  jpeg_component_info *compptr;
+  
+  for (i = 0; i < NUM_ARITH_TBLS; i++)
+    dc_in_use[i] = ac_in_use[i] = 0;
+  
+  for (i = 0; i < cinfo->comps_in_scan; i++) {
+    compptr = cinfo->cur_comp_info[i];
+    dc_in_use[compptr->dc_tbl_no] = 1;
+    ac_in_use[compptr->ac_tbl_no] = 1;
+  }
+  
+  length = 0;
+  for (i = 0; i < NUM_ARITH_TBLS; i++)
+    length += dc_in_use[i] + ac_in_use[i];
+  
+  emit_marker(cinfo, M_DAC);
+  
+  emit_2bytes(cinfo, length*2 + 2);
+  
+  for (i = 0; i < NUM_ARITH_TBLS; i++) {
+    if (dc_in_use[i]) {
+      emit_byte(cinfo, i);
+      emit_byte(cinfo, cinfo->arith_dc_L[i] + (cinfo->arith_dc_U[i]<<4));
+    }
+    if (ac_in_use[i]) {
+      emit_byte(cinfo, i + 0x10);
+      emit_byte(cinfo, cinfo->arith_ac_K[i]);
+    }
+  }
+#endif /* C_ARITH_CODING_SUPPORTED */
+}
+
+
+LOCAL(void)
+emit_dri (j_compress_ptr cinfo)
+/* Emit a DRI marker */
+{
+  emit_marker(cinfo, M_DRI);
+  
+  emit_2bytes(cinfo, 4);	/* fixed length */
+
+  emit_2bytes(cinfo, (int) cinfo->restart_interval);
+}
+
+
+LOCAL(void)
+emit_sof (j_compress_ptr cinfo, JPEG_MARKER code)
+/* Emit a SOF marker */
+{
+  int ci;
+  jpeg_component_info *compptr;
+  
+  emit_marker(cinfo, code);
+  
+  emit_2bytes(cinfo, 3 * cinfo->num_components + 2 + 5 + 1); /* length */
+
+  /* Make sure image isn't bigger than SOF field can handle */
+  if ((long) cinfo->image_height > 65535L ||
+      (long) cinfo->image_width > 65535L)
+    ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) 65535);
+
+  emit_byte(cinfo, cinfo->data_precision);
+  emit_2bytes(cinfo, (int) cinfo->image_height);
+  emit_2bytes(cinfo, (int) cinfo->image_width);
+
+  emit_byte(cinfo, cinfo->num_components);
+
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    emit_byte(cinfo, compptr->component_id);
+    emit_byte(cinfo, (compptr->h_samp_factor << 4) + compptr->v_samp_factor);
+    emit_byte(cinfo, compptr->quant_tbl_no);
+  }
+}
+
+
+LOCAL(void)
+emit_sos (j_compress_ptr cinfo)
+/* Emit a SOS marker */
+{
+  int i, td, ta;
+  jpeg_component_info *compptr;
+  
+  emit_marker(cinfo, M_SOS);
+  
+  emit_2bytes(cinfo, 2 * cinfo->comps_in_scan + 2 + 1 + 3); /* length */
+  
+  emit_byte(cinfo, cinfo->comps_in_scan);
+  
+  for (i = 0; i < cinfo->comps_in_scan; i++) {
+    compptr = cinfo->cur_comp_info[i];
+    emit_byte(cinfo, compptr->component_id);
+    td = compptr->dc_tbl_no;
+    ta = compptr->ac_tbl_no;
+    if (cinfo->progressive_mode) {
+      /* Progressive mode: only DC or only AC tables are used in one scan;
+       * furthermore, Huffman coding of DC refinement uses no table at all.
+       * We emit 0 for unused field(s); this is recommended by the P&M text
+       * but does not seem to be specified in the standard.
+       */
+      if (cinfo->Ss == 0) {
+	ta = 0;			/* DC scan */
+	if (cinfo->Ah != 0 && !cinfo->arith_code)
+	  td = 0;		/* no DC table either */
+      } else {
+	td = 0;			/* AC scan */
+      }
+    }
+    emit_byte(cinfo, (td << 4) + ta);
+  }
+
+  emit_byte(cinfo, cinfo->Ss);
+  emit_byte(cinfo, cinfo->Se);
+  emit_byte(cinfo, (cinfo->Ah << 4) + cinfo->Al);
+}
+
+
+LOCAL(void)
+emit_jfif_app0 (j_compress_ptr cinfo)
+/* Emit a JFIF-compliant APP0 marker */
+{
+  /*
+   * Length of APP0 block	(2 bytes)
+   * Block ID			(4 bytes - ASCII "JFIF")
+   * Zero byte			(1 byte to terminate the ID string)
+   * Version Major, Minor	(2 bytes - major first)
+   * Units			(1 byte - 0x00 = none, 0x01 = inch, 0x02 = cm)
+   * Xdpu			(2 bytes - dots per unit horizontal)
+   * Ydpu			(2 bytes - dots per unit vertical)
+   * Thumbnail X size		(1 byte)
+   * Thumbnail Y size		(1 byte)
+   */
+  
+  emit_marker(cinfo, M_APP0);
+  
+  emit_2bytes(cinfo, 2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); /* length */
+
+  emit_byte(cinfo, 0x4A);	/* Identifier: ASCII "JFIF" */
+  emit_byte(cinfo, 0x46);
+  emit_byte(cinfo, 0x49);
+  emit_byte(cinfo, 0x46);
+  emit_byte(cinfo, 0);
+  emit_byte(cinfo, cinfo->JFIF_major_version); /* Version fields */
+  emit_byte(cinfo, cinfo->JFIF_minor_version);
+  emit_byte(cinfo, cinfo->density_unit); /* Pixel size information */
+  emit_2bytes(cinfo, (int) cinfo->X_density);
+  emit_2bytes(cinfo, (int) cinfo->Y_density);
+  emit_byte(cinfo, 0);		/* No thumbnail image */
+  emit_byte(cinfo, 0);
+}
+
+
+LOCAL(void)
+emit_adobe_app14 (j_compress_ptr cinfo)
+/* Emit an Adobe APP14 marker */
+{
+  /*
+   * Length of APP14 block	(2 bytes)
+   * Block ID			(5 bytes - ASCII "Adobe")
+   * Version Number		(2 bytes - currently 100)
+   * Flags0			(2 bytes - currently 0)
+   * Flags1			(2 bytes - currently 0)
+   * Color transform		(1 byte)
+   *
+   * Although Adobe TN 5116 mentions Version = 101, all the Adobe files
+   * now in circulation seem to use Version = 100, so that's what we write.
+   *
+   * We write the color transform byte as 1 if the JPEG color space is
+   * YCbCr, 2 if it's YCCK, 0 otherwise.  Adobe's definition has to do with
+   * whether the encoder performed a transformation, which is pretty useless.
+   */
+  
+  emit_marker(cinfo, M_APP14);
+  
+  emit_2bytes(cinfo, 2 + 5 + 2 + 2 + 2 + 1); /* length */
+
+  emit_byte(cinfo, 0x41);	/* Identifier: ASCII "Adobe" */
+  emit_byte(cinfo, 0x64);
+  emit_byte(cinfo, 0x6F);
+  emit_byte(cinfo, 0x62);
+  emit_byte(cinfo, 0x65);
+  emit_2bytes(cinfo, 100);	/* Version */
+  emit_2bytes(cinfo, 0);	/* Flags0 */
+  emit_2bytes(cinfo, 0);	/* Flags1 */
+  switch (cinfo->jpeg_color_space) {
+  case JCS_YCbCr:
+    emit_byte(cinfo, 1);	/* Color transform = 1 */
+    break;
+  case JCS_YCCK:
+    emit_byte(cinfo, 2);	/* Color transform = 2 */
+    break;
+  default:
+    emit_byte(cinfo, 0);	/* Color transform = 0 */
+    break;
+  }
+}
+
+
+/*
+ * These routines allow writing an arbitrary marker with parameters.
+ * The only intended use is to emit COM or APPn markers after calling
+ * write_file_header and before calling write_frame_header.
+ * Other uses are not guaranteed to produce desirable results.
+ * Counting the parameter bytes properly is the caller's responsibility.
+ */
+
+METHODDEF(void)
+write_marker_header (j_compress_ptr cinfo, int marker, unsigned int datalen)
+/* Emit an arbitrary marker header */
+{
+  if (datalen > (unsigned int) 65533)		/* safety check */
+    ERREXIT(cinfo, JERR_BAD_LENGTH);
+
+  emit_marker(cinfo, (JPEG_MARKER) marker);
+
+  emit_2bytes(cinfo, (int) (datalen + 2));	/* total length */
+}
+
+METHODDEF(void)
+write_marker_byte (j_compress_ptr cinfo, int val)
+/* Emit one byte of marker parameters following write_marker_header */
+{
+  emit_byte(cinfo, val);
+}
+
+
+/*
+ * Write datastream header.
+ * This consists of an SOI and optional APPn markers.
+ * We recommend use of the JFIF marker, but not the Adobe marker,
+ * when using YCbCr or grayscale data.  The JFIF marker should NOT
+ * be used for any other JPEG colorspace.  The Adobe marker is helpful
+ * to distinguish RGB, CMYK, and YCCK colorspaces.
+ * Note that an application can write additional header markers after
+ * jpeg_start_compress returns.
+ */
+
+METHODDEF(void)
+write_file_header (j_compress_ptr cinfo)
+{
+  my_marker_ptr marker = (my_marker_ptr) cinfo->marker;
+
+  emit_marker(cinfo, M_SOI);	/* first the SOI */
+
+  /* SOI is defined to reset restart interval to 0 */
+  marker->last_restart_interval = 0;
+
+  if (cinfo->write_JFIF_header)	/* next an optional JFIF APP0 */
+    emit_jfif_app0(cinfo);
+  if (cinfo->write_Adobe_marker) /* next an optional Adobe APP14 */
+    emit_adobe_app14(cinfo);
+}
+
+
+/*
+ * Write frame header.
+ * This consists of DQT and SOFn markers.
+ * Note that we do not emit the SOF until we have emitted the DQT(s).
+ * This avoids compatibility problems with incorrect implementations that
+ * try to error-check the quant table numbers as soon as they see the SOF.
+ */
+
+METHODDEF(void)
+write_frame_header (j_compress_ptr cinfo)
+{
+  int ci, prec;
+  boolean is_baseline;
+  jpeg_component_info *compptr;
+  
+  /* Emit DQT for each quantization table.
+   * Note that emit_dqt() suppresses any duplicate tables.
+   */
+  prec = 0;
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    prec += emit_dqt(cinfo, compptr->quant_tbl_no);
+  }
+  /* now prec is nonzero iff there are any 16-bit quant tables. */
+
+  /* Check for a non-baseline specification.
+   * Note we assume that Huffman table numbers won't be changed later.
+   */
+  if (cinfo->arith_code || cinfo->progressive_mode ||
+      cinfo->data_precision != 8) {
+    is_baseline = FALSE;
+  } else {
+    is_baseline = TRUE;
+    for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+	 ci++, compptr++) {
+      if (compptr->dc_tbl_no > 1 || compptr->ac_tbl_no > 1)
+	is_baseline = FALSE;
+    }
+    if (prec && is_baseline) {
+      is_baseline = FALSE;
+      /* If it's baseline except for quantizer size, warn the user */
+      TRACEMS(cinfo, 0, JTRC_16BIT_TABLES);
+    }
+  }
+
+  /* Emit the proper SOF marker */
+  if (cinfo->arith_code) {
+    emit_sof(cinfo, M_SOF9);	/* SOF code for arithmetic coding */
+  } else {
+    if (cinfo->progressive_mode)
+      emit_sof(cinfo, M_SOF2);	/* SOF code for progressive Huffman */
+    else if (is_baseline)
+      emit_sof(cinfo, M_SOF0);	/* SOF code for baseline implementation */
+    else
+      emit_sof(cinfo, M_SOF1);	/* SOF code for non-baseline Huffman file */
+  }
+}
+
+
+/*
+ * Write scan header.
+ * This consists of DHT or DAC markers, optional DRI, and SOS.
+ * Compressed data will be written following the SOS.
+ */
+
+METHODDEF(void)
+write_scan_header (j_compress_ptr cinfo)
+{
+  my_marker_ptr marker = (my_marker_ptr) cinfo->marker;
+  int i;
+  jpeg_component_info *compptr;
+
+  if (cinfo->arith_code) {
+    /* Emit arith conditioning info.  We may have some duplication
+     * if the file has multiple scans, but it's so small it's hardly
+     * worth worrying about.
+     */
+    emit_dac(cinfo);
+  } else {
+    /* Emit Huffman tables.
+     * Note that emit_dht() suppresses any duplicate tables.
+     */
+    for (i = 0; i < cinfo->comps_in_scan; i++) {
+      compptr = cinfo->cur_comp_info[i];
+      if (cinfo->progressive_mode) {
+	/* Progressive mode: only DC or only AC tables are used in one scan */
+	if (cinfo->Ss == 0) {
+	  if (cinfo->Ah == 0)	/* DC needs no table for refinement scan */
+	    emit_dht(cinfo, compptr->dc_tbl_no, FALSE);
+	} else {
+	  emit_dht(cinfo, compptr->ac_tbl_no, TRUE);
+	}
+      } else {
+	/* Sequential mode: need both DC and AC tables */
+	emit_dht(cinfo, compptr->dc_tbl_no, FALSE);
+	emit_dht(cinfo, compptr->ac_tbl_no, TRUE);
+      }
+    }
+  }
+
+  /* Emit DRI if required --- note that DRI value could change for each scan.
+   * We avoid wasting space with unnecessary DRIs, however.
+   */
+  if (cinfo->restart_interval != marker->last_restart_interval) {
+    emit_dri(cinfo);
+    marker->last_restart_interval = cinfo->restart_interval;
+  }
+
+  emit_sos(cinfo);
+}
+
+
+/*
+ * Write datastream trailer.
+ */
+
+METHODDEF(void)
+write_file_trailer (j_compress_ptr cinfo)
+{
+  emit_marker(cinfo, M_EOI);
+}
+
+
+/*
+ * Write an abbreviated table-specification datastream.
+ * This consists of SOI, DQT and DHT tables, and EOI.
+ * Any table that is defined and not marked sent_table = TRUE will be
+ * emitted.  Note that all tables will be marked sent_table = TRUE at exit.
+ */
+
+METHODDEF(void)
+write_tables_only (j_compress_ptr cinfo)
+{
+  int i;
+
+  emit_marker(cinfo, M_SOI);
+
+  for (i = 0; i < NUM_QUANT_TBLS; i++) {
+    if (cinfo->quant_tbl_ptrs[i] != NULL)
+      (void) emit_dqt(cinfo, i);
+  }
+
+  if (! cinfo->arith_code) {
+    for (i = 0; i < NUM_HUFF_TBLS; i++) {
+      if (cinfo->dc_huff_tbl_ptrs[i] != NULL)
+	emit_dht(cinfo, i, FALSE);
+      if (cinfo->ac_huff_tbl_ptrs[i] != NULL)
+	emit_dht(cinfo, i, TRUE);
+    }
+  }
+
+  emit_marker(cinfo, M_EOI);
+}
+
+
+/*
+ * Initialize the marker writer module.
+ */
+
+GLOBAL(void)
+jinit_marker_writer (j_compress_ptr cinfo)
+{
+  my_marker_ptr marker;
+
+  /* Create the subobject */
+  marker = (my_marker_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_marker_writer));
+  cinfo->marker = (struct jpeg_marker_writer *) marker;
+  /* Initialize method pointers */
+  marker->pub.write_file_header = write_file_header;
+  marker->pub.write_frame_header = write_frame_header;
+  marker->pub.write_scan_header = write_scan_header;
+  marker->pub.write_file_trailer = write_file_trailer;
+  marker->pub.write_tables_only = write_tables_only;
+  marker->pub.write_marker_header = write_marker_header;
+  marker->pub.write_marker_byte = write_marker_byte;
+  /* Initialize private state */
+  marker->last_restart_interval = 0;
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jcmaster.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jcmaster.c
new file mode 100644
index 0000000000..aab4020b87
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jcmaster.c
@@ -0,0 +1,590 @@
+/*
+ * jcmaster.c
+ *
+ * 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.
+ *
+ * This file contains master control logic for the JPEG compressor.
+ * These routines are concerned with parameter validation, initial setup,
+ * and inter-pass control (determining the number of passes and the work 
+ * to be done in each pass).
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/* Private state */
+
+typedef enum {
+	main_pass,		/* input data, also do first output step */
+	huff_opt_pass,		/* Huffman code optimization pass */
+	output_pass		/* data output pass */
+} c_pass_type;
+
+typedef struct {
+  struct jpeg_comp_master pub;	/* public fields */
+
+  c_pass_type pass_type;	/* the type of the current pass */
+
+  int pass_number;		/* # of passes completed */
+  int total_passes;		/* total # of passes needed */
+
+  int scan_number;		/* current index in scan_info[] */
+} my_comp_master;
+
+typedef my_comp_master * my_master_ptr;
+
+
+/*
+ * Support routines that do various essential calculations.
+ */
+
+LOCAL(void)
+initial_setup (j_compress_ptr cinfo)
+/* Do computations that are needed before master selection phase */
+{
+  int ci;
+  jpeg_component_info *compptr;
+  long samplesperrow;
+  JDIMENSION jd_samplesperrow;
+
+  /* Sanity check on image dimensions */
+  if (cinfo->image_height <= 0 || cinfo->image_width <= 0
+      || cinfo->num_components <= 0 || cinfo->input_components <= 0)
+    ERREXIT(cinfo, JERR_EMPTY_IMAGE);
+
+  /* Make sure image isn't bigger than I can handle */
+  if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION ||
+      (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION)
+    ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION);
+
+  /* Width of an input scanline must be representable as JDIMENSION. */
+  samplesperrow = (long) cinfo->image_width * (long) cinfo->input_components;
+  jd_samplesperrow = (JDIMENSION) samplesperrow;
+  if ((long) jd_samplesperrow != samplesperrow)
+    ERREXIT(cinfo, JERR_WIDTH_OVERFLOW);
+
+  /* For now, precision must match compiled-in value... */
+  if (cinfo->data_precision != BITS_IN_JSAMPLE)
+    ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision);
+
+  /* Check that number of components won't exceed internal array sizes */
+  if (cinfo->num_components > MAX_COMPONENTS)
+    ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components,
+	     MAX_COMPONENTS);
+
+  /* Compute maximum sampling factors; check factor validity */
+  cinfo->max_h_samp_factor = 1;
+  cinfo->max_v_samp_factor = 1;
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR ||
+	compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR)
+      ERREXIT(cinfo, JERR_BAD_SAMPLING);
+    cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor,
+				   compptr->h_samp_factor);
+    cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor,
+				   compptr->v_samp_factor);
+  }
+
+  /* Compute dimensions of components */
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    /* Fill in the correct component_index value; don't rely on application */
+    compptr->component_index = ci;
+    /* For compression, we never do DCT scaling. */
+    compptr->DCT_scaled_size = DCTSIZE;
+    /* Size in DCT blocks */
+    compptr->width_in_blocks = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor,
+		    (long) (cinfo->max_h_samp_factor * DCTSIZE));
+    compptr->height_in_blocks = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor,
+		    (long) (cinfo->max_v_samp_factor * DCTSIZE));
+    /* Size in samples */
+    compptr->downsampled_width = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor,
+		    (long) cinfo->max_h_samp_factor);
+    compptr->downsampled_height = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor,
+		    (long) cinfo->max_v_samp_factor);
+    /* Mark component needed (this flag isn't actually used for compression) */
+    compptr->component_needed = TRUE;
+  }
+
+  /* Compute number of fully interleaved MCU rows (number of times that
+   * main controller will call coefficient controller).
+   */
+  cinfo->total_iMCU_rows = (JDIMENSION)
+    jdiv_round_up((long) cinfo->image_height,
+		  (long) (cinfo->max_v_samp_factor*DCTSIZE));
+}
+
+
+#ifdef C_MULTISCAN_FILES_SUPPORTED
+
+LOCAL(void)
+validate_script (j_compress_ptr cinfo)
+/* Verify that the scan script in cinfo->scan_info[] is valid; also
+ * determine whether it uses progressive JPEG, and set cinfo->progressive_mode.
+ */
+{
+  const jpeg_scan_info * scanptr;
+  int scanno, ncomps, ci, coefi, thisi;
+  int Ss, Se, Ah, Al;
+  boolean component_sent[MAX_COMPONENTS];
+#ifdef C_PROGRESSIVE_SUPPORTED
+  int * last_bitpos_ptr;
+  int last_bitpos[MAX_COMPONENTS][DCTSIZE2];
+  /* -1 until that coefficient has been seen; then last Al for it */
+#endif
+
+  if (cinfo->num_scans <= 0)
+    ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, 0);
+
+  /* For sequential JPEG, all scans must have Ss=0, Se=DCTSIZE2-1;
+   * for progressive JPEG, no scan can have this.
+   */
+  scanptr = cinfo->scan_info;
+  if (scanptr->Ss != 0 || scanptr->Se != DCTSIZE2-1) {
+#ifdef C_PROGRESSIVE_SUPPORTED
+    cinfo->progressive_mode = TRUE;
+    last_bitpos_ptr = & last_bitpos[0][0];
+    for (ci = 0; ci < cinfo->num_components; ci++) 
+      for (coefi = 0; coefi < DCTSIZE2; coefi++)
+	*last_bitpos_ptr++ = -1;
+#else
+    ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif
+  } else {
+    cinfo->progressive_mode = FALSE;
+    for (ci = 0; ci < cinfo->num_components; ci++) 
+      component_sent[ci] = FALSE;
+  }
+
+  for (scanno = 1; scanno <= cinfo->num_scans; scanptr++, scanno++) {
+    /* Validate component indexes */
+    ncomps = scanptr->comps_in_scan;
+    if (ncomps <= 0 || ncomps > MAX_COMPS_IN_SCAN)
+      ERREXIT2(cinfo, JERR_COMPONENT_COUNT, ncomps, MAX_COMPS_IN_SCAN);
+    for (ci = 0; ci < ncomps; ci++) {
+      thisi = scanptr->component_index[ci];
+      if (thisi < 0 || thisi >= cinfo->num_components)
+	ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno);
+      /* Components must appear in SOF order within each scan */
+      if (ci > 0 && thisi <= scanptr->component_index[ci-1])
+	ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno);
+    }
+    /* Validate progression parameters */
+    Ss = scanptr->Ss;
+    Se = scanptr->Se;
+    Ah = scanptr->Ah;
+    Al = scanptr->Al;
+    if (cinfo->progressive_mode) {
+#ifdef C_PROGRESSIVE_SUPPORTED
+      /* The JPEG spec simply gives the ranges 0..13 for Ah and Al, but that
+       * seems wrong: the upper bound ought to depend on data precision.
+       * Perhaps they really meant 0..N+1 for N-bit precision.
+       * Here we allow 0..10 for 8-bit data; Al larger than 10 results in
+       * out-of-range reconstructed DC values during the first DC scan,
+       * which might cause problems for some decoders.
+       */
+#if BITS_IN_JSAMPLE == 8
+#define MAX_AH_AL 10
+#else
+#define MAX_AH_AL 13
+#endif
+      if (Ss < 0 || Ss >= DCTSIZE2 || Se < Ss || Se >= DCTSIZE2 ||
+	  Ah < 0 || Ah > MAX_AH_AL || Al < 0 || Al > MAX_AH_AL)
+	ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
+      if (Ss == 0) {
+	if (Se != 0)		/* DC and AC together not OK */
+	  ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
+      } else {
+	if (ncomps != 1)	/* AC scans must be for only one component */
+	  ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
+      }
+      for (ci = 0; ci < ncomps; ci++) {
+	last_bitpos_ptr = & last_bitpos[scanptr->component_index[ci]][0];
+	if (Ss != 0 && last_bitpos_ptr[0] < 0) /* AC without prior DC scan */
+	  ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
+	for (coefi = Ss; coefi <= Se; coefi++) {
+	  if (last_bitpos_ptr[coefi] < 0) {
+	    /* first scan of this coefficient */
+	    if (Ah != 0)
+	      ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
+	  } else {
+	    /* not first scan */
+	    if (Ah != last_bitpos_ptr[coefi] || Al != Ah-1)
+	      ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
+	  }
+	  last_bitpos_ptr[coefi] = Al;
+	}
+      }
+#endif
+    } else {
+      /* For sequential JPEG, all progression parameters must be these: */
+      if (Ss != 0 || Se != DCTSIZE2-1 || Ah != 0 || Al != 0)
+	ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
+      /* Make sure components are not sent twice */
+      for (ci = 0; ci < ncomps; ci++) {
+	thisi = scanptr->component_index[ci];
+	if (component_sent[thisi])
+	  ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno);
+	component_sent[thisi] = TRUE;
+      }
+    }
+  }
+
+  /* Now verify that everything got sent. */
+  if (cinfo->progressive_mode) {
+#ifdef C_PROGRESSIVE_SUPPORTED
+    /* For progressive mode, we only check that at least some DC data
+     * got sent for each component; the spec does not require that all bits
+     * of all coefficients be transmitted.  Would it be wiser to enforce
+     * transmission of all coefficient bits??
+     */
+    for (ci = 0; ci < cinfo->num_components; ci++) {
+      if (last_bitpos[ci][0] < 0)
+	ERREXIT(cinfo, JERR_MISSING_DATA);
+    }
+#endif
+  } else {
+    for (ci = 0; ci < cinfo->num_components; ci++) {
+      if (! component_sent[ci])
+	ERREXIT(cinfo, JERR_MISSING_DATA);
+    }
+  }
+}
+
+#endif /* C_MULTISCAN_FILES_SUPPORTED */
+
+
+LOCAL(void)
+select_scan_parameters (j_compress_ptr cinfo)
+/* Set up the scan parameters for the current scan */
+{
+  int ci;
+
+#ifdef C_MULTISCAN_FILES_SUPPORTED
+  if (cinfo->scan_info != NULL) {
+    /* Prepare for current scan --- the script is already validated */
+    my_master_ptr master = (my_master_ptr) cinfo->master;
+    const jpeg_scan_info * scanptr = cinfo->scan_info + master->scan_number;
+
+    cinfo->comps_in_scan = scanptr->comps_in_scan;
+    for (ci = 0; ci < scanptr->comps_in_scan; ci++) {
+      cinfo->cur_comp_info[ci] =
+	&cinfo->comp_info[scanptr->component_index[ci]];
+    }
+    cinfo->Ss = scanptr->Ss;
+    cinfo->Se = scanptr->Se;
+    cinfo->Ah = scanptr->Ah;
+    cinfo->Al = scanptr->Al;
+  }
+  else
+#endif
+  {
+    /* Prepare for single sequential-JPEG scan containing all components */
+    if (cinfo->num_components > MAX_COMPS_IN_SCAN)
+      ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components,
+	       MAX_COMPS_IN_SCAN);
+    cinfo->comps_in_scan = cinfo->num_components;
+    for (ci = 0; ci < cinfo->num_components; ci++) {
+      cinfo->cur_comp_info[ci] = &cinfo->comp_info[ci];
+    }
+    cinfo->Ss = 0;
+    cinfo->Se = DCTSIZE2-1;
+    cinfo->Ah = 0;
+    cinfo->Al = 0;
+  }
+}
+
+
+LOCAL(void)
+per_scan_setup (j_compress_ptr cinfo)
+/* Do computations that are needed before processing a JPEG scan */
+/* cinfo->comps_in_scan and cinfo->cur_comp_info[] are already set */
+{
+  int ci, mcublks, tmp;
+  jpeg_component_info *compptr;
+  
+  if (cinfo->comps_in_scan == 1) {
+    
+    /* Noninterleaved (single-component) scan */
+    compptr = cinfo->cur_comp_info[0];
+    
+    /* Overall image size in MCUs */
+    cinfo->MCUs_per_row = compptr->width_in_blocks;
+    cinfo->MCU_rows_in_scan = compptr->height_in_blocks;
+    
+    /* For noninterleaved scan, always one block per MCU */
+    compptr->MCU_width = 1;
+    compptr->MCU_height = 1;
+    compptr->MCU_blocks = 1;
+    compptr->MCU_sample_width = DCTSIZE;
+    compptr->last_col_width = 1;
+    /* For noninterleaved scans, it is convenient to define last_row_height
+     * as the number of block rows present in the last iMCU row.
+     */
+    tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor);
+    if (tmp == 0) tmp = compptr->v_samp_factor;
+    compptr->last_row_height = tmp;
+    
+    /* Prepare array describing MCU composition */
+    cinfo->blocks_in_MCU = 1;
+    cinfo->MCU_membership[0] = 0;
+    
+  } else {
+    
+    /* Interleaved (multi-component) scan */
+    if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN)
+      ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan,
+	       MAX_COMPS_IN_SCAN);
+    
+    /* Overall image size in MCUs */
+    cinfo->MCUs_per_row = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_width,
+		    (long) (cinfo->max_h_samp_factor*DCTSIZE));
+    cinfo->MCU_rows_in_scan = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_height,
+		    (long) (cinfo->max_v_samp_factor*DCTSIZE));
+    
+    cinfo->blocks_in_MCU = 0;
+    
+    for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+      compptr = cinfo->cur_comp_info[ci];
+      /* Sampling factors give # of blocks of component in each MCU */
+      compptr->MCU_width = compptr->h_samp_factor;
+      compptr->MCU_height = compptr->v_samp_factor;
+      compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height;
+      compptr->MCU_sample_width = compptr->MCU_width * DCTSIZE;
+      /* Figure number of non-dummy blocks in last MCU column & row */
+      tmp = (int) (compptr->width_in_blocks % compptr->MCU_width);
+      if (tmp == 0) tmp = compptr->MCU_width;
+      compptr->last_col_width = tmp;
+      tmp = (int) (compptr->height_in_blocks % compptr->MCU_height);
+      if (tmp == 0) tmp = compptr->MCU_height;
+      compptr->last_row_height = tmp;
+      /* Prepare array describing MCU composition */
+      mcublks = compptr->MCU_blocks;
+      if (cinfo->blocks_in_MCU + mcublks > C_MAX_BLOCKS_IN_MCU)
+	ERREXIT(cinfo, JERR_BAD_MCU_SIZE);
+      while (mcublks-- > 0) {
+	cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci;
+      }
+    }
+    
+  }
+
+  /* Convert restart specified in rows to actual MCU count. */
+  /* Note that count must fit in 16 bits, so we provide limiting. */
+  if (cinfo->restart_in_rows > 0) {
+    long nominal = (long) cinfo->restart_in_rows * (long) cinfo->MCUs_per_row;
+    cinfo->restart_interval = (unsigned int) MIN(nominal, 65535L);
+  }
+}
+
+
+/*
+ * Per-pass setup.
+ * This is called at the beginning of each pass.  We determine which modules
+ * will be active during this pass and give them appropriate start_pass calls.
+ * We also set is_last_pass to indicate whether any more passes will be
+ * required.
+ */
+
+METHODDEF(void)
+prepare_for_pass (j_compress_ptr cinfo)
+{
+  my_master_ptr master = (my_master_ptr) cinfo->master;
+
+  switch (master->pass_type) {
+  case main_pass:
+    /* Initial pass: will collect input data, and do either Huffman
+     * optimization or data output for the first scan.
+     */
+    select_scan_parameters(cinfo);
+    per_scan_setup(cinfo);
+    if (! cinfo->raw_data_in) {
+      (*cinfo->cconvert->start_pass) (cinfo);
+      (*cinfo->downsample->start_pass) (cinfo);
+      (*cinfo->prep->start_pass) (cinfo, JBUF_PASS_THRU);
+    }
+    (*cinfo->fdct->start_pass) (cinfo);
+    (*cinfo->entropy->start_pass) (cinfo, cinfo->optimize_coding);
+    (*cinfo->coef->start_pass) (cinfo,
+				(master->total_passes > 1 ?
+				 JBUF_SAVE_AND_PASS : JBUF_PASS_THRU));
+    (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU);
+    if (cinfo->optimize_coding) {
+      /* No immediate data output; postpone writing frame/scan headers */
+      master->pub.call_pass_startup = FALSE;
+    } else {
+      /* Will write frame/scan headers at first jpeg_write_scanlines call */
+      master->pub.call_pass_startup = TRUE;
+    }
+    break;
+#ifdef ENTROPY_OPT_SUPPORTED
+  case huff_opt_pass:
+    /* Do Huffman optimization for a scan after the first one. */
+    select_scan_parameters(cinfo);
+    per_scan_setup(cinfo);
+    if (cinfo->Ss != 0 || cinfo->Ah == 0 || cinfo->arith_code) {
+      (*cinfo->entropy->start_pass) (cinfo, TRUE);
+      (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST);
+      master->pub.call_pass_startup = FALSE;
+      break;
+    }
+    /* Special case: Huffman DC refinement scans need no Huffman table
+     * and therefore we can skip the optimization pass for them.
+     */
+    master->pass_type = output_pass;
+    master->pass_number++;
+    /*FALLTHROUGH*/
+#endif
+  case output_pass:
+    /* Do a data-output pass. */
+    /* We need not repeat per-scan setup if prior optimization pass did it. */
+    if (! cinfo->optimize_coding) {
+      select_scan_parameters(cinfo);
+      per_scan_setup(cinfo);
+    }
+    (*cinfo->entropy->start_pass) (cinfo, FALSE);
+    (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST);
+    /* We emit frame/scan headers now */
+    if (master->scan_number == 0)
+      (*cinfo->marker->write_frame_header) (cinfo);
+    (*cinfo->marker->write_scan_header) (cinfo);
+    master->pub.call_pass_startup = FALSE;
+    break;
+  default:
+    ERREXIT(cinfo, JERR_NOT_COMPILED);
+  }
+
+  master->pub.is_last_pass = (master->pass_number == master->total_passes-1);
+
+  /* Set up progress monitor's pass info if present */
+  if (cinfo->progress != NULL) {
+    cinfo->progress->completed_passes = master->pass_number;
+    cinfo->progress->total_passes = master->total_passes;
+  }
+}
+
+
+/*
+ * Special start-of-pass hook.
+ * This is called by jpeg_write_scanlines if call_pass_startup is TRUE.
+ * In single-pass processing, we need this hook because we don't want to
+ * write frame/scan headers during jpeg_start_compress; we want to let the
+ * application write COM markers etc. between jpeg_start_compress and the
+ * jpeg_write_scanlines loop.
+ * In multi-pass processing, this routine is not used.
+ */
+
+METHODDEF(void)
+pass_startup (j_compress_ptr cinfo)
+{
+  cinfo->master->call_pass_startup = FALSE; /* reset flag so call only once */
+
+  (*cinfo->marker->write_frame_header) (cinfo);
+  (*cinfo->marker->write_scan_header) (cinfo);
+}
+
+
+/*
+ * Finish up at end of pass.
+ */
+
+METHODDEF(void)
+finish_pass_master (j_compress_ptr cinfo)
+{
+  my_master_ptr master = (my_master_ptr) cinfo->master;
+
+  /* The entropy coder always needs an end-of-pass call,
+   * either to analyze statistics or to flush its output buffer.
+   */
+  (*cinfo->entropy->finish_pass) (cinfo);
+
+  /* Update state for next pass */
+  switch (master->pass_type) {
+  case main_pass:
+    /* next pass is either output of scan 0 (after optimization)
+     * or output of scan 1 (if no optimization).
+     */
+    master->pass_type = output_pass;
+    if (! cinfo->optimize_coding)
+      master->scan_number++;
+    break;
+  case huff_opt_pass:
+    /* next pass is always output of current scan */
+    master->pass_type = output_pass;
+    break;
+  case output_pass:
+    /* next pass is either optimization or output of next scan */
+    if (cinfo->optimize_coding)
+      master->pass_type = huff_opt_pass;
+    master->scan_number++;
+    break;
+  }
+
+  master->pass_number++;
+}
+
+
+/*
+ * Initialize master compression control.
+ */
+
+GLOBAL(void)
+jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only)
+{
+  my_master_ptr master;
+
+  master = (my_master_ptr)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				  SIZEOF(my_comp_master));
+  cinfo->master = (struct jpeg_comp_master *) master;
+  master->pub.prepare_for_pass = prepare_for_pass;
+  master->pub.pass_startup = pass_startup;
+  master->pub.finish_pass = finish_pass_master;
+  master->pub.is_last_pass = FALSE;
+
+  /* Validate parameters, determine derived values */
+  initial_setup(cinfo);
+
+  if (cinfo->scan_info != NULL) {
+#ifdef C_MULTISCAN_FILES_SUPPORTED
+    validate_script(cinfo);
+#else
+    ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif
+  } else {
+    cinfo->progressive_mode = FALSE;
+    cinfo->num_scans = 1;
+  }
+
+  if (cinfo->progressive_mode)	/*  TEMPORARY HACK ??? */
+    cinfo->optimize_coding = TRUE; /* assume default tables no good for progressive mode */
+
+  /* Initialize my private state */
+  if (transcode_only) {
+    /* no main pass in transcoding */
+    if (cinfo->optimize_coding)
+      master->pass_type = huff_opt_pass;
+    else
+      master->pass_type = output_pass;
+  } else {
+    /* for normal compression, first pass is always this type: */
+    master->pass_type = main_pass;
+  }
+  master->scan_number = 0;
+  master->pass_number = 0;
+  if (cinfo->optimize_coding)
+    master->total_passes = cinfo->num_scans * 2;
+  else
+    master->total_passes = cinfo->num_scans;
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jcomapi.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jcomapi.c
new file mode 100644
index 0000000000..9b1fa7568a
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jcomapi.c
@@ -0,0 +1,106 @@
+/*
+ * jcomapi.c
+ *
+ * Copyright (C) 1994-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.
+ *
+ * This file contains application interface routines that are used for both
+ * compression and decompression.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/*
+ * Abort processing of a JPEG compression or decompression operation,
+ * but don't destroy the object itself.
+ *
+ * For this, we merely clean up all the nonpermanent memory pools.
+ * Note that temp files (virtual arrays) are not allowed to belong to
+ * the permanent pool, so we will be able to close all temp files here.
+ * Closing a data source or destination, if necessary, is the application's
+ * responsibility.
+ */
+
+GLOBAL(void)
+jpeg_abort (j_common_ptr cinfo)
+{
+  int pool;
+
+  /* Do nothing if called on a not-initialized or destroyed JPEG object. */
+  if (cinfo->mem == NULL)
+    return;
+
+  /* Releasing pools in reverse order might help avoid fragmentation
+   * with some (brain-damaged) malloc libraries.
+   */
+  for (pool = JPOOL_NUMPOOLS-1; pool > JPOOL_PERMANENT; pool--) {
+    (*cinfo->mem->free_pool) (cinfo, pool);
+  }
+
+  /* Reset overall state for possible reuse of object */
+  if (cinfo->is_decompressor) {
+    cinfo->global_state = DSTATE_START;
+    /* Try to keep application from accessing now-deleted marker list.
+     * A bit kludgy to do it here, but this is the most central place.
+     */
+    ((j_decompress_ptr) cinfo)->marker_list = NULL;
+  } else {
+    cinfo->global_state = CSTATE_START;
+  }
+}
+
+
+/*
+ * Destruction of a JPEG object.
+ *
+ * Everything gets deallocated except the master jpeg_compress_struct itself
+ * and the error manager struct.  Both of these are supplied by the application
+ * and must be freed, if necessary, by the application.  (Often they are on
+ * the stack and so don't need to be freed anyway.)
+ * Closing a data source or destination, if necessary, is the application's
+ * responsibility.
+ */
+
+GLOBAL(void)
+jpeg_destroy (j_common_ptr cinfo)
+{
+  /* We need only tell the memory manager to release everything. */
+  /* NB: mem pointer is NULL if memory mgr failed to initialize. */
+  if (cinfo->mem != NULL)
+    (*cinfo->mem->self_destruct) (cinfo);
+  cinfo->mem = NULL;		/* be safe if jpeg_destroy is called twice */
+  cinfo->global_state = 0;	/* mark it destroyed */
+}
+
+
+/*
+ * Convenience routines for allocating quantization and Huffman tables.
+ * (Would jutils.c be a more reasonable place to put these?)
+ */
+
+GLOBAL(JQUANT_TBL *)
+jpeg_alloc_quant_table (j_common_ptr cinfo)
+{
+  JQUANT_TBL *tbl;
+
+  tbl = (JQUANT_TBL *)
+    (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JQUANT_TBL));
+  tbl->sent_table = FALSE;	/* make sure this is false in any new table */
+  return tbl;
+}
+
+
+GLOBAL(JHUFF_TBL *)
+jpeg_alloc_huff_table (j_common_ptr cinfo)
+{
+  JHUFF_TBL *tbl;
+
+  tbl = (JHUFF_TBL *)
+    (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JHUFF_TBL));
+  tbl->sent_table = FALSE;	/* make sure this is false in any new table */
+  return tbl;
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jconfig.h b/Utilities/GDAL/frmts/jpeg/libjpeg/jconfig.h
new file mode 100644
index 0000000000..b01fe7bd7c
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jconfig.h
@@ -0,0 +1,45 @@
+/* jconfig.h.  Generated automatically by configure.  */
+/* jconfig.cfg --- source file edited by configure script */
+/* see jconfig.doc for explanations */
+
+#define HAVE_PROTOTYPES 
+#define HAVE_UNSIGNED_CHAR 
+#define HAVE_UNSIGNED_SHORT 
+#undef void
+#undef const
+#undef CHAR_IS_UNSIGNED
+#define HAVE_STDDEF_H 
+#define HAVE_STDLIB_H 
+#undef NEED_BSD_STRINGS
+#undef NEED_SYS_TYPES_H
+#undef NEED_FAR_POINTERS
+#undef NEED_SHORT_EXTERNAL_NAMES
+/* Define this if you get warnings about undefined structures. */
+#undef INCOMPLETE_TYPES_BROKEN
+
+#ifdef JPEG_INTERNALS
+
+#undef RIGHT_SHIFT_IS_UNSIGNED
+#define INLINE 
+/* These are for configuring the JPEG memory manager. */
+#undef DEFAULT_MAX_MEM
+#undef NO_MKTEMP
+
+#endif /* JPEG_INTERNALS */
+
+#ifdef JPEG_CJPEG_DJPEG
+
+#define BMP_SUPPORTED		/* BMP image file format */
+#define GIF_SUPPORTED		/* GIF image file format */
+#define PPM_SUPPORTED		/* PBMPLUS PPM/PGM image file format */
+#undef RLE_SUPPORTED		/* Utah RLE image file format */
+#define TARGA_SUPPORTED		/* Targa image file format */
+
+#undef TWO_FILE_COMMANDLINE
+#undef NEED_SIGNAL_CATCHER
+#undef DONT_USE_B_MODE
+
+/* Define this if you want percent-done progress reports from cjpeg/djpeg. */
+#undef PROGRESS_REPORT
+
+#endif /* JPEG_CJPEG_DJPEG */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jcparam.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jcparam.c
new file mode 100644
index 0000000000..6fc48f5365
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jcparam.c
@@ -0,0 +1,610 @@
+/*
+ * jcparam.c
+ *
+ * Copyright (C) 1991-1998, 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 optional default-setting code for the JPEG compressor.
+ * Applications do not have to use this file, but those that don't use it
+ * must know a lot more about the innards of the JPEG code.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/*
+ * Quantization table setup routines
+ */
+
+GLOBAL(void)
+jpeg_add_quant_table (j_compress_ptr cinfo, int which_tbl,
+		      const unsigned int *basic_table,
+		      int scale_factor, boolean force_baseline)
+/* Define a quantization table equal to the basic_table times
+ * a scale factor (given as a percentage).
+ * If force_baseline is TRUE, the computed quantization table entries
+ * are limited to 1..255 for JPEG baseline compatibility.
+ */
+{
+  JQUANT_TBL ** qtblptr;
+  int i;
+  long temp;
+
+  /* Safety check to ensure start_compress not called yet. */
+  if (cinfo->global_state != CSTATE_START)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+
+  if (which_tbl < 0 || which_tbl >= NUM_QUANT_TBLS)
+    ERREXIT1(cinfo, JERR_DQT_INDEX, which_tbl);
+
+  qtblptr = & cinfo->quant_tbl_ptrs[which_tbl];
+
+  if (*qtblptr == NULL)
+    *qtblptr = jpeg_alloc_quant_table((j_common_ptr) cinfo);
+
+  for (i = 0; i < DCTSIZE2; i++) {
+    temp = ((long) basic_table[i] * scale_factor + 50L) / 100L;
+    /* limit the values to the valid range */
+    if (temp <= 0L) temp = 1L;
+    if (temp > 32767L) temp = 32767L; /* max quantizer needed for 12 bits */
+    if (force_baseline && temp > 255L)
+      temp = 255L;		/* limit to baseline range if requested */
+    (*qtblptr)->quantval[i] = (UINT16) temp;
+  }
+
+  /* Initialize sent_table FALSE so table will be written to JPEG file. */
+  (*qtblptr)->sent_table = FALSE;
+}
+
+
+GLOBAL(void)
+jpeg_set_linear_quality (j_compress_ptr cinfo, int scale_factor,
+			 boolean force_baseline)
+/* Set or change the 'quality' (quantization) setting, using default tables
+ * and a straight percentage-scaling quality scale.  In most cases it's better
+ * to use jpeg_set_quality (below); this entry point is provided for
+ * applications that insist on a linear percentage scaling.
+ */
+{
+  /* These are the sample quantization tables given in JPEG spec section K.1.
+   * The spec says that the values given produce "good" quality, and
+   * when divided by 2, "very good" quality.
+   */
+  static const unsigned int std_luminance_quant_tbl[DCTSIZE2] = {
+    16,  11,  10,  16,  24,  40,  51,  61,
+    12,  12,  14,  19,  26,  58,  60,  55,
+    14,  13,  16,  24,  40,  57,  69,  56,
+    14,  17,  22,  29,  51,  87,  80,  62,
+    18,  22,  37,  56,  68, 109, 103,  77,
+    24,  35,  55,  64,  81, 104, 113,  92,
+    49,  64,  78,  87, 103, 121, 120, 101,
+    72,  92,  95,  98, 112, 100, 103,  99
+  };
+  static const unsigned int std_chrominance_quant_tbl[DCTSIZE2] = {
+    17,  18,  24,  47,  99,  99,  99,  99,
+    18,  21,  26,  66,  99,  99,  99,  99,
+    24,  26,  56,  99,  99,  99,  99,  99,
+    47,  66,  99,  99,  99,  99,  99,  99,
+    99,  99,  99,  99,  99,  99,  99,  99,
+    99,  99,  99,  99,  99,  99,  99,  99,
+    99,  99,  99,  99,  99,  99,  99,  99,
+    99,  99,  99,  99,  99,  99,  99,  99
+  };
+
+  /* Set up two quantization tables using the specified scaling */
+  jpeg_add_quant_table(cinfo, 0, std_luminance_quant_tbl,
+		       scale_factor, force_baseline);
+  jpeg_add_quant_table(cinfo, 1, std_chrominance_quant_tbl,
+		       scale_factor, force_baseline);
+}
+
+
+GLOBAL(int)
+jpeg_quality_scaling (int quality)
+/* Convert a user-specified quality rating to a percentage scaling factor
+ * for an underlying quantization table, using our recommended scaling curve.
+ * The input 'quality' factor should be 0 (terrible) to 100 (very good).
+ */
+{
+  /* Safety limit on quality factor.  Convert 0 to 1 to avoid zero divide. */
+  if (quality <= 0) quality = 1;
+  if (quality > 100) quality = 100;
+
+  /* The basic table is used as-is (scaling 100) for a quality of 50.
+   * Qualities 50..100 are converted to scaling percentage 200 - 2*Q;
+   * note that at Q=100 the scaling is 0, which will cause jpeg_add_quant_table
+   * to make all the table entries 1 (hence, minimum quantization loss).
+   * Qualities 1..50 are converted to scaling percentage 5000/Q.
+   */
+  if (quality < 50)
+    quality = 5000 / quality;
+  else
+    quality = 200 - quality*2;
+
+  return quality;
+}
+
+
+GLOBAL(void)
+jpeg_set_quality (j_compress_ptr cinfo, int quality, boolean force_baseline)
+/* Set or change the 'quality' (quantization) setting, using default tables.
+ * This is the standard quality-adjusting entry point for typical user
+ * interfaces; only those who want detailed control over quantization tables
+ * would use the preceding three routines directly.
+ */
+{
+  /* Convert user 0-100 rating to percentage scaling */
+  quality = jpeg_quality_scaling(quality);
+
+  /* Set up standard quality tables */
+  jpeg_set_linear_quality(cinfo, quality, force_baseline);
+}
+
+
+/*
+ * Huffman table setup routines
+ */
+
+LOCAL(void)
+add_huff_table (j_compress_ptr cinfo,
+		JHUFF_TBL **htblptr, const UINT8 *bits, const UINT8 *val)
+/* Define a Huffman table */
+{
+  int nsymbols, len;
+
+  if (*htblptr == NULL)
+    *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo);
+
+  /* Copy the number-of-symbols-of-each-code-length counts */
+  MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits));
+
+  /* Validate the counts.  We do this here mainly so we can copy the right
+   * number of symbols from the val[] array, without risking marching off
+   * the end of memory.  jchuff.c will do a more thorough test later.
+   */
+  nsymbols = 0;
+  for (len = 1; len <= 16; len++)
+    nsymbols += bits[len];
+  if (nsymbols < 1 || nsymbols > 256)
+    ERREXIT(cinfo, JERR_BAD_HUFF_TABLE);
+
+  MEMCOPY((*htblptr)->huffval, val, nsymbols * SIZEOF(UINT8));
+
+  /* Initialize sent_table FALSE so table will be written to JPEG file. */
+  (*htblptr)->sent_table = FALSE;
+}
+
+
+LOCAL(void)
+std_huff_tables (j_compress_ptr cinfo)
+/* Set up the standard Huffman tables (cf. JPEG standard section K.3) */
+/* IMPORTANT: these are only valid for 8-bit data precision! */
+{
+  static const UINT8 bits_dc_luminance[17] =
+    { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 };
+  static const UINT8 val_dc_luminance[] =
+    { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
+  
+  static const UINT8 bits_dc_chrominance[17] =
+    { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 };
+  static const UINT8 val_dc_chrominance[] =
+    { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
+  
+  static const UINT8 bits_ac_luminance[17] =
+    { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d };
+  static const UINT8 val_ac_luminance[] =
+    { 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 };
+  
+  static const UINT8 bits_ac_chrominance[17] =
+    { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 };
+  static const UINT8 val_ac_chrominance[] =
+    { 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 };
+  
+  add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[0],
+		 bits_dc_luminance, val_dc_luminance);
+  add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[0],
+		 bits_ac_luminance, val_ac_luminance);
+  add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[1],
+		 bits_dc_chrominance, val_dc_chrominance);
+  add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[1],
+		 bits_ac_chrominance, val_ac_chrominance);
+}
+
+
+/*
+ * Default parameter setup for compression.
+ *
+ * Applications that don't choose to use this routine must do their
+ * own setup of all these parameters.  Alternately, you can call this
+ * to establish defaults and then alter parameters selectively.  This
+ * is the recommended approach since, if we add any new parameters,
+ * your code will still work (they'll be set to reasonable defaults).
+ */
+
+GLOBAL(void)
+jpeg_set_defaults (j_compress_ptr cinfo)
+{
+  int i;
+
+  /* Safety check to ensure start_compress not called yet. */
+  if (cinfo->global_state != CSTATE_START)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+
+  /* Allocate comp_info array large enough for maximum component count.
+   * Array is made permanent in case application wants to compress
+   * multiple images at same param settings.
+   */
+  if (cinfo->comp_info == NULL)
+    cinfo->comp_info = (jpeg_component_info *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+				  MAX_COMPONENTS * SIZEOF(jpeg_component_info));
+
+  /* Initialize everything not dependent on the color space */
+
+  cinfo->data_precision = BITS_IN_JSAMPLE;
+  /* Set up two quantization tables using default quality of 75 */
+  jpeg_set_quality(cinfo, 75, TRUE);
+  /* Set up two Huffman tables */
+  std_huff_tables(cinfo);
+
+  /* Initialize default arithmetic coding conditioning */
+  for (i = 0; i < NUM_ARITH_TBLS; i++) {
+    cinfo->arith_dc_L[i] = 0;
+    cinfo->arith_dc_U[i] = 1;
+    cinfo->arith_ac_K[i] = 5;
+  }
+
+  /* Default is no multiple-scan output */
+  cinfo->scan_info = NULL;
+  cinfo->num_scans = 0;
+
+  /* Expect normal source image, not raw downsampled data */
+  cinfo->raw_data_in = FALSE;
+
+  /* Use Huffman coding, not arithmetic coding, by default */
+  cinfo->arith_code = FALSE;
+
+  /* By default, don't do extra passes to optimize entropy coding */
+  cinfo->optimize_coding = FALSE;
+  /* The standard Huffman tables are only valid for 8-bit data precision.
+   * If the precision is higher, force optimization on so that usable
+   * tables will be computed.  This test can be removed if default tables
+   * are supplied that are valid for the desired precision.
+   */
+  if (cinfo->data_precision > 8)
+    cinfo->optimize_coding = TRUE;
+
+  /* By default, use the simpler non-cosited sampling alignment */
+  cinfo->CCIR601_sampling = FALSE;
+
+  /* No input smoothing */
+  cinfo->smoothing_factor = 0;
+
+  /* DCT algorithm preference */
+  cinfo->dct_method = JDCT_DEFAULT;
+
+  /* No restart markers */
+  cinfo->restart_interval = 0;
+  cinfo->restart_in_rows = 0;
+
+  /* Fill in default JFIF marker parameters.  Note that whether the marker
+   * will actually be written is determined by jpeg_set_colorspace.
+   *
+   * By default, the library emits JFIF version code 1.01.
+   * An application that wants to emit JFIF 1.02 extension markers should set
+   * JFIF_minor_version to 2.  We could probably get away with just defaulting
+   * to 1.02, but there may still be some decoders in use that will complain
+   * about that; saying 1.01 should minimize compatibility problems.
+   */
+  cinfo->JFIF_major_version = 1; /* Default JFIF version = 1.01 */
+  cinfo->JFIF_minor_version = 1;
+  cinfo->density_unit = 0;	/* Pixel size is unknown by default */
+  cinfo->X_density = 1;		/* Pixel aspect ratio is square by default */
+  cinfo->Y_density = 1;
+
+  /* Choose JPEG colorspace based on input space, set defaults accordingly */
+
+  jpeg_default_colorspace(cinfo);
+}
+
+
+/*
+ * Select an appropriate JPEG colorspace for in_color_space.
+ */
+
+GLOBAL(void)
+jpeg_default_colorspace (j_compress_ptr cinfo)
+{
+  switch (cinfo->in_color_space) {
+  case JCS_GRAYSCALE:
+    jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
+    break;
+  case JCS_RGB:
+    jpeg_set_colorspace(cinfo, JCS_YCbCr);
+    break;
+  case JCS_YCbCr:
+    jpeg_set_colorspace(cinfo, JCS_YCbCr);
+    break;
+  case JCS_CMYK:
+    jpeg_set_colorspace(cinfo, JCS_CMYK); /* By default, no translation */
+    break;
+  case JCS_YCCK:
+    jpeg_set_colorspace(cinfo, JCS_YCCK);
+    break;
+  case JCS_UNKNOWN:
+    jpeg_set_colorspace(cinfo, JCS_UNKNOWN);
+    break;
+  default:
+    ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
+  }
+}
+
+
+/*
+ * Set the JPEG colorspace, and choose colorspace-dependent default values.
+ */
+
+GLOBAL(void)
+jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace)
+{
+  jpeg_component_info * compptr;
+  int ci;
+
+#define SET_COMP(index,id,hsamp,vsamp,quant,dctbl,actbl)  \
+  (compptr = &cinfo->comp_info[index], \
+   compptr->component_id = (id), \
+   compptr->h_samp_factor = (hsamp), \
+   compptr->v_samp_factor = (vsamp), \
+   compptr->quant_tbl_no = (quant), \
+   compptr->dc_tbl_no = (dctbl), \
+   compptr->ac_tbl_no = (actbl) )
+
+  /* Safety check to ensure start_compress not called yet. */
+  if (cinfo->global_state != CSTATE_START)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+
+  /* For all colorspaces, we use Q and Huff tables 0 for luminance components,
+   * tables 1 for chrominance components.
+   */
+
+  cinfo->jpeg_color_space = colorspace;
+
+  cinfo->write_JFIF_header = FALSE; /* No marker for non-JFIF colorspaces */
+  cinfo->write_Adobe_marker = FALSE; /* write no Adobe marker by default */
+
+  switch (colorspace) {
+  case JCS_GRAYSCALE:
+    cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */
+    cinfo->num_components = 1;
+    /* JFIF specifies component ID 1 */
+    SET_COMP(0, 1, 1,1, 0, 0,0);
+    break;
+  case JCS_RGB:
+    cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag RGB */
+    cinfo->num_components = 3;
+    SET_COMP(0, 0x52 /* 'R' */, 1,1, 0, 0,0);
+    SET_COMP(1, 0x47 /* 'G' */, 1,1, 0, 0,0);
+    SET_COMP(2, 0x42 /* 'B' */, 1,1, 0, 0,0);
+    break;
+  case JCS_YCbCr:
+    cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */
+    cinfo->num_components = 3;
+    /* JFIF specifies component IDs 1,2,3 */
+    /* We default to 2x2 subsamples of chrominance */
+    SET_COMP(0, 1, 2,2, 0, 0,0);
+    SET_COMP(1, 2, 1,1, 1, 1,1);
+    SET_COMP(2, 3, 1,1, 1, 1,1);
+    break;
+  case JCS_CMYK:
+    cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag CMYK */
+    cinfo->num_components = 4;
+    SET_COMP(0, 0x43 /* 'C' */, 1,1, 0, 0,0);
+    SET_COMP(1, 0x4D /* 'M' */, 1,1, 0, 0,0);
+    SET_COMP(2, 0x59 /* 'Y' */, 1,1, 0, 0,0);
+    SET_COMP(3, 0x4B /* 'K' */, 1,1, 0, 0,0);
+    break;
+  case JCS_YCCK:
+    cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag YCCK */
+    cinfo->num_components = 4;
+    SET_COMP(0, 1, 2,2, 0, 0,0);
+    SET_COMP(1, 2, 1,1, 1, 1,1);
+    SET_COMP(2, 3, 1,1, 1, 1,1);
+    SET_COMP(3, 4, 2,2, 0, 0,0);
+    break;
+  case JCS_UNKNOWN:
+    cinfo->num_components = cinfo->input_components;
+    if (cinfo->num_components < 1 || cinfo->num_components > MAX_COMPONENTS)
+      ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components,
+	       MAX_COMPONENTS);
+    for (ci = 0; ci < cinfo->num_components; ci++) {
+      SET_COMP(ci, ci, 1,1, 0, 0,0);
+    }
+    break;
+  default:
+    ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+  }
+}
+
+
+#ifdef C_PROGRESSIVE_SUPPORTED
+
+LOCAL(jpeg_scan_info *)
+fill_a_scan (jpeg_scan_info * scanptr, int ci,
+	     int Ss, int Se, int Ah, int Al)
+/* Support routine: generate one scan for specified component */
+{
+  scanptr->comps_in_scan = 1;
+  scanptr->component_index[0] = ci;
+  scanptr->Ss = Ss;
+  scanptr->Se = Se;
+  scanptr->Ah = Ah;
+  scanptr->Al = Al;
+  scanptr++;
+  return scanptr;
+}
+
+LOCAL(jpeg_scan_info *)
+fill_scans (jpeg_scan_info * scanptr, int ncomps,
+	    int Ss, int Se, int Ah, int Al)
+/* Support routine: generate one scan for each component */
+{
+  int ci;
+
+  for (ci = 0; ci < ncomps; ci++) {
+    scanptr->comps_in_scan = 1;
+    scanptr->component_index[0] = ci;
+    scanptr->Ss = Ss;
+    scanptr->Se = Se;
+    scanptr->Ah = Ah;
+    scanptr->Al = Al;
+    scanptr++;
+  }
+  return scanptr;
+}
+
+LOCAL(jpeg_scan_info *)
+fill_dc_scans (jpeg_scan_info * scanptr, int ncomps, int Ah, int Al)
+/* Support routine: generate interleaved DC scan if possible, else N scans */
+{
+  int ci;
+
+  if (ncomps <= MAX_COMPS_IN_SCAN) {
+    /* Single interleaved DC scan */
+    scanptr->comps_in_scan = ncomps;
+    for (ci = 0; ci < ncomps; ci++)
+      scanptr->component_index[ci] = ci;
+    scanptr->Ss = scanptr->Se = 0;
+    scanptr->Ah = Ah;
+    scanptr->Al = Al;
+    scanptr++;
+  } else {
+    /* Noninterleaved DC scan for each component */
+    scanptr = fill_scans(scanptr, ncomps, 0, 0, Ah, Al);
+  }
+  return scanptr;
+}
+
+
+/*
+ * Create a recommended progressive-JPEG script.
+ * cinfo->num_components and cinfo->jpeg_color_space must be correct.
+ */
+
+GLOBAL(void)
+jpeg_simple_progression (j_compress_ptr cinfo)
+{
+  int ncomps = cinfo->num_components;
+  int nscans;
+  jpeg_scan_info * scanptr;
+
+  /* Safety check to ensure start_compress not called yet. */
+  if (cinfo->global_state != CSTATE_START)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+
+  /* Figure space needed for script.  Calculation must match code below! */
+  if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) {
+    /* Custom script for YCbCr color images. */
+    nscans = 10;
+  } else {
+    /* All-purpose script for other color spaces. */
+    if (ncomps > MAX_COMPS_IN_SCAN)
+      nscans = 6 * ncomps;	/* 2 DC + 4 AC scans per component */
+    else
+      nscans = 2 + 4 * ncomps;	/* 2 DC scans; 4 AC scans per component */
+  }
+
+  /* Allocate space for script.
+   * We need to put it in the permanent pool in case the application performs
+   * multiple compressions without changing the settings.  To avoid a memory
+   * leak if jpeg_simple_progression is called repeatedly for the same JPEG
+   * object, we try to re-use previously allocated space, and we allocate
+   * enough space to handle YCbCr even if initially asked for grayscale.
+   */
+  if (cinfo->script_space == NULL || cinfo->script_space_size < nscans) {
+    cinfo->script_space_size = MAX(nscans, 10);
+    cinfo->script_space = (jpeg_scan_info *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+			cinfo->script_space_size * SIZEOF(jpeg_scan_info));
+  }
+  scanptr = cinfo->script_space;
+  cinfo->scan_info = scanptr;
+  cinfo->num_scans = nscans;
+
+  if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) {
+    /* Custom script for YCbCr color images. */
+    /* Initial DC scan */
+    scanptr = fill_dc_scans(scanptr, ncomps, 0, 1);
+    /* Initial AC scan: get some luma data out in a hurry */
+    scanptr = fill_a_scan(scanptr, 0, 1, 5, 0, 2);
+    /* Chroma data is too small to be worth expending many scans on */
+    scanptr = fill_a_scan(scanptr, 2, 1, 63, 0, 1);
+    scanptr = fill_a_scan(scanptr, 1, 1, 63, 0, 1);
+    /* Complete spectral selection for luma AC */
+    scanptr = fill_a_scan(scanptr, 0, 6, 63, 0, 2);
+    /* Refine next bit of luma AC */
+    scanptr = fill_a_scan(scanptr, 0, 1, 63, 2, 1);
+    /* Finish DC successive approximation */
+    scanptr = fill_dc_scans(scanptr, ncomps, 1, 0);
+    /* Finish AC successive approximation */
+    scanptr = fill_a_scan(scanptr, 2, 1, 63, 1, 0);
+    scanptr = fill_a_scan(scanptr, 1, 1, 63, 1, 0);
+    /* Luma bottom bit comes last since it's usually largest scan */
+    scanptr = fill_a_scan(scanptr, 0, 1, 63, 1, 0);
+  } else {
+    /* All-purpose script for other color spaces. */
+    /* Successive approximation first pass */
+    scanptr = fill_dc_scans(scanptr, ncomps, 0, 1);
+    scanptr = fill_scans(scanptr, ncomps, 1, 5, 0, 2);
+    scanptr = fill_scans(scanptr, ncomps, 6, 63, 0, 2);
+    /* Successive approximation second pass */
+    scanptr = fill_scans(scanptr, ncomps, 1, 63, 2, 1);
+    /* Successive approximation final pass */
+    scanptr = fill_dc_scans(scanptr, ncomps, 1, 0);
+    scanptr = fill_scans(scanptr, ncomps, 1, 63, 1, 0);
+  }
+}
+
+#endif /* C_PROGRESSIVE_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jcphuff.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jcphuff.c
new file mode 100644
index 0000000000..07f9178b01
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jcphuff.c
@@ -0,0 +1,833 @@
+/*
+ * jcphuff.c
+ *
+ * Copyright (C) 1995-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.
+ *
+ * This file contains Huffman entropy encoding routines for progressive JPEG.
+ *
+ * We do not support output suspension in this module, since the library
+ * currently does not allow multiple-scan files to be written with output
+ * suspension.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jchuff.h"		/* Declarations shared with jchuff.c */
+
+#ifdef C_PROGRESSIVE_SUPPORTED
+
+/* Expanded entropy encoder object for progressive Huffman encoding. */
+
+typedef struct {
+  struct jpeg_entropy_encoder pub; /* public fields */
+
+  /* Mode flag: TRUE for optimization, FALSE for actual data output */
+  boolean gather_statistics;
+
+  /* Bit-level coding status.
+   * next_output_byte/free_in_buffer are local copies of cinfo->dest fields.
+   */
+  JOCTET * next_output_byte;	/* => next byte to write in buffer */
+  size_t free_in_buffer;	/* # of byte spaces remaining in buffer */
+  INT32 put_buffer;		/* current bit-accumulation buffer */
+  int put_bits;			/* # of bits now in it */
+  j_compress_ptr cinfo;		/* link to cinfo (needed for dump_buffer) */
+
+  /* Coding status for DC components */
+  int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */
+
+  /* Coding status for AC components */
+  int ac_tbl_no;		/* the table number of the single component */
+  unsigned int EOBRUN;		/* run length of EOBs */
+  unsigned int BE;		/* # of buffered correction bits before MCU */
+  char * bit_buffer;		/* buffer for correction bits (1 per char) */
+  /* packing correction bits tightly would save some space but cost time... */
+
+  unsigned int restarts_to_go;	/* MCUs left in this restart interval */
+  int next_restart_num;		/* next restart number to write (0-7) */
+
+  /* Pointers to derived tables (these workspaces have image lifespan).
+   * Since any one scan codes only DC or only AC, we only need one set
+   * of tables, not one for DC and one for AC.
+   */
+  c_derived_tbl * derived_tbls[NUM_HUFF_TBLS];
+
+  /* Statistics tables for optimization; again, one set is enough */
+  long * count_ptrs[NUM_HUFF_TBLS];
+} phuff_entropy_encoder;
+
+typedef phuff_entropy_encoder * phuff_entropy_ptr;
+
+/* MAX_CORR_BITS is the number of bits the AC refinement correction-bit
+ * buffer can hold.  Larger sizes may slightly improve compression, but
+ * 1000 is already well into the realm of overkill.
+ * The minimum safe size is 64 bits.
+ */
+
+#define MAX_CORR_BITS  1000	/* Max # of correction bits I can buffer */
+
+/* IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than INT32.
+ * We assume that int right shift is unsigned if INT32 right shift is,
+ * which should be safe.
+ */
+
+#ifdef RIGHT_SHIFT_IS_UNSIGNED
+#define ISHIFT_TEMPS	int ishift_temp;
+#define IRIGHT_SHIFT(x,shft)  \
+	((ishift_temp = (x)) < 0 ? \
+	 (ishift_temp >> (shft)) | ((~0) << (16-(shft))) : \
+	 (ishift_temp >> (shft)))
+#else
+#define ISHIFT_TEMPS
+#define IRIGHT_SHIFT(x,shft)	((x) >> (shft))
+#endif
+
+/* Forward declarations */
+METHODDEF(boolean) encode_mcu_DC_first JPP((j_compress_ptr cinfo,
+					    JBLOCKROW *MCU_data));
+METHODDEF(boolean) encode_mcu_AC_first JPP((j_compress_ptr cinfo,
+					    JBLOCKROW *MCU_data));
+METHODDEF(boolean) encode_mcu_DC_refine JPP((j_compress_ptr cinfo,
+					     JBLOCKROW *MCU_data));
+METHODDEF(boolean) encode_mcu_AC_refine JPP((j_compress_ptr cinfo,
+					     JBLOCKROW *MCU_data));
+METHODDEF(void) finish_pass_phuff JPP((j_compress_ptr cinfo));
+METHODDEF(void) finish_pass_gather_phuff JPP((j_compress_ptr cinfo));
+
+
+/*
+ * Initialize for a Huffman-compressed scan using progressive JPEG.
+ */
+
+METHODDEF(void)
+start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics)
+{  
+  phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
+  boolean is_DC_band;
+  int ci, tbl;
+  jpeg_component_info * compptr;
+
+  entropy->cinfo = cinfo;
+  entropy->gather_statistics = gather_statistics;
+
+  is_DC_band = (cinfo->Ss == 0);
+
+  /* We assume jcmaster.c already validated the scan parameters. */
+
+  /* Select execution routines */
+  if (cinfo->Ah == 0) {
+    if (is_DC_band)
+      entropy->pub.encode_mcu = encode_mcu_DC_first;
+    else
+      entropy->pub.encode_mcu = encode_mcu_AC_first;
+  } else {
+    if (is_DC_band)
+      entropy->pub.encode_mcu = encode_mcu_DC_refine;
+    else {
+      entropy->pub.encode_mcu = encode_mcu_AC_refine;
+      /* AC refinement needs a correction bit buffer */
+      if (entropy->bit_buffer == NULL)
+	entropy->bit_buffer = (char *)
+	  (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				      MAX_CORR_BITS * SIZEOF(char));
+    }
+  }
+  if (gather_statistics)
+    entropy->pub.finish_pass = finish_pass_gather_phuff;
+  else
+    entropy->pub.finish_pass = finish_pass_phuff;
+
+  /* Only DC coefficients may be interleaved, so cinfo->comps_in_scan = 1
+   * for AC coefficients.
+   */
+  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+    compptr = cinfo->cur_comp_info[ci];
+    /* Initialize DC predictions to 0 */
+    entropy->last_dc_val[ci] = 0;
+    /* Get table index */
+    if (is_DC_band) {
+      if (cinfo->Ah != 0)	/* DC refinement needs no table */
+	continue;
+      tbl = compptr->dc_tbl_no;
+    } else {
+      entropy->ac_tbl_no = tbl = compptr->ac_tbl_no;
+    }
+    if (gather_statistics) {
+      /* Check for invalid table index */
+      /* (make_c_derived_tbl does this in the other path) */
+      if (tbl < 0 || tbl >= NUM_HUFF_TBLS)
+        ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tbl);
+      /* Allocate and zero the statistics tables */
+      /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */
+      if (entropy->count_ptrs[tbl] == NULL)
+	entropy->count_ptrs[tbl] = (long *)
+	  (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				      257 * SIZEOF(long));
+      MEMZERO(entropy->count_ptrs[tbl], 257 * SIZEOF(long));
+    } else {
+      /* Compute derived values for Huffman table */
+      /* We may do this more than once for a table, but it's not expensive */
+      jpeg_make_c_derived_tbl(cinfo, is_DC_band, tbl,
+			      & entropy->derived_tbls[tbl]);
+    }
+  }
+
+  /* Initialize AC stuff */
+  entropy->EOBRUN = 0;
+  entropy->BE = 0;
+
+  /* Initialize bit buffer to empty */
+  entropy->put_buffer = 0;
+  entropy->put_bits = 0;
+
+  /* Initialize restart stuff */
+  entropy->restarts_to_go = cinfo->restart_interval;
+  entropy->next_restart_num = 0;
+}
+
+
+/* Outputting bytes to the file.
+ * NB: these must be called only when actually outputting,
+ * that is, entropy->gather_statistics == FALSE.
+ */
+
+/* Emit a byte */
+#define emit_byte(entropy,val)  \
+	{ *(entropy)->next_output_byte++ = (JOCTET) (val);  \
+	  if (--(entropy)->free_in_buffer == 0)  \
+	    dump_buffer(entropy); }
+
+
+LOCAL(void)
+dump_buffer (phuff_entropy_ptr entropy)
+/* Empty the output buffer; we do not support suspension in this module. */
+{
+  struct jpeg_destination_mgr * dest = entropy->cinfo->dest;
+
+  if (! (*dest->empty_output_buffer) (entropy->cinfo))
+    ERREXIT(entropy->cinfo, JERR_CANT_SUSPEND);
+  /* After a successful buffer dump, must reset buffer pointers */
+  entropy->next_output_byte = dest->next_output_byte;
+  entropy->free_in_buffer = dest->free_in_buffer;
+}
+
+
+/* Outputting bits to the file */
+
+/* Only the right 24 bits of put_buffer are used; the valid bits are
+ * left-justified in this part.  At most 16 bits can be passed to emit_bits
+ * in one call, and we never retain more than 7 bits in put_buffer
+ * between calls, so 24 bits are sufficient.
+ */
+
+INLINE
+LOCAL(void)
+emit_bits (phuff_entropy_ptr entropy, unsigned int code, int size)
+/* Emit some bits, unless we are in gather mode */
+{
+  /* This routine is heavily used, so it's worth coding tightly. */
+  register INT32 put_buffer = (INT32) code;
+  register int put_bits = entropy->put_bits;
+
+  /* if size is 0, caller used an invalid Huffman table entry */
+  if (size == 0)
+    ERREXIT(entropy->cinfo, JERR_HUFF_MISSING_CODE);
+
+  if (entropy->gather_statistics)
+    return;			/* do nothing if we're only getting stats */
+
+  put_buffer &= (((INT32) 1)<<size) - 1; /* mask off any extra bits in code */
+  
+  put_bits += size;		/* new number of bits in buffer */
+  
+  put_buffer <<= 24 - put_bits; /* align incoming bits */
+
+  put_buffer |= entropy->put_buffer; /* and merge with old buffer contents */
+
+  while (put_bits >= 8) {
+    int c = (int) ((put_buffer >> 16) & 0xFF);
+    
+    emit_byte(entropy, c);
+    if (c == 0xFF) {		/* need to stuff a zero byte? */
+      emit_byte(entropy, 0);
+    }
+    put_buffer <<= 8;
+    put_bits -= 8;
+  }
+
+  entropy->put_buffer = put_buffer; /* update variables */
+  entropy->put_bits = put_bits;
+}
+
+
+LOCAL(void)
+flush_bits (phuff_entropy_ptr entropy)
+{
+  emit_bits(entropy, 0x7F, 7); /* fill any partial byte with ones */
+  entropy->put_buffer = 0;     /* and reset bit-buffer to empty */
+  entropy->put_bits = 0;
+}
+
+
+/*
+ * Emit (or just count) a Huffman symbol.
+ */
+
+INLINE
+LOCAL(void)
+emit_symbol (phuff_entropy_ptr entropy, int tbl_no, int symbol)
+{
+  if (entropy->gather_statistics)
+    entropy->count_ptrs[tbl_no][symbol]++;
+  else {
+    c_derived_tbl * tbl = entropy->derived_tbls[tbl_no];
+    emit_bits(entropy, tbl->ehufco[symbol], tbl->ehufsi[symbol]);
+  }
+}
+
+
+/*
+ * Emit bits from a correction bit buffer.
+ */
+
+LOCAL(void)
+emit_buffered_bits (phuff_entropy_ptr entropy, char * bufstart,
+		    unsigned int nbits)
+{
+  if (entropy->gather_statistics)
+    return;			/* no real work */
+
+  while (nbits > 0) {
+    emit_bits(entropy, (unsigned int) (*bufstart), 1);
+    bufstart++;
+    nbits--;
+  }
+}
+
+
+/*
+ * Emit any pending EOBRUN symbol.
+ */
+
+LOCAL(void)
+emit_eobrun (phuff_entropy_ptr entropy)
+{
+  register int temp, nbits;
+
+  if (entropy->EOBRUN > 0) {	/* if there is any pending EOBRUN */
+    temp = entropy->EOBRUN;
+    nbits = 0;
+    while ((temp >>= 1))
+      nbits++;
+    /* safety check: shouldn't happen given limited correction-bit buffer */
+    if (nbits > 14)
+      ERREXIT(entropy->cinfo, JERR_HUFF_MISSING_CODE);
+
+    emit_symbol(entropy, entropy->ac_tbl_no, nbits << 4);
+    if (nbits)
+      emit_bits(entropy, entropy->EOBRUN, nbits);
+
+    entropy->EOBRUN = 0;
+
+    /* Emit any buffered correction bits */
+    emit_buffered_bits(entropy, entropy->bit_buffer, entropy->BE);
+    entropy->BE = 0;
+  }
+}
+
+
+/*
+ * Emit a restart marker & resynchronize predictions.
+ */
+
+LOCAL(void)
+emit_restart (phuff_entropy_ptr entropy, int restart_num)
+{
+  int ci;
+
+  emit_eobrun(entropy);
+
+  if (! entropy->gather_statistics) {
+    flush_bits(entropy);
+    emit_byte(entropy, 0xFF);
+    emit_byte(entropy, JPEG_RST0 + restart_num);
+  }
+
+  if (entropy->cinfo->Ss == 0) {
+    /* Re-initialize DC predictions to 0 */
+    for (ci = 0; ci < entropy->cinfo->comps_in_scan; ci++)
+      entropy->last_dc_val[ci] = 0;
+  } else {
+    /* Re-initialize all AC-related fields to 0 */
+    entropy->EOBRUN = 0;
+    entropy->BE = 0;
+  }
+}
+
+
+/*
+ * MCU encoding for DC initial scan (either spectral selection,
+ * or first pass of successive approximation).
+ */
+
+METHODDEF(boolean)
+encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data)
+{
+  phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
+  register int temp, temp2;
+  register int nbits;
+  int blkn, ci;
+  int Al = cinfo->Al;
+  JBLOCKROW block;
+  jpeg_component_info * compptr;
+  ISHIFT_TEMPS
+
+  entropy->next_output_byte = cinfo->dest->next_output_byte;
+  entropy->free_in_buffer = cinfo->dest->free_in_buffer;
+
+  /* Emit restart marker if needed */
+  if (cinfo->restart_interval)
+    if (entropy->restarts_to_go == 0)
+      emit_restart(entropy, entropy->next_restart_num);
+
+  /* Encode the MCU data blocks */
+  for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
+    block = MCU_data[blkn];
+    ci = cinfo->MCU_membership[blkn];
+    compptr = cinfo->cur_comp_info[ci];
+
+    /* Compute the DC value after the required point transform by Al.
+     * This is simply an arithmetic right shift.
+     */
+    temp2 = IRIGHT_SHIFT((int) ((*block)[0]), Al);
+
+    /* DC differences are figured on the point-transformed values. */
+    temp = temp2 - entropy->last_dc_val[ci];
+    entropy->last_dc_val[ci] = temp2;
+
+    /* Encode the DC coefficient difference per section G.1.2.1 */
+    temp2 = temp;
+    if (temp < 0) {
+      temp = -temp;		/* temp is abs value of input */
+      /* For a negative input, want temp2 = bitwise complement of abs(input) */
+      /* This code assumes we are on a two's complement machine */
+      temp2--;
+    }
+    
+    /* Find the number of bits needed for the magnitude of the coefficient */
+    nbits = 0;
+    while (temp) {
+      nbits++;
+      temp >>= 1;
+    }
+    /* Check for out-of-range coefficient values.
+     * Since we're encoding a difference, the range limit is twice as much.
+     */
+    if (nbits > MAX_COEF_BITS+1)
+      ERREXIT(cinfo, JERR_BAD_DCT_COEF);
+    
+    /* Count/emit the Huffman-coded symbol for the number of bits */
+    emit_symbol(entropy, compptr->dc_tbl_no, nbits);
+    
+    /* Emit that number of bits of the value, if positive, */
+    /* or the complement of its magnitude, if negative. */
+    if (nbits)			/* emit_bits rejects calls with size 0 */
+      emit_bits(entropy, (unsigned int) temp2, nbits);
+  }
+
+  cinfo->dest->next_output_byte = entropy->next_output_byte;
+  cinfo->dest->free_in_buffer = entropy->free_in_buffer;
+
+  /* Update restart-interval state too */
+  if (cinfo->restart_interval) {
+    if (entropy->restarts_to_go == 0) {
+      entropy->restarts_to_go = cinfo->restart_interval;
+      entropy->next_restart_num++;
+      entropy->next_restart_num &= 7;
+    }
+    entropy->restarts_to_go--;
+  }
+
+  return TRUE;
+}
+
+
+/*
+ * MCU encoding for AC initial scan (either spectral selection,
+ * or first pass of successive approximation).
+ */
+
+METHODDEF(boolean)
+encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data)
+{
+  phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
+  register int temp, temp2;
+  register int nbits;
+  register int r, k;
+  int Se = cinfo->Se;
+  int Al = cinfo->Al;
+  JBLOCKROW block;
+
+  entropy->next_output_byte = cinfo->dest->next_output_byte;
+  entropy->free_in_buffer = cinfo->dest->free_in_buffer;
+
+  /* Emit restart marker if needed */
+  if (cinfo->restart_interval)
+    if (entropy->restarts_to_go == 0)
+      emit_restart(entropy, entropy->next_restart_num);
+
+  /* Encode the MCU data block */
+  block = MCU_data[0];
+
+  /* Encode the AC coefficients per section G.1.2.2, fig. G.3 */
+  
+  r = 0;			/* r = run length of zeros */
+   
+  for (k = cinfo->Ss; k <= Se; k++) {
+    if ((temp = (*block)[jpeg_natural_order[k]]) == 0) {
+      r++;
+      continue;
+    }
+    /* We must apply the point transform by Al.  For AC coefficients this
+     * is an integer division with rounding towards 0.  To do this portably
+     * in C, we shift after obtaining the absolute value; so the code is
+     * interwoven with finding the abs value (temp) and output bits (temp2).
+     */
+    if (temp < 0) {
+      temp = -temp;		/* temp is abs value of input */
+      temp >>= Al;		/* apply the point transform */
+      /* For a negative coef, want temp2 = bitwise complement of abs(coef) */
+      temp2 = ~temp;
+    } else {
+      temp >>= Al;		/* apply the point transform */
+      temp2 = temp;
+    }
+    /* Watch out for case that nonzero coef is zero after point transform */
+    if (temp == 0) {
+      r++;
+      continue;
+    }
+
+    /* Emit any pending EOBRUN */
+    if (entropy->EOBRUN > 0)
+      emit_eobrun(entropy);
+    /* if run length > 15, must emit special run-length-16 codes (0xF0) */
+    while (r > 15) {
+      emit_symbol(entropy, entropy->ac_tbl_no, 0xF0);
+      r -= 16;
+    }
+
+    /* Find the number of bits needed for the magnitude of the coefficient */
+    nbits = 1;			/* there must be at least one 1 bit */
+    while ((temp >>= 1))
+      nbits++;
+    /* Check for out-of-range coefficient values */
+    if (nbits > MAX_COEF_BITS)
+      ERREXIT(cinfo, JERR_BAD_DCT_COEF);
+
+    /* Count/emit Huffman symbol for run length / number of bits */
+    emit_symbol(entropy, entropy->ac_tbl_no, (r << 4) + nbits);
+
+    /* Emit that number of bits of the value, if positive, */
+    /* or the complement of its magnitude, if negative. */
+    emit_bits(entropy, (unsigned int) temp2, nbits);
+
+    r = 0;			/* reset zero run length */
+  }
+
+  if (r > 0) {			/* If there are trailing zeroes, */
+    entropy->EOBRUN++;		/* count an EOB */
+    if (entropy->EOBRUN == 0x7FFF)
+      emit_eobrun(entropy);	/* force it out to avoid overflow */
+  }
+
+  cinfo->dest->next_output_byte = entropy->next_output_byte;
+  cinfo->dest->free_in_buffer = entropy->free_in_buffer;
+
+  /* Update restart-interval state too */
+  if (cinfo->restart_interval) {
+    if (entropy->restarts_to_go == 0) {
+      entropy->restarts_to_go = cinfo->restart_interval;
+      entropy->next_restart_num++;
+      entropy->next_restart_num &= 7;
+    }
+    entropy->restarts_to_go--;
+  }
+
+  return TRUE;
+}
+
+
+/*
+ * MCU encoding for DC successive approximation refinement scan.
+ * Note: we assume such scans can be multi-component, although the spec
+ * is not very clear on the point.
+ */
+
+METHODDEF(boolean)
+encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data)
+{
+  phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
+  register int temp;
+  int blkn;
+  int Al = cinfo->Al;
+  JBLOCKROW block;
+
+  entropy->next_output_byte = cinfo->dest->next_output_byte;
+  entropy->free_in_buffer = cinfo->dest->free_in_buffer;
+
+  /* Emit restart marker if needed */
+  if (cinfo->restart_interval)
+    if (entropy->restarts_to_go == 0)
+      emit_restart(entropy, entropy->next_restart_num);
+
+  /* Encode the MCU data blocks */
+  for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
+    block = MCU_data[blkn];
+
+    /* We simply emit the Al'th bit of the DC coefficient value. */
+    temp = (*block)[0];
+    emit_bits(entropy, (unsigned int) (temp >> Al), 1);
+  }
+
+  cinfo->dest->next_output_byte = entropy->next_output_byte;
+  cinfo->dest->free_in_buffer = entropy->free_in_buffer;
+
+  /* Update restart-interval state too */
+  if (cinfo->restart_interval) {
+    if (entropy->restarts_to_go == 0) {
+      entropy->restarts_to_go = cinfo->restart_interval;
+      entropy->next_restart_num++;
+      entropy->next_restart_num &= 7;
+    }
+    entropy->restarts_to_go--;
+  }
+
+  return TRUE;
+}
+
+
+/*
+ * MCU encoding for AC successive approximation refinement scan.
+ */
+
+METHODDEF(boolean)
+encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data)
+{
+  phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
+  register int temp;
+  register int r, k;
+  int EOB;
+  char *BR_buffer;
+  unsigned int BR;
+  int Se = cinfo->Se;
+  int Al = cinfo->Al;
+  JBLOCKROW block;
+  int absvalues[DCTSIZE2];
+
+  entropy->next_output_byte = cinfo->dest->next_output_byte;
+  entropy->free_in_buffer = cinfo->dest->free_in_buffer;
+
+  /* Emit restart marker if needed */
+  if (cinfo->restart_interval)
+    if (entropy->restarts_to_go == 0)
+      emit_restart(entropy, entropy->next_restart_num);
+
+  /* Encode the MCU data block */
+  block = MCU_data[0];
+
+  /* It is convenient to make a pre-pass to determine the transformed
+   * coefficients' absolute values and the EOB position.
+   */
+  EOB = 0;
+  for (k = cinfo->Ss; k <= Se; k++) {
+    temp = (*block)[jpeg_natural_order[k]];
+    /* We must apply the point transform by Al.  For AC coefficients this
+     * is an integer division with rounding towards 0.  To do this portably
+     * in C, we shift after obtaining the absolute value.
+     */
+    if (temp < 0)
+      temp = -temp;		/* temp is abs value of input */
+    temp >>= Al;		/* apply the point transform */
+    absvalues[k] = temp;	/* save abs value for main pass */
+    if (temp == 1)
+      EOB = k;			/* EOB = index of last newly-nonzero coef */
+  }
+
+  /* Encode the AC coefficients per section G.1.2.3, fig. G.7 */
+  
+  r = 0;			/* r = run length of zeros */
+  BR = 0;			/* BR = count of buffered bits added now */
+  BR_buffer = entropy->bit_buffer + entropy->BE; /* Append bits to buffer */
+
+  for (k = cinfo->Ss; k <= Se; k++) {
+    if ((temp = absvalues[k]) == 0) {
+      r++;
+      continue;
+    }
+
+    /* Emit any required ZRLs, but not if they can be folded into EOB */
+    while (r > 15 && k <= EOB) {
+      /* emit any pending EOBRUN and the BE correction bits */
+      emit_eobrun(entropy);
+      /* Emit ZRL */
+      emit_symbol(entropy, entropy->ac_tbl_no, 0xF0);
+      r -= 16;
+      /* Emit buffered correction bits that must be associated with ZRL */
+      emit_buffered_bits(entropy, BR_buffer, BR);
+      BR_buffer = entropy->bit_buffer; /* BE bits are gone now */
+      BR = 0;
+    }
+
+    /* If the coef was previously nonzero, it only needs a correction bit.
+     * NOTE: a straight translation of the spec's figure G.7 would suggest
+     * that we also need to test r > 15.  But if r > 15, we can only get here
+     * if k > EOB, which implies that this coefficient is not 1.
+     */
+    if (temp > 1) {
+      /* The correction bit is the next bit of the absolute value. */
+      BR_buffer[BR++] = (char) (temp & 1);
+      continue;
+    }
+
+    /* Emit any pending EOBRUN and the BE correction bits */
+    emit_eobrun(entropy);
+
+    /* Count/emit Huffman symbol for run length / number of bits */
+    emit_symbol(entropy, entropy->ac_tbl_no, (r << 4) + 1);
+
+    /* Emit output bit for newly-nonzero coef */
+    temp = ((*block)[jpeg_natural_order[k]] < 0) ? 0 : 1;
+    emit_bits(entropy, (unsigned int) temp, 1);
+
+    /* Emit buffered correction bits that must be associated with this code */
+    emit_buffered_bits(entropy, BR_buffer, BR);
+    BR_buffer = entropy->bit_buffer; /* BE bits are gone now */
+    BR = 0;
+    r = 0;			/* reset zero run length */
+  }
+
+  if (r > 0 || BR > 0) {	/* If there are trailing zeroes, */
+    entropy->EOBRUN++;		/* count an EOB */
+    entropy->BE += BR;		/* concat my correction bits to older ones */
+    /* We force out the EOB if we risk either:
+     * 1. overflow of the EOB counter;
+     * 2. overflow of the correction bit buffer during the next MCU.
+     */
+    if (entropy->EOBRUN == 0x7FFF || entropy->BE > (MAX_CORR_BITS-DCTSIZE2+1))
+      emit_eobrun(entropy);
+  }
+
+  cinfo->dest->next_output_byte = entropy->next_output_byte;
+  cinfo->dest->free_in_buffer = entropy->free_in_buffer;
+
+  /* Update restart-interval state too */
+  if (cinfo->restart_interval) {
+    if (entropy->restarts_to_go == 0) {
+      entropy->restarts_to_go = cinfo->restart_interval;
+      entropy->next_restart_num++;
+      entropy->next_restart_num &= 7;
+    }
+    entropy->restarts_to_go--;
+  }
+
+  return TRUE;
+}
+
+
+/*
+ * Finish up at the end of a Huffman-compressed progressive scan.
+ */
+
+METHODDEF(void)
+finish_pass_phuff (j_compress_ptr cinfo)
+{   
+  phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
+
+  entropy->next_output_byte = cinfo->dest->next_output_byte;
+  entropy->free_in_buffer = cinfo->dest->free_in_buffer;
+
+  /* Flush out any buffered data */
+  emit_eobrun(entropy);
+  flush_bits(entropy);
+
+  cinfo->dest->next_output_byte = entropy->next_output_byte;
+  cinfo->dest->free_in_buffer = entropy->free_in_buffer;
+}
+
+
+/*
+ * Finish up a statistics-gathering pass and create the new Huffman tables.
+ */
+
+METHODDEF(void)
+finish_pass_gather_phuff (j_compress_ptr cinfo)
+{
+  phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
+  boolean is_DC_band;
+  int ci, tbl;
+  jpeg_component_info * compptr;
+  JHUFF_TBL **htblptr;
+  boolean did[NUM_HUFF_TBLS];
+
+  /* Flush out buffered data (all we care about is counting the EOB symbol) */
+  emit_eobrun(entropy);
+
+  is_DC_band = (cinfo->Ss == 0);
+
+  /* It's important not to apply jpeg_gen_optimal_table more than once
+   * per table, because it clobbers the input frequency counts!
+   */
+  MEMZERO(did, SIZEOF(did));
+
+  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+    compptr = cinfo->cur_comp_info[ci];
+    if (is_DC_band) {
+      if (cinfo->Ah != 0)	/* DC refinement needs no table */
+	continue;
+      tbl = compptr->dc_tbl_no;
+    } else {
+      tbl = compptr->ac_tbl_no;
+    }
+    if (! did[tbl]) {
+      if (is_DC_band)
+        htblptr = & cinfo->dc_huff_tbl_ptrs[tbl];
+      else
+        htblptr = & cinfo->ac_huff_tbl_ptrs[tbl];
+      if (*htblptr == NULL)
+        *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo);
+      jpeg_gen_optimal_table(cinfo, *htblptr, entropy->count_ptrs[tbl]);
+      did[tbl] = TRUE;
+    }
+  }
+}
+
+
+/*
+ * Module initialization routine for progressive Huffman entropy encoding.
+ */
+
+GLOBAL(void)
+jinit_phuff_encoder (j_compress_ptr cinfo)
+{
+  phuff_entropy_ptr entropy;
+  int i;
+
+  entropy = (phuff_entropy_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(phuff_entropy_encoder));
+  cinfo->entropy = (struct jpeg_entropy_encoder *) entropy;
+  entropy->pub.start_pass = start_pass_phuff;
+
+  /* Mark tables unallocated */
+  for (i = 0; i < NUM_HUFF_TBLS; i++) {
+    entropy->derived_tbls[i] = NULL;
+    entropy->count_ptrs[i] = NULL;
+  }
+  entropy->bit_buffer = NULL;	/* needed only in AC refinement scan */
+}
+
+#endif /* C_PROGRESSIVE_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jcprepct.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jcprepct.c
new file mode 100644
index 0000000000..fa93333db2
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jcprepct.c
@@ -0,0 +1,354 @@
+/*
+ * jcprepct.c
+ *
+ * Copyright (C) 1994-1996, 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 compression preprocessing controller.
+ * This controller manages the color conversion, downsampling,
+ * and edge expansion steps.
+ *
+ * Most of the complexity here is associated with buffering input rows
+ * as required by the downsampler.  See the comments at the head of
+ * jcsample.c for the downsampler's needs.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/* At present, jcsample.c can request context rows only for smoothing.
+ * In the future, we might also need context rows for CCIR601 sampling
+ * or other more-complex downsampling procedures.  The code to support
+ * context rows should be compiled only if needed.
+ */
+#ifdef INPUT_SMOOTHING_SUPPORTED
+#define CONTEXT_ROWS_SUPPORTED
+#endif
+
+
+/*
+ * For the simple (no-context-row) case, we just need to buffer one
+ * row group's worth of pixels for the downsampling step.  At the bottom of
+ * the image, we pad to a full row group by replicating the last pixel row.
+ * The downsampler's last output row is then replicated if needed to pad
+ * out to a full iMCU row.
+ *
+ * When providing context rows, we must buffer three row groups' worth of
+ * pixels.  Three row groups are physically allocated, but the row pointer
+ * arrays are made five row groups high, with the extra pointers above and
+ * below "wrapping around" to point to the last and first real row groups.
+ * This allows the downsampler to access the proper context rows.
+ * At the top and bottom of the image, we create dummy context rows by
+ * copying the first or last real pixel row.  This copying could be avoided
+ * by pointer hacking as is done in jdmainct.c, but it doesn't seem worth the
+ * trouble on the compression side.
+ */
+
+
+/* Private buffer controller object */
+
+typedef struct {
+  struct jpeg_c_prep_controller pub; /* public fields */
+
+  /* Downsampling input buffer.  This buffer holds color-converted data
+   * until we have enough to do a downsample step.
+   */
+  JSAMPARRAY color_buf[MAX_COMPONENTS];
+
+  JDIMENSION rows_to_go;	/* counts rows remaining in source image */
+  int next_buf_row;		/* index of next row to store in color_buf */
+
+#ifdef CONTEXT_ROWS_SUPPORTED	/* only needed for context case */
+  int this_row_group;		/* starting row index of group to process */
+  int next_buf_stop;		/* downsample when we reach this index */
+#endif
+} my_prep_controller;
+
+typedef my_prep_controller * my_prep_ptr;
+
+
+/*
+ * Initialize for a processing pass.
+ */
+
+METHODDEF(void)
+start_pass_prep (j_compress_ptr cinfo, J_BUF_MODE pass_mode)
+{
+  my_prep_ptr prep = (my_prep_ptr) cinfo->prep;
+
+  if (pass_mode != JBUF_PASS_THRU)
+    ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+
+  /* Initialize total-height counter for detecting bottom of image */
+  prep->rows_to_go = cinfo->image_height;
+  /* Mark the conversion buffer empty */
+  prep->next_buf_row = 0;
+#ifdef CONTEXT_ROWS_SUPPORTED
+  /* Preset additional state variables for context mode.
+   * These aren't used in non-context mode, so we needn't test which mode.
+   */
+  prep->this_row_group = 0;
+  /* Set next_buf_stop to stop after two row groups have been read in. */
+  prep->next_buf_stop = 2 * cinfo->max_v_samp_factor;
+#endif
+}
+
+
+/*
+ * Expand an image vertically from height input_rows to height output_rows,
+ * by duplicating the bottom row.
+ */
+
+LOCAL(void)
+expand_bottom_edge (JSAMPARRAY image_data, JDIMENSION num_cols,
+		    int input_rows, int output_rows)
+{
+  register int row;
+
+  for (row = input_rows; row < output_rows; row++) {
+    jcopy_sample_rows(image_data, input_rows-1, image_data, row,
+		      1, num_cols);
+  }
+}
+
+
+/*
+ * Process some data in the simple no-context case.
+ *
+ * Preprocessor output data is counted in "row groups".  A row group
+ * is defined to be v_samp_factor sample rows of each component.
+ * Downsampling will produce this much data from each max_v_samp_factor
+ * input rows.
+ */
+
+METHODDEF(void)
+pre_process_data (j_compress_ptr cinfo,
+		  JSAMPARRAY input_buf, JDIMENSION *in_row_ctr,
+		  JDIMENSION in_rows_avail,
+		  JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr,
+		  JDIMENSION out_row_groups_avail)
+{
+  my_prep_ptr prep = (my_prep_ptr) cinfo->prep;
+  int numrows, ci;
+  JDIMENSION inrows;
+  jpeg_component_info * compptr;
+
+  while (*in_row_ctr < in_rows_avail &&
+	 *out_row_group_ctr < out_row_groups_avail) {
+    /* Do color conversion to fill the conversion buffer. */
+    inrows = in_rows_avail - *in_row_ctr;
+    numrows = cinfo->max_v_samp_factor - prep->next_buf_row;
+    numrows = (int) MIN((JDIMENSION) numrows, inrows);
+    (*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr,
+				       prep->color_buf,
+				       (JDIMENSION) prep->next_buf_row,
+				       numrows);
+    *in_row_ctr += numrows;
+    prep->next_buf_row += numrows;
+    prep->rows_to_go -= numrows;
+    /* If at bottom of image, pad to fill the conversion buffer. */
+    if (prep->rows_to_go == 0 &&
+	prep->next_buf_row < cinfo->max_v_samp_factor) {
+      for (ci = 0; ci < cinfo->num_components; ci++) {
+	expand_bottom_edge(prep->color_buf[ci], cinfo->image_width,
+			   prep->next_buf_row, cinfo->max_v_samp_factor);
+      }
+      prep->next_buf_row = cinfo->max_v_samp_factor;
+    }
+    /* If we've filled the conversion buffer, empty it. */
+    if (prep->next_buf_row == cinfo->max_v_samp_factor) {
+      (*cinfo->downsample->downsample) (cinfo,
+					prep->color_buf, (JDIMENSION) 0,
+					output_buf, *out_row_group_ctr);
+      prep->next_buf_row = 0;
+      (*out_row_group_ctr)++;
+    }
+    /* If at bottom of image, pad the output to a full iMCU height.
+     * Note we assume the caller is providing a one-iMCU-height output buffer!
+     */
+    if (prep->rows_to_go == 0 &&
+	*out_row_group_ctr < out_row_groups_avail) {
+      for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+	   ci++, compptr++) {
+	expand_bottom_edge(output_buf[ci],
+			   compptr->width_in_blocks * DCTSIZE,
+			   (int) (*out_row_group_ctr * compptr->v_samp_factor),
+			   (int) (out_row_groups_avail * compptr->v_samp_factor));
+      }
+      *out_row_group_ctr = out_row_groups_avail;
+      break;			/* can exit outer loop without test */
+    }
+  }
+}
+
+
+#ifdef CONTEXT_ROWS_SUPPORTED
+
+/*
+ * Process some data in the context case.
+ */
+
+METHODDEF(void)
+pre_process_context (j_compress_ptr cinfo,
+		     JSAMPARRAY input_buf, JDIMENSION *in_row_ctr,
+		     JDIMENSION in_rows_avail,
+		     JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr,
+		     JDIMENSION out_row_groups_avail)
+{
+  my_prep_ptr prep = (my_prep_ptr) cinfo->prep;
+  int numrows, ci;
+  int buf_height = cinfo->max_v_samp_factor * 3;
+  JDIMENSION inrows;
+
+  while (*out_row_group_ctr < out_row_groups_avail) {
+    if (*in_row_ctr < in_rows_avail) {
+      /* Do color conversion to fill the conversion buffer. */
+      inrows = in_rows_avail - *in_row_ctr;
+      numrows = prep->next_buf_stop - prep->next_buf_row;
+      numrows = (int) MIN((JDIMENSION) numrows, inrows);
+      (*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr,
+					 prep->color_buf,
+					 (JDIMENSION) prep->next_buf_row,
+					 numrows);
+      /* Pad at top of image, if first time through */
+      if (prep->rows_to_go == cinfo->image_height) {
+	for (ci = 0; ci < cinfo->num_components; ci++) {
+	  int row;
+	  for (row = 1; row <= cinfo->max_v_samp_factor; row++) {
+	    jcopy_sample_rows(prep->color_buf[ci], 0,
+			      prep->color_buf[ci], -row,
+			      1, cinfo->image_width);
+	  }
+	}
+      }
+      *in_row_ctr += numrows;
+      prep->next_buf_row += numrows;
+      prep->rows_to_go -= numrows;
+    } else {
+      /* Return for more data, unless we are at the bottom of the image. */
+      if (prep->rows_to_go != 0)
+	break;
+      /* When at bottom of image, pad to fill the conversion buffer. */
+      if (prep->next_buf_row < prep->next_buf_stop) {
+	for (ci = 0; ci < cinfo->num_components; ci++) {
+	  expand_bottom_edge(prep->color_buf[ci], cinfo->image_width,
+			     prep->next_buf_row, prep->next_buf_stop);
+	}
+	prep->next_buf_row = prep->next_buf_stop;
+      }
+    }
+    /* If we've gotten enough data, downsample a row group. */
+    if (prep->next_buf_row == prep->next_buf_stop) {
+      (*cinfo->downsample->downsample) (cinfo,
+					prep->color_buf,
+					(JDIMENSION) prep->this_row_group,
+					output_buf, *out_row_group_ctr);
+      (*out_row_group_ctr)++;
+      /* Advance pointers with wraparound as necessary. */
+      prep->this_row_group += cinfo->max_v_samp_factor;
+      if (prep->this_row_group >= buf_height)
+	prep->this_row_group = 0;
+      if (prep->next_buf_row >= buf_height)
+	prep->next_buf_row = 0;
+      prep->next_buf_stop = prep->next_buf_row + cinfo->max_v_samp_factor;
+    }
+  }
+}
+
+
+/*
+ * Create the wrapped-around downsampling input buffer needed for context mode.
+ */
+
+LOCAL(void)
+create_context_buffer (j_compress_ptr cinfo)
+{
+  my_prep_ptr prep = (my_prep_ptr) cinfo->prep;
+  int rgroup_height = cinfo->max_v_samp_factor;
+  int ci, i;
+  jpeg_component_info * compptr;
+  JSAMPARRAY true_buffer, fake_buffer;
+
+  /* Grab enough space for fake row pointers for all the components;
+   * we need five row groups' worth of pointers for each component.
+   */
+  fake_buffer = (JSAMPARRAY)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				(cinfo->num_components * 5 * rgroup_height) *
+				SIZEOF(JSAMPROW));
+
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    /* Allocate the actual buffer space (3 row groups) for this component.
+     * We make the buffer wide enough to allow the downsampler to edge-expand
+     * horizontally within the buffer, if it so chooses.
+     */
+    true_buffer = (*cinfo->mem->alloc_sarray)
+      ((j_common_ptr) cinfo, JPOOL_IMAGE,
+       (JDIMENSION) (((long) compptr->width_in_blocks * DCTSIZE *
+		      cinfo->max_h_samp_factor) / compptr->h_samp_factor),
+       (JDIMENSION) (3 * rgroup_height));
+    /* Copy true buffer row pointers into the middle of the fake row array */
+    MEMCOPY(fake_buffer + rgroup_height, true_buffer,
+	    3 * rgroup_height * SIZEOF(JSAMPROW));
+    /* Fill in the above and below wraparound pointers */
+    for (i = 0; i < rgroup_height; i++) {
+      fake_buffer[i] = true_buffer[2 * rgroup_height + i];
+      fake_buffer[4 * rgroup_height + i] = true_buffer[i];
+    }
+    prep->color_buf[ci] = fake_buffer + rgroup_height;
+    fake_buffer += 5 * rgroup_height; /* point to space for next component */
+  }
+}
+
+#endif /* CONTEXT_ROWS_SUPPORTED */
+
+
+/*
+ * Initialize preprocessing controller.
+ */
+
+GLOBAL(void)
+jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer)
+{
+  my_prep_ptr prep;
+  int ci;
+  jpeg_component_info * compptr;
+
+  if (need_full_buffer)		/* safety check */
+    ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+
+  prep = (my_prep_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_prep_controller));
+  cinfo->prep = (struct jpeg_c_prep_controller *) prep;
+  prep->pub.start_pass = start_pass_prep;
+
+  /* Allocate the color conversion buffer.
+   * We make the buffer wide enough to allow the downsampler to edge-expand
+   * horizontally within the buffer, if it so chooses.
+   */
+  if (cinfo->downsample->need_context_rows) {
+    /* Set up to provide context rows */
+#ifdef CONTEXT_ROWS_SUPPORTED
+    prep->pub.pre_process_data = pre_process_context;
+    create_context_buffer(cinfo);
+#else
+    ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif
+  } else {
+    /* No context, just make it tall enough for one row group */
+    prep->pub.pre_process_data = pre_process_data;
+    for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+	 ci++, compptr++) {
+      prep->color_buf[ci] = (*cinfo->mem->alloc_sarray)
+	((j_common_ptr) cinfo, JPOOL_IMAGE,
+	 (JDIMENSION) (((long) compptr->width_in_blocks * DCTSIZE *
+			cinfo->max_h_samp_factor) / compptr->h_samp_factor),
+	 (JDIMENSION) cinfo->max_v_samp_factor);
+    }
+  }
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jcsample.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jcsample.c
new file mode 100644
index 0000000000..212ec8757c
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jcsample.c
@@ -0,0 +1,519 @@
+/*
+ * jcsample.c
+ *
+ * Copyright (C) 1991-1996, 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 downsampling routines.
+ *
+ * Downsampling input data is counted in "row groups".  A row group
+ * is defined to be max_v_samp_factor pixel rows of each component,
+ * from which the downsampler produces v_samp_factor sample rows.
+ * A single row group is processed in each call to the downsampler module.
+ *
+ * The downsampler is responsible for edge-expansion of its output data
+ * to fill an integral number of DCT blocks horizontally.  The source buffer
+ * may be modified if it is helpful for this purpose (the source buffer is
+ * allocated wide enough to correspond to the desired output width).
+ * The caller (the prep controller) is responsible for vertical padding.
+ *
+ * The downsampler may request "context rows" by setting need_context_rows
+ * during startup.  In this case, the input arrays will contain at least
+ * one row group's worth of pixels above and below the passed-in data;
+ * the caller will create dummy rows at image top and bottom by replicating
+ * the first or last real pixel row.
+ *
+ * An excellent reference for image resampling is
+ *   Digital Image Warping, George Wolberg, 1990.
+ *   Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7.
+ *
+ * The downsampling algorithm used here is a simple average of the source
+ * pixels covered by the output pixel.  The hi-falutin sampling literature
+ * refers to this as a "box filter".  In general the characteristics of a box
+ * filter are not very good, but for the specific cases we normally use (1:1
+ * and 2:1 ratios) the box is equivalent to a "triangle filter" which is not
+ * nearly so bad.  If you intend to use other sampling ratios, you'd be well
+ * advised to improve this code.
+ *
+ * A simple input-smoothing capability is provided.  This is mainly intended
+ * for cleaning up color-dithered GIF input files (if you find it inadequate,
+ * we suggest using an external filtering program such as pnmconvol).  When
+ * enabled, each input pixel P is replaced by a weighted sum of itself and its
+ * eight neighbors.  P's weight is 1-8*SF and each neighbor's weight is SF,
+ * where SF = (smoothing_factor / 1024).
+ * Currently, smoothing is only supported for 2h2v sampling factors.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/* Pointer to routine to downsample a single component */
+typedef JMETHOD(void, downsample1_ptr,
+		(j_compress_ptr cinfo, jpeg_component_info * compptr,
+		 JSAMPARRAY input_data, JSAMPARRAY output_data));
+
+/* Private subobject */
+
+typedef struct {
+  struct jpeg_downsampler pub;	/* public fields */
+
+  /* Downsampling method pointers, one per component */
+  downsample1_ptr methods[MAX_COMPONENTS];
+} my_downsampler;
+
+typedef my_downsampler * my_downsample_ptr;
+
+
+/*
+ * Initialize for a downsampling pass.
+ */
+
+METHODDEF(void)
+start_pass_downsample (j_compress_ptr cinfo)
+{
+  /* no work for now */
+}
+
+
+/*
+ * Expand a component horizontally from width input_cols to width output_cols,
+ * by duplicating the rightmost samples.
+ */
+
+LOCAL(void)
+expand_right_edge (JSAMPARRAY image_data, int num_rows,
+		   JDIMENSION input_cols, JDIMENSION output_cols)
+{
+  register JSAMPROW ptr;
+  register JSAMPLE pixval;
+  register int count;
+  int row;
+  int numcols = (int) (output_cols - input_cols);
+
+  if (numcols > 0) {
+    for (row = 0; row < num_rows; row++) {
+      ptr = image_data[row] + input_cols;
+      pixval = ptr[-1];		/* don't need GETJSAMPLE() here */
+      for (count = numcols; count > 0; count--)
+	*ptr++ = pixval;
+    }
+  }
+}
+
+
+/*
+ * Do downsampling for a whole row group (all components).
+ *
+ * In this version we simply downsample each component independently.
+ */
+
+METHODDEF(void)
+sep_downsample (j_compress_ptr cinfo,
+		JSAMPIMAGE input_buf, JDIMENSION in_row_index,
+		JSAMPIMAGE output_buf, JDIMENSION out_row_group_index)
+{
+  my_downsample_ptr downsample = (my_downsample_ptr) cinfo->downsample;
+  int ci;
+  jpeg_component_info * compptr;
+  JSAMPARRAY in_ptr, out_ptr;
+
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    in_ptr = input_buf[ci] + in_row_index;
+    out_ptr = output_buf[ci] + (out_row_group_index * compptr->v_samp_factor);
+    (*downsample->methods[ci]) (cinfo, compptr, in_ptr, out_ptr);
+  }
+}
+
+
+/*
+ * Downsample pixel values of a single component.
+ * One row group is processed per call.
+ * This version handles arbitrary integral sampling ratios, without smoothing.
+ * Note that this version is not actually used for customary sampling ratios.
+ */
+
+METHODDEF(void)
+int_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr,
+		JSAMPARRAY input_data, JSAMPARRAY output_data)
+{
+  int inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v;
+  JDIMENSION outcol, outcol_h;	/* outcol_h == outcol*h_expand */
+  JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
+  JSAMPROW inptr, outptr;
+  INT32 outvalue;
+
+  h_expand = cinfo->max_h_samp_factor / compptr->h_samp_factor;
+  v_expand = cinfo->max_v_samp_factor / compptr->v_samp_factor;
+  numpix = h_expand * v_expand;
+  numpix2 = numpix/2;
+
+  /* Expand input data enough to let all the output samples be generated
+   * by the standard loop.  Special-casing padded output would be more
+   * efficient.
+   */
+  expand_right_edge(input_data, cinfo->max_v_samp_factor,
+		    cinfo->image_width, output_cols * h_expand);
+
+  inrow = 0;
+  for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) {
+    outptr = output_data[outrow];
+    for (outcol = 0, outcol_h = 0; outcol < output_cols;
+	 outcol++, outcol_h += h_expand) {
+      outvalue = 0;
+      for (v = 0; v < v_expand; v++) {
+	inptr = input_data[inrow+v] + outcol_h;
+	for (h = 0; h < h_expand; h++) {
+	  outvalue += (INT32) GETJSAMPLE(*inptr++);
+	}
+      }
+      *outptr++ = (JSAMPLE) ((outvalue + numpix2) / numpix);
+    }
+    inrow += v_expand;
+  }
+}
+
+
+/*
+ * Downsample pixel values of a single component.
+ * This version handles the special case of a full-size component,
+ * without smoothing.
+ */
+
+METHODDEF(void)
+fullsize_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr,
+		     JSAMPARRAY input_data, JSAMPARRAY output_data)
+{
+  /* Copy the data */
+  jcopy_sample_rows(input_data, 0, output_data, 0,
+		    cinfo->max_v_samp_factor, cinfo->image_width);
+  /* Edge-expand */
+  expand_right_edge(output_data, cinfo->max_v_samp_factor,
+		    cinfo->image_width, compptr->width_in_blocks * DCTSIZE);
+}
+
+
+/*
+ * Downsample pixel values of a single component.
+ * This version handles the common case of 2:1 horizontal and 1:1 vertical,
+ * without smoothing.
+ *
+ * A note about the "bias" calculations: when rounding fractional values to
+ * integer, we do not want to always round 0.5 up to the next integer.
+ * If we did that, we'd introduce a noticeable bias towards larger values.
+ * Instead, this code is arranged so that 0.5 will be rounded up or down at
+ * alternate pixel locations (a simple ordered dither pattern).
+ */
+
+METHODDEF(void)
+h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr,
+		 JSAMPARRAY input_data, JSAMPARRAY output_data)
+{
+  int outrow;
+  JDIMENSION outcol;
+  JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
+  register JSAMPROW inptr, outptr;
+  register int bias;
+
+  /* Expand input data enough to let all the output samples be generated
+   * by the standard loop.  Special-casing padded output would be more
+   * efficient.
+   */
+  expand_right_edge(input_data, cinfo->max_v_samp_factor,
+		    cinfo->image_width, output_cols * 2);
+
+  for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) {
+    outptr = output_data[outrow];
+    inptr = input_data[outrow];
+    bias = 0;			/* bias = 0,1,0,1,... for successive samples */
+    for (outcol = 0; outcol < output_cols; outcol++) {
+      *outptr++ = (JSAMPLE) ((GETJSAMPLE(*inptr) + GETJSAMPLE(inptr[1])
+			      + bias) >> 1);
+      bias ^= 1;		/* 0=>1, 1=>0 */
+      inptr += 2;
+    }
+  }
+}
+
+
+/*
+ * Downsample pixel values of a single component.
+ * This version handles the standard case of 2:1 horizontal and 2:1 vertical,
+ * without smoothing.
+ */
+
+METHODDEF(void)
+h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr,
+		 JSAMPARRAY input_data, JSAMPARRAY output_data)
+{
+  int inrow, outrow;
+  JDIMENSION outcol;
+  JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
+  register JSAMPROW inptr0, inptr1, outptr;
+  register int bias;
+
+  /* Expand input data enough to let all the output samples be generated
+   * by the standard loop.  Special-casing padded output would be more
+   * efficient.
+   */
+  expand_right_edge(input_data, cinfo->max_v_samp_factor,
+		    cinfo->image_width, output_cols * 2);
+
+  inrow = 0;
+  for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) {
+    outptr = output_data[outrow];
+    inptr0 = input_data[inrow];
+    inptr1 = input_data[inrow+1];
+    bias = 1;			/* bias = 1,2,1,2,... for successive samples */
+    for (outcol = 0; outcol < output_cols; outcol++) {
+      *outptr++ = (JSAMPLE) ((GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) +
+			      GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1])
+			      + bias) >> 2);
+      bias ^= 3;		/* 1=>2, 2=>1 */
+      inptr0 += 2; inptr1 += 2;
+    }
+    inrow += 2;
+  }
+}
+
+
+#ifdef INPUT_SMOOTHING_SUPPORTED
+
+/*
+ * Downsample pixel values of a single component.
+ * This version handles the standard case of 2:1 horizontal and 2:1 vertical,
+ * with smoothing.  One row of context is required.
+ */
+
+METHODDEF(void)
+h2v2_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr,
+			JSAMPARRAY input_data, JSAMPARRAY output_data)
+{
+  int inrow, outrow;
+  JDIMENSION colctr;
+  JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
+  register JSAMPROW inptr0, inptr1, above_ptr, below_ptr, outptr;
+  INT32 membersum, neighsum, memberscale, neighscale;
+
+  /* Expand input data enough to let all the output samples be generated
+   * by the standard loop.  Special-casing padded output would be more
+   * efficient.
+   */
+  expand_right_edge(input_data - 1, cinfo->max_v_samp_factor + 2,
+		    cinfo->image_width, output_cols * 2);
+
+  /* We don't bother to form the individual "smoothed" input pixel values;
+   * we can directly compute the output which is the average of the four
+   * smoothed values.  Each of the four member pixels contributes a fraction
+   * (1-8*SF) to its own smoothed image and a fraction SF to each of the three
+   * other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final
+   * output.  The four corner-adjacent neighbor pixels contribute a fraction
+   * SF to just one smoothed pixel, or SF/4 to the final output; while the
+   * eight edge-adjacent neighbors contribute SF to each of two smoothed
+   * pixels, or SF/2 overall.  In order to use integer arithmetic, these
+   * factors are scaled by 2^16 = 65536.
+   * Also recall that SF = smoothing_factor / 1024.
+   */
+
+  memberscale = 16384 - cinfo->smoothing_factor * 80; /* scaled (1-5*SF)/4 */
+  neighscale = cinfo->smoothing_factor * 16; /* scaled SF/4 */
+
+  inrow = 0;
+  for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) {
+    outptr = output_data[outrow];
+    inptr0 = input_data[inrow];
+    inptr1 = input_data[inrow+1];
+    above_ptr = input_data[inrow-1];
+    below_ptr = input_data[inrow+2];
+
+    /* Special case for first column: pretend column -1 is same as column 0 */
+    membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) +
+		GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]);
+    neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) +
+	       GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) +
+	       GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[2]) +
+	       GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[2]);
+    neighsum += neighsum;
+    neighsum += GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[2]) +
+		GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[2]);
+    membersum = membersum * memberscale + neighsum * neighscale;
+    *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16);
+    inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2;
+
+    for (colctr = output_cols - 2; colctr > 0; colctr--) {
+      /* sum of pixels directly mapped to this output element */
+      membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) +
+		  GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]);
+      /* sum of edge-neighbor pixels */
+      neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) +
+		 GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) +
+		 GETJSAMPLE(inptr0[-1]) + GETJSAMPLE(inptr0[2]) +
+		 GETJSAMPLE(inptr1[-1]) + GETJSAMPLE(inptr1[2]);
+      /* The edge-neighbors count twice as much as corner-neighbors */
+      neighsum += neighsum;
+      /* Add in the corner-neighbors */
+      neighsum += GETJSAMPLE(above_ptr[-1]) + GETJSAMPLE(above_ptr[2]) +
+		  GETJSAMPLE(below_ptr[-1]) + GETJSAMPLE(below_ptr[2]);
+      /* form final output scaled up by 2^16 */
+      membersum = membersum * memberscale + neighsum * neighscale;
+      /* round, descale and output it */
+      *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16);
+      inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2;
+    }
+
+    /* Special case for last column */
+    membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) +
+		GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]);
+    neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) +
+	       GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) +
+	       GETJSAMPLE(inptr0[-1]) + GETJSAMPLE(inptr0[1]) +
+	       GETJSAMPLE(inptr1[-1]) + GETJSAMPLE(inptr1[1]);
+    neighsum += neighsum;
+    neighsum += GETJSAMPLE(above_ptr[-1]) + GETJSAMPLE(above_ptr[1]) +
+		GETJSAMPLE(below_ptr[-1]) + GETJSAMPLE(below_ptr[1]);
+    membersum = membersum * memberscale + neighsum * neighscale;
+    *outptr = (JSAMPLE) ((membersum + 32768) >> 16);
+
+    inrow += 2;
+  }
+}
+
+
+/*
+ * Downsample pixel values of a single component.
+ * This version handles the special case of a full-size component,
+ * with smoothing.  One row of context is required.
+ */
+
+METHODDEF(void)
+fullsize_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr,
+			    JSAMPARRAY input_data, JSAMPARRAY output_data)
+{
+  int outrow;
+  JDIMENSION colctr;
+  JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
+  register JSAMPROW inptr, above_ptr, below_ptr, outptr;
+  INT32 membersum, neighsum, memberscale, neighscale;
+  int colsum, lastcolsum, nextcolsum;
+
+  /* Expand input data enough to let all the output samples be generated
+   * by the standard loop.  Special-casing padded output would be more
+   * efficient.
+   */
+  expand_right_edge(input_data - 1, cinfo->max_v_samp_factor + 2,
+		    cinfo->image_width, output_cols);
+
+  /* Each of the eight neighbor pixels contributes a fraction SF to the
+   * smoothed pixel, while the main pixel contributes (1-8*SF).  In order
+   * to use integer arithmetic, these factors are multiplied by 2^16 = 65536.
+   * Also recall that SF = smoothing_factor / 1024.
+   */
+
+  memberscale = 65536L - cinfo->smoothing_factor * 512L; /* scaled 1-8*SF */
+  neighscale = cinfo->smoothing_factor * 64; /* scaled SF */
+
+  for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) {
+    outptr = output_data[outrow];
+    inptr = input_data[outrow];
+    above_ptr = input_data[outrow-1];
+    below_ptr = input_data[outrow+1];
+
+    /* Special case for first column */
+    colsum = GETJSAMPLE(*above_ptr++) + GETJSAMPLE(*below_ptr++) +
+	     GETJSAMPLE(*inptr);
+    membersum = GETJSAMPLE(*inptr++);
+    nextcolsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(*below_ptr) +
+		 GETJSAMPLE(*inptr);
+    neighsum = colsum + (colsum - membersum) + nextcolsum;
+    membersum = membersum * memberscale + neighsum * neighscale;
+    *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16);
+    lastcolsum = colsum; colsum = nextcolsum;
+
+    for (colctr = output_cols - 2; colctr > 0; colctr--) {
+      membersum = GETJSAMPLE(*inptr++);
+      above_ptr++; below_ptr++;
+      nextcolsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(*below_ptr) +
+		   GETJSAMPLE(*inptr);
+      neighsum = lastcolsum + (colsum - membersum) + nextcolsum;
+      membersum = membersum * memberscale + neighsum * neighscale;
+      *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16);
+      lastcolsum = colsum; colsum = nextcolsum;
+    }
+
+    /* Special case for last column */
+    membersum = GETJSAMPLE(*inptr);
+    neighsum = lastcolsum + (colsum - membersum) + colsum;
+    membersum = membersum * memberscale + neighsum * neighscale;
+    *outptr = (JSAMPLE) ((membersum + 32768) >> 16);
+
+  }
+}
+
+#endif /* INPUT_SMOOTHING_SUPPORTED */
+
+
+/*
+ * Module initialization routine for downsampling.
+ * Note that we must select a routine for each component.
+ */
+
+GLOBAL(void)
+jinit_downsampler (j_compress_ptr cinfo)
+{
+  my_downsample_ptr downsample;
+  int ci;
+  jpeg_component_info * compptr;
+  boolean smoothok = TRUE;
+
+  downsample = (my_downsample_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_downsampler));
+  cinfo->downsample = (struct jpeg_downsampler *) downsample;
+  downsample->pub.start_pass = start_pass_downsample;
+  downsample->pub.downsample = sep_downsample;
+  downsample->pub.need_context_rows = FALSE;
+
+  if (cinfo->CCIR601_sampling)
+    ERREXIT(cinfo, JERR_CCIR601_NOTIMPL);
+
+  /* Verify we can handle the sampling factors, and set up method pointers */
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    if (compptr->h_samp_factor == cinfo->max_h_samp_factor &&
+	compptr->v_samp_factor == cinfo->max_v_samp_factor) {
+#ifdef INPUT_SMOOTHING_SUPPORTED
+      if (cinfo->smoothing_factor) {
+	downsample->methods[ci] = fullsize_smooth_downsample;
+	downsample->pub.need_context_rows = TRUE;
+      } else
+#endif
+	downsample->methods[ci] = fullsize_downsample;
+    } else if (compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor &&
+	       compptr->v_samp_factor == cinfo->max_v_samp_factor) {
+      smoothok = FALSE;
+      downsample->methods[ci] = h2v1_downsample;
+    } else if (compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor &&
+	       compptr->v_samp_factor * 2 == cinfo->max_v_samp_factor) {
+#ifdef INPUT_SMOOTHING_SUPPORTED
+      if (cinfo->smoothing_factor) {
+	downsample->methods[ci] = h2v2_smooth_downsample;
+	downsample->pub.need_context_rows = TRUE;
+      } else
+#endif
+	downsample->methods[ci] = h2v2_downsample;
+    } else if ((cinfo->max_h_samp_factor % compptr->h_samp_factor) == 0 &&
+	       (cinfo->max_v_samp_factor % compptr->v_samp_factor) == 0) {
+      smoothok = FALSE;
+      downsample->methods[ci] = int_downsample;
+    } else
+      ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL);
+  }
+
+#ifdef INPUT_SMOOTHING_SUPPORTED
+  if (cinfo->smoothing_factor && !smoothok)
+    TRACEMS(cinfo, 0, JTRC_SMOOTH_NOTIMPL);
+#endif
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jctrans.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jctrans.c
new file mode 100644
index 0000000000..0e6d70769d
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jctrans.c
@@ -0,0 +1,388 @@
+/*
+ * jctrans.c
+ *
+ * Copyright (C) 1995-1998, 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 library routines for transcoding compression,
+ * that is, writing raw DCT coefficient arrays to an output JPEG file.
+ * The routines in jcapimin.c will also be needed by a transcoder.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/* Forward declarations */
+LOCAL(void) transencode_master_selection
+	JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays));
+LOCAL(void) transencode_coef_controller
+	JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays));
+
+
+/*
+ * Compression initialization for writing raw-coefficient data.
+ * Before calling this, all parameters and a data destination must be set up.
+ * Call jpeg_finish_compress() to actually write the data.
+ *
+ * The number of passed virtual arrays must match cinfo->num_components.
+ * Note that the virtual arrays need not be filled or even realized at
+ * the time write_coefficients is called; indeed, if the virtual arrays
+ * were requested from this compression object's memory manager, they
+ * typically will be realized during this routine and filled afterwards.
+ */
+
+GLOBAL(void)
+jpeg_write_coefficients (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)
+{
+  if (cinfo->global_state != CSTATE_START)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+  /* Mark all tables to be written */
+  jpeg_suppress_tables(cinfo, FALSE);
+  /* (Re)initialize error mgr and destination modules */
+  (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo);
+  (*cinfo->dest->init_destination) (cinfo);
+  /* Perform master selection of active modules */
+  transencode_master_selection(cinfo, coef_arrays);
+  /* Wait for jpeg_finish_compress() call */
+  cinfo->next_scanline = 0;	/* so jpeg_write_marker works */
+  cinfo->global_state = CSTATE_WRCOEFS;
+}
+
+
+/*
+ * Initialize the compression object with default parameters,
+ * then copy from the source object all parameters needed for lossless
+ * transcoding.  Parameters that can be varied without loss (such as
+ * scan script and Huffman optimization) are left in their default states.
+ */
+
+GLOBAL(void)
+jpeg_copy_critical_parameters (j_decompress_ptr srcinfo,
+			       j_compress_ptr dstinfo)
+{
+  JQUANT_TBL ** qtblptr;
+  jpeg_component_info *incomp, *outcomp;
+  JQUANT_TBL *c_quant, *slot_quant;
+  int tblno, ci, coefi;
+
+  /* Safety check to ensure start_compress not called yet. */
+  if (dstinfo->global_state != CSTATE_START)
+    ERREXIT1(dstinfo, JERR_BAD_STATE, dstinfo->global_state);
+  /* Copy fundamental image dimensions */
+  dstinfo->image_width = srcinfo->image_width;
+  dstinfo->image_height = srcinfo->image_height;
+  dstinfo->input_components = srcinfo->num_components;
+  dstinfo->in_color_space = srcinfo->jpeg_color_space;
+  /* Initialize all parameters to default values */
+  jpeg_set_defaults(dstinfo);
+  /* jpeg_set_defaults may choose wrong colorspace, eg YCbCr if input is RGB.
+   * Fix it to get the right header markers for the image colorspace.
+   */
+  jpeg_set_colorspace(dstinfo, srcinfo->jpeg_color_space);
+  dstinfo->data_precision = srcinfo->data_precision;
+  dstinfo->CCIR601_sampling = srcinfo->CCIR601_sampling;
+  /* Copy the source's quantization tables. */
+  for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) {
+    if (srcinfo->quant_tbl_ptrs[tblno] != NULL) {
+      qtblptr = & dstinfo->quant_tbl_ptrs[tblno];
+      if (*qtblptr == NULL)
+	*qtblptr = jpeg_alloc_quant_table((j_common_ptr) dstinfo);
+      MEMCOPY((*qtblptr)->quantval,
+	      srcinfo->quant_tbl_ptrs[tblno]->quantval,
+	      SIZEOF((*qtblptr)->quantval));
+      (*qtblptr)->sent_table = FALSE;
+    }
+  }
+  /* Copy the source's per-component info.
+   * Note we assume jpeg_set_defaults has allocated the dest comp_info array.
+   */
+  dstinfo->num_components = srcinfo->num_components;
+  if (dstinfo->num_components < 1 || dstinfo->num_components > MAX_COMPONENTS)
+    ERREXIT2(dstinfo, JERR_COMPONENT_COUNT, dstinfo->num_components,
+	     MAX_COMPONENTS);
+  for (ci = 0, incomp = srcinfo->comp_info, outcomp = dstinfo->comp_info;
+       ci < dstinfo->num_components; ci++, incomp++, outcomp++) {
+    outcomp->component_id = incomp->component_id;
+    outcomp->h_samp_factor = incomp->h_samp_factor;
+    outcomp->v_samp_factor = incomp->v_samp_factor;
+    outcomp->quant_tbl_no = incomp->quant_tbl_no;
+    /* Make sure saved quantization table for component matches the qtable
+     * slot.  If not, the input file re-used this qtable slot.
+     * IJG encoder currently cannot duplicate this.
+     */
+    tblno = outcomp->quant_tbl_no;
+    if (tblno < 0 || tblno >= NUM_QUANT_TBLS ||
+	srcinfo->quant_tbl_ptrs[tblno] == NULL)
+      ERREXIT1(dstinfo, JERR_NO_QUANT_TABLE, tblno);
+    slot_quant = srcinfo->quant_tbl_ptrs[tblno];
+    c_quant = incomp->quant_table;
+    if (c_quant != NULL) {
+      for (coefi = 0; coefi < DCTSIZE2; coefi++) {
+	if (c_quant->quantval[coefi] != slot_quant->quantval[coefi])
+	  ERREXIT1(dstinfo, JERR_MISMATCHED_QUANT_TABLE, tblno);
+      }
+    }
+    /* Note: we do not copy the source's Huffman table assignments;
+     * instead we rely on jpeg_set_colorspace to have made a suitable choice.
+     */
+  }
+  /* Also copy JFIF version and resolution information, if available.
+   * Strictly speaking this isn't "critical" info, but it's nearly
+   * always appropriate to copy it if available.  In particular,
+   * if the application chooses to copy JFIF 1.02 extension markers from
+   * the source file, we need to copy the version to make sure we don't
+   * emit a file that has 1.02 extensions but a claimed version of 1.01.
+   * We will *not*, however, copy version info from mislabeled "2.01" files.
+   */
+  if (srcinfo->saw_JFIF_marker) {
+    if (srcinfo->JFIF_major_version == 1) {
+      dstinfo->JFIF_major_version = srcinfo->JFIF_major_version;
+      dstinfo->JFIF_minor_version = srcinfo->JFIF_minor_version;
+    }
+    dstinfo->density_unit = srcinfo->density_unit;
+    dstinfo->X_density = srcinfo->X_density;
+    dstinfo->Y_density = srcinfo->Y_density;
+  }
+}
+
+
+/*
+ * Master selection of compression modules for transcoding.
+ * This substitutes for jcinit.c's initialization of the full compressor.
+ */
+
+LOCAL(void)
+transencode_master_selection (j_compress_ptr cinfo,
+			      jvirt_barray_ptr * coef_arrays)
+{
+  /* Although we don't actually use input_components for transcoding,
+   * jcmaster.c's initial_setup will complain if input_components is 0.
+   */
+  cinfo->input_components = 1;
+  /* Initialize master control (includes parameter checking/processing) */
+  jinit_c_master_control(cinfo, TRUE /* transcode only */);
+
+  /* Entropy encoding: either Huffman or arithmetic coding. */
+  if (cinfo->arith_code) {
+    ERREXIT(cinfo, JERR_ARITH_NOTIMPL);
+  } else {
+    if (cinfo->progressive_mode) {
+#ifdef C_PROGRESSIVE_SUPPORTED
+      jinit_phuff_encoder(cinfo);
+#else
+      ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif
+    } else
+      jinit_huff_encoder(cinfo);
+  }
+
+  /* We need a special coefficient buffer controller. */
+  transencode_coef_controller(cinfo, coef_arrays);
+
+  jinit_marker_writer(cinfo);
+
+  /* We can now tell the memory manager to allocate virtual arrays. */
+  (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo);
+
+  /* Write the datastream header (SOI, JFIF) immediately.
+   * Frame and scan headers are postponed till later.
+   * This lets application insert special markers after the SOI.
+   */
+  (*cinfo->marker->write_file_header) (cinfo);
+}
+
+
+/*
+ * The rest of this file is a special implementation of the coefficient
+ * buffer controller.  This is similar to jccoefct.c, but it handles only
+ * output from presupplied virtual arrays.  Furthermore, we generate any
+ * dummy padding blocks on-the-fly rather than expecting them to be present
+ * in the arrays.
+ */
+
+/* Private buffer controller object */
+
+typedef struct {
+  struct jpeg_c_coef_controller pub; /* public fields */
+
+  JDIMENSION iMCU_row_num;	/* iMCU row # within image */
+  JDIMENSION mcu_ctr;		/* counts MCUs processed in current row */
+  int MCU_vert_offset;		/* counts MCU rows within iMCU row */
+  int MCU_rows_per_iMCU_row;	/* number of such rows needed */
+
+  /* Virtual block array for each component. */
+  jvirt_barray_ptr * whole_image;
+
+  /* Workspace for constructing dummy blocks at right/bottom edges. */
+  JBLOCKROW dummy_buffer[C_MAX_BLOCKS_IN_MCU];
+} my_coef_controller;
+
+typedef my_coef_controller * my_coef_ptr;
+
+
+LOCAL(void)
+start_iMCU_row (j_compress_ptr cinfo)
+/* Reset within-iMCU-row counters for a new row */
+{
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+
+  /* In an interleaved scan, an MCU row is the same as an iMCU row.
+   * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows.
+   * But at the bottom of the image, process only what's left.
+   */
+  if (cinfo->comps_in_scan > 1) {
+    coef->MCU_rows_per_iMCU_row = 1;
+  } else {
+    if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1))
+      coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor;
+    else
+      coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height;
+  }
+
+  coef->mcu_ctr = 0;
+  coef->MCU_vert_offset = 0;
+}
+
+
+/*
+ * Initialize for a processing pass.
+ */
+
+METHODDEF(void)
+start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode)
+{
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+
+  if (pass_mode != JBUF_CRANK_DEST)
+    ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+
+  coef->iMCU_row_num = 0;
+  start_iMCU_row(cinfo);
+}
+
+
+/*
+ * Process some data.
+ * We process the equivalent of one fully interleaved MCU row ("iMCU" row)
+ * per call, ie, v_samp_factor block rows for each component in the scan.
+ * The data is obtained from the virtual arrays and fed to the entropy coder.
+ * Returns TRUE if the iMCU row is completed, FALSE if suspended.
+ *
+ * NB: input_buf is ignored; it is likely to be a NULL pointer.
+ */
+
+METHODDEF(boolean)
+compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
+{
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+  JDIMENSION MCU_col_num;	/* index of current MCU within row */
+  JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1;
+  JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1;
+  int blkn, ci, xindex, yindex, yoffset, blockcnt;
+  JDIMENSION start_col;
+  JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN];
+  JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU];
+  JBLOCKROW buffer_ptr;
+  jpeg_component_info *compptr;
+
+  /* Align the virtual buffers for the components used in this scan. */
+  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+    compptr = cinfo->cur_comp_info[ci];
+    buffer[ci] = (*cinfo->mem->access_virt_barray)
+      ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index],
+       coef->iMCU_row_num * compptr->v_samp_factor,
+       (JDIMENSION) compptr->v_samp_factor, FALSE);
+  }
+
+  /* Loop to process one whole iMCU row */
+  for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row;
+       yoffset++) {
+    for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row;
+	 MCU_col_num++) {
+      /* Construct list of pointers to DCT blocks belonging to this MCU */
+      blkn = 0;			/* index of current DCT block within MCU */
+      for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+	compptr = cinfo->cur_comp_info[ci];
+	start_col = MCU_col_num * compptr->MCU_width;
+	blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width
+						: compptr->last_col_width;
+	for (yindex = 0; yindex < compptr->MCU_height; yindex++) {
+	  if (coef->iMCU_row_num < last_iMCU_row ||
+	      yindex+yoffset < compptr->last_row_height) {
+	    /* Fill in pointers to real blocks in this row */
+	    buffer_ptr = buffer[ci][yindex+yoffset] + start_col;
+	    for (xindex = 0; xindex < blockcnt; xindex++)
+	      MCU_buffer[blkn++] = buffer_ptr++;
+	  } else {
+	    /* At bottom of image, need a whole row of dummy blocks */
+	    xindex = 0;
+	  }
+	  /* Fill in any dummy blocks needed in this row.
+	   * Dummy blocks are filled in the same way as in jccoefct.c:
+	   * all zeroes in the AC entries, DC entries equal to previous
+	   * block's DC value.  The init routine has already zeroed the
+	   * AC entries, so we need only set the DC entries correctly.
+	   */
+	  for (; xindex < compptr->MCU_width; xindex++) {
+	    MCU_buffer[blkn] = coef->dummy_buffer[blkn];
+	    MCU_buffer[blkn][0][0] = MCU_buffer[blkn-1][0][0];
+	    blkn++;
+	  }
+	}
+      }
+      /* Try to write the MCU. */
+      if (! (*cinfo->entropy->encode_mcu) (cinfo, MCU_buffer)) {
+	/* Suspension forced; update state counters and exit */
+	coef->MCU_vert_offset = yoffset;
+	coef->mcu_ctr = MCU_col_num;
+	return FALSE;
+      }
+    }
+    /* Completed an MCU row, but perhaps not an iMCU row */
+    coef->mcu_ctr = 0;
+  }
+  /* Completed the iMCU row, advance counters for next one */
+  coef->iMCU_row_num++;
+  start_iMCU_row(cinfo);
+  return TRUE;
+}
+
+
+/*
+ * Initialize coefficient buffer controller.
+ *
+ * Each passed coefficient array must be the right size for that
+ * coefficient: width_in_blocks wide and height_in_blocks high,
+ * with unitheight at least v_samp_factor.
+ */
+
+LOCAL(void)
+transencode_coef_controller (j_compress_ptr cinfo,
+			     jvirt_barray_ptr * coef_arrays)
+{
+  my_coef_ptr coef;
+  JBLOCKROW buffer;
+  int i;
+
+  coef = (my_coef_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_coef_controller));
+  cinfo->coef = (struct jpeg_c_coef_controller *) coef;
+  coef->pub.start_pass = start_pass_coef;
+  coef->pub.compress_data = compress_output;
+
+  /* Save pointer to virtual arrays */
+  coef->whole_image = coef_arrays;
+
+  /* Allocate and pre-zero space for dummy DCT blocks. */
+  buffer = (JBLOCKROW)
+    (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK));
+  jzero_far((void FAR *) buffer, C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK));
+  for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) {
+    coef->dummy_buffer[i] = buffer + i;
+  }
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdapimin.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdapimin.c
new file mode 100644
index 0000000000..cadb59fce3
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdapimin.c
@@ -0,0 +1,395 @@
+/*
+ * jdapimin.c
+ *
+ * Copyright (C) 1994-1998, 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 application interface code for the decompression half
+ * of the JPEG library.  These are the "minimum" API routines that may be
+ * needed in either the normal full-decompression case or the
+ * transcoding-only case.
+ *
+ * Most of the routines intended to be called directly by an application
+ * are in this file or in jdapistd.c.  But also see jcomapi.c for routines
+ * shared by compression and decompression, and jdtrans.c for the transcoding
+ * case.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/*
+ * Initialization of a JPEG decompression object.
+ * The error manager must already be set up (in case memory manager fails).
+ */
+
+GLOBAL(void)
+jpeg_CreateDecompress (j_decompress_ptr cinfo, int version, size_t structsize)
+{
+  int i;
+
+  /* Guard against version mismatches between library and caller. */
+  cinfo->mem = NULL;		/* so jpeg_destroy knows mem mgr not called */
+  if (version != JPEG_LIB_VERSION)
+    ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version);
+  if (structsize != SIZEOF(struct jpeg_decompress_struct))
+    ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, 
+	     (int) SIZEOF(struct jpeg_decompress_struct), (int) structsize);
+
+  /* For debugging purposes, we zero the whole master structure.
+   * But the application has already set the err pointer, and may have set
+   * client_data, so we have to save and restore those fields.
+   * Note: if application hasn't set client_data, tools like Purify may
+   * complain here.
+   */
+  {
+    struct jpeg_error_mgr * err = cinfo->err;
+    void * client_data = cinfo->client_data; /* ignore Purify complaint here */
+    MEMZERO(cinfo, SIZEOF(struct jpeg_decompress_struct));
+    cinfo->err = err;
+    cinfo->client_data = client_data;
+  }
+  cinfo->is_decompressor = TRUE;
+
+  /* Initialize a memory manager instance for this object */
+  jinit_memory_mgr((j_common_ptr) cinfo);
+
+  /* Zero out pointers to permanent structures. */
+  cinfo->progress = NULL;
+  cinfo->src = NULL;
+
+  for (i = 0; i < NUM_QUANT_TBLS; i++)
+    cinfo->quant_tbl_ptrs[i] = NULL;
+
+  for (i = 0; i < NUM_HUFF_TBLS; i++) {
+    cinfo->dc_huff_tbl_ptrs[i] = NULL;
+    cinfo->ac_huff_tbl_ptrs[i] = NULL;
+  }
+
+  /* Initialize marker processor so application can override methods
+   * for COM, APPn markers before calling jpeg_read_header.
+   */
+  cinfo->marker_list = NULL;
+  jinit_marker_reader(cinfo);
+
+  /* And initialize the overall input controller. */
+  jinit_input_controller(cinfo);
+
+  /* OK, I'm ready */
+  cinfo->global_state = DSTATE_START;
+}
+
+
+/*
+ * Destruction of a JPEG decompression object
+ */
+
+GLOBAL(void)
+jpeg_destroy_decompress (j_decompress_ptr cinfo)
+{
+  jpeg_destroy((j_common_ptr) cinfo); /* use common routine */
+}
+
+
+/*
+ * Abort processing of a JPEG decompression operation,
+ * but don't destroy the object itself.
+ */
+
+GLOBAL(void)
+jpeg_abort_decompress (j_decompress_ptr cinfo)
+{
+  jpeg_abort((j_common_ptr) cinfo); /* use common routine */
+}
+
+
+/*
+ * Set default decompression parameters.
+ */
+
+LOCAL(void)
+default_decompress_parms (j_decompress_ptr cinfo)
+{
+  /* Guess the input colorspace, and set output colorspace accordingly. */
+  /* (Wish JPEG committee had provided a real way to specify this...) */
+  /* Note application may override our guesses. */
+  switch (cinfo->num_components) {
+  case 1:
+    cinfo->jpeg_color_space = JCS_GRAYSCALE;
+    cinfo->out_color_space = JCS_GRAYSCALE;
+    break;
+    
+  case 3:
+    if (cinfo->saw_JFIF_marker) {
+      cinfo->jpeg_color_space = JCS_YCbCr; /* JFIF implies YCbCr */
+    } else if (cinfo->saw_Adobe_marker) {
+      switch (cinfo->Adobe_transform) {
+      case 0:
+	cinfo->jpeg_color_space = JCS_RGB;
+	break;
+      case 1:
+	cinfo->jpeg_color_space = JCS_YCbCr;
+	break;
+      default:
+	WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform);
+	cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */
+	break;
+      }
+    } else {
+      /* Saw no special markers, try to guess from the component IDs */
+      int cid0 = cinfo->comp_info[0].component_id;
+      int cid1 = cinfo->comp_info[1].component_id;
+      int cid2 = cinfo->comp_info[2].component_id;
+
+      if (cid0 == 1 && cid1 == 2 && cid2 == 3)
+	cinfo->jpeg_color_space = JCS_YCbCr; /* assume JFIF w/out marker */
+      else if (cid0 == 82 && cid1 == 71 && cid2 == 66)
+	cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */
+      else {
+	TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2);
+	cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */
+      }
+    }
+    /* Always guess RGB is proper output colorspace. */
+    cinfo->out_color_space = JCS_RGB;
+    break;
+    
+  case 4:
+    if (cinfo->saw_Adobe_marker) {
+      switch (cinfo->Adobe_transform) {
+      case 0:
+	cinfo->jpeg_color_space = JCS_CMYK;
+	break;
+      case 2:
+	cinfo->jpeg_color_space = JCS_YCCK;
+	break;
+      default:
+	WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform);
+	cinfo->jpeg_color_space = JCS_YCCK; /* assume it's YCCK */
+	break;
+      }
+    } else {
+      /* No special markers, assume straight CMYK. */
+      cinfo->jpeg_color_space = JCS_CMYK;
+    }
+    cinfo->out_color_space = JCS_CMYK;
+    break;
+    
+  default:
+    cinfo->jpeg_color_space = JCS_UNKNOWN;
+    cinfo->out_color_space = JCS_UNKNOWN;
+    break;
+  }
+
+  /* Set defaults for other decompression parameters. */
+  cinfo->scale_num = 1;		/* 1:1 scaling */
+  cinfo->scale_denom = 1;
+  cinfo->output_gamma = 1.0;
+  cinfo->buffered_image = FALSE;
+  cinfo->raw_data_out = FALSE;
+  cinfo->dct_method = JDCT_DEFAULT;
+  cinfo->do_fancy_upsampling = TRUE;
+  cinfo->do_block_smoothing = TRUE;
+  cinfo->quantize_colors = FALSE;
+  /* We set these in case application only sets quantize_colors. */
+  cinfo->dither_mode = JDITHER_FS;
+#ifdef QUANT_2PASS_SUPPORTED
+  cinfo->two_pass_quantize = TRUE;
+#else
+  cinfo->two_pass_quantize = FALSE;
+#endif
+  cinfo->desired_number_of_colors = 256;
+  cinfo->colormap = NULL;
+  /* Initialize for no mode change in buffered-image mode. */
+  cinfo->enable_1pass_quant = FALSE;
+  cinfo->enable_external_quant = FALSE;
+  cinfo->enable_2pass_quant = FALSE;
+}
+
+
+/*
+ * Decompression startup: read start of JPEG datastream to see what's there.
+ * Need only initialize JPEG object and supply a data source before calling.
+ *
+ * This routine will read as far as the first SOS marker (ie, actual start of
+ * compressed data), and will save all tables and parameters in the JPEG
+ * object.  It will also initialize the decompression parameters to default
+ * values, and finally return JPEG_HEADER_OK.  On return, the application may
+ * adjust the decompression parameters and then call jpeg_start_decompress.
+ * (Or, if the application only wanted to determine the image parameters,
+ * the data need not be decompressed.  In that case, call jpeg_abort or
+ * jpeg_destroy to release any temporary space.)
+ * If an abbreviated (tables only) datastream is presented, the routine will
+ * return JPEG_HEADER_TABLES_ONLY upon reaching EOI.  The application may then
+ * re-use the JPEG object to read the abbreviated image datastream(s).
+ * It is unnecessary (but OK) to call jpeg_abort in this case.
+ * The JPEG_SUSPENDED return code only occurs if the data source module
+ * requests suspension of the decompressor.  In this case the application
+ * should load more source data and then re-call jpeg_read_header to resume
+ * processing.
+ * If a non-suspending data source is used and require_image is TRUE, then the
+ * return code need not be inspected since only JPEG_HEADER_OK is possible.
+ *
+ * This routine is now just a front end to jpeg_consume_input, with some
+ * extra error checking.
+ */
+
+GLOBAL(int)
+jpeg_read_header (j_decompress_ptr cinfo, boolean require_image)
+{
+  int retcode;
+
+  if (cinfo->global_state != DSTATE_START &&
+      cinfo->global_state != DSTATE_INHEADER)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+
+  retcode = jpeg_consume_input(cinfo);
+
+  switch (retcode) {
+  case JPEG_REACHED_SOS:
+    retcode = JPEG_HEADER_OK;
+    break;
+  case JPEG_REACHED_EOI:
+    if (require_image)		/* Complain if application wanted an image */
+      ERREXIT(cinfo, JERR_NO_IMAGE);
+    /* Reset to start state; it would be safer to require the application to
+     * call jpeg_abort, but we can't change it now for compatibility reasons.
+     * A side effect is to free any temporary memory (there shouldn't be any).
+     */
+    jpeg_abort((j_common_ptr) cinfo); /* sets state = DSTATE_START */
+    retcode = JPEG_HEADER_TABLES_ONLY;
+    break;
+  case JPEG_SUSPENDED:
+    /* no work */
+    break;
+  }
+
+  return retcode;
+}
+
+
+/*
+ * Consume data in advance of what the decompressor requires.
+ * This can be called at any time once the decompressor object has
+ * been created and a data source has been set up.
+ *
+ * This routine is essentially a state machine that handles a couple
+ * of critical state-transition actions, namely initial setup and
+ * transition from header scanning to ready-for-start_decompress.
+ * All the actual input is done via the input controller's consume_input
+ * method.
+ */
+
+GLOBAL(int)
+jpeg_consume_input (j_decompress_ptr cinfo)
+{
+  int retcode = JPEG_SUSPENDED;
+
+  /* NB: every possible DSTATE value should be listed in this switch */
+  switch (cinfo->global_state) {
+  case DSTATE_START:
+    /* Start-of-datastream actions: reset appropriate modules */
+    (*cinfo->inputctl->reset_input_controller) (cinfo);
+    /* Initialize application's data source module */
+    (*cinfo->src->init_source) (cinfo);
+    cinfo->global_state = DSTATE_INHEADER;
+    /*FALLTHROUGH*/
+  case DSTATE_INHEADER:
+    retcode = (*cinfo->inputctl->consume_input) (cinfo);
+    if (retcode == JPEG_REACHED_SOS) { /* Found SOS, prepare to decompress */
+      /* Set up default parameters based on header data */
+      default_decompress_parms(cinfo);
+      /* Set global state: ready for start_decompress */
+      cinfo->global_state = DSTATE_READY;
+    }
+    break;
+  case DSTATE_READY:
+    /* Can't advance past first SOS until start_decompress is called */
+    retcode = JPEG_REACHED_SOS;
+    break;
+  case DSTATE_PRELOAD:
+  case DSTATE_PRESCAN:
+  case DSTATE_SCANNING:
+  case DSTATE_RAW_OK:
+  case DSTATE_BUFIMAGE:
+  case DSTATE_BUFPOST:
+  case DSTATE_STOPPING:
+    retcode = (*cinfo->inputctl->consume_input) (cinfo);
+    break;
+  default:
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+  }
+  return retcode;
+}
+
+
+/*
+ * Have we finished reading the input file?
+ */
+
+GLOBAL(boolean)
+jpeg_input_complete (j_decompress_ptr cinfo)
+{
+  /* Check for valid jpeg object */
+  if (cinfo->global_state < DSTATE_START ||
+      cinfo->global_state > DSTATE_STOPPING)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+  return cinfo->inputctl->eoi_reached;
+}
+
+
+/*
+ * Is there more than one scan?
+ */
+
+GLOBAL(boolean)
+jpeg_has_multiple_scans (j_decompress_ptr cinfo)
+{
+  /* Only valid after jpeg_read_header completes */
+  if (cinfo->global_state < DSTATE_READY ||
+      cinfo->global_state > DSTATE_STOPPING)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+  return cinfo->inputctl->has_multiple_scans;
+}
+
+
+/*
+ * Finish JPEG decompression.
+ *
+ * This will normally just verify the file trailer and release temp storage.
+ *
+ * Returns FALSE if suspended.  The return value need be inspected only if
+ * a suspending data source is used.
+ */
+
+GLOBAL(boolean)
+jpeg_finish_decompress (j_decompress_ptr cinfo)
+{
+  if ((cinfo->global_state == DSTATE_SCANNING ||
+       cinfo->global_state == DSTATE_RAW_OK) && ! cinfo->buffered_image) {
+    /* Terminate final pass of non-buffered mode */
+    if (cinfo->output_scanline < cinfo->output_height)
+      ERREXIT(cinfo, JERR_TOO_LITTLE_DATA);
+    (*cinfo->master->finish_output_pass) (cinfo);
+    cinfo->global_state = DSTATE_STOPPING;
+  } else if (cinfo->global_state == DSTATE_BUFIMAGE) {
+    /* Finishing after a buffered-image operation */
+    cinfo->global_state = DSTATE_STOPPING;
+  } else if (cinfo->global_state != DSTATE_STOPPING) {
+    /* STOPPING = repeat call after a suspension, anything else is error */
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+  }
+  /* Read until EOI */
+  while (! cinfo->inputctl->eoi_reached) {
+    if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED)
+      return FALSE;		/* Suspend, come back later */
+  }
+  /* Do final cleanup */
+  (*cinfo->src->term_source) (cinfo);
+  /* We can use jpeg_abort to release memory and reset global_state */
+  jpeg_abort((j_common_ptr) cinfo);
+  return TRUE;
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdapistd.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdapistd.c
new file mode 100644
index 0000000000..c8e3fa0c35
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdapistd.c
@@ -0,0 +1,275 @@
+/*
+ * jdapistd.c
+ *
+ * Copyright (C) 1994-1996, 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 application interface code for the decompression half
+ * of the JPEG library.  These are the "standard" API routines that are
+ * used in the normal full-decompression case.  They are not used by a
+ * transcoding-only application.  Note that if an application links in
+ * jpeg_start_decompress, it will end up linking in the entire decompressor.
+ * We thus must separate this file from jdapimin.c to avoid linking the
+ * whole decompression library into a transcoder.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/* Forward declarations */
+LOCAL(boolean) output_pass_setup JPP((j_decompress_ptr cinfo));
+
+
+/*
+ * Decompression initialization.
+ * jpeg_read_header must be completed before calling this.
+ *
+ * If a multipass operating mode was selected, this will do all but the
+ * last pass, and thus may take a great deal of time.
+ *
+ * Returns FALSE if suspended.  The return value need be inspected only if
+ * a suspending data source is used.
+ */
+
+GLOBAL(boolean)
+jpeg_start_decompress (j_decompress_ptr cinfo)
+{
+  if (cinfo->global_state == DSTATE_READY) {
+    /* First call: initialize master control, select active modules */
+    jinit_master_decompress(cinfo);
+    if (cinfo->buffered_image) {
+      /* No more work here; expecting jpeg_start_output next */
+      cinfo->global_state = DSTATE_BUFIMAGE;
+      return TRUE;
+    }
+    cinfo->global_state = DSTATE_PRELOAD;
+  }
+  if (cinfo->global_state == DSTATE_PRELOAD) {
+    /* If file has multiple scans, absorb them all into the coef buffer */
+    if (cinfo->inputctl->has_multiple_scans) {
+#ifdef D_MULTISCAN_FILES_SUPPORTED
+      for (;;) {
+	int retcode;
+	/* Call progress monitor hook if present */
+	if (cinfo->progress != NULL)
+	  (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
+	/* Absorb some more input */
+	retcode = (*cinfo->inputctl->consume_input) (cinfo);
+	if (retcode == JPEG_SUSPENDED)
+	  return FALSE;
+	if (retcode == JPEG_REACHED_EOI)
+	  break;
+	/* Advance progress counter if appropriate */
+	if (cinfo->progress != NULL &&
+	    (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) {
+	  if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) {
+	    /* jdmaster underestimated number of scans; ratchet up one scan */
+	    cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows;
+	  }
+	}
+      }
+#else
+      ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif /* D_MULTISCAN_FILES_SUPPORTED */
+    }
+    cinfo->output_scan_number = cinfo->input_scan_number;
+  } else if (cinfo->global_state != DSTATE_PRESCAN)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+  /* Perform any dummy output passes, and set up for the final pass */
+  return output_pass_setup(cinfo);
+}
+
+
+/*
+ * Set up for an output pass, and perform any dummy pass(es) needed.
+ * Common subroutine for jpeg_start_decompress and jpeg_start_output.
+ * Entry: global_state = DSTATE_PRESCAN only if previously suspended.
+ * Exit: If done, returns TRUE and sets global_state for proper output mode.
+ *       If suspended, returns FALSE and sets global_state = DSTATE_PRESCAN.
+ */
+
+LOCAL(boolean)
+output_pass_setup (j_decompress_ptr cinfo)
+{
+  if (cinfo->global_state != DSTATE_PRESCAN) {
+    /* First call: do pass setup */
+    (*cinfo->master->prepare_for_output_pass) (cinfo);
+    cinfo->output_scanline = 0;
+    cinfo->global_state = DSTATE_PRESCAN;
+  }
+  /* Loop over any required dummy passes */
+  while (cinfo->master->is_dummy_pass) {
+#ifdef QUANT_2PASS_SUPPORTED
+    /* Crank through the dummy pass */
+    while (cinfo->output_scanline < cinfo->output_height) {
+      JDIMENSION last_scanline;
+      /* Call progress monitor hook if present */
+      if (cinfo->progress != NULL) {
+	cinfo->progress->pass_counter = (long) cinfo->output_scanline;
+	cinfo->progress->pass_limit = (long) cinfo->output_height;
+	(*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
+      }
+      /* Process some data */
+      last_scanline = cinfo->output_scanline;
+      (*cinfo->main->process_data) (cinfo, (JSAMPARRAY) NULL,
+				    &cinfo->output_scanline, (JDIMENSION) 0);
+      if (cinfo->output_scanline == last_scanline)
+	return FALSE;		/* No progress made, must suspend */
+    }
+    /* Finish up dummy pass, and set up for another one */
+    (*cinfo->master->finish_output_pass) (cinfo);
+    (*cinfo->master->prepare_for_output_pass) (cinfo);
+    cinfo->output_scanline = 0;
+#else
+    ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif /* QUANT_2PASS_SUPPORTED */
+  }
+  /* Ready for application to drive output pass through
+   * jpeg_read_scanlines or jpeg_read_raw_data.
+   */
+  cinfo->global_state = cinfo->raw_data_out ? DSTATE_RAW_OK : DSTATE_SCANNING;
+  return TRUE;
+}
+
+
+/*
+ * Read some scanlines of data from the JPEG decompressor.
+ *
+ * The return value will be the number of lines actually read.
+ * This may be less than the number requested in several cases,
+ * including bottom of image, data source suspension, and operating
+ * modes that emit multiple scanlines at a time.
+ *
+ * Note: we warn about excess calls to jpeg_read_scanlines() since
+ * this likely signals an application programmer error.  However,
+ * an oversize buffer (max_lines > scanlines remaining) is not an error.
+ */
+
+GLOBAL(JDIMENSION)
+jpeg_read_scanlines (j_decompress_ptr cinfo, JSAMPARRAY scanlines,
+		     JDIMENSION max_lines)
+{
+  JDIMENSION row_ctr;
+
+  if (cinfo->global_state != DSTATE_SCANNING)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+  if (cinfo->output_scanline >= cinfo->output_height) {
+    WARNMS(cinfo, JWRN_TOO_MUCH_DATA);
+    return 0;
+  }
+
+  /* Call progress monitor hook if present */
+  if (cinfo->progress != NULL) {
+    cinfo->progress->pass_counter = (long) cinfo->output_scanline;
+    cinfo->progress->pass_limit = (long) cinfo->output_height;
+    (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
+  }
+
+  /* Process some data */
+  row_ctr = 0;
+  (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, max_lines);
+  cinfo->output_scanline += row_ctr;
+  return row_ctr;
+}
+
+
+/*
+ * Alternate entry point to read raw data.
+ * Processes exactly one iMCU row per call, unless suspended.
+ */
+
+GLOBAL(JDIMENSION)
+jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data,
+		    JDIMENSION max_lines)
+{
+  JDIMENSION lines_per_iMCU_row;
+
+  if (cinfo->global_state != DSTATE_RAW_OK)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+  if (cinfo->output_scanline >= cinfo->output_height) {
+    WARNMS(cinfo, JWRN_TOO_MUCH_DATA);
+    return 0;
+  }
+
+  /* Call progress monitor hook if present */
+  if (cinfo->progress != NULL) {
+    cinfo->progress->pass_counter = (long) cinfo->output_scanline;
+    cinfo->progress->pass_limit = (long) cinfo->output_height;
+    (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
+  }
+
+  /* Verify that at least one iMCU row can be returned. */
+  lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size;
+  if (max_lines < lines_per_iMCU_row)
+    ERREXIT(cinfo, JERR_BUFFER_SIZE);
+
+  /* Decompress directly into user's buffer. */
+  if (! (*cinfo->coef->decompress_data) (cinfo, data))
+    return 0;			/* suspension forced, can do nothing more */
+
+  /* OK, we processed one iMCU row. */
+  cinfo->output_scanline += lines_per_iMCU_row;
+  return lines_per_iMCU_row;
+}
+
+
+/* Additional entry points for buffered-image mode. */
+
+#ifdef D_MULTISCAN_FILES_SUPPORTED
+
+/*
+ * Initialize for an output pass in buffered-image mode.
+ */
+
+GLOBAL(boolean)
+jpeg_start_output (j_decompress_ptr cinfo, int scan_number)
+{
+  if (cinfo->global_state != DSTATE_BUFIMAGE &&
+      cinfo->global_state != DSTATE_PRESCAN)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+  /* Limit scan number to valid range */
+  if (scan_number <= 0)
+    scan_number = 1;
+  if (cinfo->inputctl->eoi_reached &&
+      scan_number > cinfo->input_scan_number)
+    scan_number = cinfo->input_scan_number;
+  cinfo->output_scan_number = scan_number;
+  /* Perform any dummy output passes, and set up for the real pass */
+  return output_pass_setup(cinfo);
+}
+
+
+/*
+ * Finish up after an output pass in buffered-image mode.
+ *
+ * Returns FALSE if suspended.  The return value need be inspected only if
+ * a suspending data source is used.
+ */
+
+GLOBAL(boolean)
+jpeg_finish_output (j_decompress_ptr cinfo)
+{
+  if ((cinfo->global_state == DSTATE_SCANNING ||
+       cinfo->global_state == DSTATE_RAW_OK) && cinfo->buffered_image) {
+    /* Terminate this pass. */
+    /* We do not require the whole pass to have been completed. */
+    (*cinfo->master->finish_output_pass) (cinfo);
+    cinfo->global_state = DSTATE_BUFPOST;
+  } else if (cinfo->global_state != DSTATE_BUFPOST) {
+    /* BUFPOST = repeat call after a suspension, anything else is error */
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+  }
+  /* Read markers looking for SOS or EOI */
+  while (cinfo->input_scan_number <= cinfo->output_scan_number &&
+	 ! cinfo->inputctl->eoi_reached) {
+    if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED)
+      return FALSE;		/* Suspend, come back later */
+  }
+  cinfo->global_state = DSTATE_BUFIMAGE;
+  return TRUE;
+}
+
+#endif /* D_MULTISCAN_FILES_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdatadst.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdatadst.c
new file mode 100644
index 0000000000..a8f6fb0e02
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdatadst.c
@@ -0,0 +1,151 @@
+/*
+ * jdatadst.c
+ *
+ * Copyright (C) 1994-1996, 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 compression data destination routines for the case of
+ * emitting JPEG data to a file (or any stdio stream).  While these routines
+ * are sufficient for most applications, some will want to use a different
+ * destination manager.
+ * IMPORTANT: we assume that fwrite() will correctly transcribe an array of
+ * JOCTETs into 8-bit-wide elements on external storage.  If char is wider
+ * than 8 bits on your machine, you may need to do some tweaking.
+ */
+
+/* this is not a core library module, so it doesn't define JPEG_INTERNALS */
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jerror.h"
+
+
+/* Expanded data destination object for stdio output */
+
+typedef struct {
+  struct jpeg_destination_mgr pub; /* public fields */
+
+  FILE * outfile;		/* target stream */
+  JOCTET * buffer;		/* start of buffer */
+} my_destination_mgr;
+
+typedef my_destination_mgr * my_dest_ptr;
+
+#define OUTPUT_BUF_SIZE  4096	/* choose an efficiently fwrite'able size */
+
+
+/*
+ * Initialize destination --- called by jpeg_start_compress
+ * before any data is actually written.
+ */
+
+METHODDEF(void)
+init_destination (j_compress_ptr cinfo)
+{
+  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+
+  /* Allocate the output buffer --- it will be released when done with image */
+  dest->buffer = (JOCTET *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				  OUTPUT_BUF_SIZE * SIZEOF(JOCTET));
+
+  dest->pub.next_output_byte = dest->buffer;
+  dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
+}
+
+
+/*
+ * Empty the output buffer --- called whenever buffer fills up.
+ *
+ * In typical applications, this should write the entire output buffer
+ * (ignoring the current state of next_output_byte & free_in_buffer),
+ * reset the pointer & count to the start of the buffer, and return TRUE
+ * indicating that the buffer has been dumped.
+ *
+ * In applications that need to be able to suspend compression due to output
+ * overrun, a FALSE return indicates that the buffer cannot be emptied now.
+ * In this situation, the compressor will return to its caller (possibly with
+ * an indication that it has not accepted all the supplied scanlines).  The
+ * application should resume compression after it has made more room in the
+ * output buffer.  Note that there are substantial restrictions on the use of
+ * suspension --- see the documentation.
+ *
+ * When suspending, the compressor will back up to a convenient restart point
+ * (typically the start of the current MCU). next_output_byte & free_in_buffer
+ * indicate where the restart point will be if the current call returns FALSE.
+ * Data beyond this point will be regenerated after resumption, so do not
+ * write it out when emptying the buffer externally.
+ */
+
+METHODDEF(boolean)
+empty_output_buffer (j_compress_ptr cinfo)
+{
+  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+
+  if (JFWRITE(dest->outfile, dest->buffer, OUTPUT_BUF_SIZE) !=
+      (size_t) OUTPUT_BUF_SIZE)
+    ERREXIT(cinfo, JERR_FILE_WRITE);
+
+  dest->pub.next_output_byte = dest->buffer;
+  dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
+
+  return TRUE;
+}
+
+
+/*
+ * Terminate destination --- called by jpeg_finish_compress
+ * after all data has been written.  Usually needs to flush buffer.
+ *
+ * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
+ * application must deal with any cleanup that should happen even
+ * for error exit.
+ */
+
+METHODDEF(void)
+term_destination (j_compress_ptr cinfo)
+{
+  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+  size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
+
+  /* Write any data remaining in the buffer */
+  if (datacount > 0) {
+    if (JFWRITE(dest->outfile, dest->buffer, datacount) != datacount)
+      ERREXIT(cinfo, JERR_FILE_WRITE);
+  }
+  fflush(dest->outfile);
+  /* Make sure we wrote the output file OK */
+  if (ferror(dest->outfile))
+    ERREXIT(cinfo, JERR_FILE_WRITE);
+}
+
+
+/*
+ * Prepare for output to a stdio stream.
+ * The caller must have already opened the stream, and is responsible
+ * for closing it after finishing compression.
+ */
+
+GLOBAL(void)
+jpeg_stdio_dest (j_compress_ptr cinfo, FILE * outfile)
+{
+  my_dest_ptr dest;
+
+  /* The destination object is made permanent so that multiple JPEG images
+   * can be written to the same file without re-executing jpeg_stdio_dest.
+   * This makes it dangerous to use this manager and a different destination
+   * manager serially with the same JPEG object, because their private object
+   * sizes may be different.  Caveat programmer.
+   */
+  if (cinfo->dest == NULL) {	/* first time for this JPEG object? */
+    cinfo->dest = (struct jpeg_destination_mgr *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+				  SIZEOF(my_destination_mgr));
+  }
+
+  dest = (my_dest_ptr) cinfo->dest;
+  dest->pub.init_destination = init_destination;
+  dest->pub.empty_output_buffer = empty_output_buffer;
+  dest->pub.term_destination = term_destination;
+  dest->outfile = outfile;
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdatasrc.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdatasrc.c
new file mode 100644
index 0000000000..edc752bf5d
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdatasrc.c
@@ -0,0 +1,212 @@
+/*
+ * jdatasrc.c
+ *
+ * Copyright (C) 1994-1996, 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 decompression data source routines for the case of
+ * reading JPEG data from a file (or any stdio stream).  While these routines
+ * are sufficient for most applications, some will want to use a different
+ * source manager.
+ * IMPORTANT: we assume that fread() will correctly transcribe an array of
+ * JOCTETs from 8-bit-wide elements on external storage.  If char is wider
+ * than 8 bits on your machine, you may need to do some tweaking.
+ */
+
+/* this is not a core library module, so it doesn't define JPEG_INTERNALS */
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jerror.h"
+
+
+/* Expanded data source object for stdio input */
+
+typedef struct {
+  struct jpeg_source_mgr pub;	/* public fields */
+
+  FILE * infile;		/* source stream */
+  JOCTET * buffer;		/* start of buffer */
+  boolean start_of_file;	/* have we gotten any data yet? */
+} my_source_mgr;
+
+typedef my_source_mgr * my_src_ptr;
+
+#define INPUT_BUF_SIZE  4096	/* choose an efficiently fread'able size */
+
+
+/*
+ * Initialize source --- called by jpeg_read_header
+ * before any data is actually read.
+ */
+
+METHODDEF(void)
+init_source (j_decompress_ptr cinfo)
+{
+  my_src_ptr src = (my_src_ptr) cinfo->src;
+
+  /* We reset the empty-input-file flag for each image,
+   * but we don't clear the input buffer.
+   * This is correct behavior for reading a series of images from one source.
+   */
+  src->start_of_file = TRUE;
+}
+
+
+/*
+ * Fill the input buffer --- called whenever buffer is emptied.
+ *
+ * In typical applications, this should read fresh data into the buffer
+ * (ignoring the current state of next_input_byte & bytes_in_buffer),
+ * reset the pointer & count to the start of the buffer, and return TRUE
+ * indicating that the buffer has been reloaded.  It is not necessary to
+ * fill the buffer entirely, only to obtain at least one more byte.
+ *
+ * There is no such thing as an EOF return.  If the end of the file has been
+ * reached, the routine has a choice of ERREXIT() or inserting fake data into
+ * the buffer.  In most cases, generating a warning message and inserting a
+ * fake EOI marker is the best course of action --- this will allow the
+ * decompressor to output however much of the image is there.  However,
+ * the resulting error message is misleading if the real problem is an empty
+ * input file, so we handle that case specially.
+ *
+ * In applications that need to be able to suspend compression due to input
+ * not being available yet, a FALSE return indicates that no more data can be
+ * obtained right now, but more may be forthcoming later.  In this situation,
+ * the decompressor will return to its caller (with an indication of the
+ * number of scanlines it has read, if any).  The application should resume
+ * decompression after it has loaded more data into the input buffer.  Note
+ * that there are substantial restrictions on the use of suspension --- see
+ * the documentation.
+ *
+ * When suspending, the decompressor will back up to a convenient restart point
+ * (typically the start of the current MCU). next_input_byte & bytes_in_buffer
+ * indicate where the restart point will be if the current call returns FALSE.
+ * Data beyond this point must be rescanned after resumption, so move it to
+ * the front of the buffer rather than discarding it.
+ */
+
+METHODDEF(boolean)
+fill_input_buffer (j_decompress_ptr cinfo)
+{
+  my_src_ptr src = (my_src_ptr) cinfo->src;
+  size_t nbytes;
+
+  nbytes = JFREAD(src->infile, src->buffer, INPUT_BUF_SIZE);
+
+  if (nbytes <= 0) {
+    if (src->start_of_file)	/* Treat empty input file as fatal error */
+      ERREXIT(cinfo, JERR_INPUT_EMPTY);
+    WARNMS(cinfo, JWRN_JPEG_EOF);
+    /* Insert a fake EOI marker */
+    src->buffer[0] = (JOCTET) 0xFF;
+    src->buffer[1] = (JOCTET) JPEG_EOI;
+    nbytes = 2;
+  }
+
+  src->pub.next_input_byte = src->buffer;
+  src->pub.bytes_in_buffer = nbytes;
+  src->start_of_file = FALSE;
+
+  return TRUE;
+}
+
+
+/*
+ * Skip data --- used to skip over a potentially large amount of
+ * uninteresting data (such as an APPn marker).
+ *
+ * Writers of suspendable-input applications must note that skip_input_data
+ * is not granted the right to give a suspension return.  If the skip extends
+ * beyond the data currently in the buffer, the buffer can be marked empty so
+ * that the next read will cause a fill_input_buffer call that can suspend.
+ * Arranging for additional bytes to be discarded before reloading the input
+ * buffer is the application writer's problem.
+ */
+
+METHODDEF(void)
+skip_input_data (j_decompress_ptr cinfo, long num_bytes)
+{
+  my_src_ptr src = (my_src_ptr) cinfo->src;
+
+  /* Just a dumb implementation for now.  Could use fseek() except
+   * it doesn't work on pipes.  Not clear that being smart is worth
+   * any trouble anyway --- large skips are infrequent.
+   */
+  if (num_bytes > 0) {
+    while (num_bytes > (long) src->pub.bytes_in_buffer) {
+      num_bytes -= (long) src->pub.bytes_in_buffer;
+      (void) fill_input_buffer(cinfo);
+      /* note we assume that fill_input_buffer will never return FALSE,
+       * so suspension need not be handled.
+       */
+    }
+    src->pub.next_input_byte += (size_t) num_bytes;
+    src->pub.bytes_in_buffer -= (size_t) num_bytes;
+  }
+}
+
+
+/*
+ * An additional method that can be provided by data source modules is the
+ * resync_to_restart method for error recovery in the presence of RST markers.
+ * For the moment, this source module just uses the default resync method
+ * provided by the JPEG library.  That method assumes that no backtracking
+ * is possible.
+ */
+
+
+/*
+ * Terminate source --- called by jpeg_finish_decompress
+ * after all data has been read.  Often a no-op.
+ *
+ * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
+ * application must deal with any cleanup that should happen even
+ * for error exit.
+ */
+
+METHODDEF(void)
+term_source (j_decompress_ptr cinfo)
+{
+  /* no work necessary here */
+}
+
+
+/*
+ * Prepare for input from a stdio stream.
+ * The caller must have already opened the stream, and is responsible
+ * for closing it after finishing decompression.
+ */
+
+GLOBAL(void)
+jpeg_stdio_src (j_decompress_ptr cinfo, FILE * infile)
+{
+  my_src_ptr src;
+
+  /* The source object and input buffer are made permanent so that a series
+   * of JPEG images can be read from the same file by calling jpeg_stdio_src
+   * only before the first one.  (If we discarded the buffer at the end of
+   * one image, we'd likely lose the start of the next one.)
+   * This makes it unsafe to use this manager and a different source
+   * manager serially with the same JPEG object.  Caveat programmer.
+   */
+  if (cinfo->src == NULL) {	/* first time for this JPEG object? */
+    cinfo->src = (struct jpeg_source_mgr *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+				  SIZEOF(my_source_mgr));
+    src = (my_src_ptr) cinfo->src;
+    src->buffer = (JOCTET *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+				  INPUT_BUF_SIZE * SIZEOF(JOCTET));
+  }
+
+  src = (my_src_ptr) cinfo->src;
+  src->pub.init_source = init_source;
+  src->pub.fill_input_buffer = fill_input_buffer;
+  src->pub.skip_input_data = skip_input_data;
+  src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
+  src->pub.term_source = term_source;
+  src->infile = infile;
+  src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
+  src->pub.next_input_byte = NULL; /* until buffer loaded */
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdcoefct.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdcoefct.c
new file mode 100644
index 0000000000..4938d20fcb
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdcoefct.c
@@ -0,0 +1,736 @@
+/*
+ * jdcoefct.c
+ *
+ * Copyright (C) 1994-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.
+ *
+ * This file contains the coefficient buffer controller for decompression.
+ * This controller is the top level of the JPEG decompressor proper.
+ * The coefficient buffer lies between entropy decoding and inverse-DCT steps.
+ *
+ * In buffered-image mode, this controller is the interface between
+ * input-oriented processing and output-oriented processing.
+ * Also, the input side (only) is used when reading a file for transcoding.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+/* Block smoothing is only applicable for progressive JPEG, so: */
+#ifndef D_PROGRESSIVE_SUPPORTED
+#undef BLOCK_SMOOTHING_SUPPORTED
+#endif
+
+/* Private buffer controller object */
+
+typedef struct {
+  struct jpeg_d_coef_controller pub; /* public fields */
+
+  /* These variables keep track of the current location of the input side. */
+  /* cinfo->input_iMCU_row is also used for this. */
+  JDIMENSION MCU_ctr;		/* counts MCUs processed in current row */
+  int MCU_vert_offset;		/* counts MCU rows within iMCU row */
+  int MCU_rows_per_iMCU_row;	/* number of such rows needed */
+
+  /* The output side's location is represented by cinfo->output_iMCU_row. */
+
+  /* In single-pass modes, it's sufficient to buffer just one MCU.
+   * We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks,
+   * and let the entropy decoder write into that workspace each time.
+   * (On 80x86, the workspace is FAR even though it's not really very big;
+   * this is to keep the module interfaces unchanged when a large coefficient
+   * buffer is necessary.)
+   * In multi-pass modes, this array points to the current MCU's blocks
+   * within the virtual arrays; it is used only by the input side.
+   */
+  JBLOCKROW MCU_buffer[D_MAX_BLOCKS_IN_MCU];
+
+#ifdef D_MULTISCAN_FILES_SUPPORTED
+  /* In multi-pass modes, we need a virtual block array for each component. */
+  jvirt_barray_ptr whole_image[MAX_COMPONENTS];
+#endif
+
+#ifdef BLOCK_SMOOTHING_SUPPORTED
+  /* When doing block smoothing, we latch coefficient Al values here */
+  int * coef_bits_latch;
+#define SAVED_COEFS  6		/* we save coef_bits[0..5] */
+#endif
+} my_coef_controller;
+
+typedef my_coef_controller * my_coef_ptr;
+
+/* Forward declarations */
+METHODDEF(int) decompress_onepass
+	JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf));
+#ifdef D_MULTISCAN_FILES_SUPPORTED
+METHODDEF(int) decompress_data
+	JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf));
+#endif
+#ifdef BLOCK_SMOOTHING_SUPPORTED
+LOCAL(boolean) smoothing_ok JPP((j_decompress_ptr cinfo));
+METHODDEF(int) decompress_smooth_data
+	JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf));
+#endif
+
+
+LOCAL(void)
+start_iMCU_row (j_decompress_ptr cinfo)
+/* Reset within-iMCU-row counters for a new row (input side) */
+{
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+
+  /* In an interleaved scan, an MCU row is the same as an iMCU row.
+   * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows.
+   * But at the bottom of the image, process only what's left.
+   */
+  if (cinfo->comps_in_scan > 1) {
+    coef->MCU_rows_per_iMCU_row = 1;
+  } else {
+    if (cinfo->input_iMCU_row < (cinfo->total_iMCU_rows-1))
+      coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor;
+    else
+      coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height;
+  }
+
+  coef->MCU_ctr = 0;
+  coef->MCU_vert_offset = 0;
+}
+
+
+/*
+ * Initialize for an input processing pass.
+ */
+
+METHODDEF(void)
+start_input_pass (j_decompress_ptr cinfo)
+{
+  cinfo->input_iMCU_row = 0;
+  start_iMCU_row(cinfo);
+}
+
+
+/*
+ * Initialize for an output processing pass.
+ */
+
+METHODDEF(void)
+start_output_pass (j_decompress_ptr cinfo)
+{
+#ifdef BLOCK_SMOOTHING_SUPPORTED
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+
+  /* If multipass, check to see whether to use block smoothing on this pass */
+  if (coef->pub.coef_arrays != NULL) {
+    if (cinfo->do_block_smoothing && smoothing_ok(cinfo))
+      coef->pub.decompress_data = decompress_smooth_data;
+    else
+      coef->pub.decompress_data = decompress_data;
+  }
+#endif
+  cinfo->output_iMCU_row = 0;
+}
+
+
+/*
+ * Decompress and return some data in the single-pass case.
+ * Always attempts to emit one fully interleaved MCU row ("iMCU" row).
+ * Input and output must run in lockstep since we have only a one-MCU buffer.
+ * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED.
+ *
+ * NB: output_buf contains a plane for each component in image,
+ * which we index according to the component's SOF position.
+ */
+
+METHODDEF(int)
+decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf)
+{
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+  JDIMENSION MCU_col_num;	/* index of current MCU within row */
+  JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1;
+  JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1;
+  int blkn, ci, xindex, yindex, yoffset, useful_width;
+  JSAMPARRAY output_ptr;
+  JDIMENSION start_col, output_col;
+  jpeg_component_info *compptr;
+  inverse_DCT_method_ptr inverse_DCT;
+
+  /* Loop to process as much as one whole iMCU row */
+  for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row;
+       yoffset++) {
+    for (MCU_col_num = coef->MCU_ctr; MCU_col_num <= last_MCU_col;
+	 MCU_col_num++) {
+      /* Try to fetch an MCU.  Entropy decoder expects buffer to be zeroed. */
+      jzero_far((void FAR *) coef->MCU_buffer[0],
+		(size_t) (cinfo->blocks_in_MCU * SIZEOF(JBLOCK)));
+      if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) {
+	/* Suspension forced; update state counters and exit */
+	coef->MCU_vert_offset = yoffset;
+	coef->MCU_ctr = MCU_col_num;
+	return JPEG_SUSPENDED;
+      }
+      /* Determine where data should go in output_buf and do the IDCT thing.
+       * We skip dummy blocks at the right and bottom edges (but blkn gets
+       * incremented past them!).  Note the inner loop relies on having
+       * allocated the MCU_buffer[] blocks sequentially.
+       */
+      blkn = 0;			/* index of current DCT block within MCU */
+      for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+	compptr = cinfo->cur_comp_info[ci];
+	/* Don't bother to IDCT an uninteresting component. */
+	if (! compptr->component_needed) {
+	  blkn += compptr->MCU_blocks;
+	  continue;
+	}
+	inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index];
+	useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width
+						    : compptr->last_col_width;
+	output_ptr = output_buf[compptr->component_index] +
+	  yoffset * compptr->DCT_scaled_size;
+	start_col = MCU_col_num * compptr->MCU_sample_width;
+	for (yindex = 0; yindex < compptr->MCU_height; yindex++) {
+	  if (cinfo->input_iMCU_row < last_iMCU_row ||
+	      yoffset+yindex < compptr->last_row_height) {
+	    output_col = start_col;
+	    for (xindex = 0; xindex < useful_width; xindex++) {
+	      (*inverse_DCT) (cinfo, compptr,
+			      (JCOEFPTR) coef->MCU_buffer[blkn+xindex],
+			      output_ptr, output_col);
+	      output_col += compptr->DCT_scaled_size;
+	    }
+	  }
+	  blkn += compptr->MCU_width;
+	  output_ptr += compptr->DCT_scaled_size;
+	}
+      }
+    }
+    /* Completed an MCU row, but perhaps not an iMCU row */
+    coef->MCU_ctr = 0;
+  }
+  /* Completed the iMCU row, advance counters for next one */
+  cinfo->output_iMCU_row++;
+  if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) {
+    start_iMCU_row(cinfo);
+    return JPEG_ROW_COMPLETED;
+  }
+  /* Completed the scan */
+  (*cinfo->inputctl->finish_input_pass) (cinfo);
+  return JPEG_SCAN_COMPLETED;
+}
+
+
+/*
+ * Dummy consume-input routine for single-pass operation.
+ */
+
+METHODDEF(int)
+dummy_consume_data (j_decompress_ptr cinfo)
+{
+  return JPEG_SUSPENDED;	/* Always indicate nothing was done */
+}
+
+
+#ifdef D_MULTISCAN_FILES_SUPPORTED
+
+/*
+ * Consume input data and store it in the full-image coefficient buffer.
+ * We read as much as one fully interleaved MCU row ("iMCU" row) per call,
+ * ie, v_samp_factor block rows for each component in the scan.
+ * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED.
+ */
+
+METHODDEF(int)
+consume_data (j_decompress_ptr cinfo)
+{
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+  JDIMENSION MCU_col_num;	/* index of current MCU within row */
+  int blkn, ci, xindex, yindex, yoffset;
+  JDIMENSION start_col;
+  JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN];
+  JBLOCKROW buffer_ptr;
+  jpeg_component_info *compptr;
+
+  /* Align the virtual buffers for the components used in this scan. */
+  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+    compptr = cinfo->cur_comp_info[ci];
+    buffer[ci] = (*cinfo->mem->access_virt_barray)
+      ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index],
+       cinfo->input_iMCU_row * compptr->v_samp_factor,
+       (JDIMENSION) compptr->v_samp_factor, TRUE);
+    /* Note: entropy decoder expects buffer to be zeroed,
+     * but this is handled automatically by the memory manager
+     * because we requested a pre-zeroed array.
+     */
+  }
+
+  /* Loop to process one whole iMCU row */
+  for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row;
+       yoffset++) {
+    for (MCU_col_num = coef->MCU_ctr; MCU_col_num < cinfo->MCUs_per_row;
+	 MCU_col_num++) {
+      /* Construct list of pointers to DCT blocks belonging to this MCU */
+      blkn = 0;			/* index of current DCT block within MCU */
+      for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+	compptr = cinfo->cur_comp_info[ci];
+	start_col = MCU_col_num * compptr->MCU_width;
+	for (yindex = 0; yindex < compptr->MCU_height; yindex++) {
+	  buffer_ptr = buffer[ci][yindex+yoffset] + start_col;
+	  for (xindex = 0; xindex < compptr->MCU_width; xindex++) {
+	    coef->MCU_buffer[blkn++] = buffer_ptr++;
+	  }
+	}
+      }
+      /* Try to fetch the MCU. */
+      if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) {
+	/* Suspension forced; update state counters and exit */
+	coef->MCU_vert_offset = yoffset;
+	coef->MCU_ctr = MCU_col_num;
+	return JPEG_SUSPENDED;
+      }
+    }
+    /* Completed an MCU row, but perhaps not an iMCU row */
+    coef->MCU_ctr = 0;
+  }
+  /* Completed the iMCU row, advance counters for next one */
+  if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) {
+    start_iMCU_row(cinfo);
+    return JPEG_ROW_COMPLETED;
+  }
+  /* Completed the scan */
+  (*cinfo->inputctl->finish_input_pass) (cinfo);
+  return JPEG_SCAN_COMPLETED;
+}
+
+
+/*
+ * Decompress and return some data in the multi-pass case.
+ * Always attempts to emit one fully interleaved MCU row ("iMCU" row).
+ * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED.
+ *
+ * NB: output_buf contains a plane for each component in image.
+ */
+
+METHODDEF(int)
+decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf)
+{
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+  JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1;
+  JDIMENSION block_num;
+  int ci, block_row, block_rows;
+  JBLOCKARRAY buffer;
+  JBLOCKROW buffer_ptr;
+  JSAMPARRAY output_ptr;
+  JDIMENSION output_col;
+  jpeg_component_info *compptr;
+  inverse_DCT_method_ptr inverse_DCT;
+
+  /* Force some input to be done if we are getting ahead of the input. */
+  while (cinfo->input_scan_number < cinfo->output_scan_number ||
+	 (cinfo->input_scan_number == cinfo->output_scan_number &&
+	  cinfo->input_iMCU_row <= cinfo->output_iMCU_row)) {
+    if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED)
+      return JPEG_SUSPENDED;
+  }
+
+  /* OK, output from the virtual arrays. */
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    /* Don't bother to IDCT an uninteresting component. */
+    if (! compptr->component_needed)
+      continue;
+    /* Align the virtual buffer for this component. */
+    buffer = (*cinfo->mem->access_virt_barray)
+      ((j_common_ptr) cinfo, coef->whole_image[ci],
+       cinfo->output_iMCU_row * compptr->v_samp_factor,
+       (JDIMENSION) compptr->v_samp_factor, FALSE);
+    /* Count non-dummy DCT block rows in this iMCU row. */
+    if (cinfo->output_iMCU_row < last_iMCU_row)
+      block_rows = compptr->v_samp_factor;
+    else {
+      /* NB: can't use last_row_height here; it is input-side-dependent! */
+      block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor);
+      if (block_rows == 0) block_rows = compptr->v_samp_factor;
+    }
+    inverse_DCT = cinfo->idct->inverse_DCT[ci];
+    output_ptr = output_buf[ci];
+    /* Loop over all DCT blocks to be processed. */
+    for (block_row = 0; block_row < block_rows; block_row++) {
+      buffer_ptr = buffer[block_row];
+      output_col = 0;
+      for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) {
+	(*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr,
+			output_ptr, output_col);
+	buffer_ptr++;
+	output_col += compptr->DCT_scaled_size;
+      }
+      output_ptr += compptr->DCT_scaled_size;
+    }
+  }
+
+  if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows)
+    return JPEG_ROW_COMPLETED;
+  return JPEG_SCAN_COMPLETED;
+}
+
+#endif /* D_MULTISCAN_FILES_SUPPORTED */
+
+
+#ifdef BLOCK_SMOOTHING_SUPPORTED
+
+/*
+ * This code applies interblock smoothing as described by section K.8
+ * of the JPEG standard: the first 5 AC coefficients are estimated from
+ * the DC values of a DCT block and its 8 neighboring blocks.
+ * We apply smoothing only for progressive JPEG decoding, and only if
+ * the coefficients it can estimate are not yet known to full precision.
+ */
+
+/* Natural-order array positions of the first 5 zigzag-order coefficients */
+#define Q01_POS  1
+#define Q10_POS  8
+#define Q20_POS  16
+#define Q11_POS  9
+#define Q02_POS  2
+
+/*
+ * Determine whether block smoothing is applicable and safe.
+ * We also latch the current states of the coef_bits[] entries for the
+ * AC coefficients; otherwise, if the input side of the decompressor
+ * advances into a new scan, we might think the coefficients are known
+ * more accurately than they really are.
+ */
+
+LOCAL(boolean)
+smoothing_ok (j_decompress_ptr cinfo)
+{
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+  boolean smoothing_useful = FALSE;
+  int ci, coefi;
+  jpeg_component_info *compptr;
+  JQUANT_TBL * qtable;
+  int * coef_bits;
+  int * coef_bits_latch;
+
+  if (! cinfo->progressive_mode || cinfo->coef_bits == NULL)
+    return FALSE;
+
+  /* Allocate latch area if not already done */
+  if (coef->coef_bits_latch == NULL)
+    coef->coef_bits_latch = (int *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				  cinfo->num_components *
+				  (SAVED_COEFS * SIZEOF(int)));
+  coef_bits_latch = coef->coef_bits_latch;
+
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    /* All components' quantization values must already be latched. */
+    if ((qtable = compptr->quant_table) == NULL)
+      return FALSE;
+    /* Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. */
+    if (qtable->quantval[0] == 0 ||
+	qtable->quantval[Q01_POS] == 0 ||
+	qtable->quantval[Q10_POS] == 0 ||
+	qtable->quantval[Q20_POS] == 0 ||
+	qtable->quantval[Q11_POS] == 0 ||
+	qtable->quantval[Q02_POS] == 0)
+      return FALSE;
+    /* DC values must be at least partly known for all components. */
+    coef_bits = cinfo->coef_bits[ci];
+    if (coef_bits[0] < 0)
+      return FALSE;
+    /* Block smoothing is helpful if some AC coefficients remain inaccurate. */
+    for (coefi = 1; coefi <= 5; coefi++) {
+      coef_bits_latch[coefi] = coef_bits[coefi];
+      if (coef_bits[coefi] != 0)
+	smoothing_useful = TRUE;
+    }
+    coef_bits_latch += SAVED_COEFS;
+  }
+
+  return smoothing_useful;
+}
+
+
+/*
+ * Variant of decompress_data for use when doing block smoothing.
+ */
+
+METHODDEF(int)
+decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf)
+{
+  my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
+  JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1;
+  JDIMENSION block_num, last_block_column;
+  int ci, block_row, block_rows, access_rows;
+  JBLOCKARRAY buffer;
+  JBLOCKROW buffer_ptr, prev_block_row, next_block_row;
+  JSAMPARRAY output_ptr;
+  JDIMENSION output_col;
+  jpeg_component_info *compptr;
+  inverse_DCT_method_ptr inverse_DCT;
+  boolean first_row, last_row;
+  JBLOCK workspace;
+  int *coef_bits;
+  JQUANT_TBL *quanttbl;
+  INT32 Q00,Q01,Q02,Q10,Q11,Q20, num;
+  int DC1,DC2,DC3,DC4,DC5,DC6,DC7,DC8,DC9;
+  int Al, pred;
+
+  /* Force some input to be done if we are getting ahead of the input. */
+  while (cinfo->input_scan_number <= cinfo->output_scan_number &&
+	 ! cinfo->inputctl->eoi_reached) {
+    if (cinfo->input_scan_number == cinfo->output_scan_number) {
+      /* If input is working on current scan, we ordinarily want it to
+       * have completed the current row.  But if input scan is DC,
+       * we want it to keep one row ahead so that next block row's DC
+       * values are up to date.
+       */
+      JDIMENSION delta = (cinfo->Ss == 0) ? 1 : 0;
+      if (cinfo->input_iMCU_row > cinfo->output_iMCU_row+delta)
+	break;
+    }
+    if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED)
+      return JPEG_SUSPENDED;
+  }
+
+  /* OK, output from the virtual arrays. */
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    /* Don't bother to IDCT an uninteresting component. */
+    if (! compptr->component_needed)
+      continue;
+    /* Count non-dummy DCT block rows in this iMCU row. */
+    if (cinfo->output_iMCU_row < last_iMCU_row) {
+      block_rows = compptr->v_samp_factor;
+      access_rows = block_rows * 2; /* this and next iMCU row */
+      last_row = FALSE;
+    } else {
+      /* NB: can't use last_row_height here; it is input-side-dependent! */
+      block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor);
+      if (block_rows == 0) block_rows = compptr->v_samp_factor;
+      access_rows = block_rows; /* this iMCU row only */
+      last_row = TRUE;
+    }
+    /* Align the virtual buffer for this component. */
+    if (cinfo->output_iMCU_row > 0) {
+      access_rows += compptr->v_samp_factor; /* prior iMCU row too */
+      buffer = (*cinfo->mem->access_virt_barray)
+	((j_common_ptr) cinfo, coef->whole_image[ci],
+	 (cinfo->output_iMCU_row - 1) * compptr->v_samp_factor,
+	 (JDIMENSION) access_rows, FALSE);
+      buffer += compptr->v_samp_factor;	/* point to current iMCU row */
+      first_row = FALSE;
+    } else {
+      buffer = (*cinfo->mem->access_virt_barray)
+	((j_common_ptr) cinfo, coef->whole_image[ci],
+	 (JDIMENSION) 0, (JDIMENSION) access_rows, FALSE);
+      first_row = TRUE;
+    }
+    /* Fetch component-dependent info */
+    coef_bits = coef->coef_bits_latch + (ci * SAVED_COEFS);
+    quanttbl = compptr->quant_table;
+    Q00 = quanttbl->quantval[0];
+    Q01 = quanttbl->quantval[Q01_POS];
+    Q10 = quanttbl->quantval[Q10_POS];
+    Q20 = quanttbl->quantval[Q20_POS];
+    Q11 = quanttbl->quantval[Q11_POS];
+    Q02 = quanttbl->quantval[Q02_POS];
+    inverse_DCT = cinfo->idct->inverse_DCT[ci];
+    output_ptr = output_buf[ci];
+    /* Loop over all DCT blocks to be processed. */
+    for (block_row = 0; block_row < block_rows; block_row++) {
+      buffer_ptr = buffer[block_row];
+      if (first_row && block_row == 0)
+	prev_block_row = buffer_ptr;
+      else
+	prev_block_row = buffer[block_row-1];
+      if (last_row && block_row == block_rows-1)
+	next_block_row = buffer_ptr;
+      else
+	next_block_row = buffer[block_row+1];
+      /* We fetch the surrounding DC values using a sliding-register approach.
+       * Initialize all nine here so as to do the right thing on narrow pics.
+       */
+      DC1 = DC2 = DC3 = (int) prev_block_row[0][0];
+      DC4 = DC5 = DC6 = (int) buffer_ptr[0][0];
+      DC7 = DC8 = DC9 = (int) next_block_row[0][0];
+      output_col = 0;
+      last_block_column = compptr->width_in_blocks - 1;
+      for (block_num = 0; block_num <= last_block_column; block_num++) {
+	/* Fetch current DCT block into workspace so we can modify it. */
+	jcopy_block_row(buffer_ptr, (JBLOCKROW) workspace, (JDIMENSION) 1);
+	/* Update DC values */
+	if (block_num < last_block_column) {
+	  DC3 = (int) prev_block_row[1][0];
+	  DC6 = (int) buffer_ptr[1][0];
+	  DC9 = (int) next_block_row[1][0];
+	}
+	/* Compute coefficient estimates per K.8.
+	 * An estimate is applied only if coefficient is still zero,
+	 * and is not known to be fully accurate.
+	 */
+	/* AC01 */
+	if ((Al=coef_bits[1]) != 0 && workspace[1] == 0) {
+	  num = 36 * Q00 * (DC4 - DC6);
+	  if (num >= 0) {
+	    pred = (int) (((Q01<<7) + num) / (Q01<<8));
+	    if (Al > 0 && pred >= (1<<Al))
+	      pred = (1<<Al)-1;
+	  } else {
+	    pred = (int) (((Q01<<7) - num) / (Q01<<8));
+	    if (Al > 0 && pred >= (1<<Al))
+	      pred = (1<<Al)-1;
+	    pred = -pred;
+	  }
+	  workspace[1] = (JCOEF) pred;
+	}
+	/* AC10 */
+	if ((Al=coef_bits[2]) != 0 && workspace[8] == 0) {
+	  num = 36 * Q00 * (DC2 - DC8);
+	  if (num >= 0) {
+	    pred = (int) (((Q10<<7) + num) / (Q10<<8));
+	    if (Al > 0 && pred >= (1<<Al))
+	      pred = (1<<Al)-1;
+	  } else {
+	    pred = (int) (((Q10<<7) - num) / (Q10<<8));
+	    if (Al > 0 && pred >= (1<<Al))
+	      pred = (1<<Al)-1;
+	    pred = -pred;
+	  }
+	  workspace[8] = (JCOEF) pred;
+	}
+	/* AC20 */
+	if ((Al=coef_bits[3]) != 0 && workspace[16] == 0) {
+	  num = 9 * Q00 * (DC2 + DC8 - 2*DC5);
+	  if (num >= 0) {
+	    pred = (int) (((Q20<<7) + num) / (Q20<<8));
+	    if (Al > 0 && pred >= (1<<Al))
+	      pred = (1<<Al)-1;
+	  } else {
+	    pred = (int) (((Q20<<7) - num) / (Q20<<8));
+	    if (Al > 0 && pred >= (1<<Al))
+	      pred = (1<<Al)-1;
+	    pred = -pred;
+	  }
+	  workspace[16] = (JCOEF) pred;
+	}
+	/* AC11 */
+	if ((Al=coef_bits[4]) != 0 && workspace[9] == 0) {
+	  num = 5 * Q00 * (DC1 - DC3 - DC7 + DC9);
+	  if (num >= 0) {
+	    pred = (int) (((Q11<<7) + num) / (Q11<<8));
+	    if (Al > 0 && pred >= (1<<Al))
+	      pred = (1<<Al)-1;
+	  } else {
+	    pred = (int) (((Q11<<7) - num) / (Q11<<8));
+	    if (Al > 0 && pred >= (1<<Al))
+	      pred = (1<<Al)-1;
+	    pred = -pred;
+	  }
+	  workspace[9] = (JCOEF) pred;
+	}
+	/* AC02 */
+	if ((Al=coef_bits[5]) != 0 && workspace[2] == 0) {
+	  num = 9 * Q00 * (DC4 + DC6 - 2*DC5);
+	  if (num >= 0) {
+	    pred = (int) (((Q02<<7) + num) / (Q02<<8));
+	    if (Al > 0 && pred >= (1<<Al))
+	      pred = (1<<Al)-1;
+	  } else {
+	    pred = (int) (((Q02<<7) - num) / (Q02<<8));
+	    if (Al > 0 && pred >= (1<<Al))
+	      pred = (1<<Al)-1;
+	    pred = -pred;
+	  }
+	  workspace[2] = (JCOEF) pred;
+	}
+	/* OK, do the IDCT */
+	(*inverse_DCT) (cinfo, compptr, (JCOEFPTR) workspace,
+			output_ptr, output_col);
+	/* Advance for next column */
+	DC1 = DC2; DC2 = DC3;
+	DC4 = DC5; DC5 = DC6;
+	DC7 = DC8; DC8 = DC9;
+	buffer_ptr++, prev_block_row++, next_block_row++;
+	output_col += compptr->DCT_scaled_size;
+      }
+      output_ptr += compptr->DCT_scaled_size;
+    }
+  }
+
+  if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows)
+    return JPEG_ROW_COMPLETED;
+  return JPEG_SCAN_COMPLETED;
+}
+
+#endif /* BLOCK_SMOOTHING_SUPPORTED */
+
+
+/*
+ * Initialize coefficient buffer controller.
+ */
+
+GLOBAL(void)
+jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer)
+{
+  my_coef_ptr coef;
+
+  coef = (my_coef_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_coef_controller));
+  cinfo->coef = (struct jpeg_d_coef_controller *) coef;
+  coef->pub.start_input_pass = start_input_pass;
+  coef->pub.start_output_pass = start_output_pass;
+#ifdef BLOCK_SMOOTHING_SUPPORTED
+  coef->coef_bits_latch = NULL;
+#endif
+
+  /* Create the coefficient buffer. */
+  if (need_full_buffer) {
+#ifdef D_MULTISCAN_FILES_SUPPORTED
+    /* Allocate a full-image virtual array for each component, */
+    /* padded to a multiple of samp_factor DCT blocks in each direction. */
+    /* Note we ask for a pre-zeroed array. */
+    int ci, access_rows;
+    jpeg_component_info *compptr;
+
+    for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+	 ci++, compptr++) {
+      access_rows = compptr->v_samp_factor;
+#ifdef BLOCK_SMOOTHING_SUPPORTED
+      /* If block smoothing could be used, need a bigger window */
+      if (cinfo->progressive_mode)
+	access_rows *= 3;
+#endif
+      coef->whole_image[ci] = (*cinfo->mem->request_virt_barray)
+	((j_common_ptr) cinfo, JPOOL_IMAGE, TRUE,
+	 (JDIMENSION) jround_up((long) compptr->width_in_blocks,
+				(long) compptr->h_samp_factor),
+	 (JDIMENSION) jround_up((long) compptr->height_in_blocks,
+				(long) compptr->v_samp_factor),
+	 (JDIMENSION) access_rows);
+    }
+    coef->pub.consume_data = consume_data;
+    coef->pub.decompress_data = decompress_data;
+    coef->pub.coef_arrays = coef->whole_image; /* link to virtual arrays */
+#else
+    ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif
+  } else {
+    /* We only need a single-MCU buffer. */
+    JBLOCKROW buffer;
+    int i;
+
+    buffer = (JBLOCKROW)
+      (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				  D_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK));
+    for (i = 0; i < D_MAX_BLOCKS_IN_MCU; i++) {
+      coef->MCU_buffer[i] = buffer + i;
+    }
+    coef->pub.consume_data = dummy_consume_data;
+    coef->pub.decompress_data = decompress_onepass;
+    coef->pub.coef_arrays = NULL; /* flag for no virtual arrays */
+  }
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdcolor.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdcolor.c
new file mode 100644
index 0000000000..6c04dfe8aa
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdcolor.c
@@ -0,0 +1,396 @@
+/*
+ * jdcolor.c
+ *
+ * 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.
+ *
+ * This file contains output colorspace conversion routines.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/* Private subobject */
+
+typedef struct {
+  struct jpeg_color_deconverter pub; /* public fields */
+
+  /* Private state for YCC->RGB conversion */
+  int * Cr_r_tab;		/* => table for Cr to R conversion */
+  int * Cb_b_tab;		/* => table for Cb to B conversion */
+  INT32 * Cr_g_tab;		/* => table for Cr to G conversion */
+  INT32 * Cb_g_tab;		/* => table for Cb to G conversion */
+} my_color_deconverter;
+
+typedef my_color_deconverter * my_cconvert_ptr;
+
+
+/**************** YCbCr -> RGB conversion: most common case **************/
+
+/*
+ * YCbCr is defined per CCIR 601-1, except that Cb and Cr are
+ * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5.
+ * The conversion equations to be implemented are therefore
+ *	R = Y                + 1.40200 * Cr
+ *	G = Y - 0.34414 * Cb - 0.71414 * Cr
+ *	B = Y + 1.77200 * Cb
+ * where Cb and Cr represent the incoming values less CENTERJSAMPLE.
+ * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.)
+ *
+ * To avoid floating-point arithmetic, we represent the fractional constants
+ * as integers scaled up by 2^16 (about 4 digits precision); we have to divide
+ * the products by 2^16, with appropriate rounding, to get the correct answer.
+ * Notice that Y, being an integral input, does not contribute any fraction
+ * so it need not participate in the rounding.
+ *
+ * For even more speed, we avoid doing any multiplications in the inner loop
+ * by precalculating the constants times Cb and Cr for all possible values.
+ * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table);
+ * for 12-bit samples it is still acceptable.  It's not very reasonable for
+ * 16-bit samples, but if you want lossless storage you shouldn't be changing
+ * colorspace anyway.
+ * The Cr=>R and Cb=>B values can be rounded to integers in advance; the
+ * values for the G calculation are left scaled up, since we must add them
+ * together before rounding.
+ */
+
+#define SCALEBITS	16	/* speediest right-shift on some machines */
+#define ONE_HALF	((INT32) 1 << (SCALEBITS-1))
+#define FIX(x)		((INT32) ((x) * (1L<<SCALEBITS) + 0.5))
+
+
+/*
+ * Initialize tables for YCC->RGB colorspace conversion.
+ */
+
+LOCAL(void)
+build_ycc_rgb_table (j_decompress_ptr cinfo)
+{
+  my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
+  int i;
+  INT32 x;
+  SHIFT_TEMPS
+
+  cconvert->Cr_r_tab = (int *)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				(MAXJSAMPLE+1) * SIZEOF(int));
+  cconvert->Cb_b_tab = (int *)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				(MAXJSAMPLE+1) * SIZEOF(int));
+  cconvert->Cr_g_tab = (INT32 *)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				(MAXJSAMPLE+1) * SIZEOF(INT32));
+  cconvert->Cb_g_tab = (INT32 *)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				(MAXJSAMPLE+1) * SIZEOF(INT32));
+
+  for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
+    /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */
+    /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */
+    /* Cr=>R value is nearest int to 1.40200 * x */
+    cconvert->Cr_r_tab[i] = (int)
+		    RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS);
+    /* Cb=>B value is nearest int to 1.77200 * x */
+    cconvert->Cb_b_tab[i] = (int)
+		    RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS);
+    /* Cr=>G value is scaled-up -0.71414 * x */
+    cconvert->Cr_g_tab[i] = (- FIX(0.71414)) * x;
+    /* Cb=>G value is scaled-up -0.34414 * x */
+    /* We also add in ONE_HALF so that need not do it in inner loop */
+    cconvert->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF;
+  }
+}
+
+
+/*
+ * Convert some rows of samples to the output colorspace.
+ *
+ * Note that we change from noninterleaved, one-plane-per-component format
+ * to interleaved-pixel format.  The output buffer is therefore three times
+ * as wide as the input buffer.
+ * A starting row offset is provided only for the input buffer.  The caller
+ * can easily adjust the passed output_buf value to accommodate any row
+ * offset required on that side.
+ */
+
+METHODDEF(void)
+ycc_rgb_convert (j_decompress_ptr cinfo,
+		 JSAMPIMAGE input_buf, JDIMENSION input_row,
+		 JSAMPARRAY output_buf, int num_rows)
+{
+  my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
+  register int y, cb, cr;
+  register JSAMPROW outptr;
+  register JSAMPROW inptr0, inptr1, inptr2;
+  register JDIMENSION col;
+  JDIMENSION num_cols = cinfo->output_width;
+  /* copy these pointers into registers if possible */
+  register JSAMPLE * range_limit = cinfo->sample_range_limit;
+  register int * Crrtab = cconvert->Cr_r_tab;
+  register int * Cbbtab = cconvert->Cb_b_tab;
+  register INT32 * Crgtab = cconvert->Cr_g_tab;
+  register INT32 * Cbgtab = cconvert->Cb_g_tab;
+  SHIFT_TEMPS
+
+  while (--num_rows >= 0) {
+    inptr0 = input_buf[0][input_row];
+    inptr1 = input_buf[1][input_row];
+    inptr2 = input_buf[2][input_row];
+    input_row++;
+    outptr = *output_buf++;
+    for (col = 0; col < num_cols; col++) {
+      y  = GETJSAMPLE(inptr0[col]);
+      cb = GETJSAMPLE(inptr1[col]);
+      cr = GETJSAMPLE(inptr2[col]);
+      /* Range-limiting is essential due to noise introduced by DCT losses. */
+      outptr[RGB_RED] =   range_limit[y + Crrtab[cr]];
+      outptr[RGB_GREEN] = range_limit[y +
+			      ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr],
+						 SCALEBITS))];
+      outptr[RGB_BLUE] =  range_limit[y + Cbbtab[cb]];
+      outptr += RGB_PIXELSIZE;
+    }
+  }
+}
+
+
+/**************** Cases other than YCbCr -> RGB **************/
+
+
+/*
+ * Color conversion for no colorspace change: just copy the data,
+ * converting from separate-planes to interleaved representation.
+ */
+
+METHODDEF(void)
+null_convert (j_decompress_ptr cinfo,
+	      JSAMPIMAGE input_buf, JDIMENSION input_row,
+	      JSAMPARRAY output_buf, int num_rows)
+{
+  register JSAMPROW inptr, outptr;
+  register JDIMENSION count;
+  register int num_components = cinfo->num_components;
+  JDIMENSION num_cols = cinfo->output_width;
+  int ci;
+
+  while (--num_rows >= 0) {
+    for (ci = 0; ci < num_components; ci++) {
+      inptr = input_buf[ci][input_row];
+      outptr = output_buf[0] + ci;
+      for (count = num_cols; count > 0; count--) {
+	*outptr = *inptr++;	/* needn't bother with GETJSAMPLE() here */
+	outptr += num_components;
+      }
+    }
+    input_row++;
+    output_buf++;
+  }
+}
+
+
+/*
+ * Color conversion for grayscale: just copy the data.
+ * This also works for YCbCr -> grayscale conversion, in which
+ * we just copy the Y (luminance) component and ignore chrominance.
+ */
+
+METHODDEF(void)
+grayscale_convert (j_decompress_ptr cinfo,
+		   JSAMPIMAGE input_buf, JDIMENSION input_row,
+		   JSAMPARRAY output_buf, int num_rows)
+{
+  jcopy_sample_rows(input_buf[0], (int) input_row, output_buf, 0,
+		    num_rows, cinfo->output_width);
+}
+
+
+/*
+ * Convert grayscale to RGB: just duplicate the graylevel three times.
+ * This is provided to support applications that don't want to cope
+ * with grayscale as a separate case.
+ */
+
+METHODDEF(void)
+gray_rgb_convert (j_decompress_ptr cinfo,
+		  JSAMPIMAGE input_buf, JDIMENSION input_row,
+		  JSAMPARRAY output_buf, int num_rows)
+{
+  register JSAMPROW inptr, outptr;
+  register JDIMENSION col;
+  JDIMENSION num_cols = cinfo->output_width;
+
+  while (--num_rows >= 0) {
+    inptr = input_buf[0][input_row++];
+    outptr = *output_buf++;
+    for (col = 0; col < num_cols; col++) {
+      /* We can dispense with GETJSAMPLE() here */
+      outptr[RGB_RED] = outptr[RGB_GREEN] = outptr[RGB_BLUE] = inptr[col];
+      outptr += RGB_PIXELSIZE;
+    }
+  }
+}
+
+
+/*
+ * Adobe-style YCCK->CMYK conversion.
+ * We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same
+ * conversion as above, while passing K (black) unchanged.
+ * We assume build_ycc_rgb_table has been called.
+ */
+
+METHODDEF(void)
+ycck_cmyk_convert (j_decompress_ptr cinfo,
+		   JSAMPIMAGE input_buf, JDIMENSION input_row,
+		   JSAMPARRAY output_buf, int num_rows)
+{
+  my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
+  register int y, cb, cr;
+  register JSAMPROW outptr;
+  register JSAMPROW inptr0, inptr1, inptr2, inptr3;
+  register JDIMENSION col;
+  JDIMENSION num_cols = cinfo->output_width;
+  /* copy these pointers into registers if possible */
+  register JSAMPLE * range_limit = cinfo->sample_range_limit;
+  register int * Crrtab = cconvert->Cr_r_tab;
+  register int * Cbbtab = cconvert->Cb_b_tab;
+  register INT32 * Crgtab = cconvert->Cr_g_tab;
+  register INT32 * Cbgtab = cconvert->Cb_g_tab;
+  SHIFT_TEMPS
+
+  while (--num_rows >= 0) {
+    inptr0 = input_buf[0][input_row];
+    inptr1 = input_buf[1][input_row];
+    inptr2 = input_buf[2][input_row];
+    inptr3 = input_buf[3][input_row];
+    input_row++;
+    outptr = *output_buf++;
+    for (col = 0; col < num_cols; col++) {
+      y  = GETJSAMPLE(inptr0[col]);
+      cb = GETJSAMPLE(inptr1[col]);
+      cr = GETJSAMPLE(inptr2[col]);
+      /* Range-limiting is essential due to noise introduced by DCT losses. */
+      outptr[0] = range_limit[MAXJSAMPLE - (y + Crrtab[cr])];	/* red */
+      outptr[1] = range_limit[MAXJSAMPLE - (y +			/* green */
+			      ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr],
+						 SCALEBITS)))];
+      outptr[2] = range_limit[MAXJSAMPLE - (y + Cbbtab[cb])];	/* blue */
+      /* K passes through unchanged */
+      outptr[3] = inptr3[col];	/* don't need GETJSAMPLE here */
+      outptr += 4;
+    }
+  }
+}
+
+
+/*
+ * Empty method for start_pass.
+ */
+
+METHODDEF(void)
+start_pass_dcolor (j_decompress_ptr cinfo)
+{
+  /* no work needed */
+}
+
+
+/*
+ * Module initialization routine for output colorspace conversion.
+ */
+
+GLOBAL(void)
+jinit_color_deconverter (j_decompress_ptr cinfo)
+{
+  my_cconvert_ptr cconvert;
+  int ci;
+
+  cconvert = (my_cconvert_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_color_deconverter));
+  cinfo->cconvert = (struct jpeg_color_deconverter *) cconvert;
+  cconvert->pub.start_pass = start_pass_dcolor;
+
+  /* Make sure num_components agrees with jpeg_color_space */
+  switch (cinfo->jpeg_color_space) {
+  case JCS_GRAYSCALE:
+    if (cinfo->num_components != 1)
+      ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+    break;
+
+  case JCS_RGB:
+  case JCS_YCbCr:
+    if (cinfo->num_components != 3)
+      ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+    break;
+
+  case JCS_CMYK:
+  case JCS_YCCK:
+    if (cinfo->num_components != 4)
+      ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+    break;
+
+  default:			/* JCS_UNKNOWN can be anything */
+    if (cinfo->num_components < 1)
+      ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+    break;
+  }
+
+  /* Set out_color_components and conversion method based on requested space.
+   * Also clear the component_needed flags for any unused components,
+   * so that earlier pipeline stages can avoid useless computation.
+   */
+
+  switch (cinfo->out_color_space) {
+  case JCS_GRAYSCALE:
+    cinfo->out_color_components = 1;
+    if (cinfo->jpeg_color_space == JCS_GRAYSCALE ||
+	cinfo->jpeg_color_space == JCS_YCbCr) {
+      cconvert->pub.color_convert = grayscale_convert;
+      /* For color->grayscale conversion, only the Y (0) component is needed */
+      for (ci = 1; ci < cinfo->num_components; ci++)
+	cinfo->comp_info[ci].component_needed = FALSE;
+    } else
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+    break;
+
+  case JCS_RGB:
+    cinfo->out_color_components = RGB_PIXELSIZE;
+    if (cinfo->jpeg_color_space == JCS_YCbCr) {
+      cconvert->pub.color_convert = ycc_rgb_convert;
+      build_ycc_rgb_table(cinfo);
+    } else if (cinfo->jpeg_color_space == JCS_GRAYSCALE) {
+      cconvert->pub.color_convert = gray_rgb_convert;
+    } else if (cinfo->jpeg_color_space == JCS_RGB && RGB_PIXELSIZE == 3) {
+      cconvert->pub.color_convert = null_convert;
+    } else
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+    break;
+
+  case JCS_CMYK:
+    cinfo->out_color_components = 4;
+    if (cinfo->jpeg_color_space == JCS_YCCK) {
+      cconvert->pub.color_convert = ycck_cmyk_convert;
+      build_ycc_rgb_table(cinfo);
+    } else if (cinfo->jpeg_color_space == JCS_CMYK) {
+      cconvert->pub.color_convert = null_convert;
+    } else
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+    break;
+
+  default:
+    /* Permit null conversion to same output space */
+    if (cinfo->out_color_space == cinfo->jpeg_color_space) {
+      cinfo->out_color_components = cinfo->num_components;
+      cconvert->pub.color_convert = null_convert;
+    } else			/* unsupported non-null conversion */
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+    break;
+  }
+
+  if (cinfo->quantize_colors)
+    cinfo->output_components = 1; /* single colormapped output component */
+  else
+    cinfo->output_components = cinfo->out_color_components;
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdct.h b/Utilities/GDAL/frmts/jpeg/libjpeg/jdct.h
new file mode 100644
index 0000000000..04192a266a
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdct.h
@@ -0,0 +1,176 @@
+/*
+ * jdct.h
+ *
+ * Copyright (C) 1994-1996, 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 include file contains common declarations for the forward and
+ * inverse DCT modules.  These declarations are private to the DCT managers
+ * (jcdctmgr.c, jddctmgr.c) and the individual DCT algorithms.
+ * The individual DCT algorithms are kept in separate files to ease 
+ * machine-dependent tuning (e.g., assembly coding).
+ */
+
+
+/*
+ * A forward DCT routine is given a pointer to a work area of type DCTELEM[];
+ * the DCT is to be performed in-place in that buffer.  Type DCTELEM is int
+ * for 8-bit samples, INT32 for 12-bit samples.  (NOTE: Floating-point DCT
+ * implementations use an array of type FAST_FLOAT, instead.)
+ * The DCT inputs are expected to be signed (range +-CENTERJSAMPLE).
+ * The DCT outputs are returned scaled up by a factor of 8; they therefore
+ * have a range of +-8K for 8-bit data, +-128K for 12-bit data.  This
+ * convention improves accuracy in integer implementations and saves some
+ * work in floating-point ones.
+ * Quantization of the output coefficients is done by jcdctmgr.c.
+ */
+
+#if BITS_IN_JSAMPLE == 8
+typedef int DCTELEM;		/* 16 or 32 bits is fine */
+#else
+typedef INT32 DCTELEM;		/* must have 32 bits */
+#endif
+
+typedef JMETHOD(void, forward_DCT_method_ptr, (DCTELEM * data));
+typedef JMETHOD(void, float_DCT_method_ptr, (FAST_FLOAT * data));
+
+
+/*
+ * An inverse DCT routine is given a pointer to the input JBLOCK and a pointer
+ * to an output sample array.  The routine must dequantize the input data as
+ * well as perform the IDCT; for dequantization, it uses the multiplier table
+ * pointed to by compptr->dct_table.  The output data is to be placed into the
+ * sample array starting at a specified column.  (Any row offset needed will
+ * be applied to the array pointer before it is passed to the IDCT code.)
+ * Note that the number of samples emitted by the IDCT routine is
+ * DCT_scaled_size * DCT_scaled_size.
+ */
+
+/* typedef inverse_DCT_method_ptr is declared in jpegint.h */
+
+/*
+ * Each IDCT routine has its own ideas about the best dct_table element type.
+ */
+
+typedef MULTIPLIER ISLOW_MULT_TYPE; /* short or int, whichever is faster */
+#if BITS_IN_JSAMPLE == 8
+typedef MULTIPLIER IFAST_MULT_TYPE; /* 16 bits is OK, use short if faster */
+#define IFAST_SCALE_BITS  2	/* fractional bits in scale factors */
+#else
+typedef INT32 IFAST_MULT_TYPE;	/* need 32 bits for scaled quantizers */
+#define IFAST_SCALE_BITS  13	/* fractional bits in scale factors */
+#endif
+typedef FAST_FLOAT FLOAT_MULT_TYPE; /* preferred floating type */
+
+
+/*
+ * Each IDCT routine is responsible for range-limiting its results and
+ * converting them to unsigned form (0..MAXJSAMPLE).  The raw outputs could
+ * be quite far out of range if the input data is corrupt, so a bulletproof
+ * range-limiting step is required.  We use a mask-and-table-lookup method
+ * to do the combined operations quickly.  See the comments with
+ * prepare_range_limit_table (in jdmaster.c) for more info.
+ */
+
+#define IDCT_range_limit(cinfo)  ((cinfo)->sample_range_limit + CENTERJSAMPLE)
+
+#define RANGE_MASK  (MAXJSAMPLE * 4 + 3) /* 2 bits wider than legal samples */
+
+
+/* Short forms of external names for systems with brain-damaged linkers. */
+
+#ifdef NEED_SHORT_EXTERNAL_NAMES
+#define jpeg_fdct_islow		jFDislow
+#define jpeg_fdct_ifast		jFDifast
+#define jpeg_fdct_float		jFDfloat
+#define jpeg_idct_islow		jRDislow
+#define jpeg_idct_ifast		jRDifast
+#define jpeg_idct_float		jRDfloat
+#define jpeg_idct_4x4		jRD4x4
+#define jpeg_idct_2x2		jRD2x2
+#define jpeg_idct_1x1		jRD1x1
+#endif /* NEED_SHORT_EXTERNAL_NAMES */
+
+/* Extern declarations for the forward and inverse DCT routines. */
+
+EXTERN(void) jpeg_fdct_islow JPP((DCTELEM * data));
+EXTERN(void) jpeg_fdct_ifast JPP((DCTELEM * data));
+EXTERN(void) jpeg_fdct_float JPP((FAST_FLOAT * data));
+
+EXTERN(void) jpeg_idct_islow
+    JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr,
+	 JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col));
+EXTERN(void) jpeg_idct_ifast
+    JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr,
+	 JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col));
+EXTERN(void) jpeg_idct_float
+    JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr,
+	 JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col));
+EXTERN(void) jpeg_idct_4x4
+    JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr,
+	 JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col));
+EXTERN(void) jpeg_idct_2x2
+    JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr,
+	 JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col));
+EXTERN(void) jpeg_idct_1x1
+    JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr,
+	 JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col));
+
+
+/*
+ * Macros for handling fixed-point arithmetic; these are used by many
+ * but not all of the DCT/IDCT modules.
+ *
+ * All values are expected to be of type INT32.
+ * Fractional constants are scaled left by CONST_BITS bits.
+ * CONST_BITS is defined within each module using these macros,
+ * and may differ from one module to the next.
+ */
+
+#define ONE	((INT32) 1)
+#define CONST_SCALE (ONE << CONST_BITS)
+
+/* Convert a positive real constant to an integer scaled by CONST_SCALE.
+ * Caution: some C compilers fail to reduce "FIX(constant)" at compile time,
+ * thus causing a lot of useless floating-point operations at run time.
+ */
+
+#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.
+ * This macro is used only when the two inputs will actually be no more than
+ * 16 bits wide, so that a 16x16->32 bit multiply can be used instead of a
+ * full 32x32 multiply.  This provides a useful speedup on many machines.
+ * Unfortunately there is no way to specify a 16x16->32 multiply portably
+ * in C, but some C compilers will do the right thing if you provide the
+ * correct combination of casts.
+ */
+
+#ifdef SHORTxSHORT_32		/* may work if 'int' is 32 bits */
+#define MULTIPLY16C16(var,const)  (((INT16) (var)) * ((INT16) (const)))
+#endif
+#ifdef SHORTxLCONST_32		/* known to work with Microsoft C 6.0 */
+#define MULTIPLY16C16(var,const)  (((INT16) (var)) * ((INT32) (const)))
+#endif
+
+#ifndef MULTIPLY16C16		/* default definition */
+#define MULTIPLY16C16(var,const)  ((var) * (const))
+#endif
+
+/* Same except both inputs are variables. */
+
+#ifdef SHORTxSHORT_32		/* may work if 'int' is 32 bits */
+#define MULTIPLY16V16(var1,var2)  (((INT16) (var1)) * ((INT16) (var2)))
+#endif
+
+#ifndef MULTIPLY16V16		/* default definition */
+#define MULTIPLY16V16(var1,var2)  ((var1) * (var2))
+#endif
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jddctmgr.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jddctmgr.c
new file mode 100644
index 0000000000..bbf8d0e92f
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jddctmgr.c
@@ -0,0 +1,269 @@
+/*
+ * jddctmgr.c
+ *
+ * Copyright (C) 1994-1996, 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 inverse-DCT management logic.
+ * This code selects a particular IDCT implementation to be used,
+ * and it performs related housekeeping chores.  No code in this file
+ * is executed per IDCT step, only during output pass setup.
+ *
+ * Note that the IDCT routines are responsible for performing coefficient
+ * dequantization as well as the IDCT proper.  This module sets up the
+ * dequantization multiplier table needed by the IDCT routine.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jdct.h"		/* Private declarations for DCT subsystem */
+
+
+/*
+ * The decompressor input side (jdinput.c) saves away the appropriate
+ * quantization table for each component at the start of the first scan
+ * involving that component.  (This is necessary in order to correctly
+ * decode files that reuse Q-table slots.)
+ * When we are ready to make an output pass, the saved Q-table is converted
+ * to a multiplier table that will actually be used by the IDCT routine.
+ * The multiplier table contents are IDCT-method-dependent.  To support
+ * application changes in IDCT method between scans, we can remake the
+ * multiplier tables if necessary.
+ * In buffered-image mode, the first output pass may occur before any data
+ * has been seen for some components, and thus before their Q-tables have
+ * been saved away.  To handle this case, multiplier tables are preset
+ * to zeroes; the result of the IDCT will be a neutral gray level.
+ */
+
+
+/* Private subobject for this module */
+
+typedef struct {
+  struct jpeg_inverse_dct pub;	/* public fields */
+
+  /* This array contains the IDCT method code that each multiplier table
+   * is currently set up for, or -1 if it's not yet set up.
+   * The actual multiplier tables are pointed to by dct_table in the
+   * per-component comp_info structures.
+   */
+  int cur_method[MAX_COMPONENTS];
+} my_idct_controller;
+
+typedef my_idct_controller * my_idct_ptr;
+
+
+/* Allocated multiplier tables: big enough for any supported variant */
+
+typedef union {
+  ISLOW_MULT_TYPE islow_array[DCTSIZE2];
+#ifdef DCT_IFAST_SUPPORTED
+  IFAST_MULT_TYPE ifast_array[DCTSIZE2];
+#endif
+#ifdef DCT_FLOAT_SUPPORTED
+  FLOAT_MULT_TYPE float_array[DCTSIZE2];
+#endif
+} multiplier_table;
+
+
+/* The current scaled-IDCT routines require ISLOW-style multiplier tables,
+ * so be sure to compile that code if either ISLOW or SCALING is requested.
+ */
+#ifdef DCT_ISLOW_SUPPORTED
+#define PROVIDE_ISLOW_TABLES
+#else
+#ifdef IDCT_SCALING_SUPPORTED
+#define PROVIDE_ISLOW_TABLES
+#endif
+#endif
+
+
+/*
+ * Prepare for an output pass.
+ * Here we select the proper IDCT routine for each component and build
+ * a matching multiplier table.
+ */
+
+METHODDEF(void)
+start_pass (j_decompress_ptr cinfo)
+{
+  my_idct_ptr idct = (my_idct_ptr) cinfo->idct;
+  int ci, i;
+  jpeg_component_info *compptr;
+  int method = 0;
+  inverse_DCT_method_ptr method_ptr = NULL;
+  JQUANT_TBL * qtbl;
+
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    /* Select the proper IDCT routine for this component's scaling */
+    switch (compptr->DCT_scaled_size) {
+#ifdef IDCT_SCALING_SUPPORTED
+    case 1:
+      method_ptr = jpeg_idct_1x1;
+      method = JDCT_ISLOW;	/* jidctred uses islow-style table */
+      break;
+    case 2:
+      method_ptr = jpeg_idct_2x2;
+      method = JDCT_ISLOW;	/* jidctred uses islow-style table */
+      break;
+    case 4:
+      method_ptr = jpeg_idct_4x4;
+      method = JDCT_ISLOW;	/* jidctred uses islow-style table */
+      break;
+#endif
+    case DCTSIZE:
+      switch (cinfo->dct_method) {
+#ifdef DCT_ISLOW_SUPPORTED
+      case JDCT_ISLOW:
+	method_ptr = jpeg_idct_islow;
+	method = JDCT_ISLOW;
+	break;
+#endif
+#ifdef DCT_IFAST_SUPPORTED
+      case JDCT_IFAST:
+	method_ptr = jpeg_idct_ifast;
+	method = JDCT_IFAST;
+	break;
+#endif
+#ifdef DCT_FLOAT_SUPPORTED
+      case JDCT_FLOAT:
+	method_ptr = jpeg_idct_float;
+	method = JDCT_FLOAT;
+	break;
+#endif
+      default:
+	ERREXIT(cinfo, JERR_NOT_COMPILED);
+	break;
+      }
+      break;
+    default:
+      ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->DCT_scaled_size);
+      break;
+    }
+    idct->pub.inverse_DCT[ci] = method_ptr;
+    /* Create multiplier table from quant table.
+     * However, we can skip this if the component is uninteresting
+     * or if we already built the table.  Also, if no quant table
+     * has yet been saved for the component, we leave the
+     * multiplier table all-zero; we'll be reading zeroes from the
+     * coefficient controller's buffer anyway.
+     */
+    if (! compptr->component_needed || idct->cur_method[ci] == method)
+      continue;
+    qtbl = compptr->quant_table;
+    if (qtbl == NULL)		/* happens if no data yet for component */
+      continue;
+    idct->cur_method[ci] = method;
+    switch (method) {
+#ifdef PROVIDE_ISLOW_TABLES
+    case JDCT_ISLOW:
+      {
+	/* For LL&M IDCT method, multipliers are equal to raw quantization
+	 * coefficients, but are stored as ints to ensure access efficiency.
+	 */
+	ISLOW_MULT_TYPE * ismtbl = (ISLOW_MULT_TYPE *) compptr->dct_table;
+	for (i = 0; i < DCTSIZE2; i++) {
+	  ismtbl[i] = (ISLOW_MULT_TYPE) qtbl->quantval[i];
+	}
+      }
+      break;
+#endif
+#ifdef DCT_IFAST_SUPPORTED
+    case JDCT_IFAST:
+      {
+	/* For AA&N IDCT method, multipliers are equal to quantization
+	 * coefficients scaled by scalefactor[row]*scalefactor[col], where
+	 *   scalefactor[0] = 1
+	 *   scalefactor[k] = cos(k*PI/16) * sqrt(2)    for k=1..7
+	 * For integer operation, the multiplier table is to be scaled by
+	 * IFAST_SCALE_BITS.
+	 */
+	IFAST_MULT_TYPE * ifmtbl = (IFAST_MULT_TYPE *) compptr->dct_table;
+#define CONST_BITS 14
+	static const INT16 aanscales[DCTSIZE2] = {
+	  /* precomputed values scaled up by 14 bits */
+	  16384, 22725, 21407, 19266, 16384, 12873,  8867,  4520,
+	  22725, 31521, 29692, 26722, 22725, 17855, 12299,  6270,
+	  21407, 29692, 27969, 25172, 21407, 16819, 11585,  5906,
+	  19266, 26722, 25172, 22654, 19266, 15137, 10426,  5315,
+	  16384, 22725, 21407, 19266, 16384, 12873,  8867,  4520,
+	  12873, 17855, 16819, 15137, 12873, 10114,  6967,  3552,
+	   8867, 12299, 11585, 10426,  8867,  6967,  4799,  2446,
+	   4520,  6270,  5906,  5315,  4520,  3552,  2446,  1247
+	};
+	SHIFT_TEMPS
+
+	for (i = 0; i < DCTSIZE2; i++) {
+	  ifmtbl[i] = (IFAST_MULT_TYPE)
+	    DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[i],
+				  (INT32) aanscales[i]),
+		    CONST_BITS-IFAST_SCALE_BITS);
+	}
+      }
+      break;
+#endif
+#ifdef DCT_FLOAT_SUPPORTED
+    case JDCT_FLOAT:
+      {
+	/* For float AA&N IDCT method, multipliers are equal to quantization
+	 * coefficients scaled by scalefactor[row]*scalefactor[col], where
+	 *   scalefactor[0] = 1
+	 *   scalefactor[k] = cos(k*PI/16) * sqrt(2)    for k=1..7
+	 */
+	FLOAT_MULT_TYPE * fmtbl = (FLOAT_MULT_TYPE *) compptr->dct_table;
+	int row, col;
+	static const double aanscalefactor[DCTSIZE] = {
+	  1.0, 1.387039845, 1.306562965, 1.175875602,
+	  1.0, 0.785694958, 0.541196100, 0.275899379
+	};
+
+	i = 0;
+	for (row = 0; row < DCTSIZE; row++) {
+	  for (col = 0; col < DCTSIZE; col++) {
+	    fmtbl[i] = (FLOAT_MULT_TYPE)
+	      ((double) qtbl->quantval[i] *
+	       aanscalefactor[row] * aanscalefactor[col]);
+	    i++;
+	  }
+	}
+      }
+      break;
+#endif
+    default:
+      ERREXIT(cinfo, JERR_NOT_COMPILED);
+      break;
+    }
+  }
+}
+
+
+/*
+ * Initialize IDCT manager.
+ */
+
+GLOBAL(void)
+jinit_inverse_dct (j_decompress_ptr cinfo)
+{
+  my_idct_ptr idct;
+  int ci;
+  jpeg_component_info *compptr;
+
+  idct = (my_idct_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_idct_controller));
+  cinfo->idct = (struct jpeg_inverse_dct *) idct;
+  idct->pub.start_pass = start_pass;
+
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    /* Allocate and pre-zero a multiplier table for each component */
+    compptr->dct_table =
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				  SIZEOF(multiplier_table));
+    MEMZERO(compptr->dct_table, SIZEOF(multiplier_table));
+    /* Mark multiplier table not yet set up for any method */
+    idct->cur_method[ci] = -1;
+  }
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdhuff.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdhuff.c
new file mode 100644
index 0000000000..b5ba39f736
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdhuff.c
@@ -0,0 +1,651 @@
+/*
+ * jdhuff.c
+ *
+ * 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.
+ *
+ * This file contains Huffman entropy decoding routines.
+ *
+ * Much of the complexity here has to do with supporting input suspension.
+ * If the data source module demands suspension, we want to be able to back
+ * up to the start of the current MCU.  To do this, we copy state variables
+ * into local working storage, and update them back to the permanent
+ * storage only upon successful completion of an MCU.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jdhuff.h"		/* Declarations shared with jdphuff.c */
+
+
+/*
+ * Expanded entropy decoder object for Huffman decoding.
+ *
+ * The savable_state subrecord contains fields that change within an MCU,
+ * but must not be updated permanently until we complete the MCU.
+ */
+
+typedef struct {
+  int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */
+} savable_state;
+
+/* This macro is to work around compilers with missing or broken
+ * structure assignment.  You'll need to fix this code if you have
+ * such a compiler and you change MAX_COMPS_IN_SCAN.
+ */
+
+#ifndef NO_STRUCT_ASSIGN
+#define ASSIGN_STATE(dest,src)  ((dest) = (src))
+#else
+#if MAX_COMPS_IN_SCAN == 4
+#define ASSIGN_STATE(dest,src)  \
+	((dest).last_dc_val[0] = (src).last_dc_val[0], \
+	 (dest).last_dc_val[1] = (src).last_dc_val[1], \
+	 (dest).last_dc_val[2] = (src).last_dc_val[2], \
+	 (dest).last_dc_val[3] = (src).last_dc_val[3])
+#endif
+#endif
+
+
+typedef struct {
+  struct jpeg_entropy_decoder pub; /* public fields */
+
+  /* These fields are loaded into local variables at start of each MCU.
+   * In case of suspension, we exit WITHOUT updating them.
+   */
+  bitread_perm_state bitstate;	/* Bit buffer at start of MCU */
+  savable_state saved;		/* Other state at start of MCU */
+
+  /* These fields are NOT loaded into local working state. */
+  unsigned int restarts_to_go;	/* MCUs left in this restart interval */
+
+  /* Pointers to derived tables (these workspaces have image lifespan) */
+  d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS];
+  d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS];
+
+  /* Precalculated info set up by start_pass for use in decode_mcu: */
+
+  /* Pointers to derived tables to be used for each block within an MCU */
+  d_derived_tbl * dc_cur_tbls[D_MAX_BLOCKS_IN_MCU];
+  d_derived_tbl * ac_cur_tbls[D_MAX_BLOCKS_IN_MCU];
+  /* Whether we care about the DC and AC coefficient values for each block */
+  boolean dc_needed[D_MAX_BLOCKS_IN_MCU];
+  boolean ac_needed[D_MAX_BLOCKS_IN_MCU];
+} huff_entropy_decoder;
+
+typedef huff_entropy_decoder * huff_entropy_ptr;
+
+
+/*
+ * Initialize for a Huffman-compressed scan.
+ */
+
+METHODDEF(void)
+start_pass_huff_decoder (j_decompress_ptr cinfo)
+{
+  huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
+  int ci, blkn, dctbl, actbl;
+  jpeg_component_info * compptr;
+
+  /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG.
+   * This ought to be an error condition, but we make it a warning because
+   * there are some baseline files out there with all zeroes in these bytes.
+   */
+  if (cinfo->Ss != 0 || cinfo->Se != DCTSIZE2-1 ||
+      cinfo->Ah != 0 || cinfo->Al != 0)
+    WARNMS(cinfo, JWRN_NOT_SEQUENTIAL);
+
+  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+    compptr = cinfo->cur_comp_info[ci];
+    dctbl = compptr->dc_tbl_no;
+    actbl = compptr->ac_tbl_no;
+    /* Compute derived values for Huffman tables */
+    /* We may do this more than once for a table, but it's not expensive */
+    jpeg_make_d_derived_tbl(cinfo, TRUE, dctbl,
+			    & entropy->dc_derived_tbls[dctbl]);
+    jpeg_make_d_derived_tbl(cinfo, FALSE, actbl,
+			    & entropy->ac_derived_tbls[actbl]);
+    /* Initialize DC predictions to 0 */
+    entropy->saved.last_dc_val[ci] = 0;
+  }
+
+  /* Precalculate decoding info for each block in an MCU of this scan */
+  for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
+    ci = cinfo->MCU_membership[blkn];
+    compptr = cinfo->cur_comp_info[ci];
+    /* Precalculate which table to use for each block */
+    entropy->dc_cur_tbls[blkn] = entropy->dc_derived_tbls[compptr->dc_tbl_no];
+    entropy->ac_cur_tbls[blkn] = entropy->ac_derived_tbls[compptr->ac_tbl_no];
+    /* Decide whether we really care about the coefficient values */
+    if (compptr->component_needed) {
+      entropy->dc_needed[blkn] = TRUE;
+      /* we don't need the ACs if producing a 1/8th-size image */
+      entropy->ac_needed[blkn] = (compptr->DCT_scaled_size > 1);
+    } else {
+      entropy->dc_needed[blkn] = entropy->ac_needed[blkn] = FALSE;
+    }
+  }
+
+  /* Initialize bitread state variables */
+  entropy->bitstate.bits_left = 0;
+  entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */
+  entropy->pub.insufficient_data = FALSE;
+
+  /* Initialize restart counter */
+  entropy->restarts_to_go = cinfo->restart_interval;
+}
+
+
+/*
+ * Compute the derived values for a Huffman table.
+ * This routine also performs some validation checks on the table.
+ *
+ * Note this is also used by jdphuff.c.
+ */
+
+GLOBAL(void)
+jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno,
+			 d_derived_tbl ** pdtbl)
+{
+  JHUFF_TBL *htbl;
+  d_derived_tbl *dtbl;
+  int p, i, l, si, numsymbols;
+  int lookbits, ctr;
+  char huffsize[257];
+  unsigned int huffcode[257];
+  unsigned int code;
+
+  /* Note that huffsize[] and huffcode[] are filled in code-length order,
+   * paralleling the order of the symbols themselves in htbl->huffval[].
+   */
+
+  /* Find the input Huffman table */
+  if (tblno < 0 || tblno >= NUM_HUFF_TBLS)
+    ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno);
+  htbl =
+    isDC ? cinfo->dc_huff_tbl_ptrs[tblno] : cinfo->ac_huff_tbl_ptrs[tblno];
+  if (htbl == NULL)
+    ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno);
+
+  /* Allocate a workspace if we haven't already done so. */
+  if (*pdtbl == NULL)
+    *pdtbl = (d_derived_tbl *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				  SIZEOF(d_derived_tbl));
+  dtbl = *pdtbl;
+  dtbl->pub = htbl;		/* fill in back link */
+  
+  /* Figure C.1: make table of Huffman code length for each symbol */
+
+  p = 0;
+  for (l = 1; l <= 16; l++) {
+    i = (int) htbl->bits[l];
+    if (i < 0 || p + i > 256)	/* protect against table overrun */
+      ERREXIT(cinfo, JERR_BAD_HUFF_TABLE);
+    while (i--)
+      huffsize[p++] = (char) l;
+  }
+  huffsize[p] = 0;
+  numsymbols = p;
+  
+  /* Figure C.2: generate the codes themselves */
+  /* We also validate that the counts represent a legal Huffman code tree. */
+  
+  code = 0;
+  si = huffsize[0];
+  p = 0;
+  while (huffsize[p]) {
+    while (((int) huffsize[p]) == si) {
+      huffcode[p++] = code;
+      code++;
+    }
+    /* code is now 1 more than the last code used for codelength si; but
+     * it must still fit in si bits, since no code is allowed to be all ones.
+     */
+    if (((INT32) code) >= (((INT32) 1) << si))
+      ERREXIT(cinfo, JERR_BAD_HUFF_TABLE);
+    code <<= 1;
+    si++;
+  }
+
+  /* Figure F.15: generate decoding tables for bit-sequential decoding */
+
+  p = 0;
+  for (l = 1; l <= 16; l++) {
+    if (htbl->bits[l]) {
+      /* valoffset[l] = huffval[] index of 1st symbol of code length l,
+       * minus the minimum code of length l
+       */
+      dtbl->valoffset[l] = (INT32) p - (INT32) huffcode[p];
+      p += htbl->bits[l];
+      dtbl->maxcode[l] = huffcode[p-1]; /* maximum code of length l */
+    } else {
+      dtbl->maxcode[l] = -1;	/* -1 if no codes of this length */
+    }
+  }
+  dtbl->maxcode[17] = 0xFFFFFL; /* ensures jpeg_huff_decode terminates */
+
+  /* Compute lookahead tables to speed up decoding.
+   * First we set all the table entries to 0, indicating "too long";
+   * then we iterate through the Huffman codes that are short enough and
+   * fill in all the entries that correspond to bit sequences starting
+   * with that code.
+   */
+
+  MEMZERO(dtbl->look_nbits, SIZEOF(dtbl->look_nbits));
+
+  p = 0;
+  for (l = 1; l <= HUFF_LOOKAHEAD; l++) {
+    for (i = 1; i <= (int) htbl->bits[l]; i++, p++) {
+      /* l = current code's length, p = its index in huffcode[] & huffval[]. */
+      /* Generate left-justified code followed by all possible bit sequences */
+      lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l);
+      for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) {
+	dtbl->look_nbits[lookbits] = l;
+	dtbl->look_sym[lookbits] = htbl->huffval[p];
+	lookbits++;
+      }
+    }
+  }
+
+  /* Validate symbols as being reasonable.
+   * For AC tables, we make no check, but accept all byte values 0..255.
+   * For DC tables, we require the symbols to be in range 0..15.
+   * (Tighter bounds could be applied depending on the data depth and mode,
+   * but this is sufficient to ensure safe decoding.)
+   */
+  if (isDC) {
+    for (i = 0; i < numsymbols; i++) {
+      int sym = htbl->huffval[i];
+      if (sym < 0 || sym > 15)
+	ERREXIT(cinfo, JERR_BAD_HUFF_TABLE);
+    }
+  }
+}
+
+
+/*
+ * Out-of-line code for bit fetching (shared with jdphuff.c).
+ * See jdhuff.h for info about usage.
+ * Note: current values of get_buffer and bits_left are passed as parameters,
+ * but are returned in the corresponding fields of the state struct.
+ *
+ * On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width
+ * of get_buffer to be used.  (On machines with wider words, an even larger
+ * buffer could be used.)  However, on some machines 32-bit shifts are
+ * quite slow and take time proportional to the number of places shifted.
+ * (This is true with most PC compilers, for instance.)  In this case it may
+ * be a win to set MIN_GET_BITS to the minimum value of 15.  This reduces the
+ * average shift distance at the cost of more calls to jpeg_fill_bit_buffer.
+ */
+
+#ifdef SLOW_SHIFT_32
+#define MIN_GET_BITS  15	/* minimum allowable value */
+#else
+#define MIN_GET_BITS  (BIT_BUF_SIZE-7)
+#endif
+
+
+GLOBAL(boolean)
+jpeg_fill_bit_buffer (bitread_working_state * state,
+		      register bit_buf_type get_buffer, register int bits_left,
+		      int nbits)
+/* Load up the bit buffer to a depth of at least nbits */
+{
+  /* Copy heavily used state fields into locals (hopefully registers) */
+  register const JOCTET * next_input_byte = state->next_input_byte;
+  register size_t bytes_in_buffer = state->bytes_in_buffer;
+  j_decompress_ptr cinfo = state->cinfo;
+
+  /* Attempt to load at least MIN_GET_BITS bits into get_buffer. */
+  /* (It is assumed that no request will be for more than that many bits.) */
+  /* We fail to do so only if we hit a marker or are forced to suspend. */
+
+  if (cinfo->unread_marker == 0) {	/* cannot advance past a marker */
+    while (bits_left < MIN_GET_BITS) {
+      register int c;
+
+      /* Attempt to read a byte */
+      if (bytes_in_buffer == 0) {
+	if (! (*cinfo->src->fill_input_buffer) (cinfo))
+	  return FALSE;
+	next_input_byte = cinfo->src->next_input_byte;
+	bytes_in_buffer = cinfo->src->bytes_in_buffer;
+      }
+      bytes_in_buffer--;
+      c = GETJOCTET(*next_input_byte++);
+
+      /* If it's 0xFF, check and discard stuffed zero byte */
+      if (c == 0xFF) {
+	/* Loop here to discard any padding FF's on terminating marker,
+	 * so that we can save a valid unread_marker value.  NOTE: we will
+	 * accept multiple FF's followed by a 0 as meaning a single FF data
+	 * byte.  This data pattern is not valid according to the standard.
+	 */
+	do {
+	  if (bytes_in_buffer == 0) {
+	    if (! (*cinfo->src->fill_input_buffer) (cinfo))
+	      return FALSE;
+	    next_input_byte = cinfo->src->next_input_byte;
+	    bytes_in_buffer = cinfo->src->bytes_in_buffer;
+	  }
+	  bytes_in_buffer--;
+	  c = GETJOCTET(*next_input_byte++);
+	} while (c == 0xFF);
+
+	if (c == 0) {
+	  /* Found FF/00, which represents an FF data byte */
+	  c = 0xFF;
+	} else {
+	  /* Oops, it's actually a marker indicating end of compressed data.
+	   * Save the marker code for later use.
+	   * Fine point: it might appear that we should save the marker into
+	   * bitread working state, not straight into permanent state.  But
+	   * once we have hit a marker, we cannot need to suspend within the
+	   * current MCU, because we will read no more bytes from the data
+	   * source.  So it is OK to update permanent state right away.
+	   */
+	  cinfo->unread_marker = c;
+	  /* See if we need to insert some fake zero bits. */
+	  goto no_more_bytes;
+	}
+      }
+
+      /* OK, load c into get_buffer */
+      get_buffer = (get_buffer << 8) | c;
+      bits_left += 8;
+    } /* end while */
+  } else {
+  no_more_bytes:
+    /* We get here if we've read the marker that terminates the compressed
+     * data segment.  There should be enough bits in the buffer register
+     * to satisfy the request; if so, no problem.
+     */
+    if (nbits > bits_left) {
+      /* Uh-oh.  Report corrupted data to user and stuff zeroes into
+       * the data stream, so that we can produce some kind of image.
+       * We use a nonvolatile flag to ensure that only one warning message
+       * appears per data segment.
+       */
+      if (! cinfo->entropy->insufficient_data) {
+	WARNMS(cinfo, JWRN_HIT_MARKER);
+	cinfo->entropy->insufficient_data = TRUE;
+      }
+      /* Fill the buffer with zero bits */
+      get_buffer <<= MIN_GET_BITS - bits_left;
+      bits_left = MIN_GET_BITS;
+    }
+  }
+
+  /* Unload the local registers */
+  state->next_input_byte = next_input_byte;
+  state->bytes_in_buffer = bytes_in_buffer;
+  state->get_buffer = get_buffer;
+  state->bits_left = bits_left;
+
+  return TRUE;
+}
+
+
+/*
+ * Out-of-line code for Huffman code decoding.
+ * See jdhuff.h for info about usage.
+ */
+
+GLOBAL(int)
+jpeg_huff_decode (bitread_working_state * state,
+		  register bit_buf_type get_buffer, register int bits_left,
+		  d_derived_tbl * htbl, int min_bits)
+{
+  register int l = min_bits;
+  register INT32 code;
+
+  /* HUFF_DECODE has determined that the code is at least min_bits */
+  /* bits long, so fetch that many bits in one swoop. */
+
+  CHECK_BIT_BUFFER(*state, l, return -1);
+  code = GET_BITS(l);
+
+  /* Collect the rest of the Huffman code one bit at a time. */
+  /* This is per Figure F.16 in the JPEG spec. */
+
+  while (code > htbl->maxcode[l]) {
+    code <<= 1;
+    CHECK_BIT_BUFFER(*state, 1, return -1);
+    code |= GET_BITS(1);
+    l++;
+  }
+
+  /* Unload the local registers */
+  state->get_buffer = get_buffer;
+  state->bits_left = bits_left;
+
+  /* With garbage input we may reach the sentinel value l = 17. */
+
+  if (l > 16) {
+    WARNMS(state->cinfo, JWRN_HUFF_BAD_CODE);
+    return 0;			/* fake a zero as the safest result */
+  }
+
+  return htbl->pub->huffval[ (int) (code + htbl->valoffset[l]) ];
+}
+
+
+/*
+ * Figure F.12: extend sign bit.
+ * On some machines, a shift and add will be faster than a table lookup.
+ */
+
+#ifdef AVOID_TABLES
+
+#define HUFF_EXTEND(x,s)  ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x))
+
+#else
+
+#define HUFF_EXTEND(x,s)  ((x) < extend_test[s] ? (x) + extend_offset[s] : (x))
+
+static const int extend_test[16] =   /* entry n is 2**(n-1) */
+  { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
+    0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 };
+
+static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */
+  { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1,
+    ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1,
+    ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1,
+    ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 };
+
+#endif /* AVOID_TABLES */
+
+
+/*
+ * Check for a restart marker & resynchronize decoder.
+ * Returns FALSE if must suspend.
+ */
+
+LOCAL(boolean)
+process_restart (j_decompress_ptr cinfo)
+{
+  huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
+  int ci;
+
+  /* Throw away any unused bits remaining in bit buffer; */
+  /* include any full bytes in next_marker's count of discarded bytes */
+  cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8;
+  entropy->bitstate.bits_left = 0;
+
+  /* Advance past the RSTn marker */
+  if (! (*cinfo->marker->read_restart_marker) (cinfo))
+    return FALSE;
+
+  /* Re-initialize DC predictions to 0 */
+  for (ci = 0; ci < cinfo->comps_in_scan; ci++)
+    entropy->saved.last_dc_val[ci] = 0;
+
+  /* Reset restart counter */
+  entropy->restarts_to_go = cinfo->restart_interval;
+
+  /* Reset out-of-data flag, unless read_restart_marker left us smack up
+   * against a marker.  In that case we will end up treating the next data
+   * segment as empty, and we can avoid producing bogus output pixels by
+   * leaving the flag set.
+   */
+  if (cinfo->unread_marker == 0)
+    entropy->pub.insufficient_data = FALSE;
+
+  return TRUE;
+}
+
+
+/*
+ * Decode and return one MCU's worth of Huffman-compressed coefficients.
+ * The coefficients are reordered from zigzag order into natural array order,
+ * but are not dequantized.
+ *
+ * The i'th block of the MCU is stored into the block pointed to by
+ * MCU_data[i].  WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER.
+ * (Wholesale zeroing is usually a little faster than retail...)
+ *
+ * Returns FALSE if data source requested suspension.  In that case no
+ * changes have been made to permanent state.  (Exception: some output
+ * coefficients may already have been assigned.  This is harmless for
+ * this module, since we'll just re-assign them on the next call.)
+ */
+
+METHODDEF(boolean)
+decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data)
+{
+  huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
+  int blkn;
+  BITREAD_STATE_VARS;
+  savable_state state;
+
+  /* Process restart marker if needed; may have to suspend */
+  if (cinfo->restart_interval) {
+    if (entropy->restarts_to_go == 0)
+      if (! process_restart(cinfo))
+	return FALSE;
+  }
+
+  /* If we've run out of data, just leave the MCU set to zeroes.
+   * This way, we return uniform gray for the remainder of the segment.
+   */
+  if (! entropy->pub.insufficient_data) {
+
+    /* Load up working state */
+    BITREAD_LOAD_STATE(cinfo,entropy->bitstate);
+    ASSIGN_STATE(state, entropy->saved);
+
+    /* Outer loop handles each block in the MCU */
+
+    for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
+      JBLOCKROW block = MCU_data[blkn];
+      d_derived_tbl * dctbl = entropy->dc_cur_tbls[blkn];
+      d_derived_tbl * actbl = entropy->ac_cur_tbls[blkn];
+      register int s, k, r;
+
+      /* Decode a single block's worth of coefficients */
+
+      /* Section F.2.2.1: decode the DC coefficient difference */
+      HUFF_DECODE(s, br_state, dctbl, return FALSE, label1);
+      if (s) {
+	CHECK_BIT_BUFFER(br_state, s, return FALSE);
+	r = GET_BITS(s);
+	s = HUFF_EXTEND(r, s);
+      }
+
+      if (entropy->dc_needed[blkn]) {
+	/* Convert DC difference to actual value, update last_dc_val */
+	int ci = cinfo->MCU_membership[blkn];
+	s += state.last_dc_val[ci];
+	state.last_dc_val[ci] = s;
+	/* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */
+	(*block)[0] = (JCOEF) s;
+      }
+
+      if (entropy->ac_needed[blkn]) {
+
+	/* Section F.2.2.2: decode the AC coefficients */
+	/* Since zeroes are skipped, output area must be cleared beforehand */
+	for (k = 1; k < DCTSIZE2; k++) {
+	  HUFF_DECODE(s, br_state, actbl, return FALSE, label2);
+      
+	  r = s >> 4;
+	  s &= 15;
+      
+	  if (s) {
+	    k += r;
+	    CHECK_BIT_BUFFER(br_state, s, return FALSE);
+	    r = GET_BITS(s);
+	    s = HUFF_EXTEND(r, s);
+	    /* Output coefficient in natural (dezigzagged) order.
+	     * Note: the extra entries in jpeg_natural_order[] will save us
+	     * if k >= DCTSIZE2, which could happen if the data is corrupted.
+	     */
+	    (*block)[jpeg_natural_order[k]] = (JCOEF) s;
+	  } else {
+	    if (r != 15)
+	      break;
+	    k += 15;
+	  }
+	}
+
+      } else {
+
+	/* Section F.2.2.2: decode the AC coefficients */
+	/* In this path we just discard the values */
+	for (k = 1; k < DCTSIZE2; k++) {
+	  HUFF_DECODE(s, br_state, actbl, return FALSE, label3);
+      
+	  r = s >> 4;
+	  s &= 15;
+      
+	  if (s) {
+	    k += r;
+	    CHECK_BIT_BUFFER(br_state, s, return FALSE);
+	    DROP_BITS(s);
+	  } else {
+	    if (r != 15)
+	      break;
+	    k += 15;
+	  }
+	}
+
+      }
+    }
+
+    /* Completed MCU, so update state */
+    BITREAD_SAVE_STATE(cinfo,entropy->bitstate);
+    ASSIGN_STATE(entropy->saved, state);
+  }
+
+  /* Account for restart interval (no-op if not using restarts) */
+  entropy->restarts_to_go--;
+
+  return TRUE;
+}
+
+
+/*
+ * Module initialization routine for Huffman entropy decoding.
+ */
+
+GLOBAL(void)
+jinit_huff_decoder (j_decompress_ptr cinfo)
+{
+  huff_entropy_ptr entropy;
+  int i;
+
+  entropy = (huff_entropy_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(huff_entropy_decoder));
+  cinfo->entropy = (struct jpeg_entropy_decoder *) entropy;
+  entropy->pub.start_pass = start_pass_huff_decoder;
+  entropy->pub.decode_mcu = decode_mcu;
+
+  /* Mark tables unallocated */
+  for (i = 0; i < NUM_HUFF_TBLS; i++) {
+    entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL;
+  }
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdhuff.h b/Utilities/GDAL/frmts/jpeg/libjpeg/jdhuff.h
new file mode 100644
index 0000000000..ae19b6cafd
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdhuff.h
@@ -0,0 +1,201 @@
+/*
+ * jdhuff.h
+ *
+ * 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.
+ *
+ * This file contains declarations for Huffman entropy decoding routines
+ * that are shared between the sequential decoder (jdhuff.c) and the
+ * progressive decoder (jdphuff.c).  No other modules need to see these.
+ */
+
+/* Short forms of external names for systems with brain-damaged linkers. */
+
+#ifdef NEED_SHORT_EXTERNAL_NAMES
+#define jpeg_make_d_derived_tbl	jMkDDerived
+#define jpeg_fill_bit_buffer	jFilBitBuf
+#define jpeg_huff_decode	jHufDecode
+#endif /* NEED_SHORT_EXTERNAL_NAMES */
+
+
+/* Derived data constructed for each Huffman table */
+
+#define HUFF_LOOKAHEAD	8	/* # of bits of lookahead */
+
+typedef struct {
+  /* Basic tables: (element [0] of each array is unused) */
+  INT32 maxcode[18];		/* largest code of length k (-1 if none) */
+  /* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */
+  INT32 valoffset[17];		/* huffval[] offset for codes of length k */
+  /* valoffset[k] = huffval[] index of 1st symbol of code length k, less
+   * the smallest code of length k; so given a code of length k, the
+   * corresponding symbol is huffval[code + valoffset[k]]
+   */
+
+  /* Link to public Huffman table (needed only in jpeg_huff_decode) */
+  JHUFF_TBL *pub;
+
+  /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of
+   * the input data stream.  If the next Huffman code is no more
+   * than HUFF_LOOKAHEAD bits long, we can obtain its length and
+   * the corresponding symbol directly from these tables.
+   */
+  int look_nbits[1<<HUFF_LOOKAHEAD]; /* # bits, or 0 if too long */
+  UINT8 look_sym[1<<HUFF_LOOKAHEAD]; /* symbol, or unused */
+} d_derived_tbl;
+
+/* Expand a Huffman table definition into the derived format */
+EXTERN(void) jpeg_make_d_derived_tbl
+	JPP((j_decompress_ptr cinfo, boolean isDC, int tblno,
+	     d_derived_tbl ** pdtbl));
+
+
+/*
+ * Fetching the next N bits from the input stream is a time-critical operation
+ * for the Huffman decoders.  We implement it with a combination of inline
+ * macros and out-of-line subroutines.  Note that N (the number of bits
+ * demanded at one time) never exceeds 15 for JPEG use.
+ *
+ * We read source bytes into get_buffer and dole out bits as needed.
+ * If get_buffer already contains enough bits, they are fetched in-line
+ * by the macros CHECK_BIT_BUFFER and GET_BITS.  When there aren't enough
+ * bits, jpeg_fill_bit_buffer is called; it will attempt to fill get_buffer
+ * as full as possible (not just to the number of bits needed; this
+ * prefetching reduces the overhead cost of calling jpeg_fill_bit_buffer).
+ * Note that jpeg_fill_bit_buffer may return FALSE to indicate suspension.
+ * On TRUE return, jpeg_fill_bit_buffer guarantees that get_buffer contains
+ * at least the requested number of bits --- dummy zeroes are inserted if
+ * necessary.
+ */
+
+typedef INT32 bit_buf_type;	/* type of bit-extraction buffer */
+#define BIT_BUF_SIZE  32	/* size of buffer in bits */
+
+/* If long is > 32 bits on your machine, and shifting/masking longs is
+ * reasonably fast, making bit_buf_type be long and setting BIT_BUF_SIZE
+ * appropriately should be a win.  Unfortunately we can't define the size
+ * with something like  #define BIT_BUF_SIZE (sizeof(bit_buf_type)*8)
+ * because not all machines measure sizeof in 8-bit bytes.
+ */
+
+typedef struct {		/* Bitreading state saved across MCUs */
+  bit_buf_type get_buffer;	/* current bit-extraction buffer */
+  int bits_left;		/* # of unused bits in it */
+} bitread_perm_state;
+
+typedef struct {		/* Bitreading working state within an MCU */
+  /* Current data source location */
+  /* We need a copy, rather than munging the original, in case of suspension */
+  const JOCTET * next_input_byte; /* => next byte to read from source */
+  size_t bytes_in_buffer;	/* # of bytes remaining in source buffer */
+  /* Bit input buffer --- note these values are kept in register variables,
+   * not in this struct, inside the inner loops.
+   */
+  bit_buf_type get_buffer;	/* current bit-extraction buffer */
+  int bits_left;		/* # of unused bits in it */
+  /* Pointer needed by jpeg_fill_bit_buffer. */
+  j_decompress_ptr cinfo;	/* back link to decompress master record */
+} bitread_working_state;
+
+/* Macros to declare and load/save bitread local variables. */
+#define BITREAD_STATE_VARS  \
+	register bit_buf_type get_buffer;  \
+	register int bits_left;  \
+	bitread_working_state br_state
+
+#define BITREAD_LOAD_STATE(cinfop,permstate)  \
+	br_state.cinfo = cinfop; \
+	br_state.next_input_byte = cinfop->src->next_input_byte; \
+	br_state.bytes_in_buffer = cinfop->src->bytes_in_buffer; \
+	get_buffer = permstate.get_buffer; \
+	bits_left = permstate.bits_left;
+
+#define BITREAD_SAVE_STATE(cinfop,permstate)  \
+	cinfop->src->next_input_byte = br_state.next_input_byte; \
+	cinfop->src->bytes_in_buffer = br_state.bytes_in_buffer; \
+	permstate.get_buffer = get_buffer; \
+	permstate.bits_left = bits_left
+
+/*
+ * These macros provide the in-line portion of bit fetching.
+ * Use CHECK_BIT_BUFFER to ensure there are N bits in get_buffer
+ * before using GET_BITS, PEEK_BITS, or DROP_BITS.
+ * The variables get_buffer and bits_left are assumed to be locals,
+ * but the state struct might not be (jpeg_huff_decode needs this).
+ *	CHECK_BIT_BUFFER(state,n,action);
+ *		Ensure there are N bits in get_buffer; if suspend, take action.
+ *      val = GET_BITS(n);
+ *		Fetch next N bits.
+ *      val = PEEK_BITS(n);
+ *		Fetch next N bits without removing them from the buffer.
+ *	DROP_BITS(n);
+ *		Discard next N bits.
+ * The value N should be a simple variable, not an expression, because it
+ * is evaluated multiple times.
+ */
+
+#define CHECK_BIT_BUFFER(state,nbits,action) \
+	{ if (bits_left < (nbits)) {  \
+	    if (! jpeg_fill_bit_buffer(&(state),get_buffer,bits_left,nbits))  \
+	      { action; }  \
+	    get_buffer = (state).get_buffer; bits_left = (state).bits_left; } }
+
+#define GET_BITS(nbits) \
+	(((int) (get_buffer >> (bits_left -= (nbits)))) & ((1<<(nbits))-1))
+
+#define PEEK_BITS(nbits) \
+	(((int) (get_buffer >> (bits_left -  (nbits)))) & ((1<<(nbits))-1))
+
+#define DROP_BITS(nbits) \
+	(bits_left -= (nbits))
+
+/* Load up the bit buffer to a depth of at least nbits */
+EXTERN(boolean) jpeg_fill_bit_buffer
+	JPP((bitread_working_state * state, register bit_buf_type get_buffer,
+	     register int bits_left, int nbits));
+
+
+/*
+ * Code for extracting next Huffman-coded symbol from input bit stream.
+ * Again, this is time-critical and we make the main paths be macros.
+ *
+ * We use a lookahead table to process codes of up to HUFF_LOOKAHEAD bits
+ * without looping.  Usually, more than 95% of the Huffman codes will be 8
+ * or fewer bits long.  The few overlength codes are handled with a loop,
+ * which need not be inline code.
+ *
+ * Notes about the HUFF_DECODE macro:
+ * 1. Near the end of the data segment, we may fail to get enough bits
+ *    for a lookahead.  In that case, we do it the hard way.
+ * 2. If the lookahead table contains no entry, the next code must be
+ *    more than HUFF_LOOKAHEAD bits long.
+ * 3. jpeg_huff_decode returns -1 if forced to suspend.
+ */
+
+#define HUFF_DECODE(result,state,htbl,failaction,slowlabel) \
+{ register int nb, look; \
+  if (bits_left < HUFF_LOOKAHEAD) { \
+    if (! jpeg_fill_bit_buffer(&state,get_buffer,bits_left, 0)) {failaction;} \
+    get_buffer = state.get_buffer; bits_left = state.bits_left; \
+    if (bits_left < HUFF_LOOKAHEAD) { \
+      nb = 1; goto slowlabel; \
+    } \
+  } \
+  look = PEEK_BITS(HUFF_LOOKAHEAD); \
+  if ((nb = htbl->look_nbits[look]) != 0) { \
+    DROP_BITS(nb); \
+    result = htbl->look_sym[look]; \
+  } else { \
+    nb = HUFF_LOOKAHEAD+1; \
+slowlabel: \
+    if ((result=jpeg_huff_decode(&state,get_buffer,bits_left,htbl,nb)) < 0) \
+	{ failaction; } \
+    get_buffer = state.get_buffer; bits_left = state.bits_left; \
+  } \
+}
+
+/* Out-of-line case for Huffman code fetching */
+EXTERN(int) jpeg_huff_decode
+	JPP((bitread_working_state * state, register bit_buf_type get_buffer,
+	     register int bits_left, d_derived_tbl * htbl, int min_bits));
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdinput.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdinput.c
new file mode 100644
index 0000000000..0c2ac8f120
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdinput.c
@@ -0,0 +1,381 @@
+/*
+ * jdinput.c
+ *
+ * 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.
+ *
+ * This file contains input control logic for the JPEG decompressor.
+ * These routines are concerned with controlling the decompressor's input
+ * processing (marker reading and coefficient decoding).  The actual input
+ * reading is done in jdmarker.c, jdhuff.c, and jdphuff.c.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/* Private state */
+
+typedef struct {
+  struct jpeg_input_controller pub; /* public fields */
+
+  boolean inheaders;		/* TRUE until first SOS is reached */
+} my_input_controller;
+
+typedef my_input_controller * my_inputctl_ptr;
+
+
+/* Forward declarations */
+METHODDEF(int) consume_markers JPP((j_decompress_ptr cinfo));
+
+
+/*
+ * Routines to calculate various quantities related to the size of the image.
+ */
+
+LOCAL(void)
+initial_setup (j_decompress_ptr cinfo)
+/* Called once, when first SOS marker is reached */
+{
+  int ci;
+  jpeg_component_info *compptr;
+
+  /* Make sure image isn't bigger than I can handle */
+  if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION ||
+      (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION)
+    ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION);
+
+  /* For now, precision must match compiled-in value... */
+  if (cinfo->data_precision != BITS_IN_JSAMPLE)
+    ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision);
+
+  /* Check that number of components won't exceed internal array sizes */
+  if (cinfo->num_components > MAX_COMPONENTS)
+    ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components,
+	     MAX_COMPONENTS);
+
+  /* Compute maximum sampling factors; check factor validity */
+  cinfo->max_h_samp_factor = 1;
+  cinfo->max_v_samp_factor = 1;
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR ||
+	compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR)
+      ERREXIT(cinfo, JERR_BAD_SAMPLING);
+    cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor,
+				   compptr->h_samp_factor);
+    cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor,
+				   compptr->v_samp_factor);
+  }
+
+  /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE.
+   * In the full decompressor, this will be overridden by jdmaster.c;
+   * but in the transcoder, jdmaster.c is not used, so we must do it here.
+   */
+  cinfo->min_DCT_scaled_size = DCTSIZE;
+
+  /* Compute dimensions of components */
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    compptr->DCT_scaled_size = DCTSIZE;
+    /* Size in DCT blocks */
+    compptr->width_in_blocks = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor,
+		    (long) (cinfo->max_h_samp_factor * DCTSIZE));
+    compptr->height_in_blocks = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor,
+		    (long) (cinfo->max_v_samp_factor * DCTSIZE));
+    /* downsampled_width and downsampled_height will also be overridden by
+     * jdmaster.c if we are doing full decompression.  The transcoder library
+     * doesn't use these values, but the calling application might.
+     */
+    /* Size in samples */
+    compptr->downsampled_width = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor,
+		    (long) cinfo->max_h_samp_factor);
+    compptr->downsampled_height = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor,
+		    (long) cinfo->max_v_samp_factor);
+    /* Mark component needed, until color conversion says otherwise */
+    compptr->component_needed = TRUE;
+    /* Mark no quantization table yet saved for component */
+    compptr->quant_table = NULL;
+  }
+
+  /* Compute number of fully interleaved MCU rows. */
+  cinfo->total_iMCU_rows = (JDIMENSION)
+    jdiv_round_up((long) cinfo->image_height,
+		  (long) (cinfo->max_v_samp_factor*DCTSIZE));
+
+  /* Decide whether file contains multiple scans */
+  if (cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode)
+    cinfo->inputctl->has_multiple_scans = TRUE;
+  else
+    cinfo->inputctl->has_multiple_scans = FALSE;
+}
+
+
+LOCAL(void)
+per_scan_setup (j_decompress_ptr cinfo)
+/* Do computations that are needed before processing a JPEG scan */
+/* cinfo->comps_in_scan and cinfo->cur_comp_info[] were set from SOS marker */
+{
+  int ci, mcublks, tmp;
+  jpeg_component_info *compptr;
+  
+  if (cinfo->comps_in_scan == 1) {
+    
+    /* Noninterleaved (single-component) scan */
+    compptr = cinfo->cur_comp_info[0];
+    
+    /* Overall image size in MCUs */
+    cinfo->MCUs_per_row = compptr->width_in_blocks;
+    cinfo->MCU_rows_in_scan = compptr->height_in_blocks;
+    
+    /* For noninterleaved scan, always one block per MCU */
+    compptr->MCU_width = 1;
+    compptr->MCU_height = 1;
+    compptr->MCU_blocks = 1;
+    compptr->MCU_sample_width = compptr->DCT_scaled_size;
+    compptr->last_col_width = 1;
+    /* For noninterleaved scans, it is convenient to define last_row_height
+     * as the number of block rows present in the last iMCU row.
+     */
+    tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor);
+    if (tmp == 0) tmp = compptr->v_samp_factor;
+    compptr->last_row_height = tmp;
+    
+    /* Prepare array describing MCU composition */
+    cinfo->blocks_in_MCU = 1;
+    cinfo->MCU_membership[0] = 0;
+    
+  } else {
+    
+    /* Interleaved (multi-component) scan */
+    if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN)
+      ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan,
+	       MAX_COMPS_IN_SCAN);
+    
+    /* Overall image size in MCUs */
+    cinfo->MCUs_per_row = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_width,
+		    (long) (cinfo->max_h_samp_factor*DCTSIZE));
+    cinfo->MCU_rows_in_scan = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_height,
+		    (long) (cinfo->max_v_samp_factor*DCTSIZE));
+    
+    cinfo->blocks_in_MCU = 0;
+    
+    for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+      compptr = cinfo->cur_comp_info[ci];
+      /* Sampling factors give # of blocks of component in each MCU */
+      compptr->MCU_width = compptr->h_samp_factor;
+      compptr->MCU_height = compptr->v_samp_factor;
+      compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height;
+      compptr->MCU_sample_width = compptr->MCU_width * compptr->DCT_scaled_size;
+      /* Figure number of non-dummy blocks in last MCU column & row */
+      tmp = (int) (compptr->width_in_blocks % compptr->MCU_width);
+      if (tmp == 0) tmp = compptr->MCU_width;
+      compptr->last_col_width = tmp;
+      tmp = (int) (compptr->height_in_blocks % compptr->MCU_height);
+      if (tmp == 0) tmp = compptr->MCU_height;
+      compptr->last_row_height = tmp;
+      /* Prepare array describing MCU composition */
+      mcublks = compptr->MCU_blocks;
+      if (cinfo->blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU)
+	ERREXIT(cinfo, JERR_BAD_MCU_SIZE);
+      while (mcublks-- > 0) {
+	cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci;
+      }
+    }
+    
+  }
+}
+
+
+/*
+ * Save away a copy of the Q-table referenced by each component present
+ * in the current scan, unless already saved during a prior scan.
+ *
+ * In a multiple-scan JPEG file, the encoder could assign different components
+ * the same Q-table slot number, but change table definitions between scans
+ * so that each component uses a different Q-table.  (The IJG encoder is not
+ * currently capable of doing this, but other encoders might.)  Since we want
+ * to be able to dequantize all the components at the end of the file, this
+ * means that we have to save away the table actually used for each component.
+ * We do this by copying the table at the start of the first scan containing
+ * the component.
+ * The JPEG spec prohibits the encoder from changing the contents of a Q-table
+ * slot between scans of a component using that slot.  If the encoder does so
+ * anyway, this decoder will simply use the Q-table values that were current
+ * at the start of the first scan for the component.
+ *
+ * The decompressor output side looks only at the saved quant tables,
+ * not at the current Q-table slots.
+ */
+
+LOCAL(void)
+latch_quant_tables (j_decompress_ptr cinfo)
+{
+  int ci, qtblno;
+  jpeg_component_info *compptr;
+  JQUANT_TBL * qtbl;
+
+  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+    compptr = cinfo->cur_comp_info[ci];
+    /* No work if we already saved Q-table for this component */
+    if (compptr->quant_table != NULL)
+      continue;
+    /* Make sure specified quantization table is present */
+    qtblno = compptr->quant_tbl_no;
+    if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS ||
+	cinfo->quant_tbl_ptrs[qtblno] == NULL)
+      ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno);
+    /* OK, save away the quantization table */
+    qtbl = (JQUANT_TBL *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				  SIZEOF(JQUANT_TBL));
+    MEMCOPY(qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL));
+    compptr->quant_table = qtbl;
+  }
+}
+
+
+/*
+ * Initialize the input modules to read a scan of compressed data.
+ * The first call to this is done by jdmaster.c after initializing
+ * the entire decompressor (during jpeg_start_decompress).
+ * Subsequent calls come from consume_markers, below.
+ */
+
+METHODDEF(void)
+start_input_pass (j_decompress_ptr cinfo)
+{
+  per_scan_setup(cinfo);
+  latch_quant_tables(cinfo);
+  (*cinfo->entropy->start_pass) (cinfo);
+  (*cinfo->coef->start_input_pass) (cinfo);
+  cinfo->inputctl->consume_input = cinfo->coef->consume_data;
+}
+
+
+/*
+ * Finish up after inputting a compressed-data scan.
+ * This is called by the coefficient controller after it's read all
+ * the expected data of the scan.
+ */
+
+METHODDEF(void)
+finish_input_pass (j_decompress_ptr cinfo)
+{
+  cinfo->inputctl->consume_input = consume_markers;
+}
+
+
+/*
+ * Read JPEG markers before, between, or after compressed-data scans.
+ * Change state as necessary when a new scan is reached.
+ * Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI.
+ *
+ * The consume_input method pointer points either here or to the
+ * coefficient controller's consume_data routine, depending on whether
+ * we are reading a compressed data segment or inter-segment markers.
+ */
+
+METHODDEF(int)
+consume_markers (j_decompress_ptr cinfo)
+{
+  my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl;
+  int val;
+
+  if (inputctl->pub.eoi_reached) /* After hitting EOI, read no further */
+    return JPEG_REACHED_EOI;
+
+  val = (*cinfo->marker->read_markers) (cinfo);
+
+  switch (val) {
+  case JPEG_REACHED_SOS:	/* Found SOS */
+    if (inputctl->inheaders) {	/* 1st SOS */
+      initial_setup(cinfo);
+      inputctl->inheaders = FALSE;
+      /* Note: start_input_pass must be called by jdmaster.c
+       * before any more input can be consumed.  jdapimin.c is
+       * responsible for enforcing this sequencing.
+       */
+    } else {			/* 2nd or later SOS marker */
+      if (! inputctl->pub.has_multiple_scans)
+	ERREXIT(cinfo, JERR_EOI_EXPECTED); /* Oops, I wasn't expecting this! */
+      start_input_pass(cinfo);
+    }
+    break;
+  case JPEG_REACHED_EOI:	/* Found EOI */
+    inputctl->pub.eoi_reached = TRUE;
+    if (inputctl->inheaders) {	/* Tables-only datastream, apparently */
+      if (cinfo->marker->saw_SOF)
+	ERREXIT(cinfo, JERR_SOF_NO_SOS);
+    } else {
+      /* Prevent infinite loop in coef ctlr's decompress_data routine
+       * if user set output_scan_number larger than number of scans.
+       */
+      if (cinfo->output_scan_number > cinfo->input_scan_number)
+	cinfo->output_scan_number = cinfo->input_scan_number;
+    }
+    break;
+  case JPEG_SUSPENDED:
+    break;
+  }
+
+  return val;
+}
+
+
+/*
+ * Reset state to begin a fresh datastream.
+ */
+
+METHODDEF(void)
+reset_input_controller (j_decompress_ptr cinfo)
+{
+  my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl;
+
+  inputctl->pub.consume_input = consume_markers;
+  inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */
+  inputctl->pub.eoi_reached = FALSE;
+  inputctl->inheaders = TRUE;
+  /* Reset other modules */
+  (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo);
+  (*cinfo->marker->reset_marker_reader) (cinfo);
+  /* Reset progression state -- would be cleaner if entropy decoder did this */
+  cinfo->coef_bits = NULL;
+}
+
+
+/*
+ * Initialize the input controller module.
+ * This is called only once, when the decompression object is created.
+ */
+
+GLOBAL(void)
+jinit_input_controller (j_decompress_ptr cinfo)
+{
+  my_inputctl_ptr inputctl;
+
+  /* Create subobject in permanent pool */
+  inputctl = (my_inputctl_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+				SIZEOF(my_input_controller));
+  cinfo->inputctl = (struct jpeg_input_controller *) inputctl;
+  /* Initialize method pointers */
+  inputctl->pub.consume_input = consume_markers;
+  inputctl->pub.reset_input_controller = reset_input_controller;
+  inputctl->pub.start_input_pass = start_input_pass;
+  inputctl->pub.finish_input_pass = finish_input_pass;
+  /* Initialize state: can't use reset_input_controller since we don't
+   * want to try to reset other modules yet.
+   */
+  inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */
+  inputctl->pub.eoi_reached = FALSE;
+  inputctl->inheaders = TRUE;
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdmainct.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdmainct.c
new file mode 100644
index 0000000000..13c956f5de
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdmainct.c
@@ -0,0 +1,512 @@
+/*
+ * jdmainct.c
+ *
+ * Copyright (C) 1994-1996, 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 main buffer controller for decompression.
+ * The main buffer lies between the JPEG decompressor proper and the
+ * post-processor; it holds downsampled data in the JPEG colorspace.
+ *
+ * Note that this code is bypassed in raw-data mode, since the application
+ * supplies the equivalent of the main buffer in that case.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/*
+ * In the current system design, the main buffer need never be a full-image
+ * buffer; any full-height buffers will be found inside the coefficient or
+ * postprocessing controllers.  Nonetheless, the main controller is not
+ * trivial.  Its responsibility is to provide context rows for upsampling/
+ * rescaling, and doing this in an efficient fashion is a bit tricky.
+ *
+ * Postprocessor input data is counted in "row groups".  A row group
+ * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size)
+ * sample rows of each component.  (We require DCT_scaled_size values to be
+ * chosen such that these numbers are integers.  In practice DCT_scaled_size
+ * values will likely be powers of two, so we actually have the stronger
+ * condition that DCT_scaled_size / min_DCT_scaled_size is an integer.)
+ * Upsampling will typically produce max_v_samp_factor pixel rows from each
+ * row group (times any additional scale factor that the upsampler is
+ * applying).
+ *
+ * The coefficient controller will deliver data to us one iMCU row at a time;
+ * each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or
+ * exactly min_DCT_scaled_size row groups.  (This amount of data corresponds
+ * to one row of MCUs when the image is fully interleaved.)  Note that the
+ * number of sample rows varies across components, but the number of row
+ * groups does not.  Some garbage sample rows may be included in the last iMCU
+ * row at the bottom of the image.
+ *
+ * Depending on the vertical scaling algorithm used, the upsampler may need
+ * access to the sample row(s) above and below its current input row group.
+ * The upsampler is required to set need_context_rows TRUE at global selection
+ * time if so.  When need_context_rows is FALSE, this controller can simply
+ * obtain one iMCU row at a time from the coefficient controller and dole it
+ * out as row groups to the postprocessor.
+ *
+ * When need_context_rows is TRUE, this controller guarantees that the buffer
+ * passed to postprocessing contains at least one row group's worth of samples
+ * above and below the row group(s) being processed.  Note that the context
+ * rows "above" the first passed row group appear at negative row offsets in
+ * the passed buffer.  At the top and bottom of the image, the required
+ * context rows are manufactured by duplicating the first or last real sample
+ * row; this avoids having special cases in the upsampling inner loops.
+ *
+ * The amount of context is fixed at one row group just because that's a
+ * convenient number for this controller to work with.  The existing
+ * upsamplers really only need one sample row of context.  An upsampler
+ * supporting arbitrary output rescaling might wish for more than one row
+ * group of context when shrinking the image; tough, we don't handle that.
+ * (This is justified by the assumption that downsizing will be handled mostly
+ * by adjusting the DCT_scaled_size values, so that the actual scale factor at
+ * the upsample step needn't be much less than one.)
+ *
+ * To provide the desired context, we have to retain the last two row groups
+ * of one iMCU row while reading in the next iMCU row.  (The last row group
+ * can't be processed until we have another row group for its below-context,
+ * and so we have to save the next-to-last group too for its above-context.)
+ * We could do this most simply by copying data around in our buffer, but
+ * that'd be very slow.  We can avoid copying any data by creating a rather
+ * strange pointer structure.  Here's how it works.  We allocate a workspace
+ * consisting of M+2 row groups (where M = min_DCT_scaled_size is the number
+ * of row groups per iMCU row).  We create two sets of redundant pointers to
+ * the workspace.  Labeling the physical row groups 0 to M+1, the synthesized
+ * pointer lists look like this:
+ *                   M+1                          M-1
+ * master pointer --> 0         master pointer --> 0
+ *                    1                            1
+ *                   ...                          ...
+ *                   M-3                          M-3
+ *                   M-2                           M
+ *                   M-1                          M+1
+ *                    M                           M-2
+ *                   M+1                          M-1
+ *                    0                            0
+ * We read alternate iMCU rows using each master pointer; thus the last two
+ * row groups of the previous iMCU row remain un-overwritten in the workspace.
+ * The pointer lists are set up so that the required context rows appear to
+ * be adjacent to the proper places when we pass the pointer lists to the
+ * upsampler.
+ *
+ * The above pictures describe the normal state of the pointer lists.
+ * At top and bottom of the image, we diddle the pointer lists to duplicate
+ * the first or last sample row as necessary (this is cheaper than copying
+ * sample rows around).
+ *
+ * This scheme breaks down if M < 2, ie, min_DCT_scaled_size is 1.  In that
+ * situation each iMCU row provides only one row group so the buffering logic
+ * must be different (eg, we must read two iMCU rows before we can emit the
+ * first row group).  For now, we simply do not support providing context
+ * rows when min_DCT_scaled_size is 1.  That combination seems unlikely to
+ * be worth providing --- if someone wants a 1/8th-size preview, they probably
+ * want it quick and dirty, so a context-free upsampler is sufficient.
+ */
+
+
+/* Private buffer controller object */
+
+typedef struct {
+  struct jpeg_d_main_controller pub; /* public fields */
+
+  /* Pointer to allocated workspace (M or M+2 row groups). */
+  JSAMPARRAY buffer[MAX_COMPONENTS];
+
+  boolean buffer_full;		/* Have we gotten an iMCU row from decoder? */
+  JDIMENSION rowgroup_ctr;	/* counts row groups output to postprocessor */
+
+  /* Remaining fields are only used in the context case. */
+
+  /* These are the master pointers to the funny-order pointer lists. */
+  JSAMPIMAGE xbuffer[2];	/* pointers to weird pointer lists */
+
+  int whichptr;			/* indicates which pointer set is now in use */
+  int context_state;		/* process_data state machine status */
+  JDIMENSION rowgroups_avail;	/* row groups available to postprocessor */
+  JDIMENSION iMCU_row_ctr;	/* counts iMCU rows to detect image top/bot */
+} my_main_controller;
+
+typedef my_main_controller * my_main_ptr;
+
+/* context_state values: */
+#define CTX_PREPARE_FOR_IMCU	0	/* need to prepare for MCU row */
+#define CTX_PROCESS_IMCU	1	/* feeding iMCU to postprocessor */
+#define CTX_POSTPONED_ROW	2	/* feeding postponed row group */
+
+
+/* Forward declarations */
+METHODDEF(void) process_data_simple_main
+	JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf,
+	     JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail));
+METHODDEF(void) process_data_context_main
+	JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf,
+	     JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail));
+#ifdef QUANT_2PASS_SUPPORTED
+METHODDEF(void) process_data_crank_post
+	JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf,
+	     JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail));
+#endif
+
+
+LOCAL(void)
+alloc_funny_pointers (j_decompress_ptr cinfo)
+/* Allocate space for the funny pointer lists.
+ * This is done only once, not once per pass.
+ */
+{
+  my_main_ptr main = (my_main_ptr) cinfo->main;
+  int ci, rgroup;
+  int M = cinfo->min_DCT_scaled_size;
+  jpeg_component_info *compptr;
+  JSAMPARRAY xbuf;
+
+  /* Get top-level space for component array pointers.
+   * We alloc both arrays with one call to save a few cycles.
+   */
+  main->xbuffer[0] = (JSAMPIMAGE)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				cinfo->num_components * 2 * SIZEOF(JSAMPARRAY));
+  main->xbuffer[1] = main->xbuffer[0] + cinfo->num_components;
+
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) /
+      cinfo->min_DCT_scaled_size; /* height of a row group of component */
+    /* Get space for pointer lists --- M+4 row groups in each list.
+     * We alloc both pointer lists with one call to save a few cycles.
+     */
+    xbuf = (JSAMPARRAY)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				  2 * (rgroup * (M + 4)) * SIZEOF(JSAMPROW));
+    xbuf += rgroup;		/* want one row group at negative offsets */
+    main->xbuffer[0][ci] = xbuf;
+    xbuf += rgroup * (M + 4);
+    main->xbuffer[1][ci] = xbuf;
+  }
+}
+
+
+LOCAL(void)
+make_funny_pointers (j_decompress_ptr cinfo)
+/* Create the funny pointer lists discussed in the comments above.
+ * The actual workspace is already allocated (in main->buffer),
+ * and the space for the pointer lists is allocated too.
+ * This routine just fills in the curiously ordered lists.
+ * This will be repeated at the beginning of each pass.
+ */
+{
+  my_main_ptr main = (my_main_ptr) cinfo->main;
+  int ci, i, rgroup;
+  int M = cinfo->min_DCT_scaled_size;
+  jpeg_component_info *compptr;
+  JSAMPARRAY buf, xbuf0, xbuf1;
+
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) /
+      cinfo->min_DCT_scaled_size; /* height of a row group of component */
+    xbuf0 = main->xbuffer[0][ci];
+    xbuf1 = main->xbuffer[1][ci];
+    /* First copy the workspace pointers as-is */
+    buf = main->buffer[ci];
+    for (i = 0; i < rgroup * (M + 2); i++) {
+      xbuf0[i] = xbuf1[i] = buf[i];
+    }
+    /* In the second list, put the last four row groups in swapped order */
+    for (i = 0; i < rgroup * 2; i++) {
+      xbuf1[rgroup*(M-2) + i] = buf[rgroup*M + i];
+      xbuf1[rgroup*M + i] = buf[rgroup*(M-2) + i];
+    }
+    /* The wraparound pointers at top and bottom will be filled later
+     * (see set_wraparound_pointers, below).  Initially we want the "above"
+     * pointers to duplicate the first actual data line.  This only needs
+     * to happen in xbuffer[0].
+     */
+    for (i = 0; i < rgroup; i++) {
+      xbuf0[i - rgroup] = xbuf0[0];
+    }
+  }
+}
+
+
+LOCAL(void)
+set_wraparound_pointers (j_decompress_ptr cinfo)
+/* Set up the "wraparound" pointers at top and bottom of the pointer lists.
+ * This changes the pointer list state from top-of-image to the normal state.
+ */
+{
+  my_main_ptr main = (my_main_ptr) cinfo->main;
+  int ci, i, rgroup;
+  int M = cinfo->min_DCT_scaled_size;
+  jpeg_component_info *compptr;
+  JSAMPARRAY xbuf0, xbuf1;
+
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) /
+      cinfo->min_DCT_scaled_size; /* height of a row group of component */
+    xbuf0 = main->xbuffer[0][ci];
+    xbuf1 = main->xbuffer[1][ci];
+    for (i = 0; i < rgroup; i++) {
+      xbuf0[i - rgroup] = xbuf0[rgroup*(M+1) + i];
+      xbuf1[i - rgroup] = xbuf1[rgroup*(M+1) + i];
+      xbuf0[rgroup*(M+2) + i] = xbuf0[i];
+      xbuf1[rgroup*(M+2) + i] = xbuf1[i];
+    }
+  }
+}
+
+
+LOCAL(void)
+set_bottom_pointers (j_decompress_ptr cinfo)
+/* Change the pointer lists to duplicate the last sample row at the bottom
+ * of the image.  whichptr indicates which xbuffer holds the final iMCU row.
+ * Also sets rowgroups_avail to indicate number of nondummy row groups in row.
+ */
+{
+  my_main_ptr main = (my_main_ptr) cinfo->main;
+  int ci, i, rgroup, iMCUheight, rows_left;
+  jpeg_component_info *compptr;
+  JSAMPARRAY xbuf;
+
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    /* Count sample rows in one iMCU row and in one row group */
+    iMCUheight = compptr->v_samp_factor * compptr->DCT_scaled_size;
+    rgroup = iMCUheight / cinfo->min_DCT_scaled_size;
+    /* Count nondummy sample rows remaining for this component */
+    rows_left = (int) (compptr->downsampled_height % (JDIMENSION) iMCUheight);
+    if (rows_left == 0) rows_left = iMCUheight;
+    /* Count nondummy row groups.  Should get same answer for each component,
+     * so we need only do it once.
+     */
+    if (ci == 0) {
+      main->rowgroups_avail = (JDIMENSION) ((rows_left-1) / rgroup + 1);
+    }
+    /* Duplicate the last real sample row rgroup*2 times; this pads out the
+     * last partial rowgroup and ensures at least one full rowgroup of context.
+     */
+    xbuf = main->xbuffer[main->whichptr][ci];
+    for (i = 0; i < rgroup * 2; i++) {
+      xbuf[rows_left + i] = xbuf[rows_left-1];
+    }
+  }
+}
+
+
+/*
+ * Initialize for a processing pass.
+ */
+
+METHODDEF(void)
+start_pass_main (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)
+{
+  my_main_ptr main = (my_main_ptr) cinfo->main;
+
+  switch (pass_mode) {
+  case JBUF_PASS_THRU:
+    if (cinfo->upsample->need_context_rows) {
+      main->pub.process_data = process_data_context_main;
+      make_funny_pointers(cinfo); /* Create the xbuffer[] lists */
+      main->whichptr = 0;	/* Read first iMCU row into xbuffer[0] */
+      main->context_state = CTX_PREPARE_FOR_IMCU;
+      main->iMCU_row_ctr = 0;
+    } else {
+      /* Simple case with no context needed */
+      main->pub.process_data = process_data_simple_main;
+    }
+    main->buffer_full = FALSE;	/* Mark buffer empty */
+    main->rowgroup_ctr = 0;
+    break;
+#ifdef QUANT_2PASS_SUPPORTED
+  case JBUF_CRANK_DEST:
+    /* For last pass of 2-pass quantization, just crank the postprocessor */
+    main->pub.process_data = process_data_crank_post;
+    break;
+#endif
+  default:
+    ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+    break;
+  }
+}
+
+
+/*
+ * Process some data.
+ * This handles the simple case where no context is required.
+ */
+
+METHODDEF(void)
+process_data_simple_main (j_decompress_ptr cinfo,
+			  JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+			  JDIMENSION out_rows_avail)
+{
+  my_main_ptr main = (my_main_ptr) cinfo->main;
+  JDIMENSION rowgroups_avail;
+
+  /* Read input data if we haven't filled the main buffer yet */
+  if (! main->buffer_full) {
+    if (! (*cinfo->coef->decompress_data) (cinfo, main->buffer))
+      return;			/* suspension forced, can do nothing more */
+    main->buffer_full = TRUE;	/* OK, we have an iMCU row to work with */
+  }
+
+  /* There are always min_DCT_scaled_size row groups in an iMCU row. */
+  rowgroups_avail = (JDIMENSION) cinfo->min_DCT_scaled_size;
+  /* Note: at the bottom of the image, we may pass extra garbage row groups
+   * to the postprocessor.  The postprocessor has to check for bottom
+   * of image anyway (at row resolution), so no point in us doing it too.
+   */
+
+  /* Feed the postprocessor */
+  (*cinfo->post->post_process_data) (cinfo, main->buffer,
+				     &main->rowgroup_ctr, rowgroups_avail,
+				     output_buf, out_row_ctr, out_rows_avail);
+
+  /* Has postprocessor consumed all the data yet? If so, mark buffer empty */
+  if (main->rowgroup_ctr >= rowgroups_avail) {
+    main->buffer_full = FALSE;
+    main->rowgroup_ctr = 0;
+  }
+}
+
+
+/*
+ * Process some data.
+ * This handles the case where context rows must be provided.
+ */
+
+METHODDEF(void)
+process_data_context_main (j_decompress_ptr cinfo,
+			   JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+			   JDIMENSION out_rows_avail)
+{
+  my_main_ptr main = (my_main_ptr) cinfo->main;
+
+  /* Read input data if we haven't filled the main buffer yet */
+  if (! main->buffer_full) {
+    if (! (*cinfo->coef->decompress_data) (cinfo,
+					   main->xbuffer[main->whichptr]))
+      return;			/* suspension forced, can do nothing more */
+    main->buffer_full = TRUE;	/* OK, we have an iMCU row to work with */
+    main->iMCU_row_ctr++;	/* count rows received */
+  }
+
+  /* Postprocessor typically will not swallow all the input data it is handed
+   * in one call (due to filling the output buffer first).  Must be prepared
+   * to exit and restart.  This switch lets us keep track of how far we got.
+   * Note that each case falls through to the next on successful completion.
+   */
+  switch (main->context_state) {
+  case CTX_POSTPONED_ROW:
+    /* Call postprocessor using previously set pointers for postponed row */
+    (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr],
+			&main->rowgroup_ctr, main->rowgroups_avail,
+			output_buf, out_row_ctr, out_rows_avail);
+    if (main->rowgroup_ctr < main->rowgroups_avail)
+      return;			/* Need to suspend */
+    main->context_state = CTX_PREPARE_FOR_IMCU;
+    if (*out_row_ctr >= out_rows_avail)
+      return;			/* Postprocessor exactly filled output buf */
+    /*FALLTHROUGH*/
+  case CTX_PREPARE_FOR_IMCU:
+    /* Prepare to process first M-1 row groups of this iMCU row */
+    main->rowgroup_ctr = 0;
+    main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size - 1);
+    /* Check for bottom of image: if so, tweak pointers to "duplicate"
+     * the last sample row, and adjust rowgroups_avail to ignore padding rows.
+     */
+    if (main->iMCU_row_ctr == cinfo->total_iMCU_rows)
+      set_bottom_pointers(cinfo);
+    main->context_state = CTX_PROCESS_IMCU;
+    /*FALLTHROUGH*/
+  case CTX_PROCESS_IMCU:
+    /* Call postprocessor using previously set pointers */
+    (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr],
+			&main->rowgroup_ctr, main->rowgroups_avail,
+			output_buf, out_row_ctr, out_rows_avail);
+    if (main->rowgroup_ctr < main->rowgroups_avail)
+      return;			/* Need to suspend */
+    /* After the first iMCU, change wraparound pointers to normal state */
+    if (main->iMCU_row_ctr == 1)
+      set_wraparound_pointers(cinfo);
+    /* Prepare to load new iMCU row using other xbuffer list */
+    main->whichptr ^= 1;	/* 0=>1 or 1=>0 */
+    main->buffer_full = FALSE;
+    /* Still need to process last row group of this iMCU row, */
+    /* which is saved at index M+1 of the other xbuffer */
+    main->rowgroup_ctr = (JDIMENSION) (cinfo->min_DCT_scaled_size + 1);
+    main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size + 2);
+    main->context_state = CTX_POSTPONED_ROW;
+  }
+}
+
+
+/*
+ * Process some data.
+ * Final pass of two-pass quantization: just call the postprocessor.
+ * Source data will be the postprocessor controller's internal buffer.
+ */
+
+#ifdef QUANT_2PASS_SUPPORTED
+
+METHODDEF(void)
+process_data_crank_post (j_decompress_ptr cinfo,
+			 JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+			 JDIMENSION out_rows_avail)
+{
+  (*cinfo->post->post_process_data) (cinfo, (JSAMPIMAGE) NULL,
+				     (JDIMENSION *) NULL, (JDIMENSION) 0,
+				     output_buf, out_row_ctr, out_rows_avail);
+}
+
+#endif /* QUANT_2PASS_SUPPORTED */
+
+
+/*
+ * Initialize main buffer controller.
+ */
+
+GLOBAL(void)
+jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer)
+{
+  my_main_ptr main;
+  int ci, rgroup, ngroups;
+  jpeg_component_info *compptr;
+
+  main = (my_main_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_main_controller));
+  cinfo->main = (struct jpeg_d_main_controller *) main;
+  main->pub.start_pass = start_pass_main;
+
+  if (need_full_buffer)		/* shouldn't happen */
+    ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+
+  /* Allocate the workspace.
+   * ngroups is the number of row groups we need.
+   */
+  if (cinfo->upsample->need_context_rows) {
+    if (cinfo->min_DCT_scaled_size < 2) /* unsupported, see comments above */
+      ERREXIT(cinfo, JERR_NOTIMPL);
+    alloc_funny_pointers(cinfo); /* Alloc space for xbuffer[] lists */
+    ngroups = cinfo->min_DCT_scaled_size + 2;
+  } else {
+    ngroups = cinfo->min_DCT_scaled_size;
+  }
+
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) /
+      cinfo->min_DCT_scaled_size; /* height of a row group of component */
+    main->buffer[ci] = (*cinfo->mem->alloc_sarray)
+			((j_common_ptr) cinfo, JPOOL_IMAGE,
+			 compptr->width_in_blocks * compptr->DCT_scaled_size,
+			 (JDIMENSION) (rgroup * ngroups));
+  }
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdmarker.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdmarker.c
new file mode 100644
index 0000000000..f4cca8cc83
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdmarker.c
@@ -0,0 +1,1360 @@
+/*
+ * jdmarker.c
+ *
+ * Copyright (C) 1991-1998, 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 routines to decode JPEG datastream markers.
+ * Most of the complexity arises from our desire to support input
+ * suspension: if not all of the data for a marker is available,
+ * we must exit back to the application.  On resumption, we reprocess
+ * the marker.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+typedef enum {			/* JPEG marker codes */
+  M_SOF0  = 0xc0,
+  M_SOF1  = 0xc1,
+  M_SOF2  = 0xc2,
+  M_SOF3  = 0xc3,
+  
+  M_SOF5  = 0xc5,
+  M_SOF6  = 0xc6,
+  M_SOF7  = 0xc7,
+  
+  M_JPG   = 0xc8,
+  M_SOF9  = 0xc9,
+  M_SOF10 = 0xca,
+  M_SOF11 = 0xcb,
+  
+  M_SOF13 = 0xcd,
+  M_SOF14 = 0xce,
+  M_SOF15 = 0xcf,
+  
+  M_DHT   = 0xc4,
+  
+  M_DAC   = 0xcc,
+  
+  M_RST0  = 0xd0,
+  M_RST1  = 0xd1,
+  M_RST2  = 0xd2,
+  M_RST3  = 0xd3,
+  M_RST4  = 0xd4,
+  M_RST5  = 0xd5,
+  M_RST6  = 0xd6,
+  M_RST7  = 0xd7,
+  
+  M_SOI   = 0xd8,
+  M_EOI   = 0xd9,
+  M_SOS   = 0xda,
+  M_DQT   = 0xdb,
+  M_DNL   = 0xdc,
+  M_DRI   = 0xdd,
+  M_DHP   = 0xde,
+  M_EXP   = 0xdf,
+  
+  M_APP0  = 0xe0,
+  M_APP1  = 0xe1,
+  M_APP2  = 0xe2,
+  M_APP3  = 0xe3,
+  M_APP4  = 0xe4,
+  M_APP5  = 0xe5,
+  M_APP6  = 0xe6,
+  M_APP7  = 0xe7,
+  M_APP8  = 0xe8,
+  M_APP9  = 0xe9,
+  M_APP10 = 0xea,
+  M_APP11 = 0xeb,
+  M_APP12 = 0xec,
+  M_APP13 = 0xed,
+  M_APP14 = 0xee,
+  M_APP15 = 0xef,
+  
+  M_JPG0  = 0xf0,
+  M_JPG13 = 0xfd,
+  M_COM   = 0xfe,
+  
+  M_TEM   = 0x01,
+  
+  M_ERROR = 0x100
+} JPEG_MARKER;
+
+
+/* Private state */
+
+typedef struct {
+  struct jpeg_marker_reader pub; /* public fields */
+
+  /* Application-overridable marker processing methods */
+  jpeg_marker_parser_method process_COM;
+  jpeg_marker_parser_method process_APPn[16];
+
+  /* Limit on marker data length to save for each marker type */
+  unsigned int length_limit_COM;
+  unsigned int length_limit_APPn[16];
+
+  /* Status of COM/APPn marker saving */
+  jpeg_saved_marker_ptr cur_marker;	/* NULL if not processing a marker */
+  unsigned int bytes_read;		/* data bytes read so far in marker */
+  /* Note: cur_marker is not linked into marker_list until it's all read. */
+} my_marker_reader;
+
+typedef my_marker_reader * my_marker_ptr;
+
+
+/*
+ * Macros for fetching data from the data source module.
+ *
+ * At all times, cinfo->src->next_input_byte and ->bytes_in_buffer reflect
+ * the current restart point; we update them only when we have reached a
+ * suitable place to restart if a suspension occurs.
+ */
+
+/* Declare and initialize local copies of input pointer/count */
+#define INPUT_VARS(cinfo)  \
+	struct jpeg_source_mgr * datasrc = (cinfo)->src;  \
+	const JOCTET * next_input_byte = datasrc->next_input_byte;  \
+	size_t bytes_in_buffer = datasrc->bytes_in_buffer
+
+/* Unload the local copies --- do this only at a restart boundary */
+#define INPUT_SYNC(cinfo)  \
+	( datasrc->next_input_byte = next_input_byte,  \
+	  datasrc->bytes_in_buffer = bytes_in_buffer )
+
+/* Reload the local copies --- used only in MAKE_BYTE_AVAIL */
+#define INPUT_RELOAD(cinfo)  \
+	( next_input_byte = datasrc->next_input_byte,  \
+	  bytes_in_buffer = datasrc->bytes_in_buffer )
+
+/* Internal macro for INPUT_BYTE and INPUT_2BYTES: make a byte available.
+ * Note we do *not* do INPUT_SYNC before calling fill_input_buffer,
+ * but we must reload the local copies after a successful fill.
+ */
+#define MAKE_BYTE_AVAIL(cinfo,action)  \
+	if (bytes_in_buffer == 0) {  \
+	  if (! (*datasrc->fill_input_buffer) (cinfo))  \
+	    { action; }  \
+	  INPUT_RELOAD(cinfo);  \
+	}
+
+/* Read a byte into variable V.
+ * If must suspend, take the specified action (typically "return FALSE").
+ */
+#define INPUT_BYTE(cinfo,V,action)  \
+	MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \
+		  bytes_in_buffer--; \
+		  V = GETJOCTET(*next_input_byte++); )
+
+/* As above, but read two bytes interpreted as an unsigned 16-bit integer.
+ * V should be declared unsigned int or perhaps INT32.
+ */
+#define INPUT_2BYTES(cinfo,V,action)  \
+	MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \
+		  bytes_in_buffer--; \
+		  V = ((unsigned int) GETJOCTET(*next_input_byte++)) << 8; \
+		  MAKE_BYTE_AVAIL(cinfo,action); \
+		  bytes_in_buffer--; \
+		  V += GETJOCTET(*next_input_byte++); )
+
+
+/*
+ * Routines to process JPEG markers.
+ *
+ * Entry condition: JPEG marker itself has been read and its code saved
+ *   in cinfo->unread_marker; input restart point is just after the marker.
+ *
+ * Exit: if return TRUE, have read and processed any parameters, and have
+ *   updated the restart point to point after the parameters.
+ *   If return FALSE, was forced to suspend before reaching end of
+ *   marker parameters; restart point has not been moved.  Same routine
+ *   will be called again after application supplies more input data.
+ *
+ * This approach to suspension assumes that all of a marker's parameters
+ * can fit into a single input bufferload.  This should hold for "normal"
+ * markers.  Some COM/APPn markers might have large parameter segments
+ * that might not fit.  If we are simply dropping such a marker, we use
+ * skip_input_data to get past it, and thereby put the problem on the
+ * source manager's shoulders.  If we are saving the marker's contents
+ * into memory, we use a slightly different convention: when forced to
+ * suspend, the marker processor updates the restart point to the end of
+ * what it's consumed (ie, the end of the buffer) before returning FALSE.
+ * On resumption, cinfo->unread_marker still contains the marker code,
+ * but the data source will point to the next chunk of marker data.
+ * The marker processor must retain internal state to deal with this.
+ *
+ * Note that we don't bother to avoid duplicate trace messages if a
+ * suspension occurs within marker parameters.  Other side effects
+ * require more care.
+ */
+
+
+LOCAL(boolean)
+get_soi (j_decompress_ptr cinfo)
+/* Process an SOI marker */
+{
+  int i;
+  
+  TRACEMS(cinfo, 1, JTRC_SOI);
+
+  if (cinfo->marker->saw_SOI)
+    ERREXIT(cinfo, JERR_SOI_DUPLICATE);
+
+  /* Reset all parameters that are defined to be reset by SOI */
+
+  for (i = 0; i < NUM_ARITH_TBLS; i++) {
+    cinfo->arith_dc_L[i] = 0;
+    cinfo->arith_dc_U[i] = 1;
+    cinfo->arith_ac_K[i] = 5;
+  }
+  cinfo->restart_interval = 0;
+
+  /* Set initial assumptions for colorspace etc */
+
+  cinfo->jpeg_color_space = JCS_UNKNOWN;
+  cinfo->CCIR601_sampling = FALSE; /* Assume non-CCIR sampling??? */
+
+  cinfo->saw_JFIF_marker = FALSE;
+  cinfo->JFIF_major_version = 1; /* set default JFIF APP0 values */
+  cinfo->JFIF_minor_version = 1;
+  cinfo->density_unit = 0;
+  cinfo->X_density = 1;
+  cinfo->Y_density = 1;
+  cinfo->saw_Adobe_marker = FALSE;
+  cinfo->Adobe_transform = 0;
+
+  cinfo->marker->saw_SOI = TRUE;
+
+  return TRUE;
+}
+
+
+LOCAL(boolean)
+get_sof (j_decompress_ptr cinfo, boolean is_prog, boolean is_arith)
+/* Process a SOFn marker */
+{
+  INT32 length;
+  int c, ci;
+  jpeg_component_info * compptr;
+  INPUT_VARS(cinfo);
+
+  cinfo->progressive_mode = is_prog;
+  cinfo->arith_code = is_arith;
+
+  INPUT_2BYTES(cinfo, length, return FALSE);
+
+  INPUT_BYTE(cinfo, cinfo->data_precision, return FALSE);
+  INPUT_2BYTES(cinfo, cinfo->image_height, return FALSE);
+  INPUT_2BYTES(cinfo, cinfo->image_width, return FALSE);
+  INPUT_BYTE(cinfo, cinfo->num_components, return FALSE);
+
+  length -= 8;
+
+  TRACEMS4(cinfo, 1, JTRC_SOF, cinfo->unread_marker,
+	   (int) cinfo->image_width, (int) cinfo->image_height,
+	   cinfo->num_components);
+
+  if (cinfo->marker->saw_SOF)
+    ERREXIT(cinfo, JERR_SOF_DUPLICATE);
+
+  /* We don't support files in which the image height is initially specified */
+  /* as 0 and is later redefined by DNL.  As long as we have to check that,  */
+  /* might as well have a general sanity check. */
+  if (cinfo->image_height <= 0 || cinfo->image_width <= 0
+      || cinfo->num_components <= 0)
+    ERREXIT(cinfo, JERR_EMPTY_IMAGE);
+
+  if (length != (cinfo->num_components * 3))
+    ERREXIT(cinfo, JERR_BAD_LENGTH);
+
+  if (cinfo->comp_info == NULL)	/* do only once, even if suspend */
+    cinfo->comp_info = (jpeg_component_info *) (*cinfo->mem->alloc_small)
+			((j_common_ptr) cinfo, JPOOL_IMAGE,
+			 cinfo->num_components * SIZEOF(jpeg_component_info));
+  
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    compptr->component_index = ci;
+    INPUT_BYTE(cinfo, compptr->component_id, return FALSE);
+    INPUT_BYTE(cinfo, c, return FALSE);
+    compptr->h_samp_factor = (c >> 4) & 15;
+    compptr->v_samp_factor = (c     ) & 15;
+    INPUT_BYTE(cinfo, compptr->quant_tbl_no, return FALSE);
+
+    TRACEMS4(cinfo, 1, JTRC_SOF_COMPONENT,
+	     compptr->component_id, compptr->h_samp_factor,
+	     compptr->v_samp_factor, compptr->quant_tbl_no);
+  }
+
+  cinfo->marker->saw_SOF = TRUE;
+
+  INPUT_SYNC(cinfo);
+  return TRUE;
+}
+
+
+LOCAL(boolean)
+get_sos (j_decompress_ptr cinfo)
+/* Process a SOS marker */
+{
+  INT32 length;
+  int i, ci, n, c, cc;
+  jpeg_component_info * compptr;
+  INPUT_VARS(cinfo);
+
+  if (! cinfo->marker->saw_SOF)
+    ERREXIT(cinfo, JERR_SOS_NO_SOF);
+
+  INPUT_2BYTES(cinfo, length, return FALSE);
+
+  INPUT_BYTE(cinfo, n, return FALSE); /* Number of components */
+
+  TRACEMS1(cinfo, 1, JTRC_SOS, n);
+
+  if (length != (n * 2 + 6) || n < 1 || n > MAX_COMPS_IN_SCAN)
+    ERREXIT(cinfo, JERR_BAD_LENGTH);
+
+  cinfo->comps_in_scan = n;
+
+  /* Collect the component-spec parameters */
+
+  for (i = 0; i < n; i++) {
+    INPUT_BYTE(cinfo, cc, return FALSE);
+    INPUT_BYTE(cinfo, c, return FALSE);
+    
+    for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+	 ci++, compptr++) {
+      if (cc == compptr->component_id)
+	goto id_found;
+    }
+
+    ERREXIT1(cinfo, JERR_BAD_COMPONENT_ID, cc);
+
+  id_found:
+
+    cinfo->cur_comp_info[i] = compptr;
+    compptr->dc_tbl_no = (c >> 4) & 15;
+    compptr->ac_tbl_no = (c     ) & 15;
+    
+    TRACEMS3(cinfo, 1, JTRC_SOS_COMPONENT, cc,
+	     compptr->dc_tbl_no, compptr->ac_tbl_no);
+  }
+
+  /* Collect the additional scan parameters Ss, Se, Ah/Al. */
+  INPUT_BYTE(cinfo, c, return FALSE);
+  cinfo->Ss = c;
+  INPUT_BYTE(cinfo, c, return FALSE);
+  cinfo->Se = c;
+  INPUT_BYTE(cinfo, c, return FALSE);
+  cinfo->Ah = (c >> 4) & 15;
+  cinfo->Al = (c     ) & 15;
+
+  TRACEMS4(cinfo, 1, JTRC_SOS_PARAMS, cinfo->Ss, cinfo->Se,
+	   cinfo->Ah, cinfo->Al);
+
+  /* Prepare to scan data & restart markers */
+  cinfo->marker->next_restart_num = 0;
+
+  /* Count another SOS marker */
+  cinfo->input_scan_number++;
+
+  INPUT_SYNC(cinfo);
+  return TRUE;
+}
+
+
+#ifdef D_ARITH_CODING_SUPPORTED
+
+LOCAL(boolean)
+get_dac (j_decompress_ptr cinfo)
+/* Process a DAC marker */
+{
+  INT32 length;
+  int index, val;
+  INPUT_VARS(cinfo);
+
+  INPUT_2BYTES(cinfo, length, return FALSE);
+  length -= 2;
+  
+  while (length > 0) {
+    INPUT_BYTE(cinfo, index, return FALSE);
+    INPUT_BYTE(cinfo, val, return FALSE);
+
+    length -= 2;
+
+    TRACEMS2(cinfo, 1, JTRC_DAC, index, val);
+
+    if (index < 0 || index >= (2*NUM_ARITH_TBLS))
+      ERREXIT1(cinfo, JERR_DAC_INDEX, index);
+
+    if (index >= NUM_ARITH_TBLS) { /* define AC table */
+      cinfo->arith_ac_K[index-NUM_ARITH_TBLS] = (UINT8) val;
+    } else {			/* define DC table */
+      cinfo->arith_dc_L[index] = (UINT8) (val & 0x0F);
+      cinfo->arith_dc_U[index] = (UINT8) (val >> 4);
+      if (cinfo->arith_dc_L[index] > cinfo->arith_dc_U[index])
+	ERREXIT1(cinfo, JERR_DAC_VALUE, val);
+    }
+  }
+
+  if (length != 0)
+    ERREXIT(cinfo, JERR_BAD_LENGTH);
+
+  INPUT_SYNC(cinfo);
+  return TRUE;
+}
+
+#else /* ! D_ARITH_CODING_SUPPORTED */
+
+#define get_dac(cinfo)  skip_variable(cinfo)
+
+#endif /* D_ARITH_CODING_SUPPORTED */
+
+
+LOCAL(boolean)
+get_dht (j_decompress_ptr cinfo)
+/* Process a DHT marker */
+{
+  INT32 length;
+  UINT8 bits[17];
+  UINT8 huffval[256];
+  int i, index, count;
+  JHUFF_TBL **htblptr;
+  INPUT_VARS(cinfo);
+
+  INPUT_2BYTES(cinfo, length, return FALSE);
+  length -= 2;
+  
+  while (length > 16) {
+    INPUT_BYTE(cinfo, index, return FALSE);
+
+    TRACEMS1(cinfo, 1, JTRC_DHT, index);
+      
+    bits[0] = 0;
+    count = 0;
+    for (i = 1; i <= 16; i++) {
+      INPUT_BYTE(cinfo, bits[i], return FALSE);
+      count += bits[i];
+    }
+
+    length -= 1 + 16;
+
+    TRACEMS8(cinfo, 2, JTRC_HUFFBITS,
+	     bits[1], bits[2], bits[3], bits[4],
+	     bits[5], bits[6], bits[7], bits[8]);
+    TRACEMS8(cinfo, 2, JTRC_HUFFBITS,
+	     bits[9], bits[10], bits[11], bits[12],
+	     bits[13], bits[14], bits[15], bits[16]);
+
+    /* Here we just do minimal validation of the counts to avoid walking
+     * off the end of our table space.  jdhuff.c will check more carefully.
+     */
+    if (count > 256 || ((INT32) count) > length)
+      ERREXIT(cinfo, JERR_BAD_HUFF_TABLE);
+
+    for (i = 0; i < count; i++)
+      INPUT_BYTE(cinfo, huffval[i], return FALSE);
+
+    length -= count;
+
+    if (index & 0x10) {		/* AC table definition */
+      index -= 0x10;
+      htblptr = &cinfo->ac_huff_tbl_ptrs[index];
+    } else {			/* DC table definition */
+      htblptr = &cinfo->dc_huff_tbl_ptrs[index];
+    }
+
+    if (index < 0 || index >= NUM_HUFF_TBLS)
+      ERREXIT1(cinfo, JERR_DHT_INDEX, index);
+
+    if (*htblptr == NULL)
+      *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo);
+  
+    MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits));
+    MEMCOPY((*htblptr)->huffval, huffval, SIZEOF((*htblptr)->huffval));
+  }
+
+  if (length != 0)
+    ERREXIT(cinfo, JERR_BAD_LENGTH);
+
+  INPUT_SYNC(cinfo);
+  return TRUE;
+}
+
+
+LOCAL(boolean)
+get_dqt (j_decompress_ptr cinfo)
+/* Process a DQT marker */
+{
+  INT32 length;
+  int n, i, prec;
+  unsigned int tmp;
+  JQUANT_TBL *quant_ptr;
+  INPUT_VARS(cinfo);
+
+  INPUT_2BYTES(cinfo, length, return FALSE);
+  length -= 2;
+
+  while (length > 0) {
+    INPUT_BYTE(cinfo, n, return FALSE);
+    prec = n >> 4;
+    n &= 0x0F;
+
+    TRACEMS2(cinfo, 1, JTRC_DQT, n, prec);
+
+    if (n >= NUM_QUANT_TBLS)
+      ERREXIT1(cinfo, JERR_DQT_INDEX, n);
+      
+    if (cinfo->quant_tbl_ptrs[n] == NULL)
+      cinfo->quant_tbl_ptrs[n] = jpeg_alloc_quant_table((j_common_ptr) cinfo);
+    quant_ptr = cinfo->quant_tbl_ptrs[n];
+
+    for (i = 0; i < DCTSIZE2; i++) {
+      if (prec)
+	INPUT_2BYTES(cinfo, tmp, return FALSE);
+      else
+	INPUT_BYTE(cinfo, tmp, return FALSE);
+      /* We convert the zigzag-order table to natural array order. */
+      quant_ptr->quantval[jpeg_natural_order[i]] = (UINT16) tmp;
+    }
+
+    if (cinfo->err->trace_level >= 2) {
+      for (i = 0; i < DCTSIZE2; i += 8) {
+	TRACEMS8(cinfo, 2, JTRC_QUANTVALS,
+		 quant_ptr->quantval[i],   quant_ptr->quantval[i+1],
+		 quant_ptr->quantval[i+2], quant_ptr->quantval[i+3],
+		 quant_ptr->quantval[i+4], quant_ptr->quantval[i+5],
+		 quant_ptr->quantval[i+6], quant_ptr->quantval[i+7]);
+      }
+    }
+
+    length -= DCTSIZE2+1;
+    if (prec) length -= DCTSIZE2;
+  }
+
+  if (length != 0)
+    ERREXIT(cinfo, JERR_BAD_LENGTH);
+
+  INPUT_SYNC(cinfo);
+  return TRUE;
+}
+
+
+LOCAL(boolean)
+get_dri (j_decompress_ptr cinfo)
+/* Process a DRI marker */
+{
+  INT32 length;
+  unsigned int tmp;
+  INPUT_VARS(cinfo);
+
+  INPUT_2BYTES(cinfo, length, return FALSE);
+  
+  if (length != 4)
+    ERREXIT(cinfo, JERR_BAD_LENGTH);
+
+  INPUT_2BYTES(cinfo, tmp, return FALSE);
+
+  TRACEMS1(cinfo, 1, JTRC_DRI, tmp);
+
+  cinfo->restart_interval = tmp;
+
+  INPUT_SYNC(cinfo);
+  return TRUE;
+}
+
+
+/*
+ * Routines for processing APPn and COM markers.
+ * These are either saved in memory or discarded, per application request.
+ * APP0 and APP14 are specially checked to see if they are
+ * JFIF and Adobe markers, respectively.
+ */
+
+#define APP0_DATA_LEN	14	/* Length of interesting data in APP0 */
+#define APP14_DATA_LEN	12	/* Length of interesting data in APP14 */
+#define APPN_DATA_LEN	14	/* Must be the largest of the above!! */
+
+
+LOCAL(void)
+examine_app0 (j_decompress_ptr cinfo, JOCTET FAR * data,
+	      unsigned int datalen, INT32 remaining)
+/* Examine first few bytes from an APP0.
+ * Take appropriate action if it is a JFIF marker.
+ * datalen is # of bytes at data[], remaining is length of rest of marker data.
+ */
+{
+  INT32 totallen = (INT32) datalen + remaining;
+
+  if (datalen >= APP0_DATA_LEN &&
+      GETJOCTET(data[0]) == 0x4A &&
+      GETJOCTET(data[1]) == 0x46 &&
+      GETJOCTET(data[2]) == 0x49 &&
+      GETJOCTET(data[3]) == 0x46 &&
+      GETJOCTET(data[4]) == 0) {
+    /* Found JFIF APP0 marker: save info */
+    cinfo->saw_JFIF_marker = TRUE;
+    cinfo->JFIF_major_version = GETJOCTET(data[5]);
+    cinfo->JFIF_minor_version = GETJOCTET(data[6]);
+    cinfo->density_unit = GETJOCTET(data[7]);
+    cinfo->X_density = (GETJOCTET(data[8]) << 8) + GETJOCTET(data[9]);
+    cinfo->Y_density = (GETJOCTET(data[10]) << 8) + GETJOCTET(data[11]);
+    /* Check version.
+     * Major version must be 1, anything else signals an incompatible change.
+     * (We used to treat this as an error, but now it's a nonfatal warning,
+     * because some bozo at Hijaak couldn't read the spec.)
+     * Minor version should be 0..2, but process anyway if newer.
+     */
+    if (cinfo->JFIF_major_version != 1)
+      WARNMS2(cinfo, JWRN_JFIF_MAJOR,
+	      cinfo->JFIF_major_version, cinfo->JFIF_minor_version);
+    /* Generate trace messages */
+    TRACEMS5(cinfo, 1, JTRC_JFIF,
+	     cinfo->JFIF_major_version, cinfo->JFIF_minor_version,
+	     cinfo->X_density, cinfo->Y_density, cinfo->density_unit);
+    /* Validate thumbnail dimensions and issue appropriate messages */
+    if (GETJOCTET(data[12]) | GETJOCTET(data[13]))
+      TRACEMS2(cinfo, 1, JTRC_JFIF_THUMBNAIL,
+	       GETJOCTET(data[12]), GETJOCTET(data[13]));
+    totallen -= APP0_DATA_LEN;
+    if (totallen !=
+	((INT32)GETJOCTET(data[12]) * (INT32)GETJOCTET(data[13]) * (INT32) 3))
+      TRACEMS1(cinfo, 1, JTRC_JFIF_BADTHUMBNAILSIZE, (int) totallen);
+  } else if (datalen >= 6 &&
+      GETJOCTET(data[0]) == 0x4A &&
+      GETJOCTET(data[1]) == 0x46 &&
+      GETJOCTET(data[2]) == 0x58 &&
+      GETJOCTET(data[3]) == 0x58 &&
+      GETJOCTET(data[4]) == 0) {
+    /* Found JFIF "JFXX" extension APP0 marker */
+    /* The library doesn't actually do anything with these,
+     * but we try to produce a helpful trace message.
+     */
+    switch (GETJOCTET(data[5])) {
+    case 0x10:
+      TRACEMS1(cinfo, 1, JTRC_THUMB_JPEG, (int) totallen);
+      break;
+    case 0x11:
+      TRACEMS1(cinfo, 1, JTRC_THUMB_PALETTE, (int) totallen);
+      break;
+    case 0x13:
+      TRACEMS1(cinfo, 1, JTRC_THUMB_RGB, (int) totallen);
+      break;
+    default:
+      TRACEMS2(cinfo, 1, JTRC_JFIF_EXTENSION,
+	       GETJOCTET(data[5]), (int) totallen);
+      break;
+    }
+  } else {
+    /* Start of APP0 does not match "JFIF" or "JFXX", or too short */
+    TRACEMS1(cinfo, 1, JTRC_APP0, (int) totallen);
+  }
+}
+
+
+LOCAL(void)
+examine_app14 (j_decompress_ptr cinfo, JOCTET FAR * data,
+	       unsigned int datalen, INT32 remaining)
+/* Examine first few bytes from an APP14.
+ * Take appropriate action if it is an Adobe marker.
+ * datalen is # of bytes at data[], remaining is length of rest of marker data.
+ */
+{
+  unsigned int version, flags0, flags1, transform;
+
+  if (datalen >= APP14_DATA_LEN &&
+      GETJOCTET(data[0]) == 0x41 &&
+      GETJOCTET(data[1]) == 0x64 &&
+      GETJOCTET(data[2]) == 0x6F &&
+      GETJOCTET(data[3]) == 0x62 &&
+      GETJOCTET(data[4]) == 0x65) {
+    /* Found Adobe APP14 marker */
+    version = (GETJOCTET(data[5]) << 8) + GETJOCTET(data[6]);
+    flags0 = (GETJOCTET(data[7]) << 8) + GETJOCTET(data[8]);
+    flags1 = (GETJOCTET(data[9]) << 8) + GETJOCTET(data[10]);
+    transform = GETJOCTET(data[11]);
+    TRACEMS4(cinfo, 1, JTRC_ADOBE, version, flags0, flags1, transform);
+    cinfo->saw_Adobe_marker = TRUE;
+    cinfo->Adobe_transform = (UINT8) transform;
+  } else {
+    /* Start of APP14 does not match "Adobe", or too short */
+    TRACEMS1(cinfo, 1, JTRC_APP14, (int) (datalen + remaining));
+  }
+}
+
+
+METHODDEF(boolean)
+get_interesting_appn (j_decompress_ptr cinfo)
+/* Process an APP0 or APP14 marker without saving it */
+{
+  INT32 length;
+  JOCTET b[APPN_DATA_LEN];
+  unsigned int i, numtoread;
+  INPUT_VARS(cinfo);
+
+  INPUT_2BYTES(cinfo, length, return FALSE);
+  length -= 2;
+
+  /* get the interesting part of the marker data */
+  if (length >= APPN_DATA_LEN)
+    numtoread = APPN_DATA_LEN;
+  else if (length > 0)
+    numtoread = (unsigned int) length;
+  else
+    numtoread = 0;
+  for (i = 0; i < numtoread; i++)
+    INPUT_BYTE(cinfo, b[i], return FALSE);
+  length -= numtoread;
+
+  /* process it */
+  switch (cinfo->unread_marker) {
+  case M_APP0:
+    examine_app0(cinfo, (JOCTET FAR *) b, numtoread, length);
+    break;
+  case M_APP14:
+    examine_app14(cinfo, (JOCTET FAR *) b, numtoread, length);
+    break;
+  default:
+    /* can't get here unless jpeg_save_markers chooses wrong processor */
+    ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo->unread_marker);
+    break;
+  }
+
+  /* skip any remaining data -- could be lots */
+  INPUT_SYNC(cinfo);
+  if (length > 0)
+    (*cinfo->src->skip_input_data) (cinfo, (long) length);
+
+  return TRUE;
+}
+
+
+#ifdef SAVE_MARKERS_SUPPORTED
+
+METHODDEF(boolean)
+save_marker (j_decompress_ptr cinfo)
+/* Save an APPn or COM marker into the marker list */
+{
+  my_marker_ptr marker = (my_marker_ptr) cinfo->marker;
+  jpeg_saved_marker_ptr cur_marker = marker->cur_marker;
+  unsigned int bytes_read, data_length;
+  JOCTET FAR * data;
+  INT32 length = 0;
+  INPUT_VARS(cinfo);
+
+  if (cur_marker == NULL) {
+    /* begin reading a marker */
+    INPUT_2BYTES(cinfo, length, return FALSE);
+    length -= 2;
+    if (length >= 0) {		/* watch out for bogus length word */
+      /* figure out how much we want to save */
+      unsigned int limit;
+      if (cinfo->unread_marker == (int) M_COM)
+	limit = marker->length_limit_COM;
+      else
+	limit = marker->length_limit_APPn[cinfo->unread_marker - (int) M_APP0];
+      if ((unsigned int) length < limit)
+	limit = (unsigned int) length;
+      /* allocate and initialize the marker item */
+      cur_marker = (jpeg_saved_marker_ptr)
+	(*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				    SIZEOF(struct jpeg_marker_struct) + limit);
+      cur_marker->next = NULL;
+      cur_marker->marker = (UINT8) cinfo->unread_marker;
+      cur_marker->original_length = (unsigned int) length;
+      cur_marker->data_length = limit;
+      /* data area is just beyond the jpeg_marker_struct */
+      data = cur_marker->data = (JOCTET FAR *) (cur_marker + 1);
+      marker->cur_marker = cur_marker;
+      marker->bytes_read = 0;
+      bytes_read = 0;
+      data_length = limit;
+    } else {
+      /* deal with bogus length word */
+      bytes_read = data_length = 0;
+      data = NULL;
+    }
+  } else {
+    /* resume reading a marker */
+    bytes_read = marker->bytes_read;
+    data_length = cur_marker->data_length;
+    data = cur_marker->data + bytes_read;
+  }
+
+  while (bytes_read < data_length) {
+    INPUT_SYNC(cinfo);		/* move the restart point to here */
+    marker->bytes_read = bytes_read;
+    /* If there's not at least one byte in buffer, suspend */
+    MAKE_BYTE_AVAIL(cinfo, return FALSE);
+    /* Copy bytes with reasonable rapidity */
+    while (bytes_read < data_length && bytes_in_buffer > 0) {
+      *data++ = *next_input_byte++;
+      bytes_in_buffer--;
+      bytes_read++;
+    }
+  }
+
+  /* Done reading what we want to read */
+  if (cur_marker != NULL) {	/* will be NULL if bogus length word */
+    /* Add new marker to end of list */
+    if (cinfo->marker_list == NULL) {
+      cinfo->marker_list = cur_marker;
+    } else {
+      jpeg_saved_marker_ptr prev = cinfo->marker_list;
+      while (prev->next != NULL)
+	prev = prev->next;
+      prev->next = cur_marker;
+    }
+    /* Reset pointer & calc remaining data length */
+    data = cur_marker->data;
+    length = cur_marker->original_length - data_length;
+  }
+  /* Reset to initial state for next marker */
+  marker->cur_marker = NULL;
+
+  /* Process the marker if interesting; else just make a generic trace msg */
+  switch (cinfo->unread_marker) {
+  case M_APP0:
+    examine_app0(cinfo, data, data_length, length);
+    break;
+  case M_APP14:
+    examine_app14(cinfo, data, data_length, length);
+    break;
+  default:
+    TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo->unread_marker,
+	     (int) (data_length + length));
+    break;
+  }
+
+  /* skip any remaining data -- could be lots */
+  INPUT_SYNC(cinfo);		/* do before skip_input_data */
+  if (length > 0)
+    (*cinfo->src->skip_input_data) (cinfo, (long) length);
+
+  return TRUE;
+}
+
+#endif /* SAVE_MARKERS_SUPPORTED */
+
+
+METHODDEF(boolean)
+skip_variable (j_decompress_ptr cinfo)
+/* Skip over an unknown or uninteresting variable-length marker */
+{
+  INT32 length;
+  INPUT_VARS(cinfo);
+
+  INPUT_2BYTES(cinfo, length, return FALSE);
+  length -= 2;
+  
+  TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo->unread_marker, (int) length);
+
+  INPUT_SYNC(cinfo);		/* do before skip_input_data */
+  if (length > 0)
+    (*cinfo->src->skip_input_data) (cinfo, (long) length);
+
+  return TRUE;
+}
+
+
+/*
+ * Find the next JPEG marker, save it in cinfo->unread_marker.
+ * Returns FALSE if had to suspend before reaching a marker;
+ * in that case cinfo->unread_marker is unchanged.
+ *
+ * Note that the result might not be a valid marker code,
+ * but it will never be 0 or FF.
+ */
+
+LOCAL(boolean)
+next_marker (j_decompress_ptr cinfo)
+{
+  int c;
+  INPUT_VARS(cinfo);
+
+  for (;;) {
+    INPUT_BYTE(cinfo, c, return FALSE);
+    /* Skip any non-FF bytes.
+     * This may look a bit inefficient, but it will not occur in a valid file.
+     * We sync after each discarded byte so that a suspending data source
+     * can discard the byte from its buffer.
+     */
+    while (c != 0xFF) {
+      cinfo->marker->discarded_bytes++;
+      INPUT_SYNC(cinfo);
+      INPUT_BYTE(cinfo, c, return FALSE);
+    }
+    /* This loop swallows any duplicate FF bytes.  Extra FFs are legal as
+     * pad bytes, so don't count them in discarded_bytes.  We assume there
+     * will not be so many consecutive FF bytes as to overflow a suspending
+     * data source's input buffer.
+     */
+    do {
+      INPUT_BYTE(cinfo, c, return FALSE);
+    } while (c == 0xFF);
+    if (c != 0)
+      break;			/* found a valid marker, exit loop */
+    /* Reach here if we found a stuffed-zero data sequence (FF/00).
+     * Discard it and loop back to try again.
+     */
+    cinfo->marker->discarded_bytes += 2;
+    INPUT_SYNC(cinfo);
+  }
+
+  if (cinfo->marker->discarded_bytes != 0) {
+    WARNMS2(cinfo, JWRN_EXTRANEOUS_DATA, cinfo->marker->discarded_bytes, c);
+    cinfo->marker->discarded_bytes = 0;
+  }
+
+  cinfo->unread_marker = c;
+
+  INPUT_SYNC(cinfo);
+  return TRUE;
+}
+
+
+LOCAL(boolean)
+first_marker (j_decompress_ptr cinfo)
+/* Like next_marker, but used to obtain the initial SOI marker. */
+/* For this marker, we do not allow preceding garbage or fill; otherwise,
+ * we might well scan an entire input file before realizing it ain't JPEG.
+ * If an application wants to process non-JFIF files, it must seek to the
+ * SOI before calling the JPEG library.
+ */
+{
+  int c, c2;
+  INPUT_VARS(cinfo);
+
+  INPUT_BYTE(cinfo, c, return FALSE);
+  INPUT_BYTE(cinfo, c2, return FALSE);
+  if (c != 0xFF || c2 != (int) M_SOI)
+    ERREXIT2(cinfo, JERR_NO_SOI, c, c2);
+
+  cinfo->unread_marker = c2;
+
+  INPUT_SYNC(cinfo);
+  return TRUE;
+}
+
+
+/*
+ * Read markers until SOS or EOI.
+ *
+ * Returns same codes as are defined for jpeg_consume_input:
+ * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI.
+ */
+
+METHODDEF(int)
+read_markers (j_decompress_ptr cinfo)
+{
+  /* Outer loop repeats once for each marker. */
+  for (;;) {
+    /* Collect the marker proper, unless we already did. */
+    /* NB: first_marker() enforces the requirement that SOI appear first. */
+    if (cinfo->unread_marker == 0) {
+      if (! cinfo->marker->saw_SOI) {
+	if (! first_marker(cinfo))
+	  return JPEG_SUSPENDED;
+      } else {
+	if (! next_marker(cinfo))
+	  return JPEG_SUSPENDED;
+      }
+    }
+    /* At this point cinfo->unread_marker contains the marker code and the
+     * input point is just past the marker proper, but before any parameters.
+     * A suspension will cause us to return with this state still true.
+     */
+    switch (cinfo->unread_marker) {
+    case M_SOI:
+      if (! get_soi(cinfo))
+	return JPEG_SUSPENDED;
+      break;
+
+    case M_SOF0:		/* Baseline */
+    case M_SOF1:		/* Extended sequential, Huffman */
+      if (! get_sof(cinfo, FALSE, FALSE))
+	return JPEG_SUSPENDED;
+      break;
+
+    case M_SOF2:		/* Progressive, Huffman */
+      if (! get_sof(cinfo, TRUE, FALSE))
+	return JPEG_SUSPENDED;
+      break;
+
+    case M_SOF9:		/* Extended sequential, arithmetic */
+      if (! get_sof(cinfo, FALSE, TRUE))
+	return JPEG_SUSPENDED;
+      break;
+
+    case M_SOF10:		/* Progressive, arithmetic */
+      if (! get_sof(cinfo, TRUE, TRUE))
+	return JPEG_SUSPENDED;
+      break;
+
+    /* Currently unsupported SOFn types */
+    case M_SOF3:		/* Lossless, Huffman */
+    case M_SOF5:		/* Differential sequential, Huffman */
+    case M_SOF6:		/* Differential progressive, Huffman */
+    case M_SOF7:		/* Differential lossless, Huffman */
+    case M_JPG:			/* Reserved for JPEG extensions */
+    case M_SOF11:		/* Lossless, arithmetic */
+    case M_SOF13:		/* Differential sequential, arithmetic */
+    case M_SOF14:		/* Differential progressive, arithmetic */
+    case M_SOF15:		/* Differential lossless, arithmetic */
+      ERREXIT1(cinfo, JERR_SOF_UNSUPPORTED, cinfo->unread_marker);
+      break;
+
+    case M_SOS:
+      if (! get_sos(cinfo))
+	return JPEG_SUSPENDED;
+      cinfo->unread_marker = 0;	/* processed the marker */
+      return JPEG_REACHED_SOS;
+    
+    case M_EOI:
+      TRACEMS(cinfo, 1, JTRC_EOI);
+      cinfo->unread_marker = 0;	/* processed the marker */
+      return JPEG_REACHED_EOI;
+      
+    case M_DAC:
+      if (! get_dac(cinfo))
+	return JPEG_SUSPENDED;
+      break;
+      
+    case M_DHT:
+      if (! get_dht(cinfo))
+	return JPEG_SUSPENDED;
+      break;
+      
+    case M_DQT:
+      if (! get_dqt(cinfo))
+	return JPEG_SUSPENDED;
+      break;
+      
+    case M_DRI:
+      if (! get_dri(cinfo))
+	return JPEG_SUSPENDED;
+      break;
+      
+    case M_APP0:
+    case M_APP1:
+    case M_APP2:
+    case M_APP3:
+    case M_APP4:
+    case M_APP5:
+    case M_APP6:
+    case M_APP7:
+    case M_APP8:
+    case M_APP9:
+    case M_APP10:
+    case M_APP11:
+    case M_APP12:
+    case M_APP13:
+    case M_APP14:
+    case M_APP15:
+      if (! (*((my_marker_ptr) cinfo->marker)->process_APPn[
+		cinfo->unread_marker - (int) M_APP0]) (cinfo))
+	return JPEG_SUSPENDED;
+      break;
+      
+    case M_COM:
+      if (! (*((my_marker_ptr) cinfo->marker)->process_COM) (cinfo))
+	return JPEG_SUSPENDED;
+      break;
+
+    case M_RST0:		/* these are all parameterless */
+    case M_RST1:
+    case M_RST2:
+    case M_RST3:
+    case M_RST4:
+    case M_RST5:
+    case M_RST6:
+    case M_RST7:
+    case M_TEM:
+      TRACEMS1(cinfo, 1, JTRC_PARMLESS_MARKER, cinfo->unread_marker);
+      break;
+
+    case M_DNL:			/* Ignore DNL ... perhaps the wrong thing */
+      if (! skip_variable(cinfo))
+	return JPEG_SUSPENDED;
+      break;
+
+    default:			/* must be DHP, EXP, JPGn, or RESn */
+      /* For now, we treat the reserved markers as fatal errors since they are
+       * likely to be used to signal incompatible JPEG Part 3 extensions.
+       * Once the JPEG 3 version-number marker is well defined, this code
+       * ought to change!
+       */
+      ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo->unread_marker);
+      break;
+    }
+    /* Successfully processed marker, so reset state variable */
+    cinfo->unread_marker = 0;
+  } /* end loop */
+}
+
+
+/*
+ * Read a restart marker, which is expected to appear next in the datastream;
+ * if the marker is not there, take appropriate recovery action.
+ * Returns FALSE if suspension is required.
+ *
+ * This is called by the entropy decoder after it has read an appropriate
+ * number of MCUs.  cinfo->unread_marker may be nonzero if the entropy decoder
+ * has already read a marker from the data source.  Under normal conditions
+ * cinfo->unread_marker will be reset to 0 before returning; if not reset,
+ * it holds a marker which the decoder will be unable to read past.
+ */
+
+METHODDEF(boolean)
+read_restart_marker (j_decompress_ptr cinfo)
+{
+  /* Obtain a marker unless we already did. */
+  /* Note that next_marker will complain if it skips any data. */
+  if (cinfo->unread_marker == 0) {
+    if (! next_marker(cinfo))
+      return FALSE;
+  }
+
+  if (cinfo->unread_marker ==
+      ((int) M_RST0 + cinfo->marker->next_restart_num)) {
+    /* Normal case --- swallow the marker and let entropy decoder continue */
+    TRACEMS1(cinfo, 3, JTRC_RST, cinfo->marker->next_restart_num);
+    cinfo->unread_marker = 0;
+  } else {
+    /* Uh-oh, the restart markers have been messed up. */
+    /* Let the data source manager determine how to resync. */
+    if (! (*cinfo->src->resync_to_restart) (cinfo,
+					    cinfo->marker->next_restart_num))
+      return FALSE;
+  }
+
+  /* Update next-restart state */
+  cinfo->marker->next_restart_num = (cinfo->marker->next_restart_num + 1) & 7;
+
+  return TRUE;
+}
+
+
+/*
+ * This is the default resync_to_restart method for data source managers
+ * to use if they don't have any better approach.  Some data source managers
+ * may be able to back up, or may have additional knowledge about the data
+ * which permits a more intelligent recovery strategy; such managers would
+ * presumably supply their own resync method.
+ *
+ * read_restart_marker calls resync_to_restart if it finds a marker other than
+ * the restart marker it was expecting.  (This code is *not* used unless
+ * a nonzero restart interval has been declared.)  cinfo->unread_marker is
+ * the marker code actually found (might be anything, except 0 or FF).
+ * The desired restart marker number (0..7) is passed as a parameter.
+ * This routine is supposed to apply whatever error recovery strategy seems
+ * appropriate in order to position the input stream to the next data segment.
+ * Note that cinfo->unread_marker is treated as a marker appearing before
+ * the current data-source input point; usually it should be reset to zero
+ * before returning.
+ * Returns FALSE if suspension is required.
+ *
+ * This implementation is substantially constrained by wanting to treat the
+ * input as a data stream; this means we can't back up.  Therefore, we have
+ * only the following actions to work with:
+ *   1. Simply discard the marker and let the entropy decoder resume at next
+ *      byte of file.
+ *   2. Read forward until we find another marker, discarding intervening
+ *      data.  (In theory we could look ahead within the current bufferload,
+ *      without having to discard data if we don't find the desired marker.
+ *      This idea is not implemented here, in part because it makes behavior
+ *      dependent on buffer size and chance buffer-boundary positions.)
+ *   3. Leave the marker unread (by failing to zero cinfo->unread_marker).
+ *      This will cause the entropy decoder to process an empty data segment,
+ *      inserting dummy zeroes, and then we will reprocess the marker.
+ *
+ * #2 is appropriate if we think the desired marker lies ahead, while #3 is
+ * appropriate if the found marker is a future restart marker (indicating
+ * that we have missed the desired restart marker, probably because it got
+ * corrupted).
+ * We apply #2 or #3 if the found marker is a restart marker no more than
+ * two counts behind or ahead of the expected one.  We also apply #2 if the
+ * found marker is not a legal JPEG marker code (it's certainly bogus data).
+ * If the found marker is a restart marker more than 2 counts away, we do #1
+ * (too much risk that the marker is erroneous; with luck we will be able to
+ * resync at some future point).
+ * For any valid non-restart JPEG marker, we apply #3.  This keeps us from
+ * overrunning the end of a scan.  An implementation limited to single-scan
+ * files might find it better to apply #2 for markers other than EOI, since
+ * any other marker would have to be bogus data in that case.
+ */
+
+GLOBAL(boolean)
+jpeg_resync_to_restart (j_decompress_ptr cinfo, int desired)
+{
+  int marker = cinfo->unread_marker;
+  int action = 1;
+  
+  /* Always put up a warning. */
+  WARNMS2(cinfo, JWRN_MUST_RESYNC, marker, desired);
+  
+  /* Outer loop handles repeated decision after scanning forward. */
+  for (;;) {
+    if (marker < (int) M_SOF0)
+      action = 2;		/* invalid marker */
+    else if (marker < (int) M_RST0 || marker > (int) M_RST7)
+      action = 3;		/* valid non-restart marker */
+    else {
+      if (marker == ((int) M_RST0 + ((desired+1) & 7)) ||
+	  marker == ((int) M_RST0 + ((desired+2) & 7)))
+	action = 3;		/* one of the next two expected restarts */
+      else if (marker == ((int) M_RST0 + ((desired-1) & 7)) ||
+	       marker == ((int) M_RST0 + ((desired-2) & 7)))
+	action = 2;		/* a prior restart, so advance */
+      else
+	action = 1;		/* desired restart or too far away */
+    }
+    TRACEMS2(cinfo, 4, JTRC_RECOVERY_ACTION, marker, action);
+    switch (action) {
+    case 1:
+      /* Discard marker and let entropy decoder resume processing. */
+      cinfo->unread_marker = 0;
+      return TRUE;
+    case 2:
+      /* Scan to the next marker, and repeat the decision loop. */
+      if (! next_marker(cinfo))
+	return FALSE;
+      marker = cinfo->unread_marker;
+      break;
+    case 3:
+      /* Return without advancing past this marker. */
+      /* Entropy decoder will be forced to process an empty segment. */
+      return TRUE;
+    }
+  } /* end loop */
+}
+
+
+/*
+ * Reset marker processing state to begin a fresh datastream.
+ */
+
+METHODDEF(void)
+reset_marker_reader (j_decompress_ptr cinfo)
+{
+  my_marker_ptr marker = (my_marker_ptr) cinfo->marker;
+
+  cinfo->comp_info = NULL;		/* until allocated by get_sof */
+  cinfo->input_scan_number = 0;		/* no SOS seen yet */
+  cinfo->unread_marker = 0;		/* no pending marker */
+  marker->pub.saw_SOI = FALSE;		/* set internal state too */
+  marker->pub.saw_SOF = FALSE;
+  marker->pub.discarded_bytes = 0;
+  marker->cur_marker = NULL;
+}
+
+
+/*
+ * Initialize the marker reader module.
+ * This is called only once, when the decompression object is created.
+ */
+
+GLOBAL(void)
+jinit_marker_reader (j_decompress_ptr cinfo)
+{
+  my_marker_ptr marker;
+  int i;
+
+  /* Create subobject in permanent pool */
+  marker = (my_marker_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+				SIZEOF(my_marker_reader));
+  cinfo->marker = (struct jpeg_marker_reader *) marker;
+  /* Initialize public method pointers */
+  marker->pub.reset_marker_reader = reset_marker_reader;
+  marker->pub.read_markers = read_markers;
+  marker->pub.read_restart_marker = read_restart_marker;
+  /* Initialize COM/APPn processing.
+   * By default, we examine and then discard APP0 and APP14,
+   * but simply discard COM and all other APPn.
+   */
+  marker->process_COM = skip_variable;
+  marker->length_limit_COM = 0;
+  for (i = 0; i < 16; i++) {
+    marker->process_APPn[i] = skip_variable;
+    marker->length_limit_APPn[i] = 0;
+  }
+  marker->process_APPn[0] = get_interesting_appn;
+  marker->process_APPn[14] = get_interesting_appn;
+  /* Reset marker processing state */
+  reset_marker_reader(cinfo);
+}
+
+
+/*
+ * Control saving of COM and APPn markers into marker_list.
+ */
+
+#ifdef SAVE_MARKERS_SUPPORTED
+
+GLOBAL(void)
+jpeg_save_markers (j_decompress_ptr cinfo, int marker_code,
+		   unsigned int length_limit)
+{
+  my_marker_ptr marker = (my_marker_ptr) cinfo->marker;
+  long maxlength;
+  jpeg_marker_parser_method processor;
+
+  /* Length limit mustn't be larger than what we can allocate
+   * (should only be a concern in a 16-bit environment).
+   */
+  maxlength = cinfo->mem->max_alloc_chunk - SIZEOF(struct jpeg_marker_struct);
+  if (((long) length_limit) > maxlength)
+    length_limit = (unsigned int) maxlength;
+
+  /* Choose processor routine to use.
+   * APP0/APP14 have special requirements.
+   */
+  if (length_limit) {
+    processor = save_marker;
+    /* If saving APP0/APP14, save at least enough for our internal use. */
+    if (marker_code == (int) M_APP0 && length_limit < APP0_DATA_LEN)
+      length_limit = APP0_DATA_LEN;
+    else if (marker_code == (int) M_APP14 && length_limit < APP14_DATA_LEN)
+      length_limit = APP14_DATA_LEN;
+  } else {
+    processor = skip_variable;
+    /* If discarding APP0/APP14, use our regular on-the-fly processor. */
+    if (marker_code == (int) M_APP0 || marker_code == (int) M_APP14)
+      processor = get_interesting_appn;
+  }
+
+  if (marker_code == (int) M_COM) {
+    marker->process_COM = processor;
+    marker->length_limit_COM = length_limit;
+  } else if (marker_code >= (int) M_APP0 && marker_code <= (int) M_APP15) {
+    marker->process_APPn[marker_code - (int) M_APP0] = processor;
+    marker->length_limit_APPn[marker_code - (int) M_APP0] = length_limit;
+  } else
+    ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code);
+}
+
+#endif /* SAVE_MARKERS_SUPPORTED */
+
+
+/*
+ * Install a special processing method for COM or APPn markers.
+ */
+
+GLOBAL(void)
+jpeg_set_marker_processor (j_decompress_ptr cinfo, int marker_code,
+			   jpeg_marker_parser_method routine)
+{
+  my_marker_ptr marker = (my_marker_ptr) cinfo->marker;
+
+  if (marker_code == (int) M_COM)
+    marker->process_COM = routine;
+  else if (marker_code >= (int) M_APP0 && marker_code <= (int) M_APP15)
+    marker->process_APPn[marker_code - (int) M_APP0] = routine;
+  else
+    ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code);
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdmaster.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdmaster.c
new file mode 100644
index 0000000000..2802c5b7b2
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdmaster.c
@@ -0,0 +1,557 @@
+/*
+ * jdmaster.c
+ *
+ * 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.
+ *
+ * This file contains master control logic for the JPEG decompressor.
+ * These routines are concerned with selecting the modules to be executed
+ * and with determining the number of passes and the work to be done in each
+ * pass.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/* Private state */
+
+typedef struct {
+  struct jpeg_decomp_master pub; /* public fields */
+
+  int pass_number;		/* # of passes completed */
+
+  boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */
+
+  /* Saved references to initialized quantizer modules,
+   * in case we need to switch modes.
+   */
+  struct jpeg_color_quantizer * quantizer_1pass;
+  struct jpeg_color_quantizer * quantizer_2pass;
+} my_decomp_master;
+
+typedef my_decomp_master * my_master_ptr;
+
+
+/*
+ * Determine whether merged upsample/color conversion should be used.
+ * CRUCIAL: this must match the actual capabilities of jdmerge.c!
+ */
+
+LOCAL(boolean)
+use_merged_upsample (j_decompress_ptr cinfo)
+{
+#ifdef UPSAMPLE_MERGING_SUPPORTED
+  /* Merging is the equivalent of plain box-filter upsampling */
+  if (cinfo->do_fancy_upsampling || cinfo->CCIR601_sampling)
+    return FALSE;
+  /* jdmerge.c only supports YCC=>RGB color conversion */
+  if (cinfo->jpeg_color_space != JCS_YCbCr || cinfo->num_components != 3 ||
+      cinfo->out_color_space != JCS_RGB ||
+      cinfo->out_color_components != RGB_PIXELSIZE)
+    return FALSE;
+  /* and it only handles 2h1v or 2h2v sampling ratios */
+  if (cinfo->comp_info[0].h_samp_factor != 2 ||
+      cinfo->comp_info[1].h_samp_factor != 1 ||
+      cinfo->comp_info[2].h_samp_factor != 1 ||
+      cinfo->comp_info[0].v_samp_factor >  2 ||
+      cinfo->comp_info[1].v_samp_factor != 1 ||
+      cinfo->comp_info[2].v_samp_factor != 1)
+    return FALSE;
+  /* furthermore, it doesn't work if we've scaled the IDCTs differently */
+  if (cinfo->comp_info[0].DCT_scaled_size != cinfo->min_DCT_scaled_size ||
+      cinfo->comp_info[1].DCT_scaled_size != cinfo->min_DCT_scaled_size ||
+      cinfo->comp_info[2].DCT_scaled_size != cinfo->min_DCT_scaled_size)
+    return FALSE;
+  /* ??? also need to test for upsample-time rescaling, when & if supported */
+  return TRUE;			/* by golly, it'll work... */
+#else
+  return FALSE;
+#endif
+}
+
+
+/*
+ * Compute output image dimensions and related values.
+ * NOTE: this is exported for possible use by application.
+ * Hence it mustn't do anything that can't be done twice.
+ * Also note that it may be called before the master module is initialized!
+ */
+
+GLOBAL(void)
+jpeg_calc_output_dimensions (j_decompress_ptr cinfo)
+/* Do computations that are needed before master selection phase */
+{
+#ifdef IDCT_SCALING_SUPPORTED
+  int ci;
+  jpeg_component_info *compptr;
+#endif
+
+  /* Prevent application from calling me at wrong times */
+  if (cinfo->global_state != DSTATE_READY)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+
+#ifdef IDCT_SCALING_SUPPORTED
+
+  /* Compute actual output image dimensions and DCT scaling choices. */
+  if (cinfo->scale_num * 8 <= cinfo->scale_denom) {
+    /* Provide 1/8 scaling */
+    cinfo->output_width = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_width, 8L);
+    cinfo->output_height = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_height, 8L);
+    cinfo->min_DCT_scaled_size = 1;
+  } else if (cinfo->scale_num * 4 <= cinfo->scale_denom) {
+    /* Provide 1/4 scaling */
+    cinfo->output_width = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_width, 4L);
+    cinfo->output_height = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_height, 4L);
+    cinfo->min_DCT_scaled_size = 2;
+  } else if (cinfo->scale_num * 2 <= cinfo->scale_denom) {
+    /* Provide 1/2 scaling */
+    cinfo->output_width = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_width, 2L);
+    cinfo->output_height = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_height, 2L);
+    cinfo->min_DCT_scaled_size = 4;
+  } else {
+    /* Provide 1/1 scaling */
+    cinfo->output_width = cinfo->image_width;
+    cinfo->output_height = cinfo->image_height;
+    cinfo->min_DCT_scaled_size = DCTSIZE;
+  }
+  /* In selecting the actual DCT scaling for each component, we try to
+   * scale up the chroma components via IDCT scaling rather than upsampling.
+   * This saves time if the upsampler gets to use 1:1 scaling.
+   * Note this code assumes that the supported DCT scalings are powers of 2.
+   */
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    int ssize = cinfo->min_DCT_scaled_size;
+    while (ssize < DCTSIZE &&
+	   (compptr->h_samp_factor * ssize * 2 <=
+	    cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size) &&
+	   (compptr->v_samp_factor * ssize * 2 <=
+	    cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size)) {
+      ssize = ssize * 2;
+    }
+    compptr->DCT_scaled_size = ssize;
+  }
+
+  /* Recompute downsampled dimensions of components;
+   * application needs to know these if using raw downsampled data.
+   */
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    /* Size in samples, after IDCT scaling */
+    compptr->downsampled_width = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_width *
+		    (long) (compptr->h_samp_factor * compptr->DCT_scaled_size),
+		    (long) (cinfo->max_h_samp_factor * DCTSIZE));
+    compptr->downsampled_height = (JDIMENSION)
+      jdiv_round_up((long) cinfo->image_height *
+		    (long) (compptr->v_samp_factor * compptr->DCT_scaled_size),
+		    (long) (cinfo->max_v_samp_factor * DCTSIZE));
+  }
+
+#else /* !IDCT_SCALING_SUPPORTED */
+
+  /* Hardwire it to "no scaling" */
+  cinfo->output_width = cinfo->image_width;
+  cinfo->output_height = cinfo->image_height;
+  /* jdinput.c has already initialized DCT_scaled_size to DCTSIZE,
+   * and has computed unscaled downsampled_width and downsampled_height.
+   */
+
+#endif /* IDCT_SCALING_SUPPORTED */
+
+  /* Report number of components in selected colorspace. */
+  /* Probably this should be in the color conversion module... */
+  switch (cinfo->out_color_space) {
+  case JCS_GRAYSCALE:
+    cinfo->out_color_components = 1;
+    break;
+  case JCS_RGB:
+#if RGB_PIXELSIZE != 3
+    cinfo->out_color_components = RGB_PIXELSIZE;
+    break;
+#endif /* else share code with YCbCr */
+  case JCS_YCbCr:
+    cinfo->out_color_components = 3;
+    break;
+  case JCS_CMYK:
+  case JCS_YCCK:
+    cinfo->out_color_components = 4;
+    break;
+  default:			/* else must be same colorspace as in file */
+    cinfo->out_color_components = cinfo->num_components;
+    break;
+  }
+  cinfo->output_components = (cinfo->quantize_colors ? 1 :
+			      cinfo->out_color_components);
+
+  /* See if upsampler will want to emit more than one row at a time */
+  if (use_merged_upsample(cinfo))
+    cinfo->rec_outbuf_height = cinfo->max_v_samp_factor;
+  else
+    cinfo->rec_outbuf_height = 1;
+}
+
+
+/*
+ * Several decompression processes need to range-limit values to the range
+ * 0..MAXJSAMPLE; the input value may fall somewhat outside this range
+ * due to noise introduced by quantization, roundoff error, etc.  These
+ * processes are inner loops and need to be as fast as possible.  On most
+ * machines, particularly CPUs with pipelines or instruction prefetch,
+ * a (subscript-check-less) C table lookup
+ *		x = sample_range_limit[x];
+ * is faster than explicit tests
+ *		if (x < 0)  x = 0;
+ *		else if (x > MAXJSAMPLE)  x = MAXJSAMPLE;
+ * These processes all use a common table prepared by the routine below.
+ *
+ * For most steps we can mathematically guarantee that the initial value
+ * of x is within MAXJSAMPLE+1 of the legal range, so a table running from
+ * -(MAXJSAMPLE+1) to 2*MAXJSAMPLE+1 is sufficient.  But for the initial
+ * limiting step (just after the IDCT), a wildly out-of-range value is 
+ * possible if the input data is corrupt.  To avoid any chance of indexing
+ * off the end of memory and getting a bad-pointer trap, we perform the
+ * post-IDCT limiting thus:
+ *		x = range_limit[x & MASK];
+ * where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit
+ * samples.  Under normal circumstances this is more than enough range and
+ * a correct output will be generated; with bogus input data the mask will
+ * cause wraparound, and we will safely generate a bogus-but-in-range output.
+ * For the post-IDCT step, we want to convert the data from signed to unsigned
+ * representation by adding CENTERJSAMPLE at the same time that we limit it.
+ * So the post-IDCT limiting table ends up looking like this:
+ *   CENTERJSAMPLE,CENTERJSAMPLE+1,...,MAXJSAMPLE,
+ *   MAXJSAMPLE (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times),
+ *   0          (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times),
+ *   0,1,...,CENTERJSAMPLE-1
+ * Negative inputs select values from the upper half of the table after
+ * masking.
+ *
+ * We can save some space by overlapping the start of the post-IDCT table
+ * with the simpler range limiting table.  The post-IDCT table begins at
+ * sample_range_limit + CENTERJSAMPLE.
+ *
+ * Note that the table is allocated in near data space on PCs; it's small
+ * enough and used often enough to justify this.
+ */
+
+LOCAL(void)
+prepare_range_limit_table (j_decompress_ptr cinfo)
+/* Allocate and fill in the sample_range_limit table */
+{
+  JSAMPLE * table;
+  int i;
+
+  table = (JSAMPLE *)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+		(5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * SIZEOF(JSAMPLE));
+  table += (MAXJSAMPLE+1);	/* allow negative subscripts of simple table */
+  cinfo->sample_range_limit = table;
+  /* First segment of "simple" table: limit[x] = 0 for x < 0 */
+  MEMZERO(table - (MAXJSAMPLE+1), (MAXJSAMPLE+1) * SIZEOF(JSAMPLE));
+  /* Main part of "simple" table: limit[x] = x */
+  for (i = 0; i <= MAXJSAMPLE; i++)
+    table[i] = (JSAMPLE) i;
+  table += CENTERJSAMPLE;	/* Point to where post-IDCT table starts */
+  /* End of simple table, rest of first half of post-IDCT table */
+  for (i = CENTERJSAMPLE; i < 2*(MAXJSAMPLE+1); i++)
+    table[i] = MAXJSAMPLE;
+  /* Second half of post-IDCT table */
+  MEMZERO(table + (2 * (MAXJSAMPLE+1)),
+	  (2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * SIZEOF(JSAMPLE));
+  MEMCOPY(table + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE),
+	  cinfo->sample_range_limit, CENTERJSAMPLE * SIZEOF(JSAMPLE));
+}
+
+
+/*
+ * Master selection of decompression modules.
+ * This is done once at jpeg_start_decompress time.  We determine
+ * which modules will be used and give them appropriate initialization calls.
+ * We also initialize the decompressor input side to begin consuming data.
+ *
+ * Since jpeg_read_header has finished, we know what is in the SOF
+ * and (first) SOS markers.  We also have all the application parameter
+ * settings.
+ */
+
+LOCAL(void)
+master_selection (j_decompress_ptr cinfo)
+{
+  my_master_ptr master = (my_master_ptr) cinfo->master;
+  boolean use_c_buffer;
+  long samplesperrow;
+  JDIMENSION jd_samplesperrow;
+
+  /* Initialize dimensions and other stuff */
+  jpeg_calc_output_dimensions(cinfo);
+  prepare_range_limit_table(cinfo);
+
+  /* Width of an output scanline must be representable as JDIMENSION. */
+  samplesperrow = (long) cinfo->output_width * (long) cinfo->out_color_components;
+  jd_samplesperrow = (JDIMENSION) samplesperrow;
+  if ((long) jd_samplesperrow != samplesperrow)
+    ERREXIT(cinfo, JERR_WIDTH_OVERFLOW);
+
+  /* Initialize my private state */
+  master->pass_number = 0;
+  master->using_merged_upsample = use_merged_upsample(cinfo);
+
+  /* Color quantizer selection */
+  master->quantizer_1pass = NULL;
+  master->quantizer_2pass = NULL;
+  /* No mode changes if not using buffered-image mode. */
+  if (! cinfo->quantize_colors || ! cinfo->buffered_image) {
+    cinfo->enable_1pass_quant = FALSE;
+    cinfo->enable_external_quant = FALSE;
+    cinfo->enable_2pass_quant = FALSE;
+  }
+  if (cinfo->quantize_colors) {
+    if (cinfo->raw_data_out)
+      ERREXIT(cinfo, JERR_NOTIMPL);
+    /* 2-pass quantizer only works in 3-component color space. */
+    if (cinfo->out_color_components != 3) {
+      cinfo->enable_1pass_quant = TRUE;
+      cinfo->enable_external_quant = FALSE;
+      cinfo->enable_2pass_quant = FALSE;
+      cinfo->colormap = NULL;
+    } else if (cinfo->colormap != NULL) {
+      cinfo->enable_external_quant = TRUE;
+    } else if (cinfo->two_pass_quantize) {
+      cinfo->enable_2pass_quant = TRUE;
+    } else {
+      cinfo->enable_1pass_quant = TRUE;
+    }
+
+    if (cinfo->enable_1pass_quant) {
+#ifdef QUANT_1PASS_SUPPORTED
+      jinit_1pass_quantizer(cinfo);
+      master->quantizer_1pass = cinfo->cquantize;
+#else
+      ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif
+    }
+
+    /* We use the 2-pass code to map to external colormaps. */
+    if (cinfo->enable_2pass_quant || cinfo->enable_external_quant) {
+#ifdef QUANT_2PASS_SUPPORTED
+      jinit_2pass_quantizer(cinfo);
+      master->quantizer_2pass = cinfo->cquantize;
+#else
+      ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif
+    }
+    /* If both quantizers are initialized, the 2-pass one is left active;
+     * this is necessary for starting with quantization to an external map.
+     */
+  }
+
+  /* Post-processing: in particular, color conversion first */
+  if (! cinfo->raw_data_out) {
+    if (master->using_merged_upsample) {
+#ifdef UPSAMPLE_MERGING_SUPPORTED
+      jinit_merged_upsampler(cinfo); /* does color conversion too */
+#else
+      ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif
+    } else {
+      jinit_color_deconverter(cinfo);
+      jinit_upsampler(cinfo);
+    }
+    jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant);
+  }
+  /* Inverse DCT */
+  jinit_inverse_dct(cinfo);
+  /* Entropy decoding: either Huffman or arithmetic coding. */
+  if (cinfo->arith_code) {
+    ERREXIT(cinfo, JERR_ARITH_NOTIMPL);
+  } else {
+    if (cinfo->progressive_mode) {
+#ifdef D_PROGRESSIVE_SUPPORTED
+      jinit_phuff_decoder(cinfo);
+#else
+      ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif
+    } else
+      jinit_huff_decoder(cinfo);
+  }
+
+  /* Initialize principal buffer controllers. */
+  use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image;
+  jinit_d_coef_controller(cinfo, use_c_buffer);
+
+  if (! cinfo->raw_data_out)
+    jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */);
+
+  /* We can now tell the memory manager to allocate virtual arrays. */
+  (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo);
+
+  /* Initialize input side of decompressor to consume first scan. */
+  (*cinfo->inputctl->start_input_pass) (cinfo);
+
+#ifdef D_MULTISCAN_FILES_SUPPORTED
+  /* If jpeg_start_decompress will read the whole file, initialize
+   * progress monitoring appropriately.  The input step is counted
+   * as one pass.
+   */
+  if (cinfo->progress != NULL && ! cinfo->buffered_image &&
+      cinfo->inputctl->has_multiple_scans) {
+    int nscans;
+    /* Estimate number of scans to set pass_limit. */
+    if (cinfo->progressive_mode) {
+      /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */
+      nscans = 2 + 3 * cinfo->num_components;
+    } else {
+      /* For a nonprogressive multiscan file, estimate 1 scan per component. */
+      nscans = cinfo->num_components;
+    }
+    cinfo->progress->pass_counter = 0L;
+    cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans;
+    cinfo->progress->completed_passes = 0;
+    cinfo->progress->total_passes = (cinfo->enable_2pass_quant ? 3 : 2);
+    /* Count the input pass as done */
+    master->pass_number++;
+  }
+#endif /* D_MULTISCAN_FILES_SUPPORTED */
+}
+
+
+/*
+ * Per-pass setup.
+ * This is called at the beginning of each output pass.  We determine which
+ * modules will be active during this pass and give them appropriate
+ * start_pass calls.  We also set is_dummy_pass to indicate whether this
+ * is a "real" output pass or a dummy pass for color quantization.
+ * (In the latter case, jdapistd.c will crank the pass to completion.)
+ */
+
+METHODDEF(void)
+prepare_for_output_pass (j_decompress_ptr cinfo)
+{
+  my_master_ptr master = (my_master_ptr) cinfo->master;
+
+  if (master->pub.is_dummy_pass) {
+#ifdef QUANT_2PASS_SUPPORTED
+    /* Final pass of 2-pass quantization */
+    master->pub.is_dummy_pass = FALSE;
+    (*cinfo->cquantize->start_pass) (cinfo, FALSE);
+    (*cinfo->post->start_pass) (cinfo, JBUF_CRANK_DEST);
+    (*cinfo->main->start_pass) (cinfo, JBUF_CRANK_DEST);
+#else
+    ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif /* QUANT_2PASS_SUPPORTED */
+  } else {
+    if (cinfo->quantize_colors && cinfo->colormap == NULL) {
+      /* Select new quantization method */
+      if (cinfo->two_pass_quantize && cinfo->enable_2pass_quant) {
+	cinfo->cquantize = master->quantizer_2pass;
+	master->pub.is_dummy_pass = TRUE;
+      } else if (cinfo->enable_1pass_quant) {
+	cinfo->cquantize = master->quantizer_1pass;
+      } else {
+	ERREXIT(cinfo, JERR_MODE_CHANGE);
+      }
+    }
+    (*cinfo->idct->start_pass) (cinfo);
+    (*cinfo->coef->start_output_pass) (cinfo);
+    if (! cinfo->raw_data_out) {
+      if (! master->using_merged_upsample)
+	(*cinfo->cconvert->start_pass) (cinfo);
+      (*cinfo->upsample->start_pass) (cinfo);
+      if (cinfo->quantize_colors)
+	(*cinfo->cquantize->start_pass) (cinfo, master->pub.is_dummy_pass);
+      (*cinfo->post->start_pass) (cinfo,
+	    (master->pub.is_dummy_pass ? JBUF_SAVE_AND_PASS : JBUF_PASS_THRU));
+      (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU);
+    }
+  }
+
+  /* Set up progress monitor's pass info if present */
+  if (cinfo->progress != NULL) {
+    cinfo->progress->completed_passes = master->pass_number;
+    cinfo->progress->total_passes = master->pass_number +
+				    (master->pub.is_dummy_pass ? 2 : 1);
+    /* In buffered-image mode, we assume one more output pass if EOI not
+     * yet reached, but no more passes if EOI has been reached.
+     */
+    if (cinfo->buffered_image && ! cinfo->inputctl->eoi_reached) {
+      cinfo->progress->total_passes += (cinfo->enable_2pass_quant ? 2 : 1);
+    }
+  }
+}
+
+
+/*
+ * Finish up at end of an output pass.
+ */
+
+METHODDEF(void)
+finish_output_pass (j_decompress_ptr cinfo)
+{
+  my_master_ptr master = (my_master_ptr) cinfo->master;
+
+  if (cinfo->quantize_colors)
+    (*cinfo->cquantize->finish_pass) (cinfo);
+  master->pass_number++;
+}
+
+
+#ifdef D_MULTISCAN_FILES_SUPPORTED
+
+/*
+ * Switch to a new external colormap between output passes.
+ */
+
+GLOBAL(void)
+jpeg_new_colormap (j_decompress_ptr cinfo)
+{
+  my_master_ptr master = (my_master_ptr) cinfo->master;
+
+  /* Prevent application from calling me at wrong times */
+  if (cinfo->global_state != DSTATE_BUFIMAGE)
+    ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+
+  if (cinfo->quantize_colors && cinfo->enable_external_quant &&
+      cinfo->colormap != NULL) {
+    /* Select 2-pass quantizer for external colormap use */
+    cinfo->cquantize = master->quantizer_2pass;
+    /* Notify quantizer of colormap change */
+    (*cinfo->cquantize->new_color_map) (cinfo);
+    master->pub.is_dummy_pass = FALSE; /* just in case */
+  } else
+    ERREXIT(cinfo, JERR_MODE_CHANGE);
+}
+
+#endif /* D_MULTISCAN_FILES_SUPPORTED */
+
+
+/*
+ * Initialize master decompression control and select active modules.
+ * This is performed at the start of jpeg_start_decompress.
+ */
+
+GLOBAL(void)
+jinit_master_decompress (j_decompress_ptr cinfo)
+{
+  my_master_ptr master;
+
+  master = (my_master_ptr)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				  SIZEOF(my_decomp_master));
+  cinfo->master = (struct jpeg_decomp_master *) master;
+  master->pub.prepare_for_output_pass = prepare_for_output_pass;
+  master->pub.finish_output_pass = finish_output_pass;
+
+  master->pub.is_dummy_pass = FALSE;
+
+  master_selection(cinfo);
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdmerge.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdmerge.c
new file mode 100644
index 0000000000..37444468c2
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdmerge.c
@@ -0,0 +1,400 @@
+/*
+ * jdmerge.c
+ *
+ * Copyright (C) 1994-1996, 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 code for merged upsampling/color conversion.
+ *
+ * This file combines functions from jdsample.c and jdcolor.c;
+ * read those files first to understand what's going on.
+ *
+ * When the chroma components are to be upsampled by simple replication
+ * (ie, box filtering), we can save some work in color conversion by
+ * calculating all the output pixels corresponding to a pair of chroma
+ * samples at one time.  In the conversion equations
+ *	R = Y           + K1 * Cr
+ *	G = Y + K2 * Cb + K3 * Cr
+ *	B = Y + K4 * Cb
+ * only the Y term varies among the group of pixels corresponding to a pair
+ * of chroma samples, so the rest of the terms can be calculated just once.
+ * At typical sampling ratios, this eliminates half or three-quarters of the
+ * multiplications needed for color conversion.
+ *
+ * This file currently provides implementations for the following cases:
+ *	YCbCr => RGB color conversion only.
+ *	Sampling ratios of 2h1v or 2h2v.
+ *	No scaling needed at upsample time.
+ *	Corner-aligned (non-CCIR601) sampling alignment.
+ * Other special cases could be added, but in most applications these are
+ * the only common cases.  (For uncommon cases we fall back on the more
+ * general code in jdsample.c and jdcolor.c.)
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+#ifdef UPSAMPLE_MERGING_SUPPORTED
+
+
+/* Private subobject */
+
+typedef struct {
+  struct jpeg_upsampler pub;	/* public fields */
+
+  /* Pointer to routine to do actual upsampling/conversion of one row group */
+  JMETHOD(void, upmethod, (j_decompress_ptr cinfo,
+			   JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr,
+			   JSAMPARRAY output_buf));
+
+  /* Private state for YCC->RGB conversion */
+  int * Cr_r_tab;		/* => table for Cr to R conversion */
+  int * Cb_b_tab;		/* => table for Cb to B conversion */
+  INT32 * Cr_g_tab;		/* => table for Cr to G conversion */
+  INT32 * Cb_g_tab;		/* => table for Cb to G conversion */
+
+  /* For 2:1 vertical sampling, we produce two output rows at a time.
+   * We need a "spare" row buffer to hold the second output row if the
+   * application provides just a one-row buffer; we also use the spare
+   * to discard the dummy last row if the image height is odd.
+   */
+  JSAMPROW spare_row;
+  boolean spare_full;		/* T if spare buffer is occupied */
+
+  JDIMENSION out_row_width;	/* samples per output row */
+  JDIMENSION rows_to_go;	/* counts rows remaining in image */
+} my_upsampler;
+
+typedef my_upsampler * my_upsample_ptr;
+
+#define SCALEBITS	16	/* speediest right-shift on some machines */
+#define ONE_HALF	((INT32) 1 << (SCALEBITS-1))
+#define FIX(x)		((INT32) ((x) * (1L<<SCALEBITS) + 0.5))
+
+
+/*
+ * Initialize tables for YCC->RGB colorspace conversion.
+ * This is taken directly from jdcolor.c; see that file for more info.
+ */
+
+LOCAL(void)
+build_ycc_rgb_table (j_decompress_ptr cinfo)
+{
+  my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
+  int i;
+  INT32 x;
+  SHIFT_TEMPS
+
+  upsample->Cr_r_tab = (int *)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				(MAXJSAMPLE+1) * SIZEOF(int));
+  upsample->Cb_b_tab = (int *)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				(MAXJSAMPLE+1) * SIZEOF(int));
+  upsample->Cr_g_tab = (INT32 *)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				(MAXJSAMPLE+1) * SIZEOF(INT32));
+  upsample->Cb_g_tab = (INT32 *)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				(MAXJSAMPLE+1) * SIZEOF(INT32));
+
+  for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
+    /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */
+    /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */
+    /* Cr=>R value is nearest int to 1.40200 * x */
+    upsample->Cr_r_tab[i] = (int)
+		    RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS);
+    /* Cb=>B value is nearest int to 1.77200 * x */
+    upsample->Cb_b_tab[i] = (int)
+		    RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS);
+    /* Cr=>G value is scaled-up -0.71414 * x */
+    upsample->Cr_g_tab[i] = (- FIX(0.71414)) * x;
+    /* Cb=>G value is scaled-up -0.34414 * x */
+    /* We also add in ONE_HALF so that need not do it in inner loop */
+    upsample->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF;
+  }
+}
+
+
+/*
+ * Initialize for an upsampling pass.
+ */
+
+METHODDEF(void)
+start_pass_merged_upsample (j_decompress_ptr cinfo)
+{
+  my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
+
+  /* Mark the spare buffer empty */
+  upsample->spare_full = FALSE;
+  /* Initialize total-height counter for detecting bottom of image */
+  upsample->rows_to_go = cinfo->output_height;
+}
+
+
+/*
+ * Control routine to do upsampling (and color conversion).
+ *
+ * The control routine just handles the row buffering considerations.
+ */
+
+METHODDEF(void)
+merged_2v_upsample (j_decompress_ptr cinfo,
+		    JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
+		    JDIMENSION in_row_groups_avail,
+		    JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+		    JDIMENSION out_rows_avail)
+/* 2:1 vertical sampling case: may need a spare row. */
+{
+  my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
+  JSAMPROW work_ptrs[2];
+  JDIMENSION num_rows;		/* number of rows returned to caller */
+
+  if (upsample->spare_full) {
+    /* If we have a spare row saved from a previous cycle, just return it. */
+    jcopy_sample_rows(& upsample->spare_row, 0, output_buf + *out_row_ctr, 0,
+		      1, upsample->out_row_width);
+    num_rows = 1;
+    upsample->spare_full = FALSE;
+  } else {
+    /* Figure number of rows to return to caller. */
+    num_rows = 2;
+    /* Not more than the distance to the end of the image. */
+    if (num_rows > upsample->rows_to_go)
+      num_rows = upsample->rows_to_go;
+    /* And not more than what the client can accept: */
+    out_rows_avail -= *out_row_ctr;
+    if (num_rows > out_rows_avail)
+      num_rows = out_rows_avail;
+    /* Create output pointer array for upsampler. */
+    work_ptrs[0] = output_buf[*out_row_ctr];
+    if (num_rows > 1) {
+      work_ptrs[1] = output_buf[*out_row_ctr + 1];
+    } else {
+      work_ptrs[1] = upsample->spare_row;
+      upsample->spare_full = TRUE;
+    }
+    /* Now do the upsampling. */
+    (*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr, work_ptrs);
+  }
+
+  /* Adjust counts */
+  *out_row_ctr += num_rows;
+  upsample->rows_to_go -= num_rows;
+  /* When the buffer is emptied, declare this input row group consumed */
+  if (! upsample->spare_full)
+    (*in_row_group_ctr)++;
+}
+
+
+METHODDEF(void)
+merged_1v_upsample (j_decompress_ptr cinfo,
+		    JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
+		    JDIMENSION in_row_groups_avail,
+		    JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+		    JDIMENSION out_rows_avail)
+/* 1:1 vertical sampling case: much easier, never need a spare row. */
+{
+  my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
+
+  /* Just do the upsampling. */
+  (*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr,
+			 output_buf + *out_row_ctr);
+  /* Adjust counts */
+  (*out_row_ctr)++;
+  (*in_row_group_ctr)++;
+}
+
+
+/*
+ * These are the routines invoked by the control routines to do
+ * the actual upsampling/conversion.  One row group is processed per call.
+ *
+ * Note: since we may be writing directly into application-supplied buffers,
+ * we have to be honest about the output width; we can't assume the buffer
+ * has been rounded up to an even width.
+ */
+
+
+/*
+ * Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical.
+ */
+
+METHODDEF(void)
+h2v1_merged_upsample (j_decompress_ptr cinfo,
+		      JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr,
+		      JSAMPARRAY output_buf)
+{
+  my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
+  register int y, cred, cgreen, cblue;
+  int cb, cr;
+  register JSAMPROW outptr;
+  JSAMPROW inptr0, inptr1, inptr2;
+  JDIMENSION col;
+  /* copy these pointers into registers if possible */
+  register JSAMPLE * range_limit = cinfo->sample_range_limit;
+  int * Crrtab = upsample->Cr_r_tab;
+  int * Cbbtab = upsample->Cb_b_tab;
+  INT32 * Crgtab = upsample->Cr_g_tab;
+  INT32 * Cbgtab = upsample->Cb_g_tab;
+  SHIFT_TEMPS
+
+  inptr0 = input_buf[0][in_row_group_ctr];
+  inptr1 = input_buf[1][in_row_group_ctr];
+  inptr2 = input_buf[2][in_row_group_ctr];
+  outptr = output_buf[0];
+  /* Loop for each pair of output pixels */
+  for (col = cinfo->output_width >> 1; col > 0; col--) {
+    /* Do the chroma part of the calculation */
+    cb = GETJSAMPLE(*inptr1++);
+    cr = GETJSAMPLE(*inptr2++);
+    cred = Crrtab[cr];
+    cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS);
+    cblue = Cbbtab[cb];
+    /* Fetch 2 Y values and emit 2 pixels */
+    y  = GETJSAMPLE(*inptr0++);
+    outptr[RGB_RED] =   range_limit[y + cred];
+    outptr[RGB_GREEN] = range_limit[y + cgreen];
+    outptr[RGB_BLUE] =  range_limit[y + cblue];
+    outptr += RGB_PIXELSIZE;
+    y  = GETJSAMPLE(*inptr0++);
+    outptr[RGB_RED] =   range_limit[y + cred];
+    outptr[RGB_GREEN] = range_limit[y + cgreen];
+    outptr[RGB_BLUE] =  range_limit[y + cblue];
+    outptr += RGB_PIXELSIZE;
+  }
+  /* If image width is odd, do the last output column separately */
+  if (cinfo->output_width & 1) {
+    cb = GETJSAMPLE(*inptr1);
+    cr = GETJSAMPLE(*inptr2);
+    cred = Crrtab[cr];
+    cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS);
+    cblue = Cbbtab[cb];
+    y  = GETJSAMPLE(*inptr0);
+    outptr[RGB_RED] =   range_limit[y + cred];
+    outptr[RGB_GREEN] = range_limit[y + cgreen];
+    outptr[RGB_BLUE] =  range_limit[y + cblue];
+  }
+}
+
+
+/*
+ * Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical.
+ */
+
+METHODDEF(void)
+h2v2_merged_upsample (j_decompress_ptr cinfo,
+		      JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr,
+		      JSAMPARRAY output_buf)
+{
+  my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
+  register int y, cred, cgreen, cblue;
+  int cb, cr;
+  register JSAMPROW outptr0, outptr1;
+  JSAMPROW inptr00, inptr01, inptr1, inptr2;
+  JDIMENSION col;
+  /* copy these pointers into registers if possible */
+  register JSAMPLE * range_limit = cinfo->sample_range_limit;
+  int * Crrtab = upsample->Cr_r_tab;
+  int * Cbbtab = upsample->Cb_b_tab;
+  INT32 * Crgtab = upsample->Cr_g_tab;
+  INT32 * Cbgtab = upsample->Cb_g_tab;
+  SHIFT_TEMPS
+
+  inptr00 = input_buf[0][in_row_group_ctr*2];
+  inptr01 = input_buf[0][in_row_group_ctr*2 + 1];
+  inptr1 = input_buf[1][in_row_group_ctr];
+  inptr2 = input_buf[2][in_row_group_ctr];
+  outptr0 = output_buf[0];
+  outptr1 = output_buf[1];
+  /* Loop for each group of output pixels */
+  for (col = cinfo->output_width >> 1; col > 0; col--) {
+    /* Do the chroma part of the calculation */
+    cb = GETJSAMPLE(*inptr1++);
+    cr = GETJSAMPLE(*inptr2++);
+    cred = Crrtab[cr];
+    cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS);
+    cblue = Cbbtab[cb];
+    /* Fetch 4 Y values and emit 4 pixels */
+    y  = GETJSAMPLE(*inptr00++);
+    outptr0[RGB_RED] =   range_limit[y + cred];
+    outptr0[RGB_GREEN] = range_limit[y + cgreen];
+    outptr0[RGB_BLUE] =  range_limit[y + cblue];
+    outptr0 += RGB_PIXELSIZE;
+    y  = GETJSAMPLE(*inptr00++);
+    outptr0[RGB_RED] =   range_limit[y + cred];
+    outptr0[RGB_GREEN] = range_limit[y + cgreen];
+    outptr0[RGB_BLUE] =  range_limit[y + cblue];
+    outptr0 += RGB_PIXELSIZE;
+    y  = GETJSAMPLE(*inptr01++);
+    outptr1[RGB_RED] =   range_limit[y + cred];
+    outptr1[RGB_GREEN] = range_limit[y + cgreen];
+    outptr1[RGB_BLUE] =  range_limit[y + cblue];
+    outptr1 += RGB_PIXELSIZE;
+    y  = GETJSAMPLE(*inptr01++);
+    outptr1[RGB_RED] =   range_limit[y + cred];
+    outptr1[RGB_GREEN] = range_limit[y + cgreen];
+    outptr1[RGB_BLUE] =  range_limit[y + cblue];
+    outptr1 += RGB_PIXELSIZE;
+  }
+  /* If image width is odd, do the last output column separately */
+  if (cinfo->output_width & 1) {
+    cb = GETJSAMPLE(*inptr1);
+    cr = GETJSAMPLE(*inptr2);
+    cred = Crrtab[cr];
+    cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS);
+    cblue = Cbbtab[cb];
+    y  = GETJSAMPLE(*inptr00);
+    outptr0[RGB_RED] =   range_limit[y + cred];
+    outptr0[RGB_GREEN] = range_limit[y + cgreen];
+    outptr0[RGB_BLUE] =  range_limit[y + cblue];
+    y  = GETJSAMPLE(*inptr01);
+    outptr1[RGB_RED] =   range_limit[y + cred];
+    outptr1[RGB_GREEN] = range_limit[y + cgreen];
+    outptr1[RGB_BLUE] =  range_limit[y + cblue];
+  }
+}
+
+
+/*
+ * Module initialization routine for merged upsampling/color conversion.
+ *
+ * NB: this is called under the conditions determined by use_merged_upsample()
+ * in jdmaster.c.  That routine MUST correspond to the actual capabilities
+ * of this module; no safety checks are made here.
+ */
+
+GLOBAL(void)
+jinit_merged_upsampler (j_decompress_ptr cinfo)
+{
+  my_upsample_ptr upsample;
+
+  upsample = (my_upsample_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_upsampler));
+  cinfo->upsample = (struct jpeg_upsampler *) upsample;
+  upsample->pub.start_pass = start_pass_merged_upsample;
+  upsample->pub.need_context_rows = FALSE;
+
+  upsample->out_row_width = cinfo->output_width * cinfo->out_color_components;
+
+  if (cinfo->max_v_samp_factor == 2) {
+    upsample->pub.upsample = merged_2v_upsample;
+    upsample->upmethod = h2v2_merged_upsample;
+    /* Allocate a spare row buffer */
+    upsample->spare_row = (JSAMPROW)
+      (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+		(size_t) (upsample->out_row_width * SIZEOF(JSAMPLE)));
+  } else {
+    upsample->pub.upsample = merged_1v_upsample;
+    upsample->upmethod = h2v1_merged_upsample;
+    /* No spare row needed */
+    upsample->spare_row = NULL;
+  }
+
+  build_ycc_rgb_table(cinfo);
+}
+
+#endif /* UPSAMPLE_MERGING_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdphuff.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdphuff.c
new file mode 100644
index 0000000000..2267809945
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdphuff.c
@@ -0,0 +1,668 @@
+/*
+ * jdphuff.c
+ *
+ * Copyright (C) 1995-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.
+ *
+ * This file contains Huffman entropy decoding routines for progressive JPEG.
+ *
+ * Much of the complexity here has to do with supporting input suspension.
+ * If the data source module demands suspension, we want to be able to back
+ * up to the start of the current MCU.  To do this, we copy state variables
+ * into local working storage, and update them back to the permanent
+ * storage only upon successful completion of an MCU.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jdhuff.h"		/* Declarations shared with jdhuff.c */
+
+
+#ifdef D_PROGRESSIVE_SUPPORTED
+
+/*
+ * Expanded entropy decoder object for progressive Huffman decoding.
+ *
+ * The savable_state subrecord contains fields that change within an MCU,
+ * but must not be updated permanently until we complete the MCU.
+ */
+
+typedef struct {
+  unsigned int EOBRUN;			/* remaining EOBs in EOBRUN */
+  int last_dc_val[MAX_COMPS_IN_SCAN];	/* last DC coef for each component */
+} savable_state;
+
+/* This macro is to work around compilers with missing or broken
+ * structure assignment.  You'll need to fix this code if you have
+ * such a compiler and you change MAX_COMPS_IN_SCAN.
+ */
+
+#ifndef NO_STRUCT_ASSIGN
+#define ASSIGN_STATE(dest,src)  ((dest) = (src))
+#else
+#if MAX_COMPS_IN_SCAN == 4
+#define ASSIGN_STATE(dest,src)  \
+	((dest).EOBRUN = (src).EOBRUN, \
+	 (dest).last_dc_val[0] = (src).last_dc_val[0], \
+	 (dest).last_dc_val[1] = (src).last_dc_val[1], \
+	 (dest).last_dc_val[2] = (src).last_dc_val[2], \
+	 (dest).last_dc_val[3] = (src).last_dc_val[3])
+#endif
+#endif
+
+
+typedef struct {
+  struct jpeg_entropy_decoder pub; /* public fields */
+
+  /* These fields are loaded into local variables at start of each MCU.
+   * In case of suspension, we exit WITHOUT updating them.
+   */
+  bitread_perm_state bitstate;	/* Bit buffer at start of MCU */
+  savable_state saved;		/* Other state at start of MCU */
+
+  /* These fields are NOT loaded into local working state. */
+  unsigned int restarts_to_go;	/* MCUs left in this restart interval */
+
+  /* Pointers to derived tables (these workspaces have image lifespan) */
+  d_derived_tbl * derived_tbls[NUM_HUFF_TBLS];
+
+  d_derived_tbl * ac_derived_tbl; /* active table during an AC scan */
+} phuff_entropy_decoder;
+
+typedef phuff_entropy_decoder * phuff_entropy_ptr;
+
+/* Forward declarations */
+METHODDEF(boolean) decode_mcu_DC_first JPP((j_decompress_ptr cinfo,
+					    JBLOCKROW *MCU_data));
+METHODDEF(boolean) decode_mcu_AC_first JPP((j_decompress_ptr cinfo,
+					    JBLOCKROW *MCU_data));
+METHODDEF(boolean) decode_mcu_DC_refine JPP((j_decompress_ptr cinfo,
+					     JBLOCKROW *MCU_data));
+METHODDEF(boolean) decode_mcu_AC_refine JPP((j_decompress_ptr cinfo,
+					     JBLOCKROW *MCU_data));
+
+
+/*
+ * Initialize for a Huffman-compressed scan.
+ */
+
+METHODDEF(void)
+start_pass_phuff_decoder (j_decompress_ptr cinfo)
+{
+  phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
+  boolean is_DC_band, bad;
+  int ci, coefi, tbl;
+  int *coef_bit_ptr;
+  jpeg_component_info * compptr;
+
+  is_DC_band = (cinfo->Ss == 0);
+
+  /* Validate scan parameters */
+  bad = FALSE;
+  if (is_DC_band) {
+    if (cinfo->Se != 0)
+      bad = TRUE;
+  } else {
+    /* need not check Ss/Se < 0 since they came from unsigned bytes */
+    if (cinfo->Ss > cinfo->Se || cinfo->Se >= DCTSIZE2)
+      bad = TRUE;
+    /* AC scans may have only one component */
+    if (cinfo->comps_in_scan != 1)
+      bad = TRUE;
+  }
+  if (cinfo->Ah != 0) {
+    /* Successive approximation refinement scan: must have Al = Ah-1. */
+    if (cinfo->Al != cinfo->Ah-1)
+      bad = TRUE;
+  }
+  if (cinfo->Al > 13)		/* need not check for < 0 */
+    bad = TRUE;
+  /* Arguably the maximum Al value should be less than 13 for 8-bit precision,
+   * but the spec doesn't say so, and we try to be liberal about what we
+   * accept.  Note: large Al values could result in out-of-range DC
+   * coefficients during early scans, leading to bizarre displays due to
+   * overflows in the IDCT math.  But we won't crash.
+   */
+  if (bad)
+    ERREXIT4(cinfo, JERR_BAD_PROGRESSION,
+	     cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al);
+  /* Update progression status, and verify that scan order is legal.
+   * Note that inter-scan inconsistencies are treated as warnings
+   * not fatal errors ... not clear if this is right way to behave.
+   */
+  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+    int cindex = cinfo->cur_comp_info[ci]->component_index;
+    coef_bit_ptr = & cinfo->coef_bits[cindex][0];
+    if (!is_DC_band && coef_bit_ptr[0] < 0) /* AC without prior DC scan */
+      WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, 0);
+    for (coefi = cinfo->Ss; coefi <= cinfo->Se; coefi++) {
+      int expected = (coef_bit_ptr[coefi] < 0) ? 0 : coef_bit_ptr[coefi];
+      if (cinfo->Ah != expected)
+	WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, coefi);
+      coef_bit_ptr[coefi] = cinfo->Al;
+    }
+  }
+
+  /* Select MCU decoding routine */
+  if (cinfo->Ah == 0) {
+    if (is_DC_band)
+      entropy->pub.decode_mcu = decode_mcu_DC_first;
+    else
+      entropy->pub.decode_mcu = decode_mcu_AC_first;
+  } else {
+    if (is_DC_band)
+      entropy->pub.decode_mcu = decode_mcu_DC_refine;
+    else
+      entropy->pub.decode_mcu = decode_mcu_AC_refine;
+  }
+
+  for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
+    compptr = cinfo->cur_comp_info[ci];
+    /* Make sure requested tables are present, and compute derived tables.
+     * We may build same derived table more than once, but it's not expensive.
+     */
+    if (is_DC_band) {
+      if (cinfo->Ah == 0) {	/* DC refinement needs no table */
+	tbl = compptr->dc_tbl_no;
+	jpeg_make_d_derived_tbl(cinfo, TRUE, tbl,
+				& entropy->derived_tbls[tbl]);
+      }
+    } else {
+      tbl = compptr->ac_tbl_no;
+      jpeg_make_d_derived_tbl(cinfo, FALSE, tbl,
+			      & entropy->derived_tbls[tbl]);
+      /* remember the single active table */
+      entropy->ac_derived_tbl = entropy->derived_tbls[tbl];
+    }
+    /* Initialize DC predictions to 0 */
+    entropy->saved.last_dc_val[ci] = 0;
+  }
+
+  /* Initialize bitread state variables */
+  entropy->bitstate.bits_left = 0;
+  entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */
+  entropy->pub.insufficient_data = FALSE;
+
+  /* Initialize private state variables */
+  entropy->saved.EOBRUN = 0;
+
+  /* Initialize restart counter */
+  entropy->restarts_to_go = cinfo->restart_interval;
+}
+
+
+/*
+ * Figure F.12: extend sign bit.
+ * On some machines, a shift and add will be faster than a table lookup.
+ */
+
+#ifdef AVOID_TABLES
+
+#define HUFF_EXTEND(x,s)  ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x))
+
+#else
+
+#define HUFF_EXTEND(x,s)  ((x) < extend_test[s] ? (x) + extend_offset[s] : (x))
+
+static const int extend_test[16] =   /* entry n is 2**(n-1) */
+  { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
+    0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 };
+
+static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */
+  { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1,
+    ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1,
+    ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1,
+    ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 };
+
+#endif /* AVOID_TABLES */
+
+
+/*
+ * Check for a restart marker & resynchronize decoder.
+ * Returns FALSE if must suspend.
+ */
+
+LOCAL(boolean)
+process_restart (j_decompress_ptr cinfo)
+{
+  phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
+  int ci;
+
+  /* Throw away any unused bits remaining in bit buffer; */
+  /* include any full bytes in next_marker's count of discarded bytes */
+  cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8;
+  entropy->bitstate.bits_left = 0;
+
+  /* Advance past the RSTn marker */
+  if (! (*cinfo->marker->read_restart_marker) (cinfo))
+    return FALSE;
+
+  /* Re-initialize DC predictions to 0 */
+  for (ci = 0; ci < cinfo->comps_in_scan; ci++)
+    entropy->saved.last_dc_val[ci] = 0;
+  /* Re-init EOB run count, too */
+  entropy->saved.EOBRUN = 0;
+
+  /* Reset restart counter */
+  entropy->restarts_to_go = cinfo->restart_interval;
+
+  /* Reset out-of-data flag, unless read_restart_marker left us smack up
+   * against a marker.  In that case we will end up treating the next data
+   * segment as empty, and we can avoid producing bogus output pixels by
+   * leaving the flag set.
+   */
+  if (cinfo->unread_marker == 0)
+    entropy->pub.insufficient_data = FALSE;
+
+  return TRUE;
+}
+
+
+/*
+ * Huffman MCU decoding.
+ * Each of these routines decodes and returns one MCU's worth of
+ * Huffman-compressed coefficients. 
+ * The coefficients are reordered from zigzag order into natural array order,
+ * but are not dequantized.
+ *
+ * The i'th block of the MCU is stored into the block pointed to by
+ * MCU_data[i].  WE ASSUME THIS AREA IS INITIALLY ZEROED BY THE CALLER.
+ *
+ * We return FALSE if data source requested suspension.  In that case no
+ * changes have been made to permanent state.  (Exception: some output
+ * coefficients may already have been assigned.  This is harmless for
+ * spectral selection, since we'll just re-assign them on the next call.
+ * Successive approximation AC refinement has to be more careful, however.)
+ */
+
+/*
+ * MCU decoding for DC initial scan (either spectral selection,
+ * or first pass of successive approximation).
+ */
+
+METHODDEF(boolean)
+decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data)
+{   
+  phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
+  int Al = cinfo->Al;
+  register int s, r;
+  int blkn, ci;
+  JBLOCKROW block;
+  BITREAD_STATE_VARS;
+  savable_state state;
+  d_derived_tbl * tbl;
+  jpeg_component_info * compptr;
+
+  /* Process restart marker if needed; may have to suspend */
+  if (cinfo->restart_interval) {
+    if (entropy->restarts_to_go == 0)
+      if (! process_restart(cinfo))
+	return FALSE;
+  }
+
+  /* If we've run out of data, just leave the MCU set to zeroes.
+   * This way, we return uniform gray for the remainder of the segment.
+   */
+  if (! entropy->pub.insufficient_data) {
+
+    /* Load up working state */
+    BITREAD_LOAD_STATE(cinfo,entropy->bitstate);
+    ASSIGN_STATE(state, entropy->saved);
+
+    /* Outer loop handles each block in the MCU */
+
+    for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
+      block = MCU_data[blkn];
+      ci = cinfo->MCU_membership[blkn];
+      compptr = cinfo->cur_comp_info[ci];
+      tbl = entropy->derived_tbls[compptr->dc_tbl_no];
+
+      /* Decode a single block's worth of coefficients */
+
+      /* Section F.2.2.1: decode the DC coefficient difference */
+      HUFF_DECODE(s, br_state, tbl, return FALSE, label1);
+      if (s) {
+	CHECK_BIT_BUFFER(br_state, s, return FALSE);
+	r = GET_BITS(s);
+	s = HUFF_EXTEND(r, s);
+      }
+
+      /* Convert DC difference to actual value, update last_dc_val */
+      s += state.last_dc_val[ci];
+      state.last_dc_val[ci] = s;
+      /* Scale and output the coefficient (assumes jpeg_natural_order[0]=0) */
+      (*block)[0] = (JCOEF) (s << Al);
+    }
+
+    /* Completed MCU, so update state */
+    BITREAD_SAVE_STATE(cinfo,entropy->bitstate);
+    ASSIGN_STATE(entropy->saved, state);
+  }
+
+  /* Account for restart interval (no-op if not using restarts) */
+  entropy->restarts_to_go--;
+
+  return TRUE;
+}
+
+
+/*
+ * MCU decoding for AC initial scan (either spectral selection,
+ * or first pass of successive approximation).
+ */
+
+METHODDEF(boolean)
+decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data)
+{   
+  phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
+  int Se = cinfo->Se;
+  int Al = cinfo->Al;
+  register int s, k, r;
+  unsigned int EOBRUN;
+  JBLOCKROW block;
+  BITREAD_STATE_VARS;
+  d_derived_tbl * tbl;
+
+  /* Process restart marker if needed; may have to suspend */
+  if (cinfo->restart_interval) {
+    if (entropy->restarts_to_go == 0)
+      if (! process_restart(cinfo))
+	return FALSE;
+  }
+
+  /* If we've run out of data, just leave the MCU set to zeroes.
+   * This way, we return uniform gray for the remainder of the segment.
+   */
+  if (! entropy->pub.insufficient_data) {
+
+    /* Load up working state.
+     * We can avoid loading/saving bitread state if in an EOB run.
+     */
+    EOBRUN = entropy->saved.EOBRUN;	/* only part of saved state we need */
+
+    /* There is always only one block per MCU */
+
+    if (EOBRUN > 0)		/* if it's a band of zeroes... */
+      EOBRUN--;			/* ...process it now (we do nothing) */
+    else {
+      BITREAD_LOAD_STATE(cinfo,entropy->bitstate);
+      block = MCU_data[0];
+      tbl = entropy->ac_derived_tbl;
+
+      for (k = cinfo->Ss; k <= Se; k++) {
+	HUFF_DECODE(s, br_state, tbl, return FALSE, label2);
+	r = s >> 4;
+	s &= 15;
+	if (s) {
+	  k += r;
+	  CHECK_BIT_BUFFER(br_state, s, return FALSE);
+	  r = GET_BITS(s);
+	  s = HUFF_EXTEND(r, s);
+	  /* Scale and output coefficient in natural (dezigzagged) order */
+	  (*block)[jpeg_natural_order[k]] = (JCOEF) (s << Al);
+	} else {
+	  if (r == 15) {	/* ZRL */
+	    k += 15;		/* skip 15 zeroes in band */
+	  } else {		/* EOBr, run length is 2^r + appended bits */
+	    EOBRUN = 1 << r;
+	    if (r) {		/* EOBr, r > 0 */
+	      CHECK_BIT_BUFFER(br_state, r, return FALSE);
+	      r = GET_BITS(r);
+	      EOBRUN += r;
+	    }
+	    EOBRUN--;		/* this band is processed at this moment */
+	    break;		/* force end-of-band */
+	  }
+	}
+      }
+
+      BITREAD_SAVE_STATE(cinfo,entropy->bitstate);
+    }
+
+    /* Completed MCU, so update state */
+    entropy->saved.EOBRUN = EOBRUN;	/* only part of saved state we need */
+  }
+
+  /* Account for restart interval (no-op if not using restarts) */
+  entropy->restarts_to_go--;
+
+  return TRUE;
+}
+
+
+/*
+ * MCU decoding for DC successive approximation refinement scan.
+ * Note: we assume such scans can be multi-component, although the spec
+ * is not very clear on the point.
+ */
+
+METHODDEF(boolean)
+decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data)
+{   
+  phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
+  int p1 = 1 << cinfo->Al;	/* 1 in the bit position being coded */
+  int blkn;
+  JBLOCKROW block;
+  BITREAD_STATE_VARS;
+
+  /* Process restart marker if needed; may have to suspend */
+  if (cinfo->restart_interval) {
+    if (entropy->restarts_to_go == 0)
+      if (! process_restart(cinfo))
+	return FALSE;
+  }
+
+  /* Not worth the cycles to check insufficient_data here,
+   * since we will not change the data anyway if we read zeroes.
+   */
+
+  /* Load up working state */
+  BITREAD_LOAD_STATE(cinfo,entropy->bitstate);
+
+  /* Outer loop handles each block in the MCU */
+
+  for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
+    block = MCU_data[blkn];
+
+    /* Encoded data is simply the next bit of the two's-complement DC value */
+    CHECK_BIT_BUFFER(br_state, 1, return FALSE);
+    if (GET_BITS(1))
+      (*block)[0] |= p1;
+    /* Note: since we use |=, repeating the assignment later is safe */
+  }
+
+  /* Completed MCU, so update state */
+  BITREAD_SAVE_STATE(cinfo,entropy->bitstate);
+
+  /* Account for restart interval (no-op if not using restarts) */
+  entropy->restarts_to_go--;
+
+  return TRUE;
+}
+
+
+/*
+ * MCU decoding for AC successive approximation refinement scan.
+ */
+
+METHODDEF(boolean)
+decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data)
+{   
+  phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
+  int Se = cinfo->Se;
+  int p1 = 1 << cinfo->Al;	/* 1 in the bit position being coded */
+  int m1 = (-1) << cinfo->Al;	/* -1 in the bit position being coded */
+  register int s, k, r;
+  unsigned int EOBRUN;
+  JBLOCKROW block;
+  JCOEFPTR thiscoef;
+  BITREAD_STATE_VARS;
+  d_derived_tbl * tbl;
+  int num_newnz;
+  int newnz_pos[DCTSIZE2];
+
+  /* Process restart marker if needed; may have to suspend */
+  if (cinfo->restart_interval) {
+    if (entropy->restarts_to_go == 0)
+      if (! process_restart(cinfo))
+	return FALSE;
+  }
+
+  /* If we've run out of data, don't modify the MCU.
+   */
+  if (! entropy->pub.insufficient_data) {
+
+    /* Load up working state */
+    BITREAD_LOAD_STATE(cinfo,entropy->bitstate);
+    EOBRUN = entropy->saved.EOBRUN; /* only part of saved state we need */
+
+    /* There is always only one block per MCU */
+    block = MCU_data[0];
+    tbl = entropy->ac_derived_tbl;
+
+    /* If we are forced to suspend, we must undo the assignments to any newly
+     * nonzero coefficients in the block, because otherwise we'd get confused
+     * next time about which coefficients were already nonzero.
+     * But we need not undo addition of bits to already-nonzero coefficients;
+     * instead, we can test the current bit to see if we already did it.
+     */
+    num_newnz = 0;
+
+    /* initialize coefficient loop counter to start of band */
+    k = cinfo->Ss;
+
+    if (EOBRUN == 0) {
+      for (; k <= Se; k++) {
+	HUFF_DECODE(s, br_state, tbl, goto undoit, label3);
+	r = s >> 4;
+	s &= 15;
+	if (s) {
+	  if (s != 1)		/* size of new coef should always be 1 */
+	    WARNMS(cinfo, JWRN_HUFF_BAD_CODE);
+	  CHECK_BIT_BUFFER(br_state, 1, goto undoit);
+	  if (GET_BITS(1))
+	    s = p1;		/* newly nonzero coef is positive */
+	  else
+	    s = m1;		/* newly nonzero coef is negative */
+	} else {
+	  if (r != 15) {
+	    EOBRUN = 1 << r;	/* EOBr, run length is 2^r + appended bits */
+	    if (r) {
+	      CHECK_BIT_BUFFER(br_state, r, goto undoit);
+	      r = GET_BITS(r);
+	      EOBRUN += r;
+	    }
+	    break;		/* rest of block is handled by EOB logic */
+	  }
+	  /* note s = 0 for processing ZRL */
+	}
+	/* Advance over already-nonzero coefs and r still-zero coefs,
+	 * appending correction bits to the nonzeroes.  A correction bit is 1
+	 * if the absolute value of the coefficient must be increased.
+	 */
+	do {
+	  thiscoef = *block + jpeg_natural_order[k];
+	  if (*thiscoef != 0) {
+	    CHECK_BIT_BUFFER(br_state, 1, goto undoit);
+	    if (GET_BITS(1)) {
+	      if ((*thiscoef & p1) == 0) { /* do nothing if already set it */
+		if (*thiscoef >= 0)
+		  *thiscoef += p1;
+		else
+		  *thiscoef += m1;
+	      }
+	    }
+	  } else {
+	    if (--r < 0)
+	      break;		/* reached target zero coefficient */
+	  }
+	  k++;
+	} while (k <= Se);
+	if (s) {
+	  int pos = jpeg_natural_order[k];
+	  /* Output newly nonzero coefficient */
+	  (*block)[pos] = (JCOEF) s;
+	  /* Remember its position in case we have to suspend */
+	  newnz_pos[num_newnz++] = pos;
+	}
+      }
+    }
+
+    if (EOBRUN > 0) {
+      /* Scan any remaining coefficient positions after the end-of-band
+       * (the last newly nonzero coefficient, if any).  Append a correction
+       * bit to each already-nonzero coefficient.  A correction bit is 1
+       * if the absolute value of the coefficient must be increased.
+       */
+      for (; k <= Se; k++) {
+	thiscoef = *block + jpeg_natural_order[k];
+	if (*thiscoef != 0) {
+	  CHECK_BIT_BUFFER(br_state, 1, goto undoit);
+	  if (GET_BITS(1)) {
+	    if ((*thiscoef & p1) == 0) { /* do nothing if already changed it */
+	      if (*thiscoef >= 0)
+		*thiscoef += p1;
+	      else
+		*thiscoef += m1;
+	    }
+	  }
+	}
+      }
+      /* Count one block completed in EOB run */
+      EOBRUN--;
+    }
+
+    /* Completed MCU, so update state */
+    BITREAD_SAVE_STATE(cinfo,entropy->bitstate);
+    entropy->saved.EOBRUN = EOBRUN; /* only part of saved state we need */
+  }
+
+  /* Account for restart interval (no-op if not using restarts) */
+  entropy->restarts_to_go--;
+
+  return TRUE;
+
+undoit:
+  /* Re-zero any output coefficients that we made newly nonzero */
+  while (num_newnz > 0)
+    (*block)[newnz_pos[--num_newnz]] = 0;
+
+  return FALSE;
+}
+
+
+/*
+ * Module initialization routine for progressive Huffman entropy decoding.
+ */
+
+GLOBAL(void)
+jinit_phuff_decoder (j_decompress_ptr cinfo)
+{
+  phuff_entropy_ptr entropy;
+  int *coef_bit_ptr;
+  int ci, i;
+
+  entropy = (phuff_entropy_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(phuff_entropy_decoder));
+  cinfo->entropy = (struct jpeg_entropy_decoder *) entropy;
+  entropy->pub.start_pass = start_pass_phuff_decoder;
+
+  /* Mark derived tables unallocated */
+  for (i = 0; i < NUM_HUFF_TBLS; i++) {
+    entropy->derived_tbls[i] = NULL;
+  }
+
+  /* Create progression status table */
+  cinfo->coef_bits = (int (*)[DCTSIZE2])
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				cinfo->num_components*DCTSIZE2*SIZEOF(int));
+  coef_bit_ptr = & cinfo->coef_bits[0][0];
+  for (ci = 0; ci < cinfo->num_components; ci++) 
+    for (i = 0; i < DCTSIZE2; i++)
+      *coef_bit_ptr++ = -1;
+}
+
+#endif /* D_PROGRESSIVE_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdpostct.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdpostct.c
new file mode 100644
index 0000000000..571563d728
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdpostct.c
@@ -0,0 +1,290 @@
+/*
+ * jdpostct.c
+ *
+ * Copyright (C) 1994-1996, 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 decompression postprocessing controller.
+ * This controller manages the upsampling, color conversion, and color
+ * quantization/reduction steps; specifically, it controls the buffering
+ * between upsample/color conversion and color quantization/reduction.
+ *
+ * If no color quantization/reduction is required, then this module has no
+ * work to do, and it just hands off to the upsample/color conversion code.
+ * An integrated upsample/convert/quantize process would replace this module
+ * entirely.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/* Private buffer controller object */
+
+typedef struct {
+  struct jpeg_d_post_controller pub; /* public fields */
+
+  /* Color quantization source buffer: this holds output data from
+   * the upsample/color conversion step to be passed to the quantizer.
+   * For two-pass color quantization, we need a full-image buffer;
+   * for one-pass operation, a strip buffer is sufficient.
+   */
+  jvirt_sarray_ptr whole_image;	/* virtual array, or NULL if one-pass */
+  JSAMPARRAY buffer;		/* strip buffer, or current strip of virtual */
+  JDIMENSION strip_height;	/* buffer size in rows */
+  /* for two-pass mode only: */
+  JDIMENSION starting_row;	/* row # of first row in current strip */
+  JDIMENSION next_row;		/* index of next row to fill/empty in strip */
+} my_post_controller;
+
+typedef my_post_controller * my_post_ptr;
+
+
+/* Forward declarations */
+METHODDEF(void) post_process_1pass
+	JPP((j_decompress_ptr cinfo,
+	     JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
+	     JDIMENSION in_row_groups_avail,
+	     JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+	     JDIMENSION out_rows_avail));
+#ifdef QUANT_2PASS_SUPPORTED
+METHODDEF(void) post_process_prepass
+	JPP((j_decompress_ptr cinfo,
+	     JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
+	     JDIMENSION in_row_groups_avail,
+	     JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+	     JDIMENSION out_rows_avail));
+METHODDEF(void) post_process_2pass
+	JPP((j_decompress_ptr cinfo,
+	     JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
+	     JDIMENSION in_row_groups_avail,
+	     JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+	     JDIMENSION out_rows_avail));
+#endif
+
+
+/*
+ * Initialize for a processing pass.
+ */
+
+METHODDEF(void)
+start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)
+{
+  my_post_ptr post = (my_post_ptr) cinfo->post;
+
+  switch (pass_mode) {
+  case JBUF_PASS_THRU:
+    if (cinfo->quantize_colors) {
+      /* Single-pass processing with color quantization. */
+      post->pub.post_process_data = post_process_1pass;
+      /* We could be doing buffered-image output before starting a 2-pass
+       * color quantization; in that case, jinit_d_post_controller did not
+       * allocate a strip buffer.  Use the virtual-array buffer as workspace.
+       */
+      if (post->buffer == NULL) {
+	post->buffer = (*cinfo->mem->access_virt_sarray)
+	  ((j_common_ptr) cinfo, post->whole_image,
+	   (JDIMENSION) 0, post->strip_height, TRUE);
+      }
+    } else {
+      /* For single-pass processing without color quantization,
+       * I have no work to do; just call the upsampler directly.
+       */
+      post->pub.post_process_data = cinfo->upsample->upsample;
+    }
+    break;
+#ifdef QUANT_2PASS_SUPPORTED
+  case JBUF_SAVE_AND_PASS:
+    /* First pass of 2-pass quantization */
+    if (post->whole_image == NULL)
+      ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+    post->pub.post_process_data = post_process_prepass;
+    break;
+  case JBUF_CRANK_DEST:
+    /* Second pass of 2-pass quantization */
+    if (post->whole_image == NULL)
+      ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+    post->pub.post_process_data = post_process_2pass;
+    break;
+#endif /* QUANT_2PASS_SUPPORTED */
+  default:
+    ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+    break;
+  }
+  post->starting_row = post->next_row = 0;
+}
+
+
+/*
+ * Process some data in the one-pass (strip buffer) case.
+ * This is used for color precision reduction as well as one-pass quantization.
+ */
+
+METHODDEF(void)
+post_process_1pass (j_decompress_ptr cinfo,
+		    JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
+		    JDIMENSION in_row_groups_avail,
+		    JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+		    JDIMENSION out_rows_avail)
+{
+  my_post_ptr post = (my_post_ptr) cinfo->post;
+  JDIMENSION num_rows, max_rows;
+
+  /* Fill the buffer, but not more than what we can dump out in one go. */
+  /* Note we rely on the upsampler to detect bottom of image. */
+  max_rows = out_rows_avail - *out_row_ctr;
+  if (max_rows > post->strip_height)
+    max_rows = post->strip_height;
+  num_rows = 0;
+  (*cinfo->upsample->upsample) (cinfo,
+		input_buf, in_row_group_ctr, in_row_groups_avail,
+		post->buffer, &num_rows, max_rows);
+  /* Quantize and emit data. */
+  (*cinfo->cquantize->color_quantize) (cinfo,
+		post->buffer, output_buf + *out_row_ctr, (int) num_rows);
+  *out_row_ctr += num_rows;
+}
+
+
+#ifdef QUANT_2PASS_SUPPORTED
+
+/*
+ * Process some data in the first pass of 2-pass quantization.
+ */
+
+METHODDEF(void)
+post_process_prepass (j_decompress_ptr cinfo,
+		      JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
+		      JDIMENSION in_row_groups_avail,
+		      JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+		      JDIMENSION out_rows_avail)
+{
+  my_post_ptr post = (my_post_ptr) cinfo->post;
+  JDIMENSION old_next_row, num_rows;
+
+  /* Reposition virtual buffer if at start of strip. */
+  if (post->next_row == 0) {
+    post->buffer = (*cinfo->mem->access_virt_sarray)
+	((j_common_ptr) cinfo, post->whole_image,
+	 post->starting_row, post->strip_height, TRUE);
+  }
+
+  /* Upsample some data (up to a strip height's worth). */
+  old_next_row = post->next_row;
+  (*cinfo->upsample->upsample) (cinfo,
+		input_buf, in_row_group_ctr, in_row_groups_avail,
+		post->buffer, &post->next_row, post->strip_height);
+
+  /* Allow quantizer to scan new data.  No data is emitted, */
+  /* but we advance out_row_ctr so outer loop can tell when we're done. */
+  if (post->next_row > old_next_row) {
+    num_rows = post->next_row - old_next_row;
+    (*cinfo->cquantize->color_quantize) (cinfo, post->buffer + old_next_row,
+					 (JSAMPARRAY) NULL, (int) num_rows);
+    *out_row_ctr += num_rows;
+  }
+
+  /* Advance if we filled the strip. */
+  if (post->next_row >= post->strip_height) {
+    post->starting_row += post->strip_height;
+    post->next_row = 0;
+  }
+}
+
+
+/*
+ * Process some data in the second pass of 2-pass quantization.
+ */
+
+METHODDEF(void)
+post_process_2pass (j_decompress_ptr cinfo,
+		    JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
+		    JDIMENSION in_row_groups_avail,
+		    JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+		    JDIMENSION out_rows_avail)
+{
+  my_post_ptr post = (my_post_ptr) cinfo->post;
+  JDIMENSION num_rows, max_rows;
+
+  /* Reposition virtual buffer if at start of strip. */
+  if (post->next_row == 0) {
+    post->buffer = (*cinfo->mem->access_virt_sarray)
+	((j_common_ptr) cinfo, post->whole_image,
+	 post->starting_row, post->strip_height, FALSE);
+  }
+
+  /* Determine number of rows to emit. */
+  num_rows = post->strip_height - post->next_row; /* available in strip */
+  max_rows = out_rows_avail - *out_row_ctr; /* available in output area */
+  if (num_rows > max_rows)
+    num_rows = max_rows;
+  /* We have to check bottom of image here, can't depend on upsampler. */
+  max_rows = cinfo->output_height - post->starting_row;
+  if (num_rows > max_rows)
+    num_rows = max_rows;
+
+  /* Quantize and emit data. */
+  (*cinfo->cquantize->color_quantize) (cinfo,
+		post->buffer + post->next_row, output_buf + *out_row_ctr,
+		(int) num_rows);
+  *out_row_ctr += num_rows;
+
+  /* Advance if we filled the strip. */
+  post->next_row += num_rows;
+  if (post->next_row >= post->strip_height) {
+    post->starting_row += post->strip_height;
+    post->next_row = 0;
+  }
+}
+
+#endif /* QUANT_2PASS_SUPPORTED */
+
+
+/*
+ * Initialize postprocessing controller.
+ */
+
+GLOBAL(void)
+jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer)
+{
+  my_post_ptr post;
+
+  post = (my_post_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_post_controller));
+  cinfo->post = (struct jpeg_d_post_controller *) post;
+  post->pub.start_pass = start_pass_dpost;
+  post->whole_image = NULL;	/* flag for no virtual arrays */
+  post->buffer = NULL;		/* flag for no strip buffer */
+
+  /* Create the quantization buffer, if needed */
+  if (cinfo->quantize_colors) {
+    /* The buffer strip height is max_v_samp_factor, which is typically
+     * an efficient number of rows for upsampling to return.
+     * (In the presence of output rescaling, we might want to be smarter?)
+     */
+    post->strip_height = (JDIMENSION) cinfo->max_v_samp_factor;
+    if (need_full_buffer) {
+      /* Two-pass color quantization: need full-image storage. */
+      /* We round up the number of rows to a multiple of the strip height. */
+#ifdef QUANT_2PASS_SUPPORTED
+      post->whole_image = (*cinfo->mem->request_virt_sarray)
+	((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
+	 cinfo->output_width * cinfo->out_color_components,
+	 (JDIMENSION) jround_up((long) cinfo->output_height,
+				(long) post->strip_height),
+	 post->strip_height);
+#else
+      ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+#endif /* QUANT_2PASS_SUPPORTED */
+    } else {
+      /* One-pass color quantization: just make a strip buffer. */
+      post->buffer = (*cinfo->mem->alloc_sarray)
+	((j_common_ptr) cinfo, JPOOL_IMAGE,
+	 cinfo->output_width * cinfo->out_color_components,
+	 post->strip_height);
+    }
+  }
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdsample.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdsample.c
new file mode 100644
index 0000000000..80ffefb2a1
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdsample.c
@@ -0,0 +1,478 @@
+/*
+ * jdsample.c
+ *
+ * Copyright (C) 1991-1996, 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 upsampling routines.
+ *
+ * Upsampling input data is counted in "row groups".  A row group
+ * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size)
+ * sample rows of each component.  Upsampling will normally produce
+ * max_v_samp_factor pixel rows from each row group (but this could vary
+ * if the upsampler is applying a scale factor of its own).
+ *
+ * An excellent reference for image resampling is
+ *   Digital Image Warping, George Wolberg, 1990.
+ *   Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/* Pointer to routine to upsample a single component */
+typedef JMETHOD(void, upsample1_ptr,
+		(j_decompress_ptr cinfo, jpeg_component_info * compptr,
+		 JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr));
+
+/* Private subobject */
+
+typedef struct {
+  struct jpeg_upsampler pub;	/* public fields */
+
+  /* Color conversion buffer.  When using separate upsampling and color
+   * conversion steps, this buffer holds one upsampled row group until it
+   * has been color converted and output.
+   * Note: we do not allocate any storage for component(s) which are full-size,
+   * ie do not need rescaling.  The corresponding entry of color_buf[] is
+   * simply set to point to the input data array, thereby avoiding copying.
+   */
+  JSAMPARRAY color_buf[MAX_COMPONENTS];
+
+  /* Per-component upsampling method pointers */
+  upsample1_ptr methods[MAX_COMPONENTS];
+
+  int next_row_out;		/* counts rows emitted from color_buf */
+  JDIMENSION rows_to_go;	/* counts rows remaining in image */
+
+  /* Height of an input row group for each component. */
+  int rowgroup_height[MAX_COMPONENTS];
+
+  /* These arrays save pixel expansion factors so that int_expand need not
+   * recompute them each time.  They are unused for other upsampling methods.
+   */
+  UINT8 h_expand[MAX_COMPONENTS];
+  UINT8 v_expand[MAX_COMPONENTS];
+} my_upsampler;
+
+typedef my_upsampler * my_upsample_ptr;
+
+
+/*
+ * Initialize for an upsampling pass.
+ */
+
+METHODDEF(void)
+start_pass_upsample (j_decompress_ptr cinfo)
+{
+  my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
+
+  /* Mark the conversion buffer empty */
+  upsample->next_row_out = cinfo->max_v_samp_factor;
+  /* Initialize total-height counter for detecting bottom of image */
+  upsample->rows_to_go = cinfo->output_height;
+}
+
+
+/*
+ * Control routine to do upsampling (and color conversion).
+ *
+ * In this version we upsample each component independently.
+ * We upsample one row group into the conversion buffer, then apply
+ * color conversion a row at a time.
+ */
+
+METHODDEF(void)
+sep_upsample (j_decompress_ptr cinfo,
+	      JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
+	      JDIMENSION in_row_groups_avail,
+	      JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+	      JDIMENSION out_rows_avail)
+{
+  my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
+  int ci;
+  jpeg_component_info * compptr;
+  JDIMENSION num_rows;
+
+  /* Fill the conversion buffer, if it's empty */
+  if (upsample->next_row_out >= cinfo->max_v_samp_factor) {
+    for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+	 ci++, compptr++) {
+      /* Invoke per-component upsample method.  Notice we pass a POINTER
+       * to color_buf[ci], so that fullsize_upsample can change it.
+       */
+      (*upsample->methods[ci]) (cinfo, compptr,
+	input_buf[ci] + (*in_row_group_ctr * upsample->rowgroup_height[ci]),
+	upsample->color_buf + ci);
+    }
+    upsample->next_row_out = 0;
+  }
+
+  /* Color-convert and emit rows */
+
+  /* How many we have in the buffer: */
+  num_rows = (JDIMENSION) (cinfo->max_v_samp_factor - upsample->next_row_out);
+  /* Not more than the distance to the end of the image.  Need this test
+   * in case the image height is not a multiple of max_v_samp_factor:
+   */
+  if (num_rows > upsample->rows_to_go) 
+    num_rows = upsample->rows_to_go;
+  /* And not more than what the client can accept: */
+  out_rows_avail -= *out_row_ctr;
+  if (num_rows > out_rows_avail)
+    num_rows = out_rows_avail;
+
+  (*cinfo->cconvert->color_convert) (cinfo, upsample->color_buf,
+				     (JDIMENSION) upsample->next_row_out,
+				     output_buf + *out_row_ctr,
+				     (int) num_rows);
+
+  /* Adjust counts */
+  *out_row_ctr += num_rows;
+  upsample->rows_to_go -= num_rows;
+  upsample->next_row_out += num_rows;
+  /* When the buffer is emptied, declare this input row group consumed */
+  if (upsample->next_row_out >= cinfo->max_v_samp_factor)
+    (*in_row_group_ctr)++;
+}
+
+
+/*
+ * These are the routines invoked by sep_upsample to upsample pixel values
+ * of a single component.  One row group is processed per call.
+ */
+
+
+/*
+ * For full-size components, we just make color_buf[ci] point at the
+ * input buffer, and thus avoid copying any data.  Note that this is
+ * safe only because sep_upsample doesn't declare the input row group
+ * "consumed" until we are done color converting and emitting it.
+ */
+
+METHODDEF(void)
+fullsize_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+		   JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)
+{
+  *output_data_ptr = input_data;
+}
+
+
+/*
+ * This is a no-op version used for "uninteresting" components.
+ * These components will not be referenced by color conversion.
+ */
+
+METHODDEF(void)
+noop_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+	       JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)
+{
+  *output_data_ptr = NULL;	/* safety check */
+}
+
+
+/*
+ * This version handles any integral sampling ratios.
+ * This is not used for typical JPEG files, so it need not be fast.
+ * Nor, for that matter, is it particularly accurate: the algorithm is
+ * simple replication of the input pixel onto the corresponding output
+ * pixels.  The hi-falutin sampling literature refers to this as a
+ * "box filter".  A box filter tends to introduce visible artifacts,
+ * so if you are actually going to use 3:1 or 4:1 sampling ratios
+ * you would be well advised to improve this code.
+ */
+
+METHODDEF(void)
+int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+	      JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)
+{
+  my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
+  JSAMPARRAY output_data = *output_data_ptr;
+  register JSAMPROW inptr, outptr;
+  register JSAMPLE invalue;
+  register int h;
+  JSAMPROW outend;
+  int h_expand, v_expand;
+  int inrow, outrow;
+
+  h_expand = upsample->h_expand[compptr->component_index];
+  v_expand = upsample->v_expand[compptr->component_index];
+
+  inrow = outrow = 0;
+  while (outrow < cinfo->max_v_samp_factor) {
+    /* Generate one output row with proper horizontal expansion */
+    inptr = input_data[inrow];
+    outptr = output_data[outrow];
+    outend = outptr + cinfo->output_width;
+    while (outptr < outend) {
+      invalue = *inptr++;	/* don't need GETJSAMPLE() here */
+      for (h = h_expand; h > 0; h--) {
+	*outptr++ = invalue;
+      }
+    }
+    /* Generate any additional output rows by duplicating the first one */
+    if (v_expand > 1) {
+      jcopy_sample_rows(output_data, outrow, output_data, outrow+1,
+			v_expand-1, cinfo->output_width);
+    }
+    inrow++;
+    outrow += v_expand;
+  }
+}
+
+
+/*
+ * Fast processing for the common case of 2:1 horizontal and 1:1 vertical.
+ * It's still a box filter.
+ */
+
+METHODDEF(void)
+h2v1_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+	       JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)
+{
+  JSAMPARRAY output_data = *output_data_ptr;
+  register JSAMPROW inptr, outptr;
+  register JSAMPLE invalue;
+  JSAMPROW outend;
+  int inrow;
+
+  for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) {
+    inptr = input_data[inrow];
+    outptr = output_data[inrow];
+    outend = outptr + cinfo->output_width;
+    while (outptr < outend) {
+      invalue = *inptr++;	/* don't need GETJSAMPLE() here */
+      *outptr++ = invalue;
+      *outptr++ = invalue;
+    }
+  }
+}
+
+
+/*
+ * Fast processing for the common case of 2:1 horizontal and 2:1 vertical.
+ * It's still a box filter.
+ */
+
+METHODDEF(void)
+h2v2_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+	       JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)
+{
+  JSAMPARRAY output_data = *output_data_ptr;
+  register JSAMPROW inptr, outptr;
+  register JSAMPLE invalue;
+  JSAMPROW outend;
+  int inrow, outrow;
+
+  inrow = outrow = 0;
+  while (outrow < cinfo->max_v_samp_factor) {
+    inptr = input_data[inrow];
+    outptr = output_data[outrow];
+    outend = outptr + cinfo->output_width;
+    while (outptr < outend) {
+      invalue = *inptr++;	/* don't need GETJSAMPLE() here */
+      *outptr++ = invalue;
+      *outptr++ = invalue;
+    }
+    jcopy_sample_rows(output_data, outrow, output_data, outrow+1,
+		      1, cinfo->output_width);
+    inrow++;
+    outrow += 2;
+  }
+}
+
+
+/*
+ * Fancy processing for the common case of 2:1 horizontal and 1:1 vertical.
+ *
+ * The upsampling algorithm is linear interpolation between pixel centers,
+ * also known as a "triangle filter".  This is a good compromise between
+ * speed and visual quality.  The centers of the output pixels are 1/4 and 3/4
+ * of the way between input pixel centers.
+ *
+ * A note about the "bias" calculations: when rounding fractional values to
+ * integer, we do not want to always round 0.5 up to the next integer.
+ * If we did that, we'd introduce a noticeable bias towards larger values.
+ * Instead, this code is arranged so that 0.5 will be rounded up or down at
+ * alternate pixel locations (a simple ordered dither pattern).
+ */
+
+METHODDEF(void)
+h2v1_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+		     JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)
+{
+  JSAMPARRAY output_data = *output_data_ptr;
+  register JSAMPROW inptr, outptr;
+  register int invalue;
+  register JDIMENSION colctr;
+  int inrow;
+
+  for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) {
+    inptr = input_data[inrow];
+    outptr = output_data[inrow];
+    /* Special case for first column */
+    invalue = GETJSAMPLE(*inptr++);
+    *outptr++ = (JSAMPLE) invalue;
+    *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(*inptr) + 2) >> 2);
+
+    for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) {
+      /* General case: 3/4 * nearer pixel + 1/4 * further pixel */
+      invalue = GETJSAMPLE(*inptr++) * 3;
+      *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(inptr[-2]) + 1) >> 2);
+      *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(*inptr) + 2) >> 2);
+    }
+
+    /* Special case for last column */
+    invalue = GETJSAMPLE(*inptr);
+    *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(inptr[-1]) + 1) >> 2);
+    *outptr++ = (JSAMPLE) invalue;
+  }
+}
+
+
+/*
+ * Fancy processing for the common case of 2:1 horizontal and 2:1 vertical.
+ * Again a triangle filter; see comments for h2v1 case, above.
+ *
+ * It is OK for us to reference the adjacent input rows because we demanded
+ * context from the main buffer controller (see initialization code).
+ */
+
+METHODDEF(void)
+h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+		     JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)
+{
+  JSAMPARRAY output_data = *output_data_ptr;
+  register JSAMPROW inptr0, inptr1, outptr;
+#if BITS_IN_JSAMPLE == 8
+  register int thiscolsum, lastcolsum, nextcolsum;
+#else
+  register INT32 thiscolsum, lastcolsum, nextcolsum;
+#endif
+  register JDIMENSION colctr;
+  int inrow, outrow, v;
+
+  inrow = outrow = 0;
+  while (outrow < cinfo->max_v_samp_factor) {
+    for (v = 0; v < 2; v++) {
+      /* inptr0 points to nearest input row, inptr1 points to next nearest */
+      inptr0 = input_data[inrow];
+      if (v == 0)		/* next nearest is row above */
+	inptr1 = input_data[inrow-1];
+      else			/* next nearest is row below */
+	inptr1 = input_data[inrow+1];
+      outptr = output_data[outrow++];
+
+      /* Special case for first column */
+      thiscolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++);
+      nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++);
+      *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 8) >> 4);
+      *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4);
+      lastcolsum = thiscolsum; thiscolsum = nextcolsum;
+
+      for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) {
+	/* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */
+	/* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */
+	nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++);
+	*outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4);
+	*outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4);
+	lastcolsum = thiscolsum; thiscolsum = nextcolsum;
+      }
+
+      /* Special case for last column */
+      *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4);
+      *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 7) >> 4);
+    }
+    inrow++;
+  }
+}
+
+
+/*
+ * Module initialization routine for upsampling.
+ */
+
+GLOBAL(void)
+jinit_upsampler (j_decompress_ptr cinfo)
+{
+  my_upsample_ptr upsample;
+  int ci;
+  jpeg_component_info * compptr;
+  boolean need_buffer, do_fancy;
+  int h_in_group, v_in_group, h_out_group, v_out_group;
+
+  upsample = (my_upsample_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_upsampler));
+  cinfo->upsample = (struct jpeg_upsampler *) upsample;
+  upsample->pub.start_pass = start_pass_upsample;
+  upsample->pub.upsample = sep_upsample;
+  upsample->pub.need_context_rows = FALSE; /* until we find out differently */
+
+  if (cinfo->CCIR601_sampling)	/* this isn't supported */
+    ERREXIT(cinfo, JERR_CCIR601_NOTIMPL);
+
+  /* jdmainct.c doesn't support context rows when min_DCT_scaled_size = 1,
+   * so don't ask for it.
+   */
+  do_fancy = cinfo->do_fancy_upsampling && cinfo->min_DCT_scaled_size > 1;
+
+  /* Verify we can handle the sampling factors, select per-component methods,
+   * and create storage as needed.
+   */
+  for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
+       ci++, compptr++) {
+    /* Compute size of an "input group" after IDCT scaling.  This many samples
+     * are to be converted to max_h_samp_factor * max_v_samp_factor pixels.
+     */
+    h_in_group = (compptr->h_samp_factor * compptr->DCT_scaled_size) /
+		 cinfo->min_DCT_scaled_size;
+    v_in_group = (compptr->v_samp_factor * compptr->DCT_scaled_size) /
+		 cinfo->min_DCT_scaled_size;
+    h_out_group = cinfo->max_h_samp_factor;
+    v_out_group = cinfo->max_v_samp_factor;
+    upsample->rowgroup_height[ci] = v_in_group; /* save for use later */
+    need_buffer = TRUE;
+    if (! compptr->component_needed) {
+      /* Don't bother to upsample an uninteresting component. */
+      upsample->methods[ci] = noop_upsample;
+      need_buffer = FALSE;
+    } else if (h_in_group == h_out_group && v_in_group == v_out_group) {
+      /* Fullsize components can be processed without any work. */
+      upsample->methods[ci] = fullsize_upsample;
+      need_buffer = FALSE;
+    } else if (h_in_group * 2 == h_out_group &&
+	       v_in_group == v_out_group) {
+      /* Special cases for 2h1v upsampling */
+      if (do_fancy && compptr->downsampled_width > 2)
+	upsample->methods[ci] = h2v1_fancy_upsample;
+      else
+	upsample->methods[ci] = h2v1_upsample;
+    } else if (h_in_group * 2 == h_out_group &&
+	       v_in_group * 2 == v_out_group) {
+      /* Special cases for 2h2v upsampling */
+      if (do_fancy && compptr->downsampled_width > 2) {
+	upsample->methods[ci] = h2v2_fancy_upsample;
+	upsample->pub.need_context_rows = TRUE;
+      } else
+	upsample->methods[ci] = h2v2_upsample;
+    } else if ((h_out_group % h_in_group) == 0 &&
+	       (v_out_group % v_in_group) == 0) {
+      /* Generic integral-factors upsampling method */
+      upsample->methods[ci] = int_upsample;
+      upsample->h_expand[ci] = (UINT8) (h_out_group / h_in_group);
+      upsample->v_expand[ci] = (UINT8) (v_out_group / v_in_group);
+    } else
+      ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL);
+    if (need_buffer) {
+      upsample->color_buf[ci] = (*cinfo->mem->alloc_sarray)
+	((j_common_ptr) cinfo, JPOOL_IMAGE,
+	 (JDIMENSION) jround_up((long) cinfo->output_width,
+				(long) cinfo->max_h_samp_factor),
+	 (JDIMENSION) cinfo->max_v_samp_factor);
+    }
+  }
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jdtrans.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jdtrans.c
new file mode 100644
index 0000000000..6c0ab715d3
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jdtrans.c
@@ -0,0 +1,143 @@
+/*
+ * jdtrans.c
+ *
+ * Copyright (C) 1995-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.
+ *
+ * This file contains library routines for transcoding decompression,
+ * that is, reading raw DCT coefficient arrays from an input JPEG file.
+ * The routines in jdapimin.c will also be needed by a transcoder.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/* Forward declarations */
+LOCAL(void) transdecode_master_selection JPP((j_decompress_ptr cinfo));
+
+
+/*
+ * Read the coefficient arrays from a JPEG file.
+ * jpeg_read_header must be completed before calling this.
+ *
+ * The entire image is read into a set of virtual coefficient-block arrays,
+ * one per component.  The return value is a pointer to the array of
+ * virtual-array descriptors.  These can be manipulated directly via the
+ * JPEG memory manager, or handed off to jpeg_write_coefficients().
+ * To release the memory occupied by the virtual arrays, call
+ * jpeg_finish_decompress() when done with the data.
+ *
+ * An alternative usage is to simply obtain access to the coefficient arrays
+ * during a buffered-image-mode decompression operation.  This is allowed
+ * after any jpeg_finish_output() call.  The arrays can be accessed until
+ * jpeg_finish_decompress() is called.  (Note that any call to the library
+ * may reposition the arrays, so don't rely on access_virt_barray() results
+ * to stay valid across library calls.)
+ *
+ * Returns NULL if suspended.  This case need be checked only if
+ * a suspending data source is used.
+ */
+
+GLOBAL(jvirt_barray_ptr *)
+jpeg_read_coefficients (j_decompress_ptr cinfo)
+{
+  if (cinfo->global_state == DSTATE_READY) {
+    /* First call: initialize active modules */
+    transdecode_master_selection(cinfo);
+    cinfo->global_state = DSTATE_RDCOEFS;
+  }
+  if (cinfo->global_state == DSTATE_RDCOEFS) {
+    /* Absorb whole file into the coef buffer */
+    for (;;) {
+      int retcode;
+      /* Call progress monitor hook if present */
+      if (cinfo->progress != NULL)
+	(*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
+      /* Absorb some more input */
+      retcode = (*cinfo->inputctl->consume_input) (cinfo);
+      if (retcode == JPEG_SUSPENDED)
+	return NULL;
+      if (retcode == JPEG_REACHED_EOI)
+	break;
+      /* Advance progress counter if appropriate */
+      if (cinfo->progress != NULL &&
+	  (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) {
+	if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) {
+	  /* startup underestimated number of scans; ratchet up one scan */
+	  cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows;
+	}
+      }
+    }
+    /* Set state so that jpeg_finish_decompress does the right thing */
+    cinfo->global_state = DSTATE_STOPPING;
+  }
+  /* At this point we should be in state DSTATE_STOPPING if being used
+   * standalone, or in state DSTATE_BUFIMAGE if being invoked to get access
+   * to the coefficients during a full buffered-image-mode decompression.
+   */
+  if ((cinfo->global_state == DSTATE_STOPPING ||
+       cinfo->global_state == DSTATE_BUFIMAGE) && cinfo->buffered_image) {
+    return cinfo->coef->coef_arrays;
+  }
+  /* Oops, improper usage */
+  ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+  return NULL;			/* keep compiler happy */
+}
+
+
+/*
+ * Master selection of decompression modules for transcoding.
+ * This substitutes for jdmaster.c's initialization of the full decompressor.
+ */
+
+LOCAL(void)
+transdecode_master_selection (j_decompress_ptr cinfo)
+{
+  /* This is effectively a buffered-image operation. */
+  cinfo->buffered_image = TRUE;
+
+  /* Entropy decoding: either Huffman or arithmetic coding. */
+  if (cinfo->arith_code) {
+    ERREXIT(cinfo, JERR_ARITH_NOTIMPL);
+  } else {
+    if (cinfo->progressive_mode) {
+#ifdef D_PROGRESSIVE_SUPPORTED
+      jinit_phuff_decoder(cinfo);
+#else
+      ERREXIT(cinfo, JERR_NOT_COMPILED);
+#endif
+    } else
+      jinit_huff_decoder(cinfo);
+  }
+
+  /* Always get a full-image coefficient buffer. */
+  jinit_d_coef_controller(cinfo, TRUE);
+
+  /* We can now tell the memory manager to allocate virtual arrays. */
+  (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo);
+
+  /* Initialize input side of decompressor to consume first scan. */
+  (*cinfo->inputctl->start_input_pass) (cinfo);
+
+  /* Initialize progress monitoring. */
+  if (cinfo->progress != NULL) {
+    int nscans;
+    /* Estimate number of scans to set pass_limit. */
+    if (cinfo->progressive_mode) {
+      /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */
+      nscans = 2 + 3 * cinfo->num_components;
+    } else if (cinfo->inputctl->has_multiple_scans) {
+      /* For a nonprogressive multiscan file, estimate 1 scan per component. */
+      nscans = cinfo->num_components;
+    } else {
+      nscans = 1;
+    }
+    cinfo->progress->pass_counter = 0L;
+    cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans;
+    cinfo->progress->completed_passes = 0;
+    cinfo->progress->total_passes = 1;
+  }
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jerror.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jerror.c
new file mode 100644
index 0000000000..3da7be86a0
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jerror.c
@@ -0,0 +1,252 @@
+/*
+ * jerror.c
+ *
+ * Copyright (C) 1991-1998, 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 simple error-reporting and trace-message routines.
+ * These are suitable for Unix-like systems and others where writing to
+ * stderr is the right thing to do.  Many applications will want to replace
+ * some or all of these routines.
+ *
+ * If you define USE_WINDOWS_MESSAGEBOX in jconfig.h or in the makefile,
+ * you get a Windows-specific hack to display error messages in a dialog box.
+ * It ain't much, but it beats dropping error messages into the bit bucket,
+ * which is what happens to output to stderr under most Windows C compilers.
+ *
+ * These routines are used by both the compression and decompression code.
+ */
+
+/* this is not a core library module, so it doesn't define JPEG_INTERNALS */
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jversion.h"
+#include "jerror.h"
+
+#ifdef USE_WINDOWS_MESSAGEBOX
+#include <windows.h>
+#endif
+
+#ifndef EXIT_FAILURE		/* define exit() codes if not provided */
+#define EXIT_FAILURE  1
+#endif
+
+
+/*
+ * Create the message string table.
+ * We do this from the master message list in jerror.h by re-reading
+ * jerror.h with a suitable definition for macro JMESSAGE.
+ * The message table is made an external symbol just in case any applications
+ * want to refer to it directly.
+ */
+
+#ifdef NEED_SHORT_EXTERNAL_NAMES
+#define jpeg_std_message_table	jMsgTable
+#endif
+
+#define JMESSAGE(code,string)	string ,
+
+const char * const jpeg_std_message_table[] = {
+#include "jerror.h"
+  NULL
+};
+
+
+/*
+ * Error exit handler: must not return to caller.
+ *
+ * Applications may override this if they want to get control back after
+ * an error.  Typically one would longjmp somewhere instead of exiting.
+ * The setjmp buffer can be made a private field within an expanded error
+ * handler object.  Note that the info needed to generate an error message
+ * is stored in the error object, so you can generate the message now or
+ * later, at your convenience.
+ * You should make sure that the JPEG object is cleaned up (with jpeg_abort
+ * or jpeg_destroy) at some point.
+ */
+
+METHODDEF(void)
+error_exit (j_common_ptr cinfo)
+{
+  /* Always display the message */
+  (*cinfo->err->output_message) (cinfo);
+
+  /* Let the memory manager delete any temp files before we die */
+  jpeg_destroy(cinfo);
+
+  exit(EXIT_FAILURE);
+}
+
+
+/*
+ * Actual output of an error or trace message.
+ * Applications may override this method to send JPEG messages somewhere
+ * other than stderr.
+ *
+ * On Windows, printing to stderr is generally completely useless,
+ * so we provide optional code to produce an error-dialog popup.
+ * Most Windows applications will still prefer to override this routine,
+ * but if they don't, it'll do something at least marginally useful.
+ *
+ * NOTE: to use the library in an environment that doesn't support the
+ * C stdio library, you may have to delete the call to fprintf() entirely,
+ * not just not use this routine.
+ */
+
+METHODDEF(void)
+output_message (j_common_ptr cinfo)
+{
+  char buffer[JMSG_LENGTH_MAX];
+
+  /* Create the message */
+  (*cinfo->err->format_message) (cinfo, buffer);
+
+#ifdef USE_WINDOWS_MESSAGEBOX
+  /* Display it in a message dialog box */
+  MessageBox(GetActiveWindow(), buffer, "JPEG Library Error",
+	     MB_OK | MB_ICONERROR);
+#else
+  /* Send it to stderr, adding a newline */
+  fprintf(stderr, "%s\n", buffer);
+#endif
+}
+
+
+/*
+ * Decide whether to emit a trace or warning message.
+ * msg_level is one of:
+ *   -1: recoverable corrupt-data warning, may want to abort.
+ *    0: important advisory messages (always display to user).
+ *    1: first level of tracing detail.
+ *    2,3,...: successively more detailed tracing messages.
+ * An application might override this method if it wanted to abort on warnings
+ * or change the policy about which messages to display.
+ */
+
+METHODDEF(void)
+emit_message (j_common_ptr cinfo, int msg_level)
+{
+  struct jpeg_error_mgr * err = cinfo->err;
+
+  if (msg_level < 0) {
+    /* It's a warning message.  Since corrupt files may generate many warnings,
+     * the policy implemented here is to show only the first warning,
+     * unless trace_level >= 3.
+     */
+    if (err->num_warnings == 0 || err->trace_level >= 3)
+      (*err->output_message) (cinfo);
+    /* Always count warnings in num_warnings. */
+    err->num_warnings++;
+  } else {
+    /* It's a trace message.  Show it if trace_level >= msg_level. */
+    if (err->trace_level >= msg_level)
+      (*err->output_message) (cinfo);
+  }
+}
+
+
+/*
+ * Format a message string for the most recent JPEG error or message.
+ * The message is stored into buffer, which should be at least JMSG_LENGTH_MAX
+ * characters.  Note that no '\n' character is added to the string.
+ * Few applications should need to override this method.
+ */
+
+METHODDEF(void)
+format_message (j_common_ptr cinfo, char * buffer)
+{
+  struct jpeg_error_mgr * err = cinfo->err;
+  int msg_code = err->msg_code;
+  const char * msgtext = NULL;
+  const char * msgptr;
+  char ch;
+  boolean isstring;
+
+  /* Look up message string in proper table */
+  if (msg_code > 0 && msg_code <= err->last_jpeg_message) {
+    msgtext = err->jpeg_message_table[msg_code];
+  } else if (err->addon_message_table != NULL &&
+	     msg_code >= err->first_addon_message &&
+	     msg_code <= err->last_addon_message) {
+    msgtext = err->addon_message_table[msg_code - err->first_addon_message];
+  }
+
+  /* Defend against bogus message number */
+  if (msgtext == NULL) {
+    err->msg_parm.i[0] = msg_code;
+    msgtext = err->jpeg_message_table[0];
+  }
+
+  /* Check for string parameter, as indicated by %s in the message text */
+  isstring = FALSE;
+  msgptr = msgtext;
+  while ((ch = *msgptr++) != '\0') {
+    if (ch == '%') {
+      if (*msgptr == 's') isstring = TRUE;
+      break;
+    }
+  }
+
+  /* Format the message into the passed buffer */
+  if (isstring)
+    sprintf(buffer, msgtext, err->msg_parm.s);
+  else
+    sprintf(buffer, msgtext,
+	    err->msg_parm.i[0], err->msg_parm.i[1],
+	    err->msg_parm.i[2], err->msg_parm.i[3],
+	    err->msg_parm.i[4], err->msg_parm.i[5],
+	    err->msg_parm.i[6], err->msg_parm.i[7]);
+}
+
+
+/*
+ * Reset error state variables at start of a new image.
+ * This is called during compression startup to reset trace/error
+ * processing to default state, without losing any application-specific
+ * method pointers.  An application might possibly want to override
+ * this method if it has additional error processing state.
+ */
+
+METHODDEF(void)
+reset_error_mgr (j_common_ptr cinfo)
+{
+  cinfo->err->num_warnings = 0;
+  /* trace_level is not reset since it is an application-supplied parameter */
+  cinfo->err->msg_code = 0;	/* may be useful as a flag for "no error" */
+}
+
+
+/*
+ * Fill in the standard error-handling methods in a jpeg_error_mgr object.
+ * Typical call is:
+ *	struct jpeg_compress_struct cinfo;
+ *	struct jpeg_error_mgr err;
+ *
+ *	cinfo.err = jpeg_std_error(&err);
+ * after which the application may override some of the methods.
+ */
+
+GLOBAL(struct jpeg_error_mgr *)
+jpeg_std_error (struct jpeg_error_mgr * err)
+{
+  err->error_exit = error_exit;
+  err->emit_message = emit_message;
+  err->output_message = output_message;
+  err->format_message = format_message;
+  err->reset_error_mgr = reset_error_mgr;
+
+  err->trace_level = 0;		/* default = no tracing */
+  err->num_warnings = 0;	/* no warnings emitted yet */
+  err->msg_code = 0;		/* may be useful as a flag for "no error" */
+
+  /* Initialize message table pointers */
+  err->jpeg_message_table = jpeg_std_message_table;
+  err->last_jpeg_message = (int) JMSG_LASTMSGCODE - 1;
+
+  err->addon_message_table = NULL;
+  err->first_addon_message = 0;	/* for safety */
+  err->last_addon_message = 0;
+
+  return err;
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jerror.h b/Utilities/GDAL/frmts/jpeg/libjpeg/jerror.h
new file mode 100644
index 0000000000..fc2fffeac2
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jerror.h
@@ -0,0 +1,291 @@
+/*
+ * jerror.h
+ *
+ * Copyright (C) 1994-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.
+ *
+ * This file defines the error and message codes for the JPEG library.
+ * Edit this file to add new codes, or to translate the message strings to
+ * some other language.
+ * A set of error-reporting macros are defined too.  Some applications using
+ * the JPEG library may wish to include this file to get the error codes
+ * and/or the macros.
+ */
+
+/*
+ * To define the enum list of message codes, include this file without
+ * defining macro JMESSAGE.  To create a message string table, include it
+ * again with a suitable JMESSAGE definition (see jerror.c for an example).
+ */
+#ifndef JMESSAGE
+#ifndef JERROR_H
+/* First time through, define the enum list */
+#define JMAKE_ENUM_LIST
+#else
+/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */
+#define JMESSAGE(code,string)
+#endif /* JERROR_H */
+#endif /* JMESSAGE */
+
+#ifdef JMAKE_ENUM_LIST
+
+typedef enum {
+
+#define JMESSAGE(code,string)	code ,
+
+#endif /* JMAKE_ENUM_LIST */
+
+JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */
+
+/* For maintenance convenience, list is alphabetical by message code name */
+JMESSAGE(JERR_ARITH_NOTIMPL,
+	 "Sorry, there are legal restrictions on arithmetic coding")
+JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix")
+JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix")
+JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode")
+JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS")
+JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range")
+JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported")
+JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition")
+JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace")
+JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace")
+JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length")
+JMESSAGE(JERR_BAD_LIB_VERSION,
+	 "Wrong JPEG library version: library is %d, caller expects %d")
+JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan")
+JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d")
+JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d")
+JMESSAGE(JERR_BAD_PROGRESSION,
+	 "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d")
+JMESSAGE(JERR_BAD_PROG_SCRIPT,
+	 "Invalid progressive parameters at scan script entry %d")
+JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors")
+JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d")
+JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d")
+JMESSAGE(JERR_BAD_STRUCT_SIZE,
+	 "JPEG parameter struct mismatch: library thinks size is %u, caller expects %u")
+JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access")
+JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small")
+JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here")
+JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet")
+JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d")
+JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request")
+JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d")
+JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x")
+JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d")
+JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d")
+JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)")
+JMESSAGE(JERR_EMS_READ, "Read from EMS failed")
+JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed")
+JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan")
+JMESSAGE(JERR_FILE_READ, "Input file read error")
+JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?")
+JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet")
+JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow")
+JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry")
+JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels")
+JMESSAGE(JERR_INPUT_EMPTY, "Empty input file")
+JMESSAGE(JERR_INPUT_EOF, "Premature end of input file")
+JMESSAGE(JERR_MISMATCHED_QUANT_TABLE,
+	 "Cannot transcode due to multiple use of quantization table %d")
+JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data")
+JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change")
+JMESSAGE(JERR_NOTIMPL, "Not implemented yet")
+JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time")
+JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported")
+JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined")
+JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image")
+JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined")
+JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x")
+JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)")
+JMESSAGE(JERR_QUANT_COMPONENTS,
+	 "Cannot quantize more than %d color components")
+JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors")
+JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors")
+JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers")
+JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker")
+JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x")
+JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers")
+JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF")
+JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s")
+JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file")
+JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file")
+JMESSAGE(JERR_TFILE_WRITE,
+	 "Write failed on temporary file --- out of disk space?")
+JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines")
+JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x")
+JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up")
+JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation")
+JMESSAGE(JERR_XMS_READ, "Read from XMS failed")
+JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed")
+JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT)
+JMESSAGE(JMSG_VERSION, JVERSION)
+JMESSAGE(JTRC_16BIT_TABLES,
+	 "Caution: quantization tables are too coarse for baseline JPEG")
+JMESSAGE(JTRC_ADOBE,
+	 "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d")
+JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u")
+JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u")
+JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x")
+JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x")
+JMESSAGE(JTRC_DQT, "Define Quantization Table %d  precision %d")
+JMESSAGE(JTRC_DRI, "Define Restart Interval %u")
+JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u")
+JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u")
+JMESSAGE(JTRC_EOI, "End Of Image")
+JMESSAGE(JTRC_HUFFBITS, "        %3d %3d %3d %3d %3d %3d %3d %3d")
+JMESSAGE(JTRC_JFIF, "JFIF APP0 marker: version %d.%02d, density %dx%d  %d")
+JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE,
+	 "Warning: thumbnail image size does not match data length %u")
+JMESSAGE(JTRC_JFIF_EXTENSION,
+	 "JFIF extension marker: type 0x%02x, length %u")
+JMESSAGE(JTRC_JFIF_THUMBNAIL, "    with %d x %d thumbnail image")
+JMESSAGE(JTRC_MISC_MARKER, "Miscellaneous marker 0x%02x, length %u")
+JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x")
+JMESSAGE(JTRC_QUANTVALS, "        %4u %4u %4u %4u %4u %4u %4u %4u")
+JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors")
+JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors")
+JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization")
+JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d")
+JMESSAGE(JTRC_RST, "RST%d")
+JMESSAGE(JTRC_SMOOTH_NOTIMPL,
+	 "Smoothing not supported with nonstandard sampling ratios")
+JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d")
+JMESSAGE(JTRC_SOF_COMPONENT, "    Component %d: %dhx%dv q=%d")
+JMESSAGE(JTRC_SOI, "Start of Image")
+JMESSAGE(JTRC_SOS, "Start Of Scan: %d components")
+JMESSAGE(JTRC_SOS_COMPONENT, "    Component %d: dc=%d ac=%d")
+JMESSAGE(JTRC_SOS_PARAMS, "  Ss=%d, Se=%d, Ah=%d, Al=%d")
+JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s")
+JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s")
+JMESSAGE(JTRC_THUMB_JPEG,
+	 "JFIF extension marker: JPEG-compressed thumbnail image, length %u")
+JMESSAGE(JTRC_THUMB_PALETTE,
+	 "JFIF extension marker: palette thumbnail image, length %u")
+JMESSAGE(JTRC_THUMB_RGB,
+	 "JFIF extension marker: RGB thumbnail image, length %u")
+JMESSAGE(JTRC_UNKNOWN_IDS,
+	 "Unrecognized component IDs %d %d %d, assuming YCbCr")
+JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u")
+JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u")
+JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d")
+JMESSAGE(JWRN_BOGUS_PROGRESSION,
+	 "Inconsistent progression sequence for component %d coefficient %d")
+JMESSAGE(JWRN_EXTRANEOUS_DATA,
+	 "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x")
+JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment")
+JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code")
+JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d")
+JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file")
+JMESSAGE(JWRN_MUST_RESYNC,
+	 "Corrupt JPEG data: found marker 0x%02x instead of RST%d")
+JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG")
+JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines")
+
+#ifdef JMAKE_ENUM_LIST
+
+  JMSG_LASTMSGCODE
+} J_MESSAGE_CODE;
+
+#undef JMAKE_ENUM_LIST
+#endif /* JMAKE_ENUM_LIST */
+
+/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */
+#undef JMESSAGE
+
+
+#ifndef JERROR_H
+#define JERROR_H
+
+/* Macros to simplify using the error and trace message stuff */
+/* The first parameter is either type of cinfo pointer */
+
+/* Fatal errors (print message and exit) */
+#define ERREXIT(cinfo,code)  \
+  ((cinfo)->err->msg_code = (code), \
+   (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo)))
+#define ERREXIT1(cinfo,code,p1)  \
+  ((cinfo)->err->msg_code = (code), \
+   (cinfo)->err->msg_parm.i[0] = (p1), \
+   (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo)))
+#define ERREXIT2(cinfo,code,p1,p2)  \
+  ((cinfo)->err->msg_code = (code), \
+   (cinfo)->err->msg_parm.i[0] = (p1), \
+   (cinfo)->err->msg_parm.i[1] = (p2), \
+   (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo)))
+#define ERREXIT3(cinfo,code,p1,p2,p3)  \
+  ((cinfo)->err->msg_code = (code), \
+   (cinfo)->err->msg_parm.i[0] = (p1), \
+   (cinfo)->err->msg_parm.i[1] = (p2), \
+   (cinfo)->err->msg_parm.i[2] = (p3), \
+   (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo)))
+#define ERREXIT4(cinfo,code,p1,p2,p3,p4)  \
+  ((cinfo)->err->msg_code = (code), \
+   (cinfo)->err->msg_parm.i[0] = (p1), \
+   (cinfo)->err->msg_parm.i[1] = (p2), \
+   (cinfo)->err->msg_parm.i[2] = (p3), \
+   (cinfo)->err->msg_parm.i[3] = (p4), \
+   (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo)))
+#define ERREXITS(cinfo,code,str)  \
+  ((cinfo)->err->msg_code = (code), \
+   strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \
+   (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo)))
+
+#define MAKESTMT(stuff)		do { stuff } while (0)
+
+/* Nonfatal errors (we can keep going, but the data is probably corrupt) */
+#define WARNMS(cinfo,code)  \
+  ((cinfo)->err->msg_code = (code), \
+   (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1))
+#define WARNMS1(cinfo,code,p1)  \
+  ((cinfo)->err->msg_code = (code), \
+   (cinfo)->err->msg_parm.i[0] = (p1), \
+   (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1))
+#define WARNMS2(cinfo,code,p1,p2)  \
+  ((cinfo)->err->msg_code = (code), \
+   (cinfo)->err->msg_parm.i[0] = (p1), \
+   (cinfo)->err->msg_parm.i[1] = (p2), \
+   (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1))
+
+/* Informational/debugging messages */
+#define TRACEMS(cinfo,lvl,code)  \
+  ((cinfo)->err->msg_code = (code), \
+   (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)))
+#define TRACEMS1(cinfo,lvl,code,p1)  \
+  ((cinfo)->err->msg_code = (code), \
+   (cinfo)->err->msg_parm.i[0] = (p1), \
+   (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)))
+#define TRACEMS2(cinfo,lvl,code,p1,p2)  \
+  ((cinfo)->err->msg_code = (code), \
+   (cinfo)->err->msg_parm.i[0] = (p1), \
+   (cinfo)->err->msg_parm.i[1] = (p2), \
+   (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)))
+#define TRACEMS3(cinfo,lvl,code,p1,p2,p3)  \
+  MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \
+	   _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \
+	   (cinfo)->err->msg_code = (code); \
+	   (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); )
+#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4)  \
+  MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \
+	   _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \
+	   (cinfo)->err->msg_code = (code); \
+	   (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); )
+#define TRACEMS5(cinfo,lvl,code,p1,p2,p3,p4,p5)  \
+  MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \
+	   _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \
+	   _mp[4] = (p5); \
+	   (cinfo)->err->msg_code = (code); \
+	   (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); )
+#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8)  \
+  MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \
+	   _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \
+	   _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \
+	   (cinfo)->err->msg_code = (code); \
+	   (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); )
+#define TRACEMSS(cinfo,lvl,code,str)  \
+  ((cinfo)->err->msg_code = (code), \
+   strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \
+   (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)))
+
+#endif /* JERROR_H */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jfdctflt.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jfdctflt.c
new file mode 100644
index 0000000000..79d7a00787
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jfdctflt.c
@@ -0,0 +1,168 @@
+/*
+ * jfdctflt.c
+ *
+ * Copyright (C) 1994-1996, 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 a floating-point implementation of the
+ * forward DCT (Discrete Cosine Transform).
+ *
+ * This implementation should be more accurate than either of the integer
+ * DCT implementations.  However, it may not give the same results on all
+ * machines because of differences in roundoff behavior.  Speed will depend
+ * on the hardware's floating point capacity.
+ *
+ * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT
+ * 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.
+ *
+ * This implementation is based on Arai, Agui, and Nakajima's algorithm for
+ * scaled DCT.  Their original paper (Trans. IEICE E-71(11):1095) is in
+ * Japanese, but the algorithm is described in the Pennebaker & Mitchell
+ * JPEG textbook (see REFERENCES section in file README).  The following code
+ * is based directly on figure 4-8 in P&M.
+ * While an 8-point DCT cannot be done in less than 11 multiplies, it is
+ * possible to arrange the computation so that many of the multiplies are
+ * simple scalings of the final outputs.  These multiplies can then be
+ * folded into the multiplications or divisions by the JPEG quantization
+ * table entries.  The AA&N method leaves only 5 multiplies and 29 adds
+ * to be done in the DCT itself.
+ * The primary disadvantage of this method is that with a fixed-point
+ * implementation, accuracy is lost due to imprecise representation of the
+ * scaled quantization values.  However, that problem does not arise if
+ * we use floating point arithmetic.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jdct.h"		/* Private declarations for DCT subsystem */
+
+#ifdef DCT_FLOAT_SUPPORTED
+
+
+/*
+ * This module is specialized to the case DCTSIZE = 8.
+ */
+
+#if DCTSIZE != 8
+  Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */
+#endif
+
+
+/*
+ * Perform the forward DCT on one block of samples.
+ */
+
+GLOBAL(void)
+jpeg_fdct_float (FAST_FLOAT * data)
+{
+  FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
+  FAST_FLOAT tmp10, tmp11, tmp12, tmp13;
+  FAST_FLOAT z1, z2, z3, z4, z5, z11, z13;
+  FAST_FLOAT *dataptr;
+  int ctr;
+
+  /* Pass 1: process rows. */
+
+  dataptr = data;
+  for (ctr = DCTSIZE-1; ctr >= 0; ctr--) {
+    tmp0 = dataptr[0] + dataptr[7];
+    tmp7 = dataptr[0] - dataptr[7];
+    tmp1 = dataptr[1] + dataptr[6];
+    tmp6 = dataptr[1] - dataptr[6];
+    tmp2 = dataptr[2] + dataptr[5];
+    tmp5 = dataptr[2] - dataptr[5];
+    tmp3 = dataptr[3] + dataptr[4];
+    tmp4 = dataptr[3] - dataptr[4];
+    
+    /* Even part */
+    
+    tmp10 = tmp0 + tmp3;	/* phase 2 */
+    tmp13 = tmp0 - tmp3;
+    tmp11 = tmp1 + tmp2;
+    tmp12 = tmp1 - tmp2;
+    
+    dataptr[0] = tmp10 + tmp11; /* phase 3 */
+    dataptr[4] = tmp10 - tmp11;
+    
+    z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */
+    dataptr[2] = tmp13 + z1;	/* phase 5 */
+    dataptr[6] = tmp13 - z1;
+    
+    /* Odd part */
+
+    tmp10 = tmp4 + tmp5;	/* phase 2 */
+    tmp11 = tmp5 + tmp6;
+    tmp12 = tmp6 + tmp7;
+
+    /* The rotator is modified from fig 4-8 to avoid extra negations. */
+    z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */
+    z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */
+    z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */
+    z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */
+
+    z11 = tmp7 + z3;		/* phase 5 */
+    z13 = tmp7 - z3;
+
+    dataptr[5] = z13 + z2;	/* phase 6 */
+    dataptr[3] = z13 - z2;
+    dataptr[1] = z11 + z4;
+    dataptr[7] = z11 - z4;
+
+    dataptr += DCTSIZE;		/* advance pointer to next row */
+  }
+
+  /* Pass 2: process columns. */
+
+  dataptr = data;
+  for (ctr = DCTSIZE-1; ctr >= 0; ctr--) {
+    tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7];
+    tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7];
+    tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6];
+    tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6];
+    tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5];
+    tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5];
+    tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4];
+    tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4];
+    
+    /* Even part */
+    
+    tmp10 = tmp0 + tmp3;	/* phase 2 */
+    tmp13 = tmp0 - tmp3;
+    tmp11 = tmp1 + tmp2;
+    tmp12 = tmp1 - tmp2;
+    
+    dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */
+    dataptr[DCTSIZE*4] = tmp10 - tmp11;
+    
+    z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */
+    dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */
+    dataptr[DCTSIZE*6] = tmp13 - z1;
+    
+    /* Odd part */
+
+    tmp10 = tmp4 + tmp5;	/* phase 2 */
+    tmp11 = tmp5 + tmp6;
+    tmp12 = tmp6 + tmp7;
+
+    /* The rotator is modified from fig 4-8 to avoid extra negations. */
+    z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */
+    z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */
+    z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */
+    z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */
+
+    z11 = tmp7 + z3;		/* phase 5 */
+    z13 = tmp7 - z3;
+
+    dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */
+    dataptr[DCTSIZE*3] = z13 - z2;
+    dataptr[DCTSIZE*1] = z11 + z4;
+    dataptr[DCTSIZE*7] = z11 - z4;
+
+    dataptr++;			/* advance pointer to next column */
+  }
+}
+
+#endif /* DCT_FLOAT_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jfdctfst.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jfdctfst.c
new file mode 100644
index 0000000000..ccb378a3b4
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jfdctfst.c
@@ -0,0 +1,224 @@
+/*
+ * jfdctfst.c
+ *
+ * Copyright (C) 1994-1996, 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 a fast, not so accurate integer implementation of the
+ * forward DCT (Discrete Cosine Transform).
+ *
+ * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT
+ * 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.
+ *
+ * This implementation is based on Arai, Agui, and Nakajima's algorithm for
+ * scaled DCT.  Their original paper (Trans. IEICE E-71(11):1095) is in
+ * Japanese, but the algorithm is described in the Pennebaker & Mitchell
+ * JPEG textbook (see REFERENCES section in file README).  The following code
+ * is based directly on figure 4-8 in P&M.
+ * While an 8-point DCT cannot be done in less than 11 multiplies, it is
+ * possible to arrange the computation so that many of the multiplies are
+ * simple scalings of the final outputs.  These multiplies can then be
+ * folded into the multiplications or divisions by the JPEG quantization
+ * table entries.  The AA&N method leaves only 5 multiplies and 29 adds
+ * to be done in the DCT itself.
+ * The primary disadvantage of this method is that with fixed-point math,
+ * accuracy is lost due to imprecise representation of the scaled
+ * quantization values.  The smaller the quantization table entry, the less
+ * precise the scaled value, so this implementation does worse with high-
+ * quality-setting files than with low-quality ones.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jdct.h"		/* Private declarations for DCT subsystem */
+
+#ifdef DCT_IFAST_SUPPORTED
+
+
+/*
+ * This module is specialized to the case DCTSIZE = 8.
+ */
+
+#if DCTSIZE != 8
+  Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */
+#endif
+
+
+/* Scaling decisions are generally the same as in the LL&M algorithm;
+ * see jfdctint.c for more details.  However, we choose to descale
+ * (right shift) multiplication products as soon as they are formed,
+ * rather than carrying additional fractional bits into subsequent additions.
+ * This compromises accuracy slightly, but it lets us save a few shifts.
+ * More importantly, 16-bit arithmetic is then adequate (for 8-bit samples)
+ * everywhere except in the multiplications proper; this saves a good deal
+ * of work on 16-bit-int machines.
+ *
+ * Again to save a few shifts, the intermediate results between pass 1 and
+ * pass 2 are not upscaled, but are represented only to integral precision.
+ *
+ * A final compromise is to represent the multiplicative constants to only
+ * 8 fractional bits, rather than 13.  This saves some shifting work on some
+ * machines, and may also reduce the cost of multiplication (since there
+ * are fewer one-bits in the constants).
+ */
+
+#define CONST_BITS  8
+
+
+/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus
+ * causing a lot of useless floating-point operations at run time.
+ * To get around this we use the following pre-calculated constants.
+ * If you change CONST_BITS you may want to add appropriate values.
+ * (With a reasonable C compiler, you can just rely on the FIX() macro...)
+ */
+
+#if CONST_BITS == 8
+#define FIX_0_382683433  ((INT32)   98)		/* FIX(0.382683433) */
+#define FIX_0_541196100  ((INT32)  139)		/* FIX(0.541196100) */
+#define FIX_0_707106781  ((INT32)  181)		/* FIX(0.707106781) */
+#define FIX_1_306562965  ((INT32)  334)		/* FIX(1.306562965) */
+#else
+#define FIX_0_382683433  FIX(0.382683433)
+#define FIX_0_541196100  FIX(0.541196100)
+#define FIX_0_707106781  FIX(0.707106781)
+#define FIX_1_306562965  FIX(1.306562965)
+#endif
+
+
+/* We can gain a little more speed, with a further compromise in accuracy,
+ * by omitting the addition in a descaling shift.  This yields an incorrectly
+ * rounded result half the time...
+ */
+
+#ifndef USE_ACCURATE_ROUNDING
+#undef DESCALE
+#define DESCALE(x,n)  RIGHT_SHIFT(x, n)
+#endif
+
+
+/* Multiply a DCTELEM variable by an INT32 constant, and immediately
+ * descale to yield a DCTELEM result.
+ */
+
+#define MULTIPLY(var,const)  ((DCTELEM) DESCALE((var) * (const), CONST_BITS))
+
+
+/*
+ * Perform the forward DCT on one block of samples.
+ */
+
+GLOBAL(void)
+jpeg_fdct_ifast (DCTELEM * data)
+{
+  DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
+  DCTELEM tmp10, tmp11, tmp12, tmp13;
+  DCTELEM z1, z2, z3, z4, z5, z11, z13;
+  DCTELEM *dataptr;
+  int ctr;
+  SHIFT_TEMPS
+
+  /* Pass 1: process rows. */
+
+  dataptr = data;
+  for (ctr = DCTSIZE-1; ctr >= 0; ctr--) {
+    tmp0 = dataptr[0] + dataptr[7];
+    tmp7 = dataptr[0] - dataptr[7];
+    tmp1 = dataptr[1] + dataptr[6];
+    tmp6 = dataptr[1] - dataptr[6];
+    tmp2 = dataptr[2] + dataptr[5];
+    tmp5 = dataptr[2] - dataptr[5];
+    tmp3 = dataptr[3] + dataptr[4];
+    tmp4 = dataptr[3] - dataptr[4];
+    
+    /* Even part */
+    
+    tmp10 = tmp0 + tmp3;	/* phase 2 */
+    tmp13 = tmp0 - tmp3;
+    tmp11 = tmp1 + tmp2;
+    tmp12 = tmp1 - tmp2;
+    
+    dataptr[0] = tmp10 + tmp11; /* phase 3 */
+    dataptr[4] = tmp10 - tmp11;
+    
+    z1 = MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */
+    dataptr[2] = tmp13 + z1;	/* phase 5 */
+    dataptr[6] = tmp13 - z1;
+    
+    /* Odd part */
+
+    tmp10 = tmp4 + tmp5;	/* phase 2 */
+    tmp11 = tmp5 + tmp6;
+    tmp12 = tmp6 + tmp7;
+
+    /* The rotator is modified from fig 4-8 to avoid extra negations. */
+    z5 = MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */
+    z2 = MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */
+    z4 = MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */
+    z3 = MULTIPLY(tmp11, FIX_0_707106781); /* c4 */
+
+    z11 = tmp7 + z3;		/* phase 5 */
+    z13 = tmp7 - z3;
+
+    dataptr[5] = z13 + z2;	/* phase 6 */
+    dataptr[3] = z13 - z2;
+    dataptr[1] = z11 + z4;
+    dataptr[7] = z11 - z4;
+
+    dataptr += DCTSIZE;		/* advance pointer to next row */
+  }
+
+  /* Pass 2: process columns. */
+
+  dataptr = data;
+  for (ctr = DCTSIZE-1; ctr >= 0; ctr--) {
+    tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7];
+    tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7];
+    tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6];
+    tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6];
+    tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5];
+    tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5];
+    tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4];
+    tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4];
+    
+    /* Even part */
+    
+    tmp10 = tmp0 + tmp3;	/* phase 2 */
+    tmp13 = tmp0 - tmp3;
+    tmp11 = tmp1 + tmp2;
+    tmp12 = tmp1 - tmp2;
+    
+    dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */
+    dataptr[DCTSIZE*4] = tmp10 - tmp11;
+    
+    z1 = MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */
+    dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */
+    dataptr[DCTSIZE*6] = tmp13 - z1;
+    
+    /* Odd part */
+
+    tmp10 = tmp4 + tmp5;	/* phase 2 */
+    tmp11 = tmp5 + tmp6;
+    tmp12 = tmp6 + tmp7;
+
+    /* The rotator is modified from fig 4-8 to avoid extra negations. */
+    z5 = MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */
+    z2 = MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */
+    z4 = MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */
+    z3 = MULTIPLY(tmp11, FIX_0_707106781); /* c4 */
+
+    z11 = tmp7 + z3;		/* phase 5 */
+    z13 = tmp7 - z3;
+
+    dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */
+    dataptr[DCTSIZE*3] = z13 - z2;
+    dataptr[DCTSIZE*1] = z11 + z4;
+    dataptr[DCTSIZE*7] = z11 - z4;
+
+    dataptr++;			/* advance pointer to next column */
+  }
+}
+
+#endif /* DCT_IFAST_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jfdctint.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jfdctint.c
new file mode 100644
index 0000000000..0a78b64aee
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jfdctint.c
@@ -0,0 +1,283 @@
+/*
+ * jfdctint.c
+ *
+ * Copyright (C) 1991-1996, 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 a slow-but-accurate integer implementation of the
+ * forward DCT (Discrete Cosine Transform).
+ *
+ * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT
+ * 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.
+ *
+ * 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.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jdct.h"		/* Private declarations for DCT subsystem */
+
+#ifdef DCT_ISLOW_SUPPORTED
+
+
+/*
+ * This module is specialized to the case DCTSIZE = 8.
+ */
+
+#if DCTSIZE != 8
+  Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */
+#endif
+
+
+/*
+ * The poop on this scaling stuff is as follows:
+ *
+ * Each 1-D DCT step produces outputs which are a factor of sqrt(N)
+ * larger than the true DCT 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 DCT,
+ * because the y0 and y4 outputs need not be divided by sqrt(N).
+ * In the IJG code, this factor of 8 is removed by the quantization step
+ * (in jcdctmgr.c), NOT in this module.
+ *
+ * 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.  (For 12-bit sample data, the intermediate
+ * array is INT32 anyway.)
+ *
+ * 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.
+ */
+
+#if BITS_IN_JSAMPLE == 8
+#define CONST_BITS  13
+#define PASS1_BITS  2
+#else
+#define CONST_BITS  13
+#define PASS1_BITS  1		/* lose a little precision to avoid overflow */
+#endif
+
+/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus
+ * causing a lot of useless floating-point operations at run time.
+ * To get around this we use the following pre-calculated constants.
+ * If you change CONST_BITS you may want to add appropriate values.
+ * (With a reasonable C compiler, you can just rely on the FIX() macro...)
+ */
+
+#if CONST_BITS == 13
+#define FIX_0_298631336  ((INT32)  2446)	/* FIX(0.298631336) */
+#define FIX_0_390180644  ((INT32)  3196)	/* FIX(0.390180644) */
+#define FIX_0_541196100  ((INT32)  4433)	/* FIX(0.541196100) */
+#define FIX_0_765366865  ((INT32)  6270)	/* FIX(0.765366865) */
+#define FIX_0_899976223  ((INT32)  7373)	/* FIX(0.899976223) */
+#define FIX_1_175875602  ((INT32)  9633)	/* FIX(1.175875602) */
+#define FIX_1_501321110  ((INT32)  12299)	/* FIX(1.501321110) */
+#define FIX_1_847759065  ((INT32)  15137)	/* FIX(1.847759065) */
+#define FIX_1_961570560  ((INT32)  16069)	/* FIX(1.961570560) */
+#define FIX_2_053119869  ((INT32)  16819)	/* FIX(2.053119869) */
+#define FIX_2_562915447  ((INT32)  20995)	/* FIX(2.562915447) */
+#define FIX_3_072711026  ((INT32)  25172)	/* FIX(3.072711026) */
+#else
+#define FIX_0_298631336  FIX(0.298631336)
+#define FIX_0_390180644  FIX(0.390180644)
+#define FIX_0_541196100  FIX(0.541196100)
+#define FIX_0_765366865  FIX(0.765366865)
+#define FIX_0_899976223  FIX(0.899976223)
+#define FIX_1_175875602  FIX(1.175875602)
+#define FIX_1_501321110  FIX(1.501321110)
+#define FIX_1_847759065  FIX(1.847759065)
+#define FIX_1_961570560  FIX(1.961570560)
+#define FIX_2_053119869  FIX(2.053119869)
+#define FIX_2_562915447  FIX(2.562915447)
+#define FIX_3_072711026  FIX(3.072711026)
+#endif
+
+
+/* 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.
+ * For 12-bit samples, a full 32-bit multiplication will be needed.
+ */
+
+#if BITS_IN_JSAMPLE == 8
+#define MULTIPLY(var,const)  MULTIPLY16C16(var,const)
+#else
+#define MULTIPLY(var,const)  ((var) * (const))
+#endif
+
+
+/*
+ * Perform the forward DCT on one block of samples.
+ */
+
+GLOBAL(void)
+jpeg_fdct_islow (DCTELEM * data)
+{
+  INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
+  INT32 tmp10, tmp11, tmp12, tmp13;
+  INT32 z1, z2, z3, z4, z5;
+  DCTELEM *dataptr;
+  int ctr;
+  SHIFT_TEMPS
+
+  /* Pass 1: process rows. */
+  /* Note results are scaled up by sqrt(8) compared to a true DCT; */
+  /* furthermore, we scale the results by 2**PASS1_BITS. */
+
+  dataptr = data;
+  for (ctr = DCTSIZE-1; ctr >= 0; ctr--) {
+    tmp0 = dataptr[0] + dataptr[7];
+    tmp7 = dataptr[0] - dataptr[7];
+    tmp1 = dataptr[1] + dataptr[6];
+    tmp6 = dataptr[1] - dataptr[6];
+    tmp2 = dataptr[2] + dataptr[5];
+    tmp5 = dataptr[2] - dataptr[5];
+    tmp3 = dataptr[3] + dataptr[4];
+    tmp4 = dataptr[3] - dataptr[4];
+    
+    /* Even part per LL&M figure 1 --- note that published figure is faulty;
+     * rotator "sqrt(2)*c1" should be "sqrt(2)*c6".
+     */
+    
+    tmp10 = tmp0 + tmp3;
+    tmp13 = tmp0 - tmp3;
+    tmp11 = tmp1 + tmp2;
+    tmp12 = tmp1 - tmp2;
+    
+    dataptr[0] = (DCTELEM) ((tmp10 + tmp11) << PASS1_BITS);
+    dataptr[4] = (DCTELEM) ((tmp10 - tmp11) << PASS1_BITS);
+    
+    z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100);
+    dataptr[2] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp13, FIX_0_765366865),
+				   CONST_BITS-PASS1_BITS);
+    dataptr[6] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp12, - FIX_1_847759065),
+				   CONST_BITS-PASS1_BITS);
+    
+    /* Odd part per figure 8 --- note paper omits factor of sqrt(2).
+     * cK represents cos(K*pi/16).
+     * i0..i3 in the paper are tmp4..tmp7 here.
+     */
+    
+    z1 = tmp4 + tmp7;
+    z2 = tmp5 + tmp6;
+    z3 = tmp4 + tmp6;
+    z4 = tmp5 + tmp7;
+    z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */
+    
+    tmp4 = MULTIPLY(tmp4, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */
+    tmp5 = MULTIPLY(tmp5, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */
+    tmp6 = MULTIPLY(tmp6, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */
+    tmp7 = MULTIPLY(tmp7, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */
+    z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */
+    z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */
+    z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */
+    z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */
+    
+    z3 += z5;
+    z4 += z5;
+    
+    dataptr[7] = (DCTELEM) DESCALE(tmp4 + z1 + z3, CONST_BITS-PASS1_BITS);
+    dataptr[5] = (DCTELEM) DESCALE(tmp5 + z2 + z4, CONST_BITS-PASS1_BITS);
+    dataptr[3] = (DCTELEM) DESCALE(tmp6 + z2 + z3, CONST_BITS-PASS1_BITS);
+    dataptr[1] = (DCTELEM) DESCALE(tmp7 + z1 + z4, CONST_BITS-PASS1_BITS);
+    
+    dataptr += DCTSIZE;		/* advance pointer to next row */
+  }
+
+  /* Pass 2: process columns.
+   * We remove the PASS1_BITS scaling, but leave the results scaled up
+   * by an overall factor of 8.
+   */
+
+  dataptr = data;
+  for (ctr = DCTSIZE-1; ctr >= 0; ctr--) {
+    tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7];
+    tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7];
+    tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6];
+    tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6];
+    tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5];
+    tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5];
+    tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4];
+    tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4];
+    
+    /* Even part per LL&M figure 1 --- note that published figure is faulty;
+     * rotator "sqrt(2)*c1" should be "sqrt(2)*c6".
+     */
+    
+    tmp10 = tmp0 + tmp3;
+    tmp13 = tmp0 - tmp3;
+    tmp11 = tmp1 + tmp2;
+    tmp12 = tmp1 - tmp2;
+    
+    dataptr[DCTSIZE*0] = (DCTELEM) DESCALE(tmp10 + tmp11, PASS1_BITS);
+    dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(tmp10 - tmp11, PASS1_BITS);
+    
+    z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100);
+    dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp13, FIX_0_765366865),
+					   CONST_BITS+PASS1_BITS);
+    dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp12, - FIX_1_847759065),
+					   CONST_BITS+PASS1_BITS);
+    
+    /* Odd part per figure 8 --- note paper omits factor of sqrt(2).
+     * cK represents cos(K*pi/16).
+     * i0..i3 in the paper are tmp4..tmp7 here.
+     */
+    
+    z1 = tmp4 + tmp7;
+    z2 = tmp5 + tmp6;
+    z3 = tmp4 + tmp6;
+    z4 = tmp5 + tmp7;
+    z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */
+    
+    tmp4 = MULTIPLY(tmp4, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */
+    tmp5 = MULTIPLY(tmp5, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */
+    tmp6 = MULTIPLY(tmp6, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */
+    tmp7 = MULTIPLY(tmp7, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */
+    z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */
+    z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */
+    z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */
+    z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */
+    
+    z3 += z5;
+    z4 += z5;
+    
+    dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp4 + z1 + z3,
+					   CONST_BITS+PASS1_BITS);
+    dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp5 + z2 + z4,
+					   CONST_BITS+PASS1_BITS);
+    dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp6 + z2 + z3,
+					   CONST_BITS+PASS1_BITS);
+    dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp7 + z1 + z4,
+					   CONST_BITS+PASS1_BITS);
+    
+    dataptr++;			/* advance pointer to next column */
+  }
+}
+
+#endif /* DCT_ISLOW_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jidctflt.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jidctflt.c
new file mode 100644
index 0000000000..0188ce3dfc
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jidctflt.c
@@ -0,0 +1,242 @@
+/*
+ * jidctflt.c
+ *
+ * Copyright (C) 1994-1998, 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 a floating-point implementation of the
+ * inverse DCT (Discrete Cosine Transform).  In the IJG code, this routine
+ * must also perform dequantization of the input coefficients.
+ *
+ * This implementation should be more accurate than either of the integer
+ * IDCT implementations.  However, it may not give the same results on all
+ * machines because of differences in roundoff behavior.  Speed will depend
+ * on the hardware's floating point capacity.
+ *
+ * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT
+ * on each row (or vice versa, but it's more convenient to emit a row at
+ * a time).  Direct algorithms are also available, but they are much more
+ * complex and seem not to be any faster when reduced to code.
+ *
+ * This implementation is based on Arai, Agui, and Nakajima's algorithm for
+ * scaled DCT.  Their original paper (Trans. IEICE E-71(11):1095) is in
+ * Japanese, but the algorithm is described in the Pennebaker & Mitchell
+ * JPEG textbook (see REFERENCES section in file README).  The following code
+ * is based directly on figure 4-8 in P&M.
+ * While an 8-point DCT cannot be done in less than 11 multiplies, it is
+ * possible to arrange the computation so that many of the multiplies are
+ * simple scalings of the final outputs.  These multiplies can then be
+ * folded into the multiplications or divisions by the JPEG quantization
+ * table entries.  The AA&N method leaves only 5 multiplies and 29 adds
+ * to be done in the DCT itself.
+ * The primary disadvantage of this method is that with a fixed-point
+ * implementation, accuracy is lost due to imprecise representation of the
+ * scaled quantization values.  However, that problem does not arise if
+ * we use floating point arithmetic.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jdct.h"		/* Private declarations for DCT subsystem */
+
+#ifdef DCT_FLOAT_SUPPORTED
+
+
+/*
+ * This module is specialized to the case DCTSIZE = 8.
+ */
+
+#if DCTSIZE != 8
+  Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */
+#endif
+
+
+/* Dequantize a coefficient by multiplying it by the multiplier-table
+ * entry; produce a float result.
+ */
+
+#define DEQUANTIZE(coef,quantval)  (((FAST_FLOAT) (coef)) * (quantval))
+
+
+/*
+ * Perform dequantization and inverse DCT on one block of coefficients.
+ */
+
+GLOBAL(void)
+jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+		 JCOEFPTR coef_block,
+		 JSAMPARRAY output_buf, JDIMENSION output_col)
+{
+  FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
+  FAST_FLOAT tmp10, tmp11, tmp12, tmp13;
+  FAST_FLOAT z5, z10, z11, z12, z13;
+  JCOEFPTR inptr;
+  FLOAT_MULT_TYPE * quantptr;
+  FAST_FLOAT * wsptr;
+  JSAMPROW outptr;
+  JSAMPLE *range_limit = IDCT_range_limit(cinfo);
+  int ctr;
+  FAST_FLOAT workspace[DCTSIZE2]; /* buffers data between passes */
+  SHIFT_TEMPS
+
+  /* Pass 1: process columns from input, store into work array. */
+
+  inptr = coef_block;
+  quantptr = (FLOAT_MULT_TYPE *) compptr->dct_table;
+  wsptr = workspace;
+  for (ctr = DCTSIZE; ctr > 0; ctr--) {
+    /* 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 column 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
+     * column DCT calculations can be simplified this way.
+     */
+    
+    if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 &&
+	inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 &&
+	inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 &&
+	inptr[DCTSIZE*7] == 0) {
+      /* AC terms all zero */
+      FAST_FLOAT dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]);
+      
+      wsptr[DCTSIZE*0] = dcval;
+      wsptr[DCTSIZE*1] = dcval;
+      wsptr[DCTSIZE*2] = dcval;
+      wsptr[DCTSIZE*3] = dcval;
+      wsptr[DCTSIZE*4] = dcval;
+      wsptr[DCTSIZE*5] = dcval;
+      wsptr[DCTSIZE*6] = dcval;
+      wsptr[DCTSIZE*7] = dcval;
+      
+      inptr++;			/* advance pointers to next column */
+      quantptr++;
+      wsptr++;
+      continue;
+    }
+    
+    /* Even part */
+
+    tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]);
+    tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]);
+    tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]);
+    tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]);
+
+    tmp10 = tmp0 + tmp2;	/* phase 3 */
+    tmp11 = tmp0 - tmp2;
+
+    tmp13 = tmp1 + tmp3;	/* phases 5-3 */
+    tmp12 = (tmp1 - tmp3) * ((FAST_FLOAT) 1.414213562) - tmp13; /* 2*c4 */
+
+    tmp0 = tmp10 + tmp13;	/* phase 2 */
+    tmp3 = tmp10 - tmp13;
+    tmp1 = tmp11 + tmp12;
+    tmp2 = tmp11 - tmp12;
+    
+    /* Odd part */
+
+    tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]);
+    tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]);
+    tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]);
+    tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]);
+
+    z13 = tmp6 + tmp5;		/* phase 6 */
+    z10 = tmp6 - tmp5;
+    z11 = tmp4 + tmp7;
+    z12 = tmp4 - tmp7;
+
+    tmp7 = z11 + z13;		/* phase 5 */
+    tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); /* 2*c4 */
+
+    z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */
+    tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */
+    tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */
+
+    tmp6 = tmp12 - tmp7;	/* phase 2 */
+    tmp5 = tmp11 - tmp6;
+    tmp4 = tmp10 + tmp5;
+
+    wsptr[DCTSIZE*0] = tmp0 + tmp7;
+    wsptr[DCTSIZE*7] = tmp0 - tmp7;
+    wsptr[DCTSIZE*1] = tmp1 + tmp6;
+    wsptr[DCTSIZE*6] = tmp1 - tmp6;
+    wsptr[DCTSIZE*2] = tmp2 + tmp5;
+    wsptr[DCTSIZE*5] = tmp2 - tmp5;
+    wsptr[DCTSIZE*4] = tmp3 + tmp4;
+    wsptr[DCTSIZE*3] = tmp3 - tmp4;
+
+    inptr++;			/* advance pointers to next column */
+    quantptr++;
+    wsptr++;
+  }
+  
+  /* Pass 2: process rows from work array, store into output array. */
+  /* Note that we must descale the results by a factor of 8 == 2**3. */
+
+  wsptr = workspace;
+  for (ctr = 0; ctr < DCTSIZE; ctr++) {
+    outptr = output_buf[ctr] + output_col;
+    /* Rows of zeroes can be exploited in the same way as we did with columns.
+     * However, the column calculation has created many nonzero AC terms, so
+     * the simplification applies less often (typically 5% to 10% of the time).
+     * And testing floats for zero is relatively expensive, so we don't bother.
+     */
+    
+    /* Even part */
+
+    tmp10 = wsptr[0] + wsptr[4];
+    tmp11 = wsptr[0] - wsptr[4];
+
+    tmp13 = wsptr[2] + wsptr[6];
+    tmp12 = (wsptr[2] - wsptr[6]) * ((FAST_FLOAT) 1.414213562) - tmp13;
+
+    tmp0 = tmp10 + tmp13;
+    tmp3 = tmp10 - tmp13;
+    tmp1 = tmp11 + tmp12;
+    tmp2 = tmp11 - tmp12;
+
+    /* Odd part */
+
+    z13 = wsptr[5] + wsptr[3];
+    z10 = wsptr[5] - wsptr[3];
+    z11 = wsptr[1] + wsptr[7];
+    z12 = wsptr[1] - wsptr[7];
+
+    tmp7 = z11 + z13;
+    tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562);
+
+    z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */
+    tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */
+    tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */
+
+    tmp6 = tmp12 - tmp7;
+    tmp5 = tmp11 - tmp6;
+    tmp4 = tmp10 + tmp5;
+
+    /* Final output stage: scale down by a factor of 8 and range-limit */
+
+    outptr[0] = range_limit[(int) DESCALE((INT32) (tmp0 + tmp7), 3)
+			    & RANGE_MASK];
+    outptr[7] = range_limit[(int) DESCALE((INT32) (tmp0 - tmp7), 3)
+			    & RANGE_MASK];
+    outptr[1] = range_limit[(int) DESCALE((INT32) (tmp1 + tmp6), 3)
+			    & RANGE_MASK];
+    outptr[6] = range_limit[(int) DESCALE((INT32) (tmp1 - tmp6), 3)
+			    & RANGE_MASK];
+    outptr[2] = range_limit[(int) DESCALE((INT32) (tmp2 + tmp5), 3)
+			    & RANGE_MASK];
+    outptr[5] = range_limit[(int) DESCALE((INT32) (tmp2 - tmp5), 3)
+			    & RANGE_MASK];
+    outptr[4] = range_limit[(int) DESCALE((INT32) (tmp3 + tmp4), 3)
+			    & RANGE_MASK];
+    outptr[3] = range_limit[(int) DESCALE((INT32) (tmp3 - tmp4), 3)
+			    & RANGE_MASK];
+    
+    wsptr += DCTSIZE;		/* advance pointer to next row */
+  }
+}
+
+#endif /* DCT_FLOAT_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jidctfst.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jidctfst.c
new file mode 100644
index 0000000000..dba4216fb9
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jidctfst.c
@@ -0,0 +1,368 @@
+/*
+ * jidctfst.c
+ *
+ * Copyright (C) 1994-1998, 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 a fast, not so accurate integer implementation of the
+ * inverse DCT (Discrete Cosine Transform).  In the IJG code, this routine
+ * must also perform dequantization of the input coefficients.
+ *
+ * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT
+ * on each row (or vice versa, but it's more convenient to emit a row at
+ * a time).  Direct algorithms are also available, but they are much more
+ * complex and seem not to be any faster when reduced to code.
+ *
+ * This implementation is based on Arai, Agui, and Nakajima's algorithm for
+ * scaled DCT.  Their original paper (Trans. IEICE E-71(11):1095) is in
+ * Japanese, but the algorithm is described in the Pennebaker & Mitchell
+ * JPEG textbook (see REFERENCES section in file README).  The following code
+ * is based directly on figure 4-8 in P&M.
+ * While an 8-point DCT cannot be done in less than 11 multiplies, it is
+ * possible to arrange the computation so that many of the multiplies are
+ * simple scalings of the final outputs.  These multiplies can then be
+ * folded into the multiplications or divisions by the JPEG quantization
+ * table entries.  The AA&N method leaves only 5 multiplies and 29 adds
+ * to be done in the DCT itself.
+ * The primary disadvantage of this method is that with fixed-point math,
+ * accuracy is lost due to imprecise representation of the scaled
+ * quantization values.  The smaller the quantization table entry, the less
+ * precise the scaled value, so this implementation does worse with high-
+ * quality-setting files than with low-quality ones.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jdct.h"		/* Private declarations for DCT subsystem */
+
+#ifdef DCT_IFAST_SUPPORTED
+
+
+/*
+ * This module is specialized to the case DCTSIZE = 8.
+ */
+
+#if DCTSIZE != 8
+  Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */
+#endif
+
+
+/* Scaling decisions are generally the same as in the LL&M algorithm;
+ * see jidctint.c for more details.  However, we choose to descale
+ * (right shift) multiplication products as soon as they are formed,
+ * rather than carrying additional fractional bits into subsequent additions.
+ * This compromises accuracy slightly, but it lets us save a few shifts.
+ * More importantly, 16-bit arithmetic is then adequate (for 8-bit samples)
+ * everywhere except in the multiplications proper; this saves a good deal
+ * of work on 16-bit-int machines.
+ *
+ * The dequantized coefficients are not integers because the AA&N scaling
+ * factors have been incorporated.  We represent them scaled up by PASS1_BITS,
+ * so that the first and second IDCT rounds have the same input scaling.
+ * For 8-bit JSAMPLEs, we choose IFAST_SCALE_BITS = PASS1_BITS so as to
+ * avoid a descaling shift; this compromises accuracy rather drastically
+ * for small quantization table entries, but it saves a lot of shifts.
+ * For 12-bit JSAMPLEs, there's no hope of using 16x16 multiplies anyway,
+ * so we use a much larger scaling factor to preserve accuracy.
+ *
+ * A final compromise is to represent the multiplicative constants to only
+ * 8 fractional bits, rather than 13.  This saves some shifting work on some
+ * machines, and may also reduce the cost of multiplication (since there
+ * are fewer one-bits in the constants).
+ */
+
+#if BITS_IN_JSAMPLE == 8
+#define CONST_BITS  8
+#define PASS1_BITS  2
+#else
+#define CONST_BITS  8
+#define PASS1_BITS  1		/* lose a little precision to avoid overflow */
+#endif
+
+/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus
+ * causing a lot of useless floating-point operations at run time.
+ * To get around this we use the following pre-calculated constants.
+ * If you change CONST_BITS you may want to add appropriate values.
+ * (With a reasonable C compiler, you can just rely on the FIX() macro...)
+ */
+
+#if CONST_BITS == 8
+#define FIX_1_082392200  ((INT32)  277)		/* FIX(1.082392200) */
+#define FIX_1_414213562  ((INT32)  362)		/* FIX(1.414213562) */
+#define FIX_1_847759065  ((INT32)  473)		/* FIX(1.847759065) */
+#define FIX_2_613125930  ((INT32)  669)		/* FIX(2.613125930) */
+#else
+#define FIX_1_082392200  FIX(1.082392200)
+#define FIX_1_414213562  FIX(1.414213562)
+#define FIX_1_847759065  FIX(1.847759065)
+#define FIX_2_613125930  FIX(2.613125930)
+#endif
+
+
+/* We can gain a little more speed, with a further compromise in accuracy,
+ * by omitting the addition in a descaling shift.  This yields an incorrectly
+ * rounded result half the time...
+ */
+
+#ifndef USE_ACCURATE_ROUNDING
+#undef DESCALE
+#define DESCALE(x,n)  RIGHT_SHIFT(x, n)
+#endif
+
+
+/* Multiply a DCTELEM variable by an INT32 constant, and immediately
+ * descale to yield a DCTELEM result.
+ */
+
+#define MULTIPLY(var,const)  ((DCTELEM) DESCALE((var) * (const), CONST_BITS))
+
+
+/* Dequantize a coefficient by multiplying it by the multiplier-table
+ * entry; produce a DCTELEM result.  For 8-bit data a 16x16->16
+ * multiplication will do.  For 12-bit data, the multiplier table is
+ * declared INT32, so a 32-bit multiply will be used.
+ */
+
+#if BITS_IN_JSAMPLE == 8
+#define DEQUANTIZE(coef,quantval)  (((IFAST_MULT_TYPE) (coef)) * (quantval))
+#else
+#define DEQUANTIZE(coef,quantval)  \
+	DESCALE((coef)*(quantval), IFAST_SCALE_BITS-PASS1_BITS)
+#endif
+
+
+/* Like DESCALE, but applies to a DCTELEM and produces an int.
+ * We assume that int right shift is unsigned if INT32 right shift is.
+ */
+
+#ifdef RIGHT_SHIFT_IS_UNSIGNED
+#define ISHIFT_TEMPS	DCTELEM ishift_temp;
+#if BITS_IN_JSAMPLE == 8
+#define DCTELEMBITS  16		/* DCTELEM may be 16 or 32 bits */
+#else
+#define DCTELEMBITS  32		/* DCTELEM must be 32 bits */
+#endif
+#define IRIGHT_SHIFT(x,shft)  \
+    ((ishift_temp = (x)) < 0 ? \
+     (ishift_temp >> (shft)) | ((~((DCTELEM) 0)) << (DCTELEMBITS-(shft))) : \
+     (ishift_temp >> (shft)))
+#else
+#define ISHIFT_TEMPS
+#define IRIGHT_SHIFT(x,shft)	((x) >> (shft))
+#endif
+
+#ifdef USE_ACCURATE_ROUNDING
+#define IDESCALE(x,n)  ((int) IRIGHT_SHIFT((x) + (1 << ((n)-1)), n))
+#else
+#define IDESCALE(x,n)  ((int) IRIGHT_SHIFT(x, n))
+#endif
+
+
+/*
+ * Perform dequantization and inverse DCT on one block of coefficients.
+ */
+
+GLOBAL(void)
+jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+		 JCOEFPTR coef_block,
+		 JSAMPARRAY output_buf, JDIMENSION output_col)
+{
+  DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
+  DCTELEM tmp10, tmp11, tmp12, tmp13;
+  DCTELEM z5, z10, z11, z12, z13;
+  JCOEFPTR inptr;
+  IFAST_MULT_TYPE * quantptr;
+  int * wsptr;
+  JSAMPROW outptr;
+  JSAMPLE *range_limit = IDCT_range_limit(cinfo);
+  int ctr;
+  int workspace[DCTSIZE2];	/* buffers data between passes */
+  SHIFT_TEMPS			/* for DESCALE */
+  ISHIFT_TEMPS			/* for IDESCALE */
+
+  /* Pass 1: process columns from input, store into work array. */
+
+  inptr = coef_block;
+  quantptr = (IFAST_MULT_TYPE *) compptr->dct_table;
+  wsptr = workspace;
+  for (ctr = DCTSIZE; ctr > 0; ctr--) {
+    /* 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 column 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
+     * column DCT calculations can be simplified this way.
+     */
+    
+    if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 &&
+	inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 &&
+	inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 &&
+	inptr[DCTSIZE*7] == 0) {
+      /* AC terms all zero */
+      int dcval = (int) DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]);
+
+      wsptr[DCTSIZE*0] = dcval;
+      wsptr[DCTSIZE*1] = dcval;
+      wsptr[DCTSIZE*2] = dcval;
+      wsptr[DCTSIZE*3] = dcval;
+      wsptr[DCTSIZE*4] = dcval;
+      wsptr[DCTSIZE*5] = dcval;
+      wsptr[DCTSIZE*6] = dcval;
+      wsptr[DCTSIZE*7] = dcval;
+      
+      inptr++;			/* advance pointers to next column */
+      quantptr++;
+      wsptr++;
+      continue;
+    }
+    
+    /* Even part */
+
+    tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]);
+    tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]);
+    tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]);
+    tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]);
+
+    tmp10 = tmp0 + tmp2;	/* phase 3 */
+    tmp11 = tmp0 - tmp2;
+
+    tmp13 = tmp1 + tmp3;	/* phases 5-3 */
+    tmp12 = MULTIPLY(tmp1 - tmp3, FIX_1_414213562) - tmp13; /* 2*c4 */
+
+    tmp0 = tmp10 + tmp13;	/* phase 2 */
+    tmp3 = tmp10 - tmp13;
+    tmp1 = tmp11 + tmp12;
+    tmp2 = tmp11 - tmp12;
+    
+    /* Odd part */
+
+    tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]);
+    tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]);
+    tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]);
+    tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]);
+
+    z13 = tmp6 + tmp5;		/* phase 6 */
+    z10 = tmp6 - tmp5;
+    z11 = tmp4 + tmp7;
+    z12 = tmp4 - tmp7;
+
+    tmp7 = z11 + z13;		/* phase 5 */
+    tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */
+
+    z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */
+    tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */
+    tmp12 = MULTIPLY(z10, - FIX_2_613125930) + z5; /* -2*(c2+c6) */
+
+    tmp6 = tmp12 - tmp7;	/* phase 2 */
+    tmp5 = tmp11 - tmp6;
+    tmp4 = tmp10 + tmp5;
+
+    wsptr[DCTSIZE*0] = (int) (tmp0 + tmp7);
+    wsptr[DCTSIZE*7] = (int) (tmp0 - tmp7);
+    wsptr[DCTSIZE*1] = (int) (tmp1 + tmp6);
+    wsptr[DCTSIZE*6] = (int) (tmp1 - tmp6);
+    wsptr[DCTSIZE*2] = (int) (tmp2 + tmp5);
+    wsptr[DCTSIZE*5] = (int) (tmp2 - tmp5);
+    wsptr[DCTSIZE*4] = (int) (tmp3 + tmp4);
+    wsptr[DCTSIZE*3] = (int) (tmp3 - tmp4);
+
+    inptr++;			/* advance pointers to next column */
+    quantptr++;
+    wsptr++;
+  }
+  
+  /* Pass 2: process rows from work array, store into output array. */
+  /* Note that we must descale the results by a factor of 8 == 2**3, */
+  /* and also undo the PASS1_BITS scaling. */
+
+  wsptr = workspace;
+  for (ctr = 0; ctr < DCTSIZE; ctr++) {
+    outptr = output_buf[ctr] + output_col;
+    /* Rows of zeroes can be exploited in the same way as we did with columns.
+     * However, the column 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.
+     */
+    
+#ifndef NO_ZERO_ROW_TEST
+    if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[4] == 0 &&
+	wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) {
+      /* AC terms all zero */
+      JSAMPLE dcval = range_limit[IDESCALE(wsptr[0], PASS1_BITS+3)
+				  & RANGE_MASK];
+      
+      outptr[0] = dcval;
+      outptr[1] = dcval;
+      outptr[2] = dcval;
+      outptr[3] = dcval;
+      outptr[4] = dcval;
+      outptr[5] = dcval;
+      outptr[6] = dcval;
+      outptr[7] = dcval;
+
+      wsptr += DCTSIZE;		/* advance pointer to next row */
+      continue;
+    }
+#endif
+    
+    /* Even part */
+
+    tmp10 = ((DCTELEM) wsptr[0] + (DCTELEM) wsptr[4]);
+    tmp11 = ((DCTELEM) wsptr[0] - (DCTELEM) wsptr[4]);
+
+    tmp13 = ((DCTELEM) wsptr[2] + (DCTELEM) wsptr[6]);
+    tmp12 = MULTIPLY((DCTELEM) wsptr[2] - (DCTELEM) wsptr[6], FIX_1_414213562)
+	    - tmp13;
+
+    tmp0 = tmp10 + tmp13;
+    tmp3 = tmp10 - tmp13;
+    tmp1 = tmp11 + tmp12;
+    tmp2 = tmp11 - tmp12;
+
+    /* Odd part */
+
+    z13 = (DCTELEM) wsptr[5] + (DCTELEM) wsptr[3];
+    z10 = (DCTELEM) wsptr[5] - (DCTELEM) wsptr[3];
+    z11 = (DCTELEM) wsptr[1] + (DCTELEM) wsptr[7];
+    z12 = (DCTELEM) wsptr[1] - (DCTELEM) wsptr[7];
+
+    tmp7 = z11 + z13;		/* phase 5 */
+    tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */
+
+    z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */
+    tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */
+    tmp12 = MULTIPLY(z10, - FIX_2_613125930) + z5; /* -2*(c2+c6) */
+
+    tmp6 = tmp12 - tmp7;	/* phase 2 */
+    tmp5 = tmp11 - tmp6;
+    tmp4 = tmp10 + tmp5;
+
+    /* Final output stage: scale down by a factor of 8 and range-limit */
+
+    outptr[0] = range_limit[IDESCALE(tmp0 + tmp7, PASS1_BITS+3)
+			    & RANGE_MASK];
+    outptr[7] = range_limit[IDESCALE(tmp0 - tmp7, PASS1_BITS+3)
+			    & RANGE_MASK];
+    outptr[1] = range_limit[IDESCALE(tmp1 + tmp6, PASS1_BITS+3)
+			    & RANGE_MASK];
+    outptr[6] = range_limit[IDESCALE(tmp1 - tmp6, PASS1_BITS+3)
+			    & RANGE_MASK];
+    outptr[2] = range_limit[IDESCALE(tmp2 + tmp5, PASS1_BITS+3)
+			    & RANGE_MASK];
+    outptr[5] = range_limit[IDESCALE(tmp2 - tmp5, PASS1_BITS+3)
+			    & RANGE_MASK];
+    outptr[4] = range_limit[IDESCALE(tmp3 + tmp4, PASS1_BITS+3)
+			    & RANGE_MASK];
+    outptr[3] = range_limit[IDESCALE(tmp3 - tmp4, PASS1_BITS+3)
+			    & RANGE_MASK];
+
+    wsptr += DCTSIZE;		/* advance pointer to next row */
+  }
+}
+
+#endif /* DCT_IFAST_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jidctint.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jidctint.c
new file mode 100644
index 0000000000..a72b3207ca
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jidctint.c
@@ -0,0 +1,389 @@
+/*
+ * jidctint.c
+ *
+ * Copyright (C) 1991-1998, 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 a slow-but-accurate integer implementation of the
+ * inverse DCT (Discrete Cosine Transform).  In the IJG code, this routine
+ * must also perform dequantization of the input coefficients.
+ *
+ * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT
+ * on each row (or vice versa, but it's more convenient to emit a row at
+ * a time).  Direct algorithms are also available, but they are much more
+ * complex and seem not to be any faster when reduced to code.
+ *
+ * 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.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jdct.h"		/* Private declarations for DCT subsystem */
+
+#ifdef DCT_ISLOW_SUPPORTED
+
+
+/*
+ * This module is specialized to the case DCTSIZE = 8.
+ */
+
+#if DCTSIZE != 8
+  Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */
+#endif
+
+
+/*
+ * 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.
+ */
+
+#if BITS_IN_JSAMPLE == 8
+#define CONST_BITS  13
+#define PASS1_BITS  2
+#else
+#define CONST_BITS  13
+#define PASS1_BITS  1		/* lose a little precision to avoid overflow */
+#endif
+
+/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus
+ * causing a lot of useless floating-point operations at run time.
+ * To get around this we use the following pre-calculated constants.
+ * If you change CONST_BITS you may want to add appropriate values.
+ * (With a reasonable C compiler, you can just rely on the FIX() macro...)
+ */
+
+#if CONST_BITS == 13
+#define FIX_0_298631336  ((INT32)  2446)	/* FIX(0.298631336) */
+#define FIX_0_390180644  ((INT32)  3196)	/* FIX(0.390180644) */
+#define FIX_0_541196100  ((INT32)  4433)	/* FIX(0.541196100) */
+#define FIX_0_765366865  ((INT32)  6270)	/* FIX(0.765366865) */
+#define FIX_0_899976223  ((INT32)  7373)	/* FIX(0.899976223) */
+#define FIX_1_175875602  ((INT32)  9633)	/* FIX(1.175875602) */
+#define FIX_1_501321110  ((INT32)  12299)	/* FIX(1.501321110) */
+#define FIX_1_847759065  ((INT32)  15137)	/* FIX(1.847759065) */
+#define FIX_1_961570560  ((INT32)  16069)	/* FIX(1.961570560) */
+#define FIX_2_053119869  ((INT32)  16819)	/* FIX(2.053119869) */
+#define FIX_2_562915447  ((INT32)  20995)	/* FIX(2.562915447) */
+#define FIX_3_072711026  ((INT32)  25172)	/* FIX(3.072711026) */
+#else
+#define FIX_0_298631336  FIX(0.298631336)
+#define FIX_0_390180644  FIX(0.390180644)
+#define FIX_0_541196100  FIX(0.541196100)
+#define FIX_0_765366865  FIX(0.765366865)
+#define FIX_0_899976223  FIX(0.899976223)
+#define FIX_1_175875602  FIX(1.175875602)
+#define FIX_1_501321110  FIX(1.501321110)
+#define FIX_1_847759065  FIX(1.847759065)
+#define FIX_1_961570560  FIX(1.961570560)
+#define FIX_2_053119869  FIX(2.053119869)
+#define FIX_2_562915447  FIX(2.562915447)
+#define FIX_3_072711026  FIX(3.072711026)
+#endif
+
+
+/* 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.
+ * For 12-bit samples, a full 32-bit multiplication will be needed.
+ */
+
+#if BITS_IN_JSAMPLE == 8
+#define MULTIPLY(var,const)  MULTIPLY16C16(var,const)
+#else
+#define MULTIPLY(var,const)  ((var) * (const))
+#endif
+
+
+/* Dequantize a coefficient by multiplying it by the multiplier-table
+ * entry; produce an int result.  In this module, both inputs and result
+ * are 16 bits or less, so either int or short multiply will work.
+ */
+
+#define DEQUANTIZE(coef,quantval)  (((ISLOW_MULT_TYPE) (coef)) * (quantval))
+
+
+/*
+ * Perform dequantization and inverse DCT on one block of coefficients.
+ */
+
+GLOBAL(void)
+jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+		 JCOEFPTR coef_block,
+		 JSAMPARRAY output_buf, JDIMENSION output_col)
+{
+  INT32 tmp0, tmp1, tmp2, tmp3;
+  INT32 tmp10, tmp11, tmp12, tmp13;
+  INT32 z1, z2, z3, z4, z5;
+  JCOEFPTR inptr;
+  ISLOW_MULT_TYPE * quantptr;
+  int * wsptr;
+  JSAMPROW outptr;
+  JSAMPLE *range_limit = IDCT_range_limit(cinfo);
+  int ctr;
+  int workspace[DCTSIZE2];	/* buffers data between passes */
+  SHIFT_TEMPS
+
+  /* Pass 1: process columns from input, store into work array. */
+  /* Note results are scaled up by sqrt(8) compared to a true IDCT; */
+  /* furthermore, we scale the results by 2**PASS1_BITS. */
+
+  inptr = coef_block;
+  quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table;
+  wsptr = workspace;
+  for (ctr = DCTSIZE; ctr > 0; ctr--) {
+    /* 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 column 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
+     * column DCT calculations can be simplified this way.
+     */
+    
+    if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 &&
+	inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 &&
+	inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 &&
+	inptr[DCTSIZE*7] == 0) {
+      /* AC terms all zero */
+      int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS;
+      
+      wsptr[DCTSIZE*0] = dcval;
+      wsptr[DCTSIZE*1] = dcval;
+      wsptr[DCTSIZE*2] = dcval;
+      wsptr[DCTSIZE*3] = dcval;
+      wsptr[DCTSIZE*4] = dcval;
+      wsptr[DCTSIZE*5] = dcval;
+      wsptr[DCTSIZE*6] = dcval;
+      wsptr[DCTSIZE*7] = dcval;
+      
+      inptr++;			/* advance pointers to next column */
+      quantptr++;
+      wsptr++;
+      continue;
+    }
+    
+    /* Even part: reverse the even part of the forward DCT. */
+    /* The rotator is sqrt(2)*c(-6). */
+    
+    z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]);
+    z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]);
+    
+    z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
+    tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065);
+    tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
+    
+    z2 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]);
+    z3 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]);
+
+    tmp0 = (z2 + z3) << CONST_BITS;
+    tmp1 = (z2 - z3) << CONST_BITS;
+    
+    tmp10 = tmp0 + tmp3;
+    tmp13 = tmp0 - tmp3;
+    tmp11 = tmp1 + tmp2;
+    tmp12 = tmp1 - tmp2;
+    
+    /* Odd part per figure 8; the matrix is unitary and hence its
+     * transpose is its inverse.  i0..i3 are y7,y5,y3,y1 respectively.
+     */
+    
+    tmp0 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]);
+    tmp1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]);
+    tmp2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]);
+    tmp3 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]);
+    
+    z1 = tmp0 + tmp3;
+    z2 = tmp1 + tmp2;
+    z3 = tmp0 + tmp2;
+    z4 = tmp1 + tmp3;
+    z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */
+    
+    tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */
+    tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */
+    tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */
+    tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */
+    z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */
+    z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */
+    z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */
+    z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */
+    
+    z3 += z5;
+    z4 += z5;
+    
+    tmp0 += z1 + z3;
+    tmp1 += z2 + z4;
+    tmp2 += z2 + z3;
+    tmp3 += z1 + z4;
+    
+    /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
+    
+    wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS);
+    wsptr[DCTSIZE*7] = (int) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS);
+    wsptr[DCTSIZE*1] = (int) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS);
+    wsptr[DCTSIZE*6] = (int) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS);
+    wsptr[DCTSIZE*2] = (int) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS);
+    wsptr[DCTSIZE*5] = (int) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS);
+    wsptr[DCTSIZE*3] = (int) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS);
+    wsptr[DCTSIZE*4] = (int) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS);
+    
+    inptr++;			/* advance pointers to next column */
+    quantptr++;
+    wsptr++;
+  }
+  
+  /* Pass 2: process rows from work array, store into output array. */
+  /* Note that we must descale the results by a factor of 8 == 2**3, */
+  /* and also undo the PASS1_BITS scaling. */
+
+  wsptr = workspace;
+  for (ctr = 0; ctr < DCTSIZE; ctr++) {
+    outptr = output_buf[ctr] + output_col;
+    /* Rows of zeroes can be exploited in the same way as we did with columns.
+     * However, the column 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.
+     */
+    
+#ifndef NO_ZERO_ROW_TEST
+    if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[4] == 0 &&
+	wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) {
+      /* AC terms all zero */
+      JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3)
+				  & RANGE_MASK];
+      
+      outptr[0] = dcval;
+      outptr[1] = dcval;
+      outptr[2] = dcval;
+      outptr[3] = dcval;
+      outptr[4] = dcval;
+      outptr[5] = dcval;
+      outptr[6] = dcval;
+      outptr[7] = dcval;
+
+      wsptr += DCTSIZE;		/* advance pointer to next row */
+      continue;
+    }
+#endif
+    
+    /* Even part: reverse the even part of the forward DCT. */
+    /* The rotator is sqrt(2)*c(-6). */
+    
+    z2 = (INT32) wsptr[2];
+    z3 = (INT32) wsptr[6];
+    
+    z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
+    tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065);
+    tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
+    
+    tmp0 = ((INT32) wsptr[0] + (INT32) wsptr[4]) << CONST_BITS;
+    tmp1 = ((INT32) wsptr[0] - (INT32) wsptr[4]) << CONST_BITS;
+    
+    tmp10 = tmp0 + tmp3;
+    tmp13 = tmp0 - tmp3;
+    tmp11 = tmp1 + tmp2;
+    tmp12 = tmp1 - tmp2;
+    
+    /* Odd part per figure 8; the matrix is unitary and hence its
+     * transpose is its inverse.  i0..i3 are y7,y5,y3,y1 respectively.
+     */
+    
+    tmp0 = (INT32) wsptr[7];
+    tmp1 = (INT32) wsptr[5];
+    tmp2 = (INT32) wsptr[3];
+    tmp3 = (INT32) wsptr[1];
+    
+    z1 = tmp0 + tmp3;
+    z2 = tmp1 + tmp2;
+    z3 = tmp0 + tmp2;
+    z4 = tmp1 + tmp3;
+    z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */
+    
+    tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */
+    tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */
+    tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */
+    tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */
+    z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */
+    z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */
+    z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */
+    z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */
+    
+    z3 += z5;
+    z4 += z5;
+    
+    tmp0 += z1 + z3;
+    tmp1 += z2 + z4;
+    tmp2 += z2 + z3;
+    tmp3 += z1 + z4;
+    
+    /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
+    
+    outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp3,
+					  CONST_BITS+PASS1_BITS+3)
+			    & RANGE_MASK];
+    outptr[7] = range_limit[(int) DESCALE(tmp10 - tmp3,
+					  CONST_BITS+PASS1_BITS+3)
+			    & RANGE_MASK];
+    outptr[1] = range_limit[(int) DESCALE(tmp11 + tmp2,
+					  CONST_BITS+PASS1_BITS+3)
+			    & RANGE_MASK];
+    outptr[6] = range_limit[(int) DESCALE(tmp11 - tmp2,
+					  CONST_BITS+PASS1_BITS+3)
+			    & RANGE_MASK];
+    outptr[2] = range_limit[(int) DESCALE(tmp12 + tmp1,
+					  CONST_BITS+PASS1_BITS+3)
+			    & RANGE_MASK];
+    outptr[5] = range_limit[(int) DESCALE(tmp12 - tmp1,
+					  CONST_BITS+PASS1_BITS+3)
+			    & RANGE_MASK];
+    outptr[3] = range_limit[(int) DESCALE(tmp13 + tmp0,
+					  CONST_BITS+PASS1_BITS+3)
+			    & RANGE_MASK];
+    outptr[4] = range_limit[(int) DESCALE(tmp13 - tmp0,
+					  CONST_BITS+PASS1_BITS+3)
+			    & RANGE_MASK];
+    
+    wsptr += DCTSIZE;		/* advance pointer to next row */
+  }
+}
+
+#endif /* DCT_ISLOW_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jidctred.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jidctred.c
new file mode 100644
index 0000000000..421f3c7ca1
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jidctred.c
@@ -0,0 +1,398 @@
+/*
+ * jidctred.c
+ *
+ * Copyright (C) 1994-1998, 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 inverse-DCT routines that produce reduced-size output:
+ * either 4x4, 2x2, or 1x1 pixels from an 8x8 DCT block.
+ *
+ * The implementation is based on the Loeffler, Ligtenberg and Moschytz (LL&M)
+ * algorithm used in jidctint.c.  We simply replace each 8-to-8 1-D IDCT step
+ * with an 8-to-4 step that produces the four averages of two adjacent outputs
+ * (or an 8-to-2 step producing two averages of four outputs, for 2x2 output).
+ * These steps were derived by computing the corresponding values at the end
+ * of the normal LL&M code, then simplifying as much as possible.
+ *
+ * 1x1 is trivial: just take the DC coefficient divided by 8.
+ *
+ * See jidctint.c for additional comments.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jdct.h"		/* Private declarations for DCT subsystem */
+
+#ifdef IDCT_SCALING_SUPPORTED
+
+
+/*
+ * This module is specialized to the case DCTSIZE = 8.
+ */
+
+#if DCTSIZE != 8
+  Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */
+#endif
+
+
+/* Scaling is the same as in jidctint.c. */
+
+#if BITS_IN_JSAMPLE == 8
+#define CONST_BITS  13
+#define PASS1_BITS  2
+#else
+#define CONST_BITS  13
+#define PASS1_BITS  1		/* lose a little precision to avoid overflow */
+#endif
+
+/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus
+ * causing a lot of useless floating-point operations at run time.
+ * To get around this we use the following pre-calculated constants.
+ * If you change CONST_BITS you may want to add appropriate values.
+ * (With a reasonable C compiler, you can just rely on the FIX() macro...)
+ */
+
+#if CONST_BITS == 13
+#define FIX_0_211164243  ((INT32)  1730)	/* FIX(0.211164243) */
+#define FIX_0_509795579  ((INT32)  4176)	/* FIX(0.509795579) */
+#define FIX_0_601344887  ((INT32)  4926)	/* FIX(0.601344887) */
+#define FIX_0_720959822  ((INT32)  5906)	/* FIX(0.720959822) */
+#define FIX_0_765366865  ((INT32)  6270)	/* FIX(0.765366865) */
+#define FIX_0_850430095  ((INT32)  6967)	/* FIX(0.850430095) */
+#define FIX_0_899976223  ((INT32)  7373)	/* FIX(0.899976223) */
+#define FIX_1_061594337  ((INT32)  8697)	/* FIX(1.061594337) */
+#define FIX_1_272758580  ((INT32)  10426)	/* FIX(1.272758580) */
+#define FIX_1_451774981  ((INT32)  11893)	/* FIX(1.451774981) */
+#define FIX_1_847759065  ((INT32)  15137)	/* FIX(1.847759065) */
+#define FIX_2_172734803  ((INT32)  17799)	/* FIX(2.172734803) */
+#define FIX_2_562915447  ((INT32)  20995)	/* FIX(2.562915447) */
+#define FIX_3_624509785  ((INT32)  29692)	/* FIX(3.624509785) */
+#else
+#define FIX_0_211164243  FIX(0.211164243)
+#define FIX_0_509795579  FIX(0.509795579)
+#define FIX_0_601344887  FIX(0.601344887)
+#define FIX_0_720959822  FIX(0.720959822)
+#define FIX_0_765366865  FIX(0.765366865)
+#define FIX_0_850430095  FIX(0.850430095)
+#define FIX_0_899976223  FIX(0.899976223)
+#define FIX_1_061594337  FIX(1.061594337)
+#define FIX_1_272758580  FIX(1.272758580)
+#define FIX_1_451774981  FIX(1.451774981)
+#define FIX_1_847759065  FIX(1.847759065)
+#define FIX_2_172734803  FIX(2.172734803)
+#define FIX_2_562915447  FIX(2.562915447)
+#define FIX_3_624509785  FIX(3.624509785)
+#endif
+
+
+/* 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.
+ * For 12-bit samples, a full 32-bit multiplication will be needed.
+ */
+
+#if BITS_IN_JSAMPLE == 8
+#define MULTIPLY(var,const)  MULTIPLY16C16(var,const)
+#else
+#define MULTIPLY(var,const)  ((var) * (const))
+#endif
+
+
+/* Dequantize a coefficient by multiplying it by the multiplier-table
+ * entry; produce an int result.  In this module, both inputs and result
+ * are 16 bits or less, so either int or short multiply will work.
+ */
+
+#define DEQUANTIZE(coef,quantval)  (((ISLOW_MULT_TYPE) (coef)) * (quantval))
+
+
+/*
+ * Perform dequantization and inverse DCT on one block of coefficients,
+ * producing a reduced-size 4x4 output block.
+ */
+
+GLOBAL(void)
+jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+	       JCOEFPTR coef_block,
+	       JSAMPARRAY output_buf, JDIMENSION output_col)
+{
+  INT32 tmp0, tmp2, tmp10, tmp12;
+  INT32 z1, z2, z3, z4;
+  JCOEFPTR inptr;
+  ISLOW_MULT_TYPE * quantptr;
+  int * wsptr;
+  JSAMPROW outptr;
+  JSAMPLE *range_limit = IDCT_range_limit(cinfo);
+  int ctr;
+  int workspace[DCTSIZE*4];	/* buffers data between passes */
+  SHIFT_TEMPS
+
+  /* Pass 1: process columns from input, store into work array. */
+
+  inptr = coef_block;
+  quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table;
+  wsptr = workspace;
+  for (ctr = DCTSIZE; ctr > 0; inptr++, quantptr++, wsptr++, ctr--) {
+    /* Don't bother to process column 4, because second pass won't use it */
+    if (ctr == DCTSIZE-4)
+      continue;
+    if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 &&
+	inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*5] == 0 &&
+	inptr[DCTSIZE*6] == 0 && inptr[DCTSIZE*7] == 0) {
+      /* AC terms all zero; we need not examine term 4 for 4x4 output */
+      int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS;
+      
+      wsptr[DCTSIZE*0] = dcval;
+      wsptr[DCTSIZE*1] = dcval;
+      wsptr[DCTSIZE*2] = dcval;
+      wsptr[DCTSIZE*3] = dcval;
+      
+      continue;
+    }
+    
+    /* Even part */
+    
+    tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]);
+    tmp0 <<= (CONST_BITS+1);
+    
+    z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]);
+    z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]);
+
+    tmp2 = MULTIPLY(z2, FIX_1_847759065) + MULTIPLY(z3, - FIX_0_765366865);
+    
+    tmp10 = tmp0 + tmp2;
+    tmp12 = tmp0 - tmp2;
+    
+    /* Odd part */
+    
+    z1 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]);
+    z2 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]);
+    z3 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]);
+    z4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]);
+    
+    tmp0 = MULTIPLY(z1, - FIX_0_211164243) /* sqrt(2) * (c3-c1) */
+	 + MULTIPLY(z2, FIX_1_451774981) /* sqrt(2) * (c3+c7) */
+	 + MULTIPLY(z3, - FIX_2_172734803) /* sqrt(2) * (-c1-c5) */
+	 + MULTIPLY(z4, FIX_1_061594337); /* sqrt(2) * (c5+c7) */
+    
+    tmp2 = MULTIPLY(z1, - FIX_0_509795579) /* sqrt(2) * (c7-c5) */
+	 + MULTIPLY(z2, - FIX_0_601344887) /* sqrt(2) * (c5-c1) */
+	 + MULTIPLY(z3, FIX_0_899976223) /* sqrt(2) * (c3-c7) */
+	 + MULTIPLY(z4, FIX_2_562915447); /* sqrt(2) * (c1+c3) */
+
+    /* Final output stage */
+    
+    wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp2, CONST_BITS-PASS1_BITS+1);
+    wsptr[DCTSIZE*3] = (int) DESCALE(tmp10 - tmp2, CONST_BITS-PASS1_BITS+1);
+    wsptr[DCTSIZE*1] = (int) DESCALE(tmp12 + tmp0, CONST_BITS-PASS1_BITS+1);
+    wsptr[DCTSIZE*2] = (int) DESCALE(tmp12 - tmp0, CONST_BITS-PASS1_BITS+1);
+  }
+  
+  /* Pass 2: process 4 rows from work array, store into output array. */
+
+  wsptr = workspace;
+  for (ctr = 0; ctr < 4; ctr++) {
+    outptr = output_buf[ctr] + output_col;
+    /* It's not clear whether a zero row test is worthwhile here ... */
+
+#ifndef NO_ZERO_ROW_TEST
+    if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 &&
+	wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) {
+      /* AC terms all zero */
+      JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3)
+				  & RANGE_MASK];
+      
+      outptr[0] = dcval;
+      outptr[1] = dcval;
+      outptr[2] = dcval;
+      outptr[3] = dcval;
+      
+      wsptr += DCTSIZE;		/* advance pointer to next row */
+      continue;
+    }
+#endif
+    
+    /* Even part */
+    
+    tmp0 = ((INT32) wsptr[0]) << (CONST_BITS+1);
+    
+    tmp2 = MULTIPLY((INT32) wsptr[2], FIX_1_847759065)
+	 + MULTIPLY((INT32) wsptr[6], - FIX_0_765366865);
+    
+    tmp10 = tmp0 + tmp2;
+    tmp12 = tmp0 - tmp2;
+    
+    /* Odd part */
+    
+    z1 = (INT32) wsptr[7];
+    z2 = (INT32) wsptr[5];
+    z3 = (INT32) wsptr[3];
+    z4 = (INT32) wsptr[1];
+    
+    tmp0 = MULTIPLY(z1, - FIX_0_211164243) /* sqrt(2) * (c3-c1) */
+	 + MULTIPLY(z2, FIX_1_451774981) /* sqrt(2) * (c3+c7) */
+	 + MULTIPLY(z3, - FIX_2_172734803) /* sqrt(2) * (-c1-c5) */
+	 + MULTIPLY(z4, FIX_1_061594337); /* sqrt(2) * (c5+c7) */
+    
+    tmp2 = MULTIPLY(z1, - FIX_0_509795579) /* sqrt(2) * (c7-c5) */
+	 + MULTIPLY(z2, - FIX_0_601344887) /* sqrt(2) * (c5-c1) */
+	 + MULTIPLY(z3, FIX_0_899976223) /* sqrt(2) * (c3-c7) */
+	 + MULTIPLY(z4, FIX_2_562915447); /* sqrt(2) * (c1+c3) */
+
+    /* Final output stage */
+    
+    outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp2,
+					  CONST_BITS+PASS1_BITS+3+1)
+			    & RANGE_MASK];
+    outptr[3] = range_limit[(int) DESCALE(tmp10 - tmp2,
+					  CONST_BITS+PASS1_BITS+3+1)
+			    & RANGE_MASK];
+    outptr[1] = range_limit[(int) DESCALE(tmp12 + tmp0,
+					  CONST_BITS+PASS1_BITS+3+1)
+			    & RANGE_MASK];
+    outptr[2] = range_limit[(int) DESCALE(tmp12 - tmp0,
+					  CONST_BITS+PASS1_BITS+3+1)
+			    & RANGE_MASK];
+    
+    wsptr += DCTSIZE;		/* advance pointer to next row */
+  }
+}
+
+
+/*
+ * Perform dequantization and inverse DCT on one block of coefficients,
+ * producing a reduced-size 2x2 output block.
+ */
+
+GLOBAL(void)
+jpeg_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+	       JCOEFPTR coef_block,
+	       JSAMPARRAY output_buf, JDIMENSION output_col)
+{
+  INT32 tmp0, tmp10, z1;
+  JCOEFPTR inptr;
+  ISLOW_MULT_TYPE * quantptr;
+  int * wsptr;
+  JSAMPROW outptr;
+  JSAMPLE *range_limit = IDCT_range_limit(cinfo);
+  int ctr;
+  int workspace[DCTSIZE*2];	/* buffers data between passes */
+  SHIFT_TEMPS
+
+  /* Pass 1: process columns from input, store into work array. */
+
+  inptr = coef_block;
+  quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table;
+  wsptr = workspace;
+  for (ctr = DCTSIZE; ctr > 0; inptr++, quantptr++, wsptr++, ctr--) {
+    /* Don't bother to process columns 2,4,6 */
+    if (ctr == DCTSIZE-2 || ctr == DCTSIZE-4 || ctr == DCTSIZE-6)
+      continue;
+    if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*3] == 0 &&
+	inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*7] == 0) {
+      /* AC terms all zero; we need not examine terms 2,4,6 for 2x2 output */
+      int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS;
+      
+      wsptr[DCTSIZE*0] = dcval;
+      wsptr[DCTSIZE*1] = dcval;
+      
+      continue;
+    }
+    
+    /* Even part */
+    
+    z1 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]);
+    tmp10 = z1 << (CONST_BITS+2);
+    
+    /* Odd part */
+
+    z1 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]);
+    tmp0 = MULTIPLY(z1, - FIX_0_720959822); /* sqrt(2) * (c7-c5+c3-c1) */
+    z1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]);
+    tmp0 += MULTIPLY(z1, FIX_0_850430095); /* sqrt(2) * (-c1+c3+c5+c7) */
+    z1 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]);
+    tmp0 += MULTIPLY(z1, - FIX_1_272758580); /* sqrt(2) * (-c1+c3-c5-c7) */
+    z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]);
+    tmp0 += MULTIPLY(z1, FIX_3_624509785); /* sqrt(2) * (c1+c3+c5+c7) */
+
+    /* Final output stage */
+    
+    wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp0, CONST_BITS-PASS1_BITS+2);
+    wsptr[DCTSIZE*1] = (int) DESCALE(tmp10 - tmp0, CONST_BITS-PASS1_BITS+2);
+  }
+  
+  /* Pass 2: process 2 rows from work array, store into output array. */
+
+  wsptr = workspace;
+  for (ctr = 0; ctr < 2; ctr++) {
+    outptr = output_buf[ctr] + output_col;
+    /* It's not clear whether a zero row test is worthwhile here ... */
+
+#ifndef NO_ZERO_ROW_TEST
+    if (wsptr[1] == 0 && wsptr[3] == 0 && wsptr[5] == 0 && wsptr[7] == 0) {
+      /* AC terms all zero */
+      JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3)
+				  & RANGE_MASK];
+      
+      outptr[0] = dcval;
+      outptr[1] = dcval;
+      
+      wsptr += DCTSIZE;		/* advance pointer to next row */
+      continue;
+    }
+#endif
+    
+    /* Even part */
+    
+    tmp10 = ((INT32) wsptr[0]) << (CONST_BITS+2);
+    
+    /* Odd part */
+
+    tmp0 = MULTIPLY((INT32) wsptr[7], - FIX_0_720959822) /* sqrt(2) * (c7-c5+c3-c1) */
+	 + MULTIPLY((INT32) wsptr[5], FIX_0_850430095) /* sqrt(2) * (-c1+c3+c5+c7) */
+	 + MULTIPLY((INT32) wsptr[3], - FIX_1_272758580) /* sqrt(2) * (-c1+c3-c5-c7) */
+	 + MULTIPLY((INT32) wsptr[1], FIX_3_624509785); /* sqrt(2) * (c1+c3+c5+c7) */
+
+    /* Final output stage */
+    
+    outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp0,
+					  CONST_BITS+PASS1_BITS+3+2)
+			    & RANGE_MASK];
+    outptr[1] = range_limit[(int) DESCALE(tmp10 - tmp0,
+					  CONST_BITS+PASS1_BITS+3+2)
+			    & RANGE_MASK];
+    
+    wsptr += DCTSIZE;		/* advance pointer to next row */
+  }
+}
+
+
+/*
+ * Perform dequantization and inverse DCT on one block of coefficients,
+ * producing a reduced-size 1x1 output block.
+ */
+
+GLOBAL(void)
+jpeg_idct_1x1 (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+	       JCOEFPTR coef_block,
+	       JSAMPARRAY output_buf, JDIMENSION output_col)
+{
+  int dcval;
+  ISLOW_MULT_TYPE * quantptr;
+  JSAMPLE *range_limit = IDCT_range_limit(cinfo);
+  SHIFT_TEMPS
+
+  /* We hardly need an inverse DCT routine for this: just take the
+   * average pixel value, which is one-eighth of the DC coefficient.
+   */
+  quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table;
+  dcval = DEQUANTIZE(coef_block[0], quantptr[0]);
+  dcval = (int) DESCALE((INT32) dcval, 3);
+
+  output_buf[0][output_col] = range_limit[dcval & RANGE_MASK];
+}
+
+#endif /* IDCT_SCALING_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jinclude.h b/Utilities/GDAL/frmts/jpeg/libjpeg/jinclude.h
new file mode 100644
index 0000000000..0a4f15146a
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jinclude.h
@@ -0,0 +1,91 @@
+/*
+ * jinclude.h
+ *
+ * Copyright (C) 1991-1994, 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 exists to provide a single place to fix any problems with
+ * including the wrong system include files.  (Common problems are taken
+ * care of by the standard jconfig symbols, but on really weird systems
+ * you may have to edit this file.)
+ *
+ * NOTE: this file is NOT intended to be included by applications using the
+ * JPEG library.  Most applications need only include jpeglib.h.
+ */
+
+
+/* Include auto-config file to find out which system include files we need. */
+
+#include "jconfig.h"		/* auto configuration options */
+#define JCONFIG_INCLUDED	/* so that jpeglib.h doesn't do it again */
+
+/*
+ * We need the NULL macro and size_t typedef.
+ * On an ANSI-conforming system it is sufficient to include <stddef.h>.
+ * Otherwise, we get them from <stdlib.h> or <stdio.h>; we may have to
+ * pull in <sys/types.h> as well.
+ * Note that the core JPEG library does not require <stdio.h>;
+ * only the default error handler and data source/destination modules do.
+ * But we must pull it in because of the references to FILE in jpeglib.h.
+ * You can remove those references if you want to compile without <stdio.h>.
+ */
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef NEED_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <stdio.h>
+
+/*
+ * We need memory copying and zeroing functions, plus strncpy().
+ * ANSI and System V implementations declare these in <string.h>.
+ * BSD doesn't have the mem() functions, but it does have bcopy()/bzero().
+ * Some systems may declare memset and memcpy in <memory.h>.
+ *
+ * NOTE: we assume the size parameters to these functions are of type size_t.
+ * Change the casts in these macros if not!
+ */
+
+#ifdef NEED_BSD_STRINGS
+
+#include <strings.h>
+#define MEMZERO(target,size)	bzero((void *)(target), (size_t)(size))
+#define MEMCOPY(dest,src,size)	bcopy((const void *)(src), (void *)(dest), (size_t)(size))
+
+#else /* not BSD, assume ANSI/SysV string lib */
+
+#include <string.h>
+#define MEMZERO(target,size)	memset((void *)(target), 0, (size_t)(size))
+#define MEMCOPY(dest,src,size)	memcpy((void *)(dest), (const void *)(src), (size_t)(size))
+
+#endif
+
+/*
+ * In ANSI C, and indeed any rational implementation, size_t is also the
+ * type returned by sizeof().  However, it seems there are some irrational
+ * implementations out there, in which sizeof() returns an int even though
+ * size_t is defined as long or unsigned long.  To ensure consistent results
+ * we always use this SIZEOF() macro in place of using sizeof() directly.
+ */
+
+#define SIZEOF(object)	((size_t) sizeof(object))
+
+/*
+ * The modules that use fread() and fwrite() always invoke them through
+ * these macros.  On some systems you may need to twiddle the argument casts.
+ * CAUTION: argument order is different from underlying functions!
+ */
+
+#define JFREAD(file,buf,sizeofbuf)  \
+  ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file)))
+#define JFWRITE(file,buf,sizeofbuf)  \
+  ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file)))
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jmemansi.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jmemansi.c
new file mode 100644
index 0000000000..2d93e49625
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jmemansi.c
@@ -0,0 +1,167 @@
+/*
+ * jmemansi.c
+ *
+ * Copyright (C) 1992-1996, 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 provides a simple generic implementation of the system-
+ * dependent portion of the JPEG memory manager.  This implementation
+ * assumes that you have the ANSI-standard library routine tmpfile().
+ * Also, the problem of determining the amount of memory available
+ * is shoved onto the user.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jmemsys.h"		/* import the system-dependent declarations */
+
+#ifndef HAVE_STDLIB_H		/* <stdlib.h> should declare malloc(),free() */
+extern void * malloc JPP((size_t size));
+extern void free JPP((void *ptr));
+#endif
+
+#ifndef SEEK_SET		/* pre-ANSI systems may not define this; */
+#define SEEK_SET  0		/* if not, assume 0 is correct */
+#endif
+
+
+/*
+ * Memory allocation and freeing are controlled by the regular library
+ * routines malloc() and free().
+ */
+
+GLOBAL(void *)
+jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject)
+{
+  return (void *) malloc(sizeofobject);
+}
+
+GLOBAL(void)
+jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject)
+{
+  free(object);
+}
+
+
+/*
+ * "Large" objects are treated the same as "small" ones.
+ * NB: although we include FAR keywords in the routine declarations,
+ * this file won't actually work in 80x86 small/medium model; at least,
+ * you probably won't be able to process useful-size images in only 64KB.
+ */
+
+GLOBAL(void FAR *)
+jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject)
+{
+  return (void FAR *) malloc(sizeofobject);
+}
+
+GLOBAL(void)
+jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject)
+{
+  free(object);
+}
+
+
+/*
+ * This routine computes the total memory space available for allocation.
+ * It's impossible to do this in a portable way; our current solution is
+ * to make the user tell us (with a default value set at compile time).
+ * If you can actually get the available space, it's a good idea to subtract
+ * a slop factor of 5% or so.
+ */
+
+#ifndef DEFAULT_MAX_MEM		/* so can override from makefile */
+#define DEFAULT_MAX_MEM		1000000L /* default: one megabyte */
+#endif
+
+GLOBAL(long)
+jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed,
+		    long max_bytes_needed, long already_allocated)
+{
+  return cinfo->mem->max_memory_to_use - already_allocated;
+}
+
+
+/*
+ * Backing store (temporary file) management.
+ * Backing store objects are only used when the value returned by
+ * jpeg_mem_available is less than the total space needed.  You can dispense
+ * with these routines if you have plenty of virtual memory; see jmemnobs.c.
+ */
+
+
+METHODDEF(void)
+read_backing_store (j_common_ptr cinfo, backing_store_ptr info,
+		    void FAR * buffer_address,
+		    long file_offset, long byte_count)
+{
+  if (fseek(info->temp_file, file_offset, SEEK_SET))
+    ERREXIT(cinfo, JERR_TFILE_SEEK);
+  if (JFREAD(info->temp_file, buffer_address, byte_count)
+      != (size_t) byte_count)
+    ERREXIT(cinfo, JERR_TFILE_READ);
+}
+
+
+METHODDEF(void)
+write_backing_store (j_common_ptr cinfo, backing_store_ptr info,
+		     void FAR * buffer_address,
+		     long file_offset, long byte_count)
+{
+  if (fseek(info->temp_file, file_offset, SEEK_SET))
+    ERREXIT(cinfo, JERR_TFILE_SEEK);
+  if (JFWRITE(info->temp_file, buffer_address, byte_count)
+      != (size_t) byte_count)
+    ERREXIT(cinfo, JERR_TFILE_WRITE);
+}
+
+
+METHODDEF(void)
+close_backing_store (j_common_ptr cinfo, backing_store_ptr info)
+{
+  fclose(info->temp_file);
+  /* Since this implementation uses tmpfile() to create the file,
+   * no explicit file deletion is needed.
+   */
+}
+
+
+/*
+ * Initial opening of a backing-store object.
+ *
+ * This version uses tmpfile(), which constructs a suitable file name
+ * behind the scenes.  We don't have to use info->temp_name[] at all;
+ * indeed, we can't even find out the actual name of the temp file.
+ */
+
+GLOBAL(void)
+jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info,
+			 long total_bytes_needed)
+{
+  if ((info->temp_file = tmpfile()) == NULL)
+    ERREXITS(cinfo, JERR_TFILE_CREATE, "");
+  info->read_backing_store = read_backing_store;
+  info->write_backing_store = write_backing_store;
+  info->close_backing_store = close_backing_store;
+}
+
+
+/*
+ * These routines take care of any system-dependent initialization and
+ * cleanup required.
+ */
+
+GLOBAL(long)
+jpeg_mem_init (j_common_ptr cinfo)
+{
+  return DEFAULT_MAX_MEM;	/* default for max_memory_to_use */
+}
+
+GLOBAL(void)
+jpeg_mem_term (j_common_ptr cinfo)
+{
+  /* no work */
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jmemmgr.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jmemmgr.c
new file mode 100644
index 0000000000..d801b322da
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jmemmgr.c
@@ -0,0 +1,1118 @@
+/*
+ * jmemmgr.c
+ *
+ * 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.
+ *
+ * This file contains the JPEG system-independent memory management
+ * routines.  This code is usable across a wide variety of machines; most
+ * of the system dependencies have been isolated in a separate file.
+ * The major functions provided here are:
+ *   * pool-based allocation and freeing of memory;
+ *   * policy decisions about how to divide available memory among the
+ *     virtual arrays;
+ *   * control logic for swapping virtual arrays between main memory and
+ *     backing storage.
+ * The separate system-dependent file provides the actual backing-storage
+ * access code, and it contains the policy decision about how much total
+ * main memory to use.
+ * This file is system-dependent in the sense that some of its functions
+ * are unnecessary in some systems.  For example, if there is enough virtual
+ * memory so that backing storage will never be used, much of the virtual
+ * array control logic could be removed.  (Of course, if you have that much
+ * memory then you shouldn't care about a little bit of unused code...)
+ */
+
+#define JPEG_INTERNALS
+#define AM_MEMORY_MANAGER	/* we define jvirt_Xarray_control structs */
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jmemsys.h"		/* import the system-dependent declarations */
+
+#ifndef NO_GETENV
+#ifndef HAVE_STDLIB_H		/* <stdlib.h> should declare getenv() */
+extern char * getenv JPP((const char * name));
+#endif
+#endif
+
+
+/*
+ * Some important notes:
+ *   The allocation routines provided here must never return NULL.
+ *   They should exit to error_exit if unsuccessful.
+ *
+ *   It's not a good idea to try to merge the sarray and barray routines,
+ *   even though they are textually almost the same, because samples are
+ *   usually stored as bytes while coefficients are shorts or ints.  Thus,
+ *   in machines where byte pointers have a different representation from
+ *   word pointers, the resulting machine code could not be the same.
+ */
+
+
+/*
+ * Many machines require storage alignment: longs must start on 4-byte
+ * boundaries, doubles on 8-byte boundaries, etc.  On such machines, malloc()
+ * always returns pointers that are multiples of the worst-case alignment
+ * requirement, and we had better do so too.
+ * There isn't any really portable way to determine the worst-case alignment
+ * requirement.  This module assumes that the alignment requirement is
+ * multiples of sizeof(ALIGN_TYPE).
+ * By default, we define ALIGN_TYPE as double.  This is necessary on some
+ * workstations (where doubles really do need 8-byte alignment) and will work
+ * fine on nearly everything.  If your machine has lesser alignment needs,
+ * you can save a few bytes by making ALIGN_TYPE smaller.
+ * The only place I know of where this will NOT work is certain Macintosh
+ * 680x0 compilers that define double as a 10-byte IEEE extended float.
+ * Doing 10-byte alignment is counterproductive because longwords won't be
+ * aligned well.  Put "#define ALIGN_TYPE long" in jconfig.h if you have
+ * such a compiler.
+ */
+
+#ifndef ALIGN_TYPE		/* so can override from jconfig.h */
+#define ALIGN_TYPE  double
+#endif
+
+
+/*
+ * We allocate objects from "pools", where each pool is gotten with a single
+ * request to jpeg_get_small() or jpeg_get_large().  There is no per-object
+ * overhead within a pool, except for alignment padding.  Each pool has a
+ * header with a link to the next pool of the same class.
+ * Small and large pool headers are identical except that the latter's
+ * link pointer must be FAR on 80x86 machines.
+ * Notice that the "real" header fields are union'ed with a dummy ALIGN_TYPE
+ * field.  This forces the compiler to make SIZEOF(small_pool_hdr) a multiple
+ * of the alignment requirement of ALIGN_TYPE.
+ */
+
+typedef union small_pool_struct * small_pool_ptr;
+
+typedef union small_pool_struct {
+  struct {
+    small_pool_ptr next;	/* next in list of pools */
+    size_t bytes_used;		/* how many bytes already used within pool */
+    size_t bytes_left;		/* bytes still available in this pool */
+  } hdr;
+  ALIGN_TYPE dummy;		/* included in union to ensure alignment */
+} small_pool_hdr;
+
+typedef union large_pool_struct FAR * large_pool_ptr;
+
+typedef union large_pool_struct {
+  struct {
+    large_pool_ptr next;	/* next in list of pools */
+    size_t bytes_used;		/* how many bytes already used within pool */
+    size_t bytes_left;		/* bytes still available in this pool */
+  } hdr;
+  ALIGN_TYPE dummy;		/* included in union to ensure alignment */
+} large_pool_hdr;
+
+
+/*
+ * Here is the full definition of a memory manager object.
+ */
+
+typedef struct {
+  struct jpeg_memory_mgr pub;	/* public fields */
+
+  /* Each pool identifier (lifetime class) names a linked list of pools. */
+  small_pool_ptr small_list[JPOOL_NUMPOOLS];
+  large_pool_ptr large_list[JPOOL_NUMPOOLS];
+
+  /* Since we only have one lifetime class of virtual arrays, only one
+   * linked list is necessary (for each datatype).  Note that the virtual
+   * array control blocks being linked together are actually stored somewhere
+   * in the small-pool list.
+   */
+  jvirt_sarray_ptr virt_sarray_list;
+  jvirt_barray_ptr virt_barray_list;
+
+  /* This counts total space obtained from jpeg_get_small/large */
+  long total_space_allocated;
+
+  /* alloc_sarray and alloc_barray set this value for use by virtual
+   * array routines.
+   */
+  JDIMENSION last_rowsperchunk;	/* from most recent alloc_sarray/barray */
+} my_memory_mgr;
+
+typedef my_memory_mgr * my_mem_ptr;
+
+
+/*
+ * The control blocks for virtual arrays.
+ * Note that these blocks are allocated in the "small" pool area.
+ * System-dependent info for the associated backing store (if any) is hidden
+ * inside the backing_store_info struct.
+ */
+
+struct jvirt_sarray_control {
+  JSAMPARRAY mem_buffer;	/* => the in-memory buffer */
+  JDIMENSION rows_in_array;	/* total virtual array height */
+  JDIMENSION samplesperrow;	/* width of array (and of memory buffer) */
+  JDIMENSION maxaccess;		/* max rows accessed by access_virt_sarray */
+  JDIMENSION rows_in_mem;	/* height of memory buffer */
+  JDIMENSION rowsperchunk;	/* allocation chunk size in mem_buffer */
+  JDIMENSION cur_start_row;	/* first logical row # in the buffer */
+  JDIMENSION first_undef_row;	/* row # of first uninitialized row */
+  boolean pre_zero;		/* pre-zero mode requested? */
+  boolean dirty;		/* do current buffer contents need written? */
+  boolean b_s_open;		/* is backing-store data valid? */
+  jvirt_sarray_ptr next;	/* link to next virtual sarray control block */
+  backing_store_info b_s_info;	/* System-dependent control info */
+};
+
+struct jvirt_barray_control {
+  JBLOCKARRAY mem_buffer;	/* => the in-memory buffer */
+  JDIMENSION rows_in_array;	/* total virtual array height */
+  JDIMENSION blocksperrow;	/* width of array (and of memory buffer) */
+  JDIMENSION maxaccess;		/* max rows accessed by access_virt_barray */
+  JDIMENSION rows_in_mem;	/* height of memory buffer */
+  JDIMENSION rowsperchunk;	/* allocation chunk size in mem_buffer */
+  JDIMENSION cur_start_row;	/* first logical row # in the buffer */
+  JDIMENSION first_undef_row;	/* row # of first uninitialized row */
+  boolean pre_zero;		/* pre-zero mode requested? */
+  boolean dirty;		/* do current buffer contents need written? */
+  boolean b_s_open;		/* is backing-store data valid? */
+  jvirt_barray_ptr next;	/* link to next virtual barray control block */
+  backing_store_info b_s_info;	/* System-dependent control info */
+};
+
+
+#ifdef MEM_STATS		/* optional extra stuff for statistics */
+
+LOCAL(void)
+print_mem_stats (j_common_ptr cinfo, int pool_id)
+{
+  my_mem_ptr mem = (my_mem_ptr) cinfo->mem;
+  small_pool_ptr shdr_ptr;
+  large_pool_ptr lhdr_ptr;
+
+  /* Since this is only a debugging stub, we can cheat a little by using
+   * fprintf directly rather than going through the trace message code.
+   * This is helpful because message parm array can't handle longs.
+   */
+  fprintf(stderr, "Freeing pool %d, total space = %ld\n",
+	  pool_id, mem->total_space_allocated);
+
+  for (lhdr_ptr = mem->large_list[pool_id]; lhdr_ptr != NULL;
+       lhdr_ptr = lhdr_ptr->hdr.next) {
+    fprintf(stderr, "  Large chunk used %ld\n",
+	    (long) lhdr_ptr->hdr.bytes_used);
+  }
+
+  for (shdr_ptr = mem->small_list[pool_id]; shdr_ptr != NULL;
+       shdr_ptr = shdr_ptr->hdr.next) {
+    fprintf(stderr, "  Small chunk used %ld free %ld\n",
+	    (long) shdr_ptr->hdr.bytes_used,
+	    (long) shdr_ptr->hdr.bytes_left);
+  }
+}
+
+#endif /* MEM_STATS */
+
+
+LOCAL(void)
+out_of_memory (j_common_ptr cinfo, int which)
+/* Report an out-of-memory error and stop execution */
+/* If we compiled MEM_STATS support, report alloc requests before dying */
+{
+#ifdef MEM_STATS
+  cinfo->err->trace_level = 2;	/* force self_destruct to report stats */
+#endif
+  ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, which);
+}
+
+
+/*
+ * Allocation of "small" objects.
+ *
+ * For these, we use pooled storage.  When a new pool must be created,
+ * we try to get enough space for the current request plus a "slop" factor,
+ * where the slop will be the amount of leftover space in the new pool.
+ * The speed vs. space tradeoff is largely determined by the slop values.
+ * A different slop value is provided for each pool class (lifetime),
+ * and we also distinguish the first pool of a class from later ones.
+ * NOTE: the values given work fairly well on both 16- and 32-bit-int
+ * machines, but may be too small if longs are 64 bits or more.
+ */
+
+static const size_t first_pool_slop[JPOOL_NUMPOOLS] = 
+{
+	1600,			/* first PERMANENT pool */
+	16000			/* first IMAGE pool */
+};
+
+static const size_t extra_pool_slop[JPOOL_NUMPOOLS] = 
+{
+	0,			/* additional PERMANENT pools */
+	5000			/* additional IMAGE pools */
+};
+
+#define MIN_SLOP  50		/* greater than 0 to avoid futile looping */
+
+
+METHODDEF(void *)
+alloc_small (j_common_ptr cinfo, int pool_id, size_t sizeofobject)
+/* Allocate a "small" object */
+{
+  my_mem_ptr mem = (my_mem_ptr) cinfo->mem;
+  small_pool_ptr hdr_ptr, prev_hdr_ptr;
+  char * data_ptr;
+  size_t odd_bytes, min_request, slop;
+
+  /* Check for unsatisfiable request (do now to ensure no overflow below) */
+  if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(small_pool_hdr)))
+    out_of_memory(cinfo, 1);	/* request exceeds malloc's ability */
+
+  /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */
+  odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE);
+  if (odd_bytes > 0)
+    sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes;
+
+  /* See if space is available in any existing pool */
+  if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS)
+    ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id);	/* safety check */
+  prev_hdr_ptr = NULL;
+  hdr_ptr = mem->small_list[pool_id];
+  while (hdr_ptr != NULL) {
+    if (hdr_ptr->hdr.bytes_left >= sizeofobject)
+      break;			/* found pool with enough space */
+    prev_hdr_ptr = hdr_ptr;
+    hdr_ptr = hdr_ptr->hdr.next;
+  }
+
+  /* Time to make a new pool? */
+  if (hdr_ptr == NULL) {
+    /* min_request is what we need now, slop is what will be leftover */
+    min_request = sizeofobject + SIZEOF(small_pool_hdr);
+    if (prev_hdr_ptr == NULL)	/* first pool in class? */
+      slop = first_pool_slop[pool_id];
+    else
+      slop = extra_pool_slop[pool_id];
+    /* Don't ask for more than MAX_ALLOC_CHUNK */
+    if (slop > (size_t) (MAX_ALLOC_CHUNK-min_request))
+      slop = (size_t) (MAX_ALLOC_CHUNK-min_request);
+    /* Try to get space, if fail reduce slop and try again */
+    for (;;) {
+      hdr_ptr = (small_pool_ptr) jpeg_get_small(cinfo, min_request + slop);
+      if (hdr_ptr != NULL)
+	break;
+      slop /= 2;
+      if (slop < MIN_SLOP)	/* give up when it gets real small */
+	out_of_memory(cinfo, 2); /* jpeg_get_small failed */
+    }
+    mem->total_space_allocated += min_request + slop;
+    /* Success, initialize the new pool header and add to end of list */
+    hdr_ptr->hdr.next = NULL;
+    hdr_ptr->hdr.bytes_used = 0;
+    hdr_ptr->hdr.bytes_left = sizeofobject + slop;
+    if (prev_hdr_ptr == NULL)	/* first pool in class? */
+      mem->small_list[pool_id] = hdr_ptr;
+    else
+      prev_hdr_ptr->hdr.next = hdr_ptr;
+  }
+
+  /* OK, allocate the object from the current pool */
+  data_ptr = (char *) (hdr_ptr + 1); /* point to first data byte in pool */
+  data_ptr += hdr_ptr->hdr.bytes_used; /* point to place for object */
+  hdr_ptr->hdr.bytes_used += sizeofobject;
+  hdr_ptr->hdr.bytes_left -= sizeofobject;
+
+  return (void *) data_ptr;
+}
+
+
+/*
+ * Allocation of "large" objects.
+ *
+ * The external semantics of these are the same as "small" objects,
+ * except that FAR pointers are used on 80x86.  However the pool
+ * management heuristics are quite different.  We assume that each
+ * request is large enough that it may as well be passed directly to
+ * jpeg_get_large; the pool management just links everything together
+ * so that we can free it all on demand.
+ * Note: the major use of "large" objects is in JSAMPARRAY and JBLOCKARRAY
+ * structures.  The routines that create these structures (see below)
+ * deliberately bunch rows together to ensure a large request size.
+ */
+
+METHODDEF(void FAR *)
+alloc_large (j_common_ptr cinfo, int pool_id, size_t sizeofobject)
+/* Allocate a "large" object */
+{
+  my_mem_ptr mem = (my_mem_ptr) cinfo->mem;
+  large_pool_ptr hdr_ptr;
+  size_t odd_bytes;
+
+  /* Check for unsatisfiable request (do now to ensure no overflow below) */
+  if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)))
+    out_of_memory(cinfo, 3);	/* request exceeds malloc's ability */
+
+  /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */
+  odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE);
+  if (odd_bytes > 0)
+    sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes;
+
+  /* Always make a new pool */
+  if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS)
+    ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id);	/* safety check */
+
+  hdr_ptr = (large_pool_ptr) jpeg_get_large(cinfo, sizeofobject +
+					    SIZEOF(large_pool_hdr));
+  if (hdr_ptr == NULL)
+    out_of_memory(cinfo, 4);	/* jpeg_get_large failed */
+  mem->total_space_allocated += sizeofobject + SIZEOF(large_pool_hdr);
+
+  /* Success, initialize the new pool header and add to list */
+  hdr_ptr->hdr.next = mem->large_list[pool_id];
+  /* We maintain space counts in each pool header for statistical purposes,
+   * even though they are not needed for allocation.
+   */
+  hdr_ptr->hdr.bytes_used = sizeofobject;
+  hdr_ptr->hdr.bytes_left = 0;
+  mem->large_list[pool_id] = hdr_ptr;
+
+  return (void FAR *) (hdr_ptr + 1); /* point to first data byte in pool */
+}
+
+
+/*
+ * Creation of 2-D sample arrays.
+ * The pointers are in near heap, the samples themselves in FAR heap.
+ *
+ * To minimize allocation overhead and to allow I/O of large contiguous
+ * blocks, we allocate the sample rows in groups of as many rows as possible
+ * without exceeding MAX_ALLOC_CHUNK total bytes per allocation request.
+ * NB: the virtual array control routines, later in this file, know about
+ * this chunking of rows.  The rowsperchunk value is left in the mem manager
+ * object so that it can be saved away if this sarray is the workspace for
+ * a virtual array.
+ */
+
+METHODDEF(JSAMPARRAY)
+alloc_sarray (j_common_ptr cinfo, int pool_id,
+	      JDIMENSION samplesperrow, JDIMENSION numrows)
+/* Allocate a 2-D sample array */
+{
+  my_mem_ptr mem = (my_mem_ptr) cinfo->mem;
+  JSAMPARRAY result;
+  JSAMPROW workspace;
+  JDIMENSION rowsperchunk, currow, i;
+  long ltemp;
+
+  /* Calculate max # of rows allowed in one allocation chunk */
+  ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) /
+	  ((long) samplesperrow * SIZEOF(JSAMPLE));
+  if (ltemp <= 0)
+    ERREXIT(cinfo, JERR_WIDTH_OVERFLOW);
+  if (ltemp < (long) numrows)
+    rowsperchunk = (JDIMENSION) ltemp;
+  else
+    rowsperchunk = numrows;
+  mem->last_rowsperchunk = rowsperchunk;
+
+  /* Get space for row pointers (small object) */
+  result = (JSAMPARRAY) alloc_small(cinfo, pool_id,
+				    (size_t) (numrows * SIZEOF(JSAMPROW)));
+
+  /* Get the rows themselves (large objects) */
+  currow = 0;
+  while (currow < numrows) {
+    rowsperchunk = MIN(rowsperchunk, numrows - currow);
+    workspace = (JSAMPROW) alloc_large(cinfo, pool_id,
+	(size_t) ((size_t) rowsperchunk * (size_t) samplesperrow
+		  * SIZEOF(JSAMPLE)));
+    for (i = rowsperchunk; i > 0; i--) {
+      result[currow++] = workspace;
+      workspace += samplesperrow;
+    }
+  }
+
+  return result;
+}
+
+
+/*
+ * Creation of 2-D coefficient-block arrays.
+ * This is essentially the same as the code for sample arrays, above.
+ */
+
+METHODDEF(JBLOCKARRAY)
+alloc_barray (j_common_ptr cinfo, int pool_id,
+	      JDIMENSION blocksperrow, JDIMENSION numrows)
+/* Allocate a 2-D coefficient-block array */
+{
+  my_mem_ptr mem = (my_mem_ptr) cinfo->mem;
+  JBLOCKARRAY result;
+  JBLOCKROW workspace;
+  JDIMENSION rowsperchunk, currow, i;
+  long ltemp;
+
+  /* Calculate max # of rows allowed in one allocation chunk */
+  ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) /
+	  ((long) blocksperrow * SIZEOF(JBLOCK));
+  if (ltemp <= 0)
+    ERREXIT(cinfo, JERR_WIDTH_OVERFLOW);
+  if (ltemp < (long) numrows)
+    rowsperchunk = (JDIMENSION) ltemp;
+  else
+    rowsperchunk = numrows;
+  mem->last_rowsperchunk = rowsperchunk;
+
+  /* Get space for row pointers (small object) */
+  result = (JBLOCKARRAY) alloc_small(cinfo, pool_id,
+				     (size_t) (numrows * SIZEOF(JBLOCKROW)));
+
+  /* Get the rows themselves (large objects) */
+  currow = 0;
+  while (currow < numrows) {
+    rowsperchunk = MIN(rowsperchunk, numrows - currow);
+    workspace = (JBLOCKROW) alloc_large(cinfo, pool_id,
+	(size_t) ((size_t) rowsperchunk * (size_t) blocksperrow
+		  * SIZEOF(JBLOCK)));
+    for (i = rowsperchunk; i > 0; i--) {
+      result[currow++] = workspace;
+      workspace += blocksperrow;
+    }
+  }
+
+  return result;
+}
+
+
+/*
+ * About virtual array management:
+ *
+ * The above "normal" array routines are only used to allocate strip buffers
+ * (as wide as the image, but just a few rows high).  Full-image-sized buffers
+ * are handled as "virtual" arrays.  The array is still accessed a strip at a
+ * time, but the memory manager must save the whole array for repeated
+ * accesses.  The intended implementation is that there is a strip buffer in
+ * memory (as high as is possible given the desired memory limit), plus a
+ * backing file that holds the rest of the array.
+ *
+ * The request_virt_array routines are told the total size of the image and
+ * the maximum number of rows that will be accessed at once.  The in-memory
+ * buffer must be at least as large as the maxaccess value.
+ *
+ * The request routines create control blocks but not the in-memory buffers.
+ * That is postponed until realize_virt_arrays is called.  At that time the
+ * total amount of space needed is known (approximately, anyway), so free
+ * memory can be divided up fairly.
+ *
+ * The access_virt_array routines are responsible for making a specific strip
+ * area accessible (after reading or writing the backing file, if necessary).
+ * Note that the access routines are told whether the caller intends to modify
+ * the accessed strip; during a read-only pass this saves having to rewrite
+ * data to disk.  The access routines are also responsible for pre-zeroing
+ * any newly accessed rows, if pre-zeroing was requested.
+ *
+ * In current usage, the access requests are usually for nonoverlapping
+ * strips; that is, successive access start_row numbers differ by exactly
+ * num_rows = maxaccess.  This means we can get good performance with simple
+ * buffer dump/reload logic, by making the in-memory buffer be a multiple
+ * of the access height; then there will never be accesses across bufferload
+ * boundaries.  The code will still work with overlapping access requests,
+ * but it doesn't handle bufferload overlaps very efficiently.
+ */
+
+
+METHODDEF(jvirt_sarray_ptr)
+request_virt_sarray (j_common_ptr cinfo, int pool_id, boolean pre_zero,
+		     JDIMENSION samplesperrow, JDIMENSION numrows,
+		     JDIMENSION maxaccess)
+/* Request a virtual 2-D sample array */
+{
+  my_mem_ptr mem = (my_mem_ptr) cinfo->mem;
+  jvirt_sarray_ptr result;
+
+  /* Only IMAGE-lifetime virtual arrays are currently supported */
+  if (pool_id != JPOOL_IMAGE)
+    ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id);	/* safety check */
+
+  /* get control block */
+  result = (jvirt_sarray_ptr) alloc_small(cinfo, pool_id,
+					  SIZEOF(struct jvirt_sarray_control));
+
+  result->mem_buffer = NULL;	/* marks array not yet realized */
+  result->rows_in_array = numrows;
+  result->samplesperrow = samplesperrow;
+  result->maxaccess = maxaccess;
+  result->pre_zero = pre_zero;
+  result->b_s_open = FALSE;	/* no associated backing-store object */
+  result->next = mem->virt_sarray_list; /* add to list of virtual arrays */
+  mem->virt_sarray_list = result;
+
+  return result;
+}
+
+
+METHODDEF(jvirt_barray_ptr)
+request_virt_barray (j_common_ptr cinfo, int pool_id, boolean pre_zero,
+		     JDIMENSION blocksperrow, JDIMENSION numrows,
+		     JDIMENSION maxaccess)
+/* Request a virtual 2-D coefficient-block array */
+{
+  my_mem_ptr mem = (my_mem_ptr) cinfo->mem;
+  jvirt_barray_ptr result;
+
+  /* Only IMAGE-lifetime virtual arrays are currently supported */
+  if (pool_id != JPOOL_IMAGE)
+    ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id);	/* safety check */
+
+  /* get control block */
+  result = (jvirt_barray_ptr) alloc_small(cinfo, pool_id,
+					  SIZEOF(struct jvirt_barray_control));
+
+  result->mem_buffer = NULL;	/* marks array not yet realized */
+  result->rows_in_array = numrows;
+  result->blocksperrow = blocksperrow;
+  result->maxaccess = maxaccess;
+  result->pre_zero = pre_zero;
+  result->b_s_open = FALSE;	/* no associated backing-store object */
+  result->next = mem->virt_barray_list; /* add to list of virtual arrays */
+  mem->virt_barray_list = result;
+
+  return result;
+}
+
+
+METHODDEF(void)
+realize_virt_arrays (j_common_ptr cinfo)
+/* Allocate the in-memory buffers for any unrealized virtual arrays */
+{
+  my_mem_ptr mem = (my_mem_ptr) cinfo->mem;
+  long space_per_minheight, maximum_space, avail_mem;
+  long minheights, max_minheights;
+  jvirt_sarray_ptr sptr;
+  jvirt_barray_ptr bptr;
+
+  /* Compute the minimum space needed (maxaccess rows in each buffer)
+   * and the maximum space needed (full image height in each buffer).
+   * These may be of use to the system-dependent jpeg_mem_available routine.
+   */
+  space_per_minheight = 0;
+  maximum_space = 0;
+  for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) {
+    if (sptr->mem_buffer == NULL) { /* if not realized yet */
+      space_per_minheight += (long) sptr->maxaccess *
+			     (long) sptr->samplesperrow * SIZEOF(JSAMPLE);
+      maximum_space += (long) sptr->rows_in_array *
+		       (long) sptr->samplesperrow * SIZEOF(JSAMPLE);
+    }
+  }
+  for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) {
+    if (bptr->mem_buffer == NULL) { /* if not realized yet */
+      space_per_minheight += (long) bptr->maxaccess *
+			     (long) bptr->blocksperrow * SIZEOF(JBLOCK);
+      maximum_space += (long) bptr->rows_in_array *
+		       (long) bptr->blocksperrow * SIZEOF(JBLOCK);
+    }
+  }
+
+  if (space_per_minheight <= 0)
+    return;			/* no unrealized arrays, no work */
+
+  /* Determine amount of memory to actually use; this is system-dependent. */
+  avail_mem = jpeg_mem_available(cinfo, space_per_minheight, maximum_space,
+				 mem->total_space_allocated);
+
+  /* If the maximum space needed is available, make all the buffers full
+   * height; otherwise parcel it out with the same number of minheights
+   * in each buffer.
+   */
+  if (avail_mem >= maximum_space)
+    max_minheights = 1000000000L;
+  else {
+    max_minheights = avail_mem / space_per_minheight;
+    /* If there doesn't seem to be enough space, try to get the minimum
+     * anyway.  This allows a "stub" implementation of jpeg_mem_available().
+     */
+    if (max_minheights <= 0)
+      max_minheights = 1;
+  }
+
+  /* Allocate the in-memory buffers and initialize backing store as needed. */
+
+  for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) {
+    if (sptr->mem_buffer == NULL) { /* if not realized yet */
+      minheights = ((long) sptr->rows_in_array - 1L) / sptr->maxaccess + 1L;
+      if (minheights <= max_minheights) {
+	/* This buffer fits in memory */
+	sptr->rows_in_mem = sptr->rows_in_array;
+      } else {
+	/* It doesn't fit in memory, create backing store. */
+	sptr->rows_in_mem = (JDIMENSION) (max_minheights * sptr->maxaccess);
+	jpeg_open_backing_store(cinfo, & sptr->b_s_info,
+				(long) sptr->rows_in_array *
+				(long) sptr->samplesperrow *
+				(long) SIZEOF(JSAMPLE));
+	sptr->b_s_open = TRUE;
+      }
+      sptr->mem_buffer = alloc_sarray(cinfo, JPOOL_IMAGE,
+				      sptr->samplesperrow, sptr->rows_in_mem);
+      sptr->rowsperchunk = mem->last_rowsperchunk;
+      sptr->cur_start_row = 0;
+      sptr->first_undef_row = 0;
+      sptr->dirty = FALSE;
+    }
+  }
+
+  for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) {
+    if (bptr->mem_buffer == NULL) { /* if not realized yet */
+      minheights = ((long) bptr->rows_in_array - 1L) / bptr->maxaccess + 1L;
+      if (minheights <= max_minheights) {
+	/* This buffer fits in memory */
+	bptr->rows_in_mem = bptr->rows_in_array;
+      } else {
+	/* It doesn't fit in memory, create backing store. */
+	bptr->rows_in_mem = (JDIMENSION) (max_minheights * bptr->maxaccess);
+	jpeg_open_backing_store(cinfo, & bptr->b_s_info,
+				(long) bptr->rows_in_array *
+				(long) bptr->blocksperrow *
+				(long) SIZEOF(JBLOCK));
+	bptr->b_s_open = TRUE;
+      }
+      bptr->mem_buffer = alloc_barray(cinfo, JPOOL_IMAGE,
+				      bptr->blocksperrow, bptr->rows_in_mem);
+      bptr->rowsperchunk = mem->last_rowsperchunk;
+      bptr->cur_start_row = 0;
+      bptr->first_undef_row = 0;
+      bptr->dirty = FALSE;
+    }
+  }
+}
+
+
+LOCAL(void)
+do_sarray_io (j_common_ptr cinfo, jvirt_sarray_ptr ptr, boolean writing)
+/* Do backing store read or write of a virtual sample array */
+{
+  long bytesperrow, file_offset, byte_count, rows, thisrow, i;
+
+  bytesperrow = (long) ptr->samplesperrow * SIZEOF(JSAMPLE);
+  file_offset = ptr->cur_start_row * bytesperrow;
+  /* Loop to read or write each allocation chunk in mem_buffer */
+  for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) {
+    /* One chunk, but check for short chunk at end of buffer */
+    rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i);
+    /* Transfer no more than is currently defined */
+    thisrow = (long) ptr->cur_start_row + i;
+    rows = MIN(rows, (long) ptr->first_undef_row - thisrow);
+    /* Transfer no more than fits in file */
+    rows = MIN(rows, (long) ptr->rows_in_array - thisrow);
+    if (rows <= 0)		/* this chunk might be past end of file! */
+      break;
+    byte_count = rows * bytesperrow;
+    if (writing)
+      (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info,
+					    (void FAR *) ptr->mem_buffer[i],
+					    file_offset, byte_count);
+    else
+      (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info,
+					   (void FAR *) ptr->mem_buffer[i],
+					   file_offset, byte_count);
+    file_offset += byte_count;
+  }
+}
+
+
+LOCAL(void)
+do_barray_io (j_common_ptr cinfo, jvirt_barray_ptr ptr, boolean writing)
+/* Do backing store read or write of a virtual coefficient-block array */
+{
+  long bytesperrow, file_offset, byte_count, rows, thisrow, i;
+
+  bytesperrow = (long) ptr->blocksperrow * SIZEOF(JBLOCK);
+  file_offset = ptr->cur_start_row * bytesperrow;
+  /* Loop to read or write each allocation chunk in mem_buffer */
+  for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) {
+    /* One chunk, but check for short chunk at end of buffer */
+    rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i);
+    /* Transfer no more than is currently defined */
+    thisrow = (long) ptr->cur_start_row + i;
+    rows = MIN(rows, (long) ptr->first_undef_row - thisrow);
+    /* Transfer no more than fits in file */
+    rows = MIN(rows, (long) ptr->rows_in_array - thisrow);
+    if (rows <= 0)		/* this chunk might be past end of file! */
+      break;
+    byte_count = rows * bytesperrow;
+    if (writing)
+      (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info,
+					    (void FAR *) ptr->mem_buffer[i],
+					    file_offset, byte_count);
+    else
+      (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info,
+					   (void FAR *) ptr->mem_buffer[i],
+					   file_offset, byte_count);
+    file_offset += byte_count;
+  }
+}
+
+
+METHODDEF(JSAMPARRAY)
+access_virt_sarray (j_common_ptr cinfo, jvirt_sarray_ptr ptr,
+		    JDIMENSION start_row, JDIMENSION num_rows,
+		    boolean writable)
+/* Access the part of a virtual sample array starting at start_row */
+/* and extending for num_rows rows.  writable is true if  */
+/* caller intends to modify the accessed area. */
+{
+  JDIMENSION end_row = start_row + num_rows;
+  JDIMENSION undef_row;
+
+  /* debugging check */
+  if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess ||
+      ptr->mem_buffer == NULL)
+    ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS);
+
+  /* Make the desired part of the virtual array accessible */
+  if (start_row < ptr->cur_start_row ||
+      end_row > ptr->cur_start_row+ptr->rows_in_mem) {
+    if (! ptr->b_s_open)
+      ERREXIT(cinfo, JERR_VIRTUAL_BUG);
+    /* Flush old buffer contents if necessary */
+    if (ptr->dirty) {
+      do_sarray_io(cinfo, ptr, TRUE);
+      ptr->dirty = FALSE;
+    }
+    /* Decide what part of virtual array to access.
+     * Algorithm: if target address > current window, assume forward scan,
+     * load starting at target address.  If target address < current window,
+     * assume backward scan, load so that target area is top of window.
+     * Note that when switching from forward write to forward read, will have
+     * start_row = 0, so the limiting case applies and we load from 0 anyway.
+     */
+    if (start_row > ptr->cur_start_row) {
+      ptr->cur_start_row = start_row;
+    } else {
+      /* use long arithmetic here to avoid overflow & unsigned problems */
+      long ltemp;
+
+      ltemp = (long) end_row - (long) ptr->rows_in_mem;
+      if (ltemp < 0)
+	ltemp = 0;		/* don't fall off front end of file */
+      ptr->cur_start_row = (JDIMENSION) ltemp;
+    }
+    /* Read in the selected part of the array.
+     * During the initial write pass, we will do no actual read
+     * because the selected part is all undefined.
+     */
+    do_sarray_io(cinfo, ptr, FALSE);
+  }
+  /* Ensure the accessed part of the array is defined; prezero if needed.
+   * To improve locality of access, we only prezero the part of the array
+   * that the caller is about to access, not the entire in-memory array.
+   */
+  if (ptr->first_undef_row < end_row) {
+    if (ptr->first_undef_row < start_row) {
+      if (writable)		/* writer skipped over a section of array */
+	ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS);
+      undef_row = start_row;	/* but reader is allowed to read ahead */
+    } else {
+      undef_row = ptr->first_undef_row;
+    }
+    if (writable)
+      ptr->first_undef_row = end_row;
+    if (ptr->pre_zero) {
+      size_t bytesperrow = (size_t) ptr->samplesperrow * SIZEOF(JSAMPLE);
+      undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */
+      end_row -= ptr->cur_start_row;
+      while (undef_row < end_row) {
+	jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow);
+	undef_row++;
+      }
+    } else {
+      if (! writable)		/* reader looking at undefined data */
+	ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS);
+    }
+  }
+  /* Flag the buffer dirty if caller will write in it */
+  if (writable)
+    ptr->dirty = TRUE;
+  /* Return address of proper part of the buffer */
+  return ptr->mem_buffer + (start_row - ptr->cur_start_row);
+}
+
+
+METHODDEF(JBLOCKARRAY)
+access_virt_barray (j_common_ptr cinfo, jvirt_barray_ptr ptr,
+		    JDIMENSION start_row, JDIMENSION num_rows,
+		    boolean writable)
+/* Access the part of a virtual block array starting at start_row */
+/* and extending for num_rows rows.  writable is true if  */
+/* caller intends to modify the accessed area. */
+{
+  JDIMENSION end_row = start_row + num_rows;
+  JDIMENSION undef_row;
+
+  /* debugging check */
+  if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess ||
+      ptr->mem_buffer == NULL)
+    ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS);
+
+  /* Make the desired part of the virtual array accessible */
+  if (start_row < ptr->cur_start_row ||
+      end_row > ptr->cur_start_row+ptr->rows_in_mem) {
+    if (! ptr->b_s_open)
+      ERREXIT(cinfo, JERR_VIRTUAL_BUG);
+    /* Flush old buffer contents if necessary */
+    if (ptr->dirty) {
+      do_barray_io(cinfo, ptr, TRUE);
+      ptr->dirty = FALSE;
+    }
+    /* Decide what part of virtual array to access.
+     * Algorithm: if target address > current window, assume forward scan,
+     * load starting at target address.  If target address < current window,
+     * assume backward scan, load so that target area is top of window.
+     * Note that when switching from forward write to forward read, will have
+     * start_row = 0, so the limiting case applies and we load from 0 anyway.
+     */
+    if (start_row > ptr->cur_start_row) {
+      ptr->cur_start_row = start_row;
+    } else {
+      /* use long arithmetic here to avoid overflow & unsigned problems */
+      long ltemp;
+
+      ltemp = (long) end_row - (long) ptr->rows_in_mem;
+      if (ltemp < 0)
+	ltemp = 0;		/* don't fall off front end of file */
+      ptr->cur_start_row = (JDIMENSION) ltemp;
+    }
+    /* Read in the selected part of the array.
+     * During the initial write pass, we will do no actual read
+     * because the selected part is all undefined.
+     */
+    do_barray_io(cinfo, ptr, FALSE);
+  }
+  /* Ensure the accessed part of the array is defined; prezero if needed.
+   * To improve locality of access, we only prezero the part of the array
+   * that the caller is about to access, not the entire in-memory array.
+   */
+  if (ptr->first_undef_row < end_row) {
+    if (ptr->first_undef_row < start_row) {
+      if (writable)		/* writer skipped over a section of array */
+	ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS);
+      undef_row = start_row;	/* but reader is allowed to read ahead */
+    } else {
+      undef_row = ptr->first_undef_row;
+    }
+    if (writable)
+      ptr->first_undef_row = end_row;
+    if (ptr->pre_zero) {
+      size_t bytesperrow = (size_t) ptr->blocksperrow * SIZEOF(JBLOCK);
+      undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */
+      end_row -= ptr->cur_start_row;
+      while (undef_row < end_row) {
+	jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow);
+	undef_row++;
+      }
+    } else {
+      if (! writable)		/* reader looking at undefined data */
+	ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS);
+    }
+  }
+  /* Flag the buffer dirty if caller will write in it */
+  if (writable)
+    ptr->dirty = TRUE;
+  /* Return address of proper part of the buffer */
+  return ptr->mem_buffer + (start_row - ptr->cur_start_row);
+}
+
+
+/*
+ * Release all objects belonging to a specified pool.
+ */
+
+METHODDEF(void)
+free_pool (j_common_ptr cinfo, int pool_id)
+{
+  my_mem_ptr mem = (my_mem_ptr) cinfo->mem;
+  small_pool_ptr shdr_ptr;
+  large_pool_ptr lhdr_ptr;
+  size_t space_freed;
+
+  if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS)
+    ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id);	/* safety check */
+
+#ifdef MEM_STATS
+  if (cinfo->err->trace_level > 1)
+    print_mem_stats(cinfo, pool_id); /* print pool's memory usage statistics */
+#endif
+
+  /* If freeing IMAGE pool, close any virtual arrays first */
+  if (pool_id == JPOOL_IMAGE) {
+    jvirt_sarray_ptr sptr;
+    jvirt_barray_ptr bptr;
+
+    for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) {
+      if (sptr->b_s_open) {	/* there may be no backing store */
+	sptr->b_s_open = FALSE;	/* prevent recursive close if error */
+	(*sptr->b_s_info.close_backing_store) (cinfo, & sptr->b_s_info);
+      }
+    }
+    mem->virt_sarray_list = NULL;
+    for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) {
+      if (bptr->b_s_open) {	/* there may be no backing store */
+	bptr->b_s_open = FALSE;	/* prevent recursive close if error */
+	(*bptr->b_s_info.close_backing_store) (cinfo, & bptr->b_s_info);
+      }
+    }
+    mem->virt_barray_list = NULL;
+  }
+
+  /* Release large objects */
+  lhdr_ptr = mem->large_list[pool_id];
+  mem->large_list[pool_id] = NULL;
+
+  while (lhdr_ptr != NULL) {
+    large_pool_ptr next_lhdr_ptr = lhdr_ptr->hdr.next;
+    space_freed = lhdr_ptr->hdr.bytes_used +
+		  lhdr_ptr->hdr.bytes_left +
+		  SIZEOF(large_pool_hdr);
+    jpeg_free_large(cinfo, (void FAR *) lhdr_ptr, space_freed);
+    mem->total_space_allocated -= space_freed;
+    lhdr_ptr = next_lhdr_ptr;
+  }
+
+  /* Release small objects */
+  shdr_ptr = mem->small_list[pool_id];
+  mem->small_list[pool_id] = NULL;
+
+  while (shdr_ptr != NULL) {
+    small_pool_ptr next_shdr_ptr = shdr_ptr->hdr.next;
+    space_freed = shdr_ptr->hdr.bytes_used +
+		  shdr_ptr->hdr.bytes_left +
+		  SIZEOF(small_pool_hdr);
+    jpeg_free_small(cinfo, (void *) shdr_ptr, space_freed);
+    mem->total_space_allocated -= space_freed;
+    shdr_ptr = next_shdr_ptr;
+  }
+}
+
+
+/*
+ * Close up shop entirely.
+ * Note that this cannot be called unless cinfo->mem is non-NULL.
+ */
+
+METHODDEF(void)
+self_destruct (j_common_ptr cinfo)
+{
+  int pool;
+
+  /* Close all backing store, release all memory.
+   * Releasing pools in reverse order might help avoid fragmentation
+   * with some (brain-damaged) malloc libraries.
+   */
+  for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) {
+    free_pool(cinfo, pool);
+  }
+
+  /* Release the memory manager control block too. */
+  jpeg_free_small(cinfo, (void *) cinfo->mem, SIZEOF(my_memory_mgr));
+  cinfo->mem = NULL;		/* ensures I will be called only once */
+
+  jpeg_mem_term(cinfo);		/* system-dependent cleanup */
+}
+
+
+/*
+ * Memory manager initialization.
+ * When this is called, only the error manager pointer is valid in cinfo!
+ */
+
+GLOBAL(void)
+jinit_memory_mgr (j_common_ptr cinfo)
+{
+  my_mem_ptr mem;
+  long max_to_use;
+  int pool;
+  size_t test_mac;
+
+  cinfo->mem = NULL;		/* for safety if init fails */
+
+  /* Check for configuration errors.
+   * SIZEOF(ALIGN_TYPE) should be a power of 2; otherwise, it probably
+   * doesn't reflect any real hardware alignment requirement.
+   * The test is a little tricky: for X>0, X and X-1 have no one-bits
+   * in common if and only if X is a power of 2, ie has only one one-bit.
+   * Some compilers may give an "unreachable code" warning here; ignore it.
+   */
+  if ((SIZEOF(ALIGN_TYPE) & (SIZEOF(ALIGN_TYPE)-1)) != 0)
+    ERREXIT(cinfo, JERR_BAD_ALIGN_TYPE);
+  /* MAX_ALLOC_CHUNK must be representable as type size_t, and must be
+   * a multiple of SIZEOF(ALIGN_TYPE).
+   * Again, an "unreachable code" warning may be ignored here.
+   * But a "constant too large" warning means you need to fix MAX_ALLOC_CHUNK.
+   */
+  test_mac = (size_t) MAX_ALLOC_CHUNK;
+  if ((long) test_mac != MAX_ALLOC_CHUNK ||
+      (MAX_ALLOC_CHUNK % SIZEOF(ALIGN_TYPE)) != 0)
+    ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK);
+
+  max_to_use = jpeg_mem_init(cinfo); /* system-dependent initialization */
+
+  /* Attempt to allocate memory manager's control block */
+  mem = (my_mem_ptr) jpeg_get_small(cinfo, SIZEOF(my_memory_mgr));
+
+  if (mem == NULL) {
+    jpeg_mem_term(cinfo);	/* system-dependent cleanup */
+    ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 0);
+  }
+
+  /* OK, fill in the method pointers */
+  mem->pub.alloc_small = alloc_small;
+  mem->pub.alloc_large = alloc_large;
+  mem->pub.alloc_sarray = alloc_sarray;
+  mem->pub.alloc_barray = alloc_barray;
+  mem->pub.request_virt_sarray = request_virt_sarray;
+  mem->pub.request_virt_barray = request_virt_barray;
+  mem->pub.realize_virt_arrays = realize_virt_arrays;
+  mem->pub.access_virt_sarray = access_virt_sarray;
+  mem->pub.access_virt_barray = access_virt_barray;
+  mem->pub.free_pool = free_pool;
+  mem->pub.self_destruct = self_destruct;
+
+  /* Make MAX_ALLOC_CHUNK accessible to other modules */
+  mem->pub.max_alloc_chunk = MAX_ALLOC_CHUNK;
+
+  /* Initialize working state */
+  mem->pub.max_memory_to_use = max_to_use;
+
+  for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) {
+    mem->small_list[pool] = NULL;
+    mem->large_list[pool] = NULL;
+  }
+  mem->virt_sarray_list = NULL;
+  mem->virt_barray_list = NULL;
+
+  mem->total_space_allocated = SIZEOF(my_memory_mgr);
+
+  /* Declare ourselves open for business */
+  cinfo->mem = & mem->pub;
+
+  /* Check for an environment variable JPEGMEM; if found, override the
+   * default max_memory setting from jpeg_mem_init.  Note that the
+   * surrounding application may again override this value.
+   * If your system doesn't support getenv(), define NO_GETENV to disable
+   * this feature.
+   */
+#ifndef NO_GETENV
+  { char * memenv;
+
+    if ((memenv = getenv("JPEGMEM")) != NULL) {
+      char ch = 'x';
+
+      if (sscanf(memenv, "%ld%c", &max_to_use, &ch) > 0) {
+	if (ch == 'm' || ch == 'M')
+	  max_to_use *= 1000L;
+	mem->pub.max_memory_to_use = max_to_use * 1000L;
+      }
+    }
+  }
+#endif
+
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jmemsys.h b/Utilities/GDAL/frmts/jpeg/libjpeg/jmemsys.h
new file mode 100644
index 0000000000..6c3c6d348f
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jmemsys.h
@@ -0,0 +1,198 @@
+/*
+ * jmemsys.h
+ *
+ * Copyright (C) 1992-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.
+ *
+ * This include file defines the interface between the system-independent
+ * and system-dependent portions of the JPEG memory manager.  No other
+ * modules need include it.  (The system-independent portion is jmemmgr.c;
+ * there are several different versions of the system-dependent portion.)
+ *
+ * This file works as-is for the system-dependent memory managers supplied
+ * in the IJG distribution.  You may need to modify it if you write a
+ * custom memory manager.  If system-dependent changes are needed in
+ * this file, the best method is to #ifdef them based on a configuration
+ * symbol supplied in jconfig.h, as we have done with USE_MSDOS_MEMMGR
+ * and USE_MAC_MEMMGR.
+ */
+
+
+/* Short forms of external names for systems with brain-damaged linkers. */
+
+#ifdef NEED_SHORT_EXTERNAL_NAMES
+#define jpeg_get_small		jGetSmall
+#define jpeg_free_small		jFreeSmall
+#define jpeg_get_large		jGetLarge
+#define jpeg_free_large		jFreeLarge
+#define jpeg_mem_available	jMemAvail
+#define jpeg_open_backing_store	jOpenBackStore
+#define jpeg_mem_init		jMemInit
+#define jpeg_mem_term		jMemTerm
+#endif /* NEED_SHORT_EXTERNAL_NAMES */
+
+
+/*
+ * These two functions are used to allocate and release small chunks of
+ * memory.  (Typically the total amount requested through jpeg_get_small is
+ * no more than 20K or so; this will be requested in chunks of a few K each.)
+ * Behavior should be the same as for the standard library functions malloc
+ * and free; in particular, jpeg_get_small must return NULL on failure.
+ * On most systems, these ARE malloc and free.  jpeg_free_small is passed the
+ * size of the object being freed, just in case it's needed.
+ * On an 80x86 machine using small-data memory model, these manage near heap.
+ */
+
+EXTERN(void *) jpeg_get_small JPP((j_common_ptr cinfo, size_t sizeofobject));
+EXTERN(void) jpeg_free_small JPP((j_common_ptr cinfo, void * object,
+				  size_t sizeofobject));
+
+/*
+ * These two functions are used to allocate and release large chunks of
+ * memory (up to the total free space designated by jpeg_mem_available).
+ * The interface is the same as above, except that on an 80x86 machine,
+ * far pointers are used.  On most other machines these are identical to
+ * the jpeg_get/free_small routines; but we keep them separate anyway,
+ * in case a different allocation strategy is desirable for large chunks.
+ */
+
+EXTERN(void FAR *) jpeg_get_large JPP((j_common_ptr cinfo,
+				       size_t sizeofobject));
+EXTERN(void) jpeg_free_large JPP((j_common_ptr cinfo, void FAR * object,
+				  size_t sizeofobject));
+
+/*
+ * The macro MAX_ALLOC_CHUNK designates the maximum number of bytes that may
+ * be requested in a single call to jpeg_get_large (and jpeg_get_small for that
+ * matter, but that case should never come into play).  This macro is needed
+ * to model the 64Kb-segment-size limit of far addressing on 80x86 machines.
+ * On those machines, we expect that jconfig.h will provide a proper value.
+ * On machines with 32-bit flat address spaces, any large constant may be used.
+ *
+ * NB: jmemmgr.c expects that MAX_ALLOC_CHUNK will be representable as type
+ * size_t and will be a multiple of sizeof(align_type).
+ */
+
+#ifndef MAX_ALLOC_CHUNK		/* may be overridden in jconfig.h */
+#define MAX_ALLOC_CHUNK  1000000000L
+#endif
+
+/*
+ * This routine computes the total space still available for allocation by
+ * jpeg_get_large.  If more space than this is needed, backing store will be
+ * used.  NOTE: any memory already allocated must not be counted.
+ *
+ * There is a minimum space requirement, corresponding to the minimum
+ * feasible buffer sizes; jmemmgr.c will request that much space even if
+ * jpeg_mem_available returns zero.  The maximum space needed, enough to hold
+ * all working storage in memory, is also passed in case it is useful.
+ * Finally, the total space already allocated is passed.  If no better
+ * method is available, cinfo->mem->max_memory_to_use - already_allocated
+ * is often a suitable calculation.
+ *
+ * It is OK for jpeg_mem_available to underestimate the space available
+ * (that'll just lead to more backing-store access than is really necessary).
+ * However, an overestimate will lead to failure.  Hence it's wise to subtract
+ * a slop factor from the true available space.  5% should be enough.
+ *
+ * On machines with lots of virtual memory, any large constant may be returned.
+ * Conversely, zero may be returned to always use the minimum amount of memory.
+ */
+
+EXTERN(long) jpeg_mem_available JPP((j_common_ptr cinfo,
+				     long min_bytes_needed,
+				     long max_bytes_needed,
+				     long already_allocated));
+
+
+/*
+ * This structure holds whatever state is needed to access a single
+ * backing-store object.  The read/write/close method pointers are called
+ * by jmemmgr.c to manipulate the backing-store object; all other fields
+ * are private to the system-dependent backing store routines.
+ */
+
+#define TEMP_NAME_LENGTH   64	/* max length of a temporary file's name */
+
+
+#ifdef USE_MSDOS_MEMMGR		/* DOS-specific junk */
+
+typedef unsigned short XMSH;	/* type of extended-memory handles */
+typedef unsigned short EMSH;	/* type of expanded-memory handles */
+
+typedef union {
+  short file_handle;		/* DOS file handle if it's a temp file */
+  XMSH xms_handle;		/* handle if it's a chunk of XMS */
+  EMSH ems_handle;		/* handle if it's a chunk of EMS */
+} handle_union;
+
+#endif /* USE_MSDOS_MEMMGR */
+
+#ifdef USE_MAC_MEMMGR		/* Mac-specific junk */
+#include <Files.h>
+#endif /* USE_MAC_MEMMGR */
+
+
+typedef struct backing_store_struct * backing_store_ptr;
+
+typedef struct backing_store_struct {
+  /* Methods for reading/writing/closing this backing-store object */
+  JMETHOD(void, read_backing_store, (j_common_ptr cinfo,
+				     backing_store_ptr info,
+				     void FAR * buffer_address,
+				     long file_offset, long byte_count));
+  JMETHOD(void, write_backing_store, (j_common_ptr cinfo,
+				      backing_store_ptr info,
+				      void FAR * buffer_address,
+				      long file_offset, long byte_count));
+  JMETHOD(void, close_backing_store, (j_common_ptr cinfo,
+				      backing_store_ptr info));
+
+  /* Private fields for system-dependent backing-store management */
+#ifdef USE_MSDOS_MEMMGR
+  /* For the MS-DOS manager (jmemdos.c), we need: */
+  handle_union handle;		/* reference to backing-store storage object */
+  char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */
+#else
+#ifdef USE_MAC_MEMMGR
+  /* For the Mac manager (jmemmac.c), we need: */
+  short temp_file;		/* file reference number to temp file */
+  FSSpec tempSpec;		/* the FSSpec for the temp file */
+  char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */
+#else
+  /* For a typical implementation with temp files, we need: */
+  FILE * temp_file;		/* stdio reference to temp file */
+  char temp_name[TEMP_NAME_LENGTH]; /* name of temp file */
+#endif
+#endif
+} backing_store_info;
+
+
+/*
+ * Initial opening of a backing-store object.  This must fill in the
+ * read/write/close pointers in the object.  The read/write routines
+ * may take an error exit if the specified maximum file size is exceeded.
+ * (If jpeg_mem_available always returns a large value, this routine can
+ * just take an error exit.)
+ */
+
+EXTERN(void) jpeg_open_backing_store JPP((j_common_ptr cinfo,
+					  backing_store_ptr info,
+					  long total_bytes_needed));
+
+
+/*
+ * These routines take care of any system-dependent initialization and
+ * cleanup required.  jpeg_mem_init will be called before anything is
+ * allocated (and, therefore, nothing in cinfo is of use except the error
+ * manager pointer).  It should return a suitable default value for
+ * max_memory_to_use; this may subsequently be overridden by the surrounding
+ * application.  (Note that max_memory_to_use is only important if
+ * jpeg_mem_available chooses to consult it ... no one else will.)
+ * jpeg_mem_term may assume that all requested memory has been freed and that
+ * all opened backing-store objects have been closed.
+ */
+
+EXTERN(long) jpeg_mem_init JPP((j_common_ptr cinfo));
+EXTERN(void) jpeg_mem_term JPP((j_common_ptr cinfo));
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jmorecfg.h b/Utilities/GDAL/frmts/jpeg/libjpeg/jmorecfg.h
new file mode 100644
index 0000000000..2c0edf9a08
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jmorecfg.h
@@ -0,0 +1,371 @@
+/*
+ * jmorecfg.h
+ *
+ * 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.
+ *
+ * This file contains additional configuration options that customize the
+ * JPEG software for special applications or support machine-dependent
+ * optimizations.  Most users will not need to touch this file.
+ */
+
+
+/*
+ * Define BITS_IN_JSAMPLE as either
+ *   8   for 8-bit sample values (the usual setting)
+ *   12  for 12-bit sample values
+ * Only 8 and 12 are legal data precisions for lossy JPEG according to the
+ * JPEG standard, and the IJG code does not support anything else!
+ * We do not support run-time selection of data precision, sorry.
+ */
+
+#define BITS_IN_JSAMPLE  8	/* use 8 or 12 */
+
+
+/*
+ * Maximum number of components (color channels) allowed in JPEG image.
+ * To meet the letter of the JPEG spec, set this to 255.  However, darn
+ * few applications need more than 4 channels (maybe 5 for CMYK + alpha
+ * mask).  We recommend 10 as a reasonable compromise; use 4 if you are
+ * really short on memory.  (Each allowed component costs a hundred or so
+ * bytes of storage, whether actually used in an image or not.)
+ */
+
+#define MAX_COMPONENTS  10	/* maximum number of image components */
+
+
+/*
+ * Basic data types.
+ * You may need to change these if you have a machine with unusual data
+ * type sizes; for example, "char" not 8 bits, "short" not 16 bits,
+ * or "long" not 32 bits.  We don't care whether "int" is 16 or 32 bits,
+ * but it had better be at least 16.
+ */
+
+/* Representation of a single sample (pixel element value).
+ * We frequently allocate large arrays of these, so it's important to keep
+ * them small.  But if you have memory to burn and access to char or short
+ * arrays is very slow on your hardware, you might want to change these.
+ */
+
+#if BITS_IN_JSAMPLE == 8
+/* JSAMPLE should be the smallest type that will hold the values 0..255.
+ * You can use a signed char by having GETJSAMPLE mask it with 0xFF.
+ */
+
+#ifdef HAVE_UNSIGNED_CHAR
+
+typedef unsigned char JSAMPLE;
+#define GETJSAMPLE(value)  ((int) (value))
+
+#else /* not HAVE_UNSIGNED_CHAR */
+
+typedef char JSAMPLE;
+#ifdef CHAR_IS_UNSIGNED
+#define GETJSAMPLE(value)  ((int) (value))
+#else
+#define GETJSAMPLE(value)  ((int) (value) & 0xFF)
+#endif /* CHAR_IS_UNSIGNED */
+
+#endif /* HAVE_UNSIGNED_CHAR */
+
+#define MAXJSAMPLE	255
+#define CENTERJSAMPLE	128
+
+#endif /* BITS_IN_JSAMPLE == 8 */
+
+
+#if BITS_IN_JSAMPLE == 12
+/* JSAMPLE should be the smallest type that will hold the values 0..4095.
+ * On nearly all machines "short" will do nicely.
+ */
+
+typedef short JSAMPLE;
+#define GETJSAMPLE(value)  ((int) (value))
+
+#define MAXJSAMPLE	4095
+#define CENTERJSAMPLE	2048
+
+#endif /* BITS_IN_JSAMPLE == 12 */
+
+
+/* Representation of a DCT frequency coefficient.
+ * This should be a signed value of at least 16 bits; "short" is usually OK.
+ * Again, we allocate large arrays of these, but you can change to int
+ * if you have memory to burn and "short" is really slow.
+ */
+
+typedef short JCOEF;
+
+
+/* Compressed datastreams are represented as arrays of JOCTET.
+ * These must be EXACTLY 8 bits wide, at least once they are written to
+ * external storage.  Note that when using the stdio data source/destination
+ * managers, this is also the data type passed to fread/fwrite.
+ */
+
+#ifdef HAVE_UNSIGNED_CHAR
+
+typedef unsigned char JOCTET;
+#define GETJOCTET(value)  (value)
+
+#else /* not HAVE_UNSIGNED_CHAR */
+
+typedef char JOCTET;
+#ifdef CHAR_IS_UNSIGNED
+#define GETJOCTET(value)  (value)
+#else
+#define GETJOCTET(value)  ((value) & 0xFF)
+#endif /* CHAR_IS_UNSIGNED */
+
+#endif /* HAVE_UNSIGNED_CHAR */
+
+
+/* These typedefs are used for various table entries and so forth.
+ * They must be at least as wide as specified; but making them too big
+ * won't cost a huge amount of memory, so we don't provide special
+ * extraction code like we did for JSAMPLE.  (In other words, these
+ * typedefs live at a different point on the speed/space tradeoff curve.)
+ */
+
+/* UINT8 must hold at least the values 0..255. */
+
+#ifdef HAVE_UNSIGNED_CHAR
+typedef unsigned char UINT8;
+#else /* not HAVE_UNSIGNED_CHAR */
+#ifdef CHAR_IS_UNSIGNED
+typedef char UINT8;
+#else /* not CHAR_IS_UNSIGNED */
+typedef short UINT8;
+#endif /* CHAR_IS_UNSIGNED */
+#endif /* HAVE_UNSIGNED_CHAR */
+
+/* UINT16 must hold at least the values 0..65535. */
+
+#ifdef HAVE_UNSIGNED_SHORT
+typedef unsigned short UINT16;
+#else /* not HAVE_UNSIGNED_SHORT */
+typedef unsigned int UINT16;
+#endif /* HAVE_UNSIGNED_SHORT */
+
+/* INT16 must hold at least the values -32768..32767. */
+
+#ifndef XMD_H			/* X11/xmd.h correctly defines INT16 */
+typedef short INT16;
+#endif
+
+/* INT32 must hold at least signed 32-bit values. */
+
+#ifndef XMD_H			/* X11/xmd.h correctly defines INT32 */
+#ifndef _BASETSD_H
+typedef long INT32;
+#endif
+#endif
+
+/* Datatype used for image dimensions.  The JPEG standard only supports
+ * images up to 64K*64K due to 16-bit fields in SOF markers.  Therefore
+ * "unsigned int" is sufficient on all machines.  However, if you need to
+ * handle larger images and you don't mind deviating from the spec, you
+ * can change this datatype.
+ */
+
+typedef unsigned int JDIMENSION;
+
+#define JPEG_MAX_DIMENSION  65500L  /* a tad under 64K to prevent overflows */
+
+
+/* These macros are used in all function definitions and extern declarations.
+ * You could modify them if you need to change function linkage conventions;
+ * in particular, you'll need to do that to make the library a Windows DLL.
+ * Another application is to make all functions global for use with debuggers
+ * or code profilers that require it.
+ */
+
+/* a function called through method pointers: */
+#define METHODDEF(type)		static type
+/* a function used only in its module: */
+#define LOCAL(type)		static type
+/* a function referenced thru EXTERNs: */
+#define GLOBAL(type)		type
+/* a reference to a GLOBAL function: */
+#define EXTERN(type)		extern type
+
+
+/* This macro is used to declare a "method", that is, a function pointer.
+ * We want to supply prototype parameters if the compiler can cope.
+ * Note that the arglist parameter must be parenthesized!
+ * Again, you can customize this if you need special linkage keywords.
+ */
+
+#ifdef HAVE_PROTOTYPES
+#define JMETHOD(type,methodname,arglist)  type (*methodname) arglist
+#else
+#define JMETHOD(type,methodname,arglist)  type (*methodname) ()
+#endif
+
+
+/* Here is the pseudo-keyword for declaring pointers that must be "far"
+ * on 80x86 machines.  Most of the specialized coding for 80x86 is handled
+ * by just saying "FAR *" where such a pointer is needed.  In a few places
+ * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol.
+ */
+
+#ifdef NEED_FAR_POINTERS
+#define FAR  far
+#else
+#define FAR
+#endif
+
+
+/*
+ * On a few systems, type boolean and/or its values FALSE, TRUE may appear
+ * in standard header files.  Or you may have conflicts with application-
+ * specific header files that you want to include together with these files.
+ * Defining HAVE_BOOLEAN before including jpeglib.h should make it work.
+ */
+
+#ifndef HAVE_BOOLEAN
+# if defined (_WIN32)
+#  ifndef __RPCNDR_H__
+    typedef unsigned char boolean;
+#  endif
+# else
+    typedef int boolean;
+# endif
+#endif
+#ifndef FALSE			/* in case these macros already exist */
+#define FALSE	0		/* values of boolean */
+#endif
+#ifndef TRUE
+#define TRUE	1
+#endif
+
+
+/*
+ * The remaining options affect code selection within the JPEG library,
+ * but they don't need to be visible to most applications using the library.
+ * To minimize application namespace pollution, the symbols won't be
+ * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined.
+ */
+
+#ifdef JPEG_INTERNALS
+#define JPEG_INTERNAL_OPTIONS
+#endif
+
+#ifdef JPEG_INTERNAL_OPTIONS
+
+
+/*
+ * These defines indicate whether to include various optional functions.
+ * Undefining some of these symbols will produce a smaller but less capable
+ * library.  Note that you can leave certain source files out of the
+ * compilation/linking process if you've #undef'd the corresponding symbols.
+ * (You may HAVE to do that if your compiler doesn't like null source files.)
+ */
+
+/* Arithmetic coding is unsupported for legal reasons.  Complaints to IBM. */
+
+/* Capability options common to encoder and decoder: */
+
+#define DCT_ISLOW_SUPPORTED	/* slow but accurate integer algorithm */
+#define DCT_IFAST_SUPPORTED	/* faster, less accurate integer method */
+#define DCT_FLOAT_SUPPORTED	/* floating-point: accurate, fast on fast HW */
+
+/* Encoder capability options: */
+
+#undef  C_ARITH_CODING_SUPPORTED    /* Arithmetic coding back end? */
+#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */
+#define C_PROGRESSIVE_SUPPORTED	    /* Progressive JPEG? (Requires MULTISCAN)*/
+#define ENTROPY_OPT_SUPPORTED	    /* Optimization of entropy coding parms? */
+/* Note: if you selected 12-bit data precision, it is dangerous to turn off
+ * ENTROPY_OPT_SUPPORTED.  The standard Huffman tables are only good for 8-bit
+ * precision, so jchuff.c normally uses entropy optimization to compute
+ * usable tables for higher precision.  If you don't want to do optimization,
+ * you'll have to supply different default Huffman tables.
+ * The exact same statements apply for progressive JPEG: the default tables
+ * don't work for progressive mode.  (This may get fixed, however.)
+ */
+#define INPUT_SMOOTHING_SUPPORTED   /* Input image smoothing option? */
+
+/* Decoder capability options: */
+
+#undef  D_ARITH_CODING_SUPPORTED    /* Arithmetic coding back end? */
+#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */
+#define D_PROGRESSIVE_SUPPORTED	    /* Progressive JPEG? (Requires MULTISCAN)*/
+#define SAVE_MARKERS_SUPPORTED	    /* jpeg_save_markers() needed? */
+#define BLOCK_SMOOTHING_SUPPORTED   /* Block smoothing? (Progressive only) */
+#define IDCT_SCALING_SUPPORTED	    /* Output rescaling via IDCT? */
+#undef  UPSAMPLE_SCALING_SUPPORTED  /* Output rescaling at upsample stage? */
+#define UPSAMPLE_MERGING_SUPPORTED  /* Fast path for sloppy upsampling? */
+#define QUANT_1PASS_SUPPORTED	    /* 1-pass color quantization? */
+#define QUANT_2PASS_SUPPORTED	    /* 2-pass color quantization? */
+
+/* more capability options later, no doubt */
+
+
+/*
+ * Ordering of RGB data in scanlines passed to or from the application.
+ * If your application wants to deal with data in the order B,G,R, just
+ * change these macros.  You can also deal with formats such as R,G,B,X
+ * (one extra byte per pixel) by changing RGB_PIXELSIZE.  Note that changing
+ * the offsets will also change the order in which colormap data is organized.
+ * RESTRICTIONS:
+ * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats.
+ * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not
+ *    useful if you are using JPEG color spaces other than YCbCr or grayscale.
+ * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE
+ *    is not 3 (they don't understand about dummy color components!).  So you
+ *    can't use color quantization if you change that value.
+ */
+
+#define RGB_RED		0	/* Offset of Red in an RGB scanline element */
+#define RGB_GREEN	1	/* Offset of Green */
+#define RGB_BLUE	2	/* Offset of Blue */
+#define RGB_PIXELSIZE	3	/* JSAMPLEs per RGB scanline element */
+
+
+/* Definitions for speed-related optimizations. */
+
+
+/* If your compiler supports inline functions, define INLINE
+ * as the inline keyword; otherwise define it as empty.
+ */
+
+#ifndef INLINE
+#ifdef __GNUC__			/* for instance, GNU C knows about inline */
+#define INLINE __inline__
+#endif
+#ifndef INLINE
+#define INLINE			/* default is to define it as empty */
+#endif
+#endif
+
+
+/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying
+ * two 16-bit shorts is faster than multiplying two ints.  Define MULTIPLIER
+ * as short on such a machine.  MULTIPLIER must be at least 16 bits wide.
+ */
+
+#ifndef MULTIPLIER
+#define MULTIPLIER  int		/* type for fastest integer multiply */
+#endif
+
+
+/* FAST_FLOAT should be either float or double, whichever is done faster
+ * by your compiler.  (Note that this type is only used in the floating point
+ * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.)
+ * Typically, float is faster in ANSI C compilers, while double is faster in
+ * pre-ANSI compilers (because they insist on converting to double anyway).
+ * The code below therefore chooses float if we have ANSI-style prototypes.
+ */
+
+#ifndef FAST_FLOAT
+#ifdef HAVE_PROTOTYPES
+#define FAST_FLOAT  float
+#else
+#define FAST_FLOAT  double
+#endif
+#endif
+
+#endif /* JPEG_INTERNAL_OPTIONS */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jpegint.h b/Utilities/GDAL/frmts/jpeg/libjpeg/jpegint.h
new file mode 100644
index 0000000000..95b00d405c
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jpegint.h
@@ -0,0 +1,392 @@
+/*
+ * jpegint.h
+ *
+ * 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.
+ *
+ * This file provides common declarations for the various JPEG modules.
+ * These declarations are considered internal to the JPEG library; most
+ * applications using the library shouldn't need to include this file.
+ */
+
+
+/* Declarations for both compression & decompression */
+
+typedef enum {			/* Operating modes for buffer controllers */
+	JBUF_PASS_THRU,		/* Plain stripwise operation */
+	/* Remaining modes require a full-image buffer to have been created */
+	JBUF_SAVE_SOURCE,	/* Run source subobject only, save output */
+	JBUF_CRANK_DEST,	/* Run dest subobject only, using saved data */
+	JBUF_SAVE_AND_PASS	/* Run both subobjects, save output */
+} J_BUF_MODE;
+
+/* Values of global_state field (jdapi.c has some dependencies on ordering!) */
+#define CSTATE_START	100	/* after create_compress */
+#define CSTATE_SCANNING	101	/* start_compress done, write_scanlines OK */
+#define CSTATE_RAW_OK	102	/* start_compress done, write_raw_data OK */
+#define CSTATE_WRCOEFS	103	/* jpeg_write_coefficients done */
+#define DSTATE_START	200	/* after create_decompress */
+#define DSTATE_INHEADER	201	/* reading header markers, no SOS yet */
+#define DSTATE_READY	202	/* found SOS, ready for start_decompress */
+#define DSTATE_PRELOAD	203	/* reading multiscan file in start_decompress*/
+#define DSTATE_PRESCAN	204	/* performing dummy pass for 2-pass quant */
+#define DSTATE_SCANNING	205	/* start_decompress done, read_scanlines OK */
+#define DSTATE_RAW_OK	206	/* start_decompress done, read_raw_data OK */
+#define DSTATE_BUFIMAGE	207	/* expecting jpeg_start_output */
+#define DSTATE_BUFPOST	208	/* looking for SOS/EOI in jpeg_finish_output */
+#define DSTATE_RDCOEFS	209	/* reading file in jpeg_read_coefficients */
+#define DSTATE_STOPPING	210	/* looking for EOI in jpeg_finish_decompress */
+
+
+/* Declarations for compression modules */
+
+/* Master control module */
+struct jpeg_comp_master {
+  JMETHOD(void, prepare_for_pass, (j_compress_ptr cinfo));
+  JMETHOD(void, pass_startup, (j_compress_ptr cinfo));
+  JMETHOD(void, finish_pass, (j_compress_ptr cinfo));
+
+  /* State variables made visible to other modules */
+  boolean call_pass_startup;	/* True if pass_startup must be called */
+  boolean is_last_pass;		/* True during last pass */
+};
+
+/* Main buffer control (downsampled-data buffer) */
+struct jpeg_c_main_controller {
+  JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode));
+  JMETHOD(void, process_data, (j_compress_ptr cinfo,
+			       JSAMPARRAY input_buf, JDIMENSION *in_row_ctr,
+			       JDIMENSION in_rows_avail));
+};
+
+/* Compression preprocessing (downsampling input buffer control) */
+struct jpeg_c_prep_controller {
+  JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode));
+  JMETHOD(void, pre_process_data, (j_compress_ptr cinfo,
+				   JSAMPARRAY input_buf,
+				   JDIMENSION *in_row_ctr,
+				   JDIMENSION in_rows_avail,
+				   JSAMPIMAGE output_buf,
+				   JDIMENSION *out_row_group_ctr,
+				   JDIMENSION out_row_groups_avail));
+};
+
+/* Coefficient buffer control */
+struct jpeg_c_coef_controller {
+  JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode));
+  JMETHOD(boolean, compress_data, (j_compress_ptr cinfo,
+				   JSAMPIMAGE input_buf));
+};
+
+/* Colorspace conversion */
+struct jpeg_color_converter {
+  JMETHOD(void, start_pass, (j_compress_ptr cinfo));
+  JMETHOD(void, color_convert, (j_compress_ptr cinfo,
+				JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+				JDIMENSION output_row, int num_rows));
+};
+
+/* Downsampling */
+struct jpeg_downsampler {
+  JMETHOD(void, start_pass, (j_compress_ptr cinfo));
+  JMETHOD(void, downsample, (j_compress_ptr cinfo,
+			     JSAMPIMAGE input_buf, JDIMENSION in_row_index,
+			     JSAMPIMAGE output_buf,
+			     JDIMENSION out_row_group_index));
+
+  boolean need_context_rows;	/* TRUE if need rows above & below */
+};
+
+/* Forward DCT (also controls coefficient quantization) */
+struct jpeg_forward_dct {
+  JMETHOD(void, start_pass, (j_compress_ptr cinfo));
+  /* perhaps this should be an array??? */
+  JMETHOD(void, forward_DCT, (j_compress_ptr cinfo,
+			      jpeg_component_info * compptr,
+			      JSAMPARRAY sample_data, JBLOCKROW coef_blocks,
+			      JDIMENSION start_row, JDIMENSION start_col,
+			      JDIMENSION num_blocks));
+};
+
+/* Entropy encoding */
+struct jpeg_entropy_encoder {
+  JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics));
+  JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data));
+  JMETHOD(void, finish_pass, (j_compress_ptr cinfo));
+};
+
+/* Marker writing */
+struct jpeg_marker_writer {
+  JMETHOD(void, write_file_header, (j_compress_ptr cinfo));
+  JMETHOD(void, write_frame_header, (j_compress_ptr cinfo));
+  JMETHOD(void, write_scan_header, (j_compress_ptr cinfo));
+  JMETHOD(void, write_file_trailer, (j_compress_ptr cinfo));
+  JMETHOD(void, write_tables_only, (j_compress_ptr cinfo));
+  /* These routines are exported to allow insertion of extra markers */
+  /* Probably only COM and APPn markers should be written this way */
+  JMETHOD(void, write_marker_header, (j_compress_ptr cinfo, int marker,
+				      unsigned int datalen));
+  JMETHOD(void, write_marker_byte, (j_compress_ptr cinfo, int val));
+};
+
+
+/* Declarations for decompression modules */
+
+/* Master control module */
+struct jpeg_decomp_master {
+  JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo));
+  JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo));
+
+  /* State variables made visible to other modules */
+  boolean is_dummy_pass;	/* True during 1st pass for 2-pass quant */
+};
+
+/* Input control module */
+struct jpeg_input_controller {
+  JMETHOD(int, consume_input, (j_decompress_ptr cinfo));
+  JMETHOD(void, reset_input_controller, (j_decompress_ptr cinfo));
+  JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo));
+  JMETHOD(void, finish_input_pass, (j_decompress_ptr cinfo));
+
+  /* State variables made visible to other modules */
+  boolean has_multiple_scans;	/* True if file has multiple scans */
+  boolean eoi_reached;		/* True when EOI has been consumed */
+};
+
+/* Main buffer control (downsampled-data buffer) */
+struct jpeg_d_main_controller {
+  JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode));
+  JMETHOD(void, process_data, (j_decompress_ptr cinfo,
+			       JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+			       JDIMENSION out_rows_avail));
+};
+
+/* Coefficient buffer control */
+struct jpeg_d_coef_controller {
+  JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo));
+  JMETHOD(int, consume_data, (j_decompress_ptr cinfo));
+  JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo));
+  JMETHOD(int, decompress_data, (j_decompress_ptr cinfo,
+				 JSAMPIMAGE output_buf));
+  /* Pointer to array of coefficient virtual arrays, or NULL if none */
+  jvirt_barray_ptr *coef_arrays;
+};
+
+/* Decompression postprocessing (color quantization buffer control) */
+struct jpeg_d_post_controller {
+  JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode));
+  JMETHOD(void, post_process_data, (j_decompress_ptr cinfo,
+				    JSAMPIMAGE input_buf,
+				    JDIMENSION *in_row_group_ctr,
+				    JDIMENSION in_row_groups_avail,
+				    JSAMPARRAY output_buf,
+				    JDIMENSION *out_row_ctr,
+				    JDIMENSION out_rows_avail));
+};
+
+/* Marker reading & parsing */
+struct jpeg_marker_reader {
+  JMETHOD(void, reset_marker_reader, (j_decompress_ptr cinfo));
+  /* Read markers until SOS or EOI.
+   * Returns same codes as are defined for jpeg_consume_input:
+   * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI.
+   */
+  JMETHOD(int, read_markers, (j_decompress_ptr cinfo));
+  /* Read a restart marker --- exported for use by entropy decoder only */
+  jpeg_marker_parser_method read_restart_marker;
+
+  /* State of marker reader --- nominally internal, but applications
+   * supplying COM or APPn handlers might like to know the state.
+   */
+  boolean saw_SOI;		/* found SOI? */
+  boolean saw_SOF;		/* found SOF? */
+  int next_restart_num;		/* next restart number expected (0-7) */
+  unsigned int discarded_bytes;	/* # of bytes skipped looking for a marker */
+};
+
+/* Entropy decoding */
+struct jpeg_entropy_decoder {
+  JMETHOD(void, start_pass, (j_decompress_ptr cinfo));
+  JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo,
+				JBLOCKROW *MCU_data));
+
+  /* This is here to share code between baseline and progressive decoders; */
+  /* other modules probably should not use it */
+  boolean insufficient_data;	/* set TRUE after emitting warning */
+};
+
+/* Inverse DCT (also performs dequantization) */
+typedef JMETHOD(void, inverse_DCT_method_ptr,
+		(j_decompress_ptr cinfo, jpeg_component_info * compptr,
+		 JCOEFPTR coef_block,
+		 JSAMPARRAY output_buf, JDIMENSION output_col));
+
+struct jpeg_inverse_dct {
+  JMETHOD(void, start_pass, (j_decompress_ptr cinfo));
+  /* It is useful to allow each component to have a separate IDCT method. */
+  inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS];
+};
+
+/* Upsampling (note that upsampler must also call color converter) */
+struct jpeg_upsampler {
+  JMETHOD(void, start_pass, (j_decompress_ptr cinfo));
+  JMETHOD(void, upsample, (j_decompress_ptr cinfo,
+			   JSAMPIMAGE input_buf,
+			   JDIMENSION *in_row_group_ctr,
+			   JDIMENSION in_row_groups_avail,
+			   JSAMPARRAY output_buf,
+			   JDIMENSION *out_row_ctr,
+			   JDIMENSION out_rows_avail));
+
+  boolean need_context_rows;	/* TRUE if need rows above & below */
+};
+
+/* Colorspace conversion */
+struct jpeg_color_deconverter {
+  JMETHOD(void, start_pass, (j_decompress_ptr cinfo));
+  JMETHOD(void, color_convert, (j_decompress_ptr cinfo,
+				JSAMPIMAGE input_buf, JDIMENSION input_row,
+				JSAMPARRAY output_buf, int num_rows));
+};
+
+/* Color quantization or color precision reduction */
+struct jpeg_color_quantizer {
+  JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan));
+  JMETHOD(void, color_quantize, (j_decompress_ptr cinfo,
+				 JSAMPARRAY input_buf, JSAMPARRAY output_buf,
+				 int num_rows));
+  JMETHOD(void, finish_pass, (j_decompress_ptr cinfo));
+  JMETHOD(void, new_color_map, (j_decompress_ptr cinfo));
+};
+
+
+/* Miscellaneous useful macros */
+
+#undef MAX
+#define MAX(a,b)	((a) > (b) ? (a) : (b))
+#undef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+
+
+/* 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
+
+
+/* Short forms of external names for systems with brain-damaged linkers. */
+
+#ifdef NEED_SHORT_EXTERNAL_NAMES
+#define jinit_compress_master	jICompress
+#define jinit_c_master_control	jICMaster
+#define jinit_c_main_controller	jICMainC
+#define jinit_c_prep_controller	jICPrepC
+#define jinit_c_coef_controller	jICCoefC
+#define jinit_color_converter	jICColor
+#define jinit_downsampler	jIDownsampler
+#define jinit_forward_dct	jIFDCT
+#define jinit_huff_encoder	jIHEncoder
+#define jinit_phuff_encoder	jIPHEncoder
+#define jinit_marker_writer	jIMWriter
+#define jinit_master_decompress	jIDMaster
+#define jinit_d_main_controller	jIDMainC
+#define jinit_d_coef_controller	jIDCoefC
+#define jinit_d_post_controller	jIDPostC
+#define jinit_input_controller	jIInCtlr
+#define jinit_marker_reader	jIMReader
+#define jinit_huff_decoder	jIHDecoder
+#define jinit_phuff_decoder	jIPHDecoder
+#define jinit_inverse_dct	jIIDCT
+#define jinit_upsampler		jIUpsampler
+#define jinit_color_deconverter	jIDColor
+#define jinit_1pass_quantizer	jI1Quant
+#define jinit_2pass_quantizer	jI2Quant
+#define jinit_merged_upsampler	jIMUpsampler
+#define jinit_memory_mgr	jIMemMgr
+#define jdiv_round_up		jDivRound
+#define jround_up		jRound
+#define jcopy_sample_rows	jCopySamples
+#define jcopy_block_row		jCopyBlocks
+#define jzero_far		jZeroFar
+#define jpeg_zigzag_order	jZIGTable
+#define jpeg_natural_order	jZAGTable
+#endif /* NEED_SHORT_EXTERNAL_NAMES */
+
+
+/* Compression module initialization routines */
+EXTERN(void) jinit_compress_master JPP((j_compress_ptr cinfo));
+EXTERN(void) jinit_c_master_control JPP((j_compress_ptr cinfo,
+					 boolean transcode_only));
+EXTERN(void) jinit_c_main_controller JPP((j_compress_ptr cinfo,
+					  boolean need_full_buffer));
+EXTERN(void) jinit_c_prep_controller JPP((j_compress_ptr cinfo,
+					  boolean need_full_buffer));
+EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo,
+					  boolean need_full_buffer));
+EXTERN(void) jinit_color_converter JPP((j_compress_ptr cinfo));
+EXTERN(void) jinit_downsampler JPP((j_compress_ptr cinfo));
+EXTERN(void) jinit_forward_dct JPP((j_compress_ptr cinfo));
+EXTERN(void) jinit_huff_encoder JPP((j_compress_ptr cinfo));
+EXTERN(void) jinit_phuff_encoder JPP((j_compress_ptr cinfo));
+EXTERN(void) jinit_marker_writer JPP((j_compress_ptr cinfo));
+/* Decompression module initialization routines */
+EXTERN(void) jinit_master_decompress JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_d_main_controller JPP((j_decompress_ptr cinfo,
+					  boolean need_full_buffer));
+EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo,
+					  boolean need_full_buffer));
+EXTERN(void) jinit_d_post_controller JPP((j_decompress_ptr cinfo,
+					  boolean need_full_buffer));
+EXTERN(void) jinit_input_controller JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_marker_reader JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_huff_decoder JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_phuff_decoder JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_upsampler JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_color_deconverter JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_1pass_quantizer JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_2pass_quantizer JPP((j_decompress_ptr cinfo));
+EXTERN(void) jinit_merged_upsampler JPP((j_decompress_ptr cinfo));
+/* Memory manager initialization */
+EXTERN(void) jinit_memory_mgr JPP((j_common_ptr cinfo));
+
+/* Utility routines in jutils.c */
+EXTERN(long) jdiv_round_up JPP((long a, long b));
+EXTERN(long) jround_up JPP((long a, long b));
+EXTERN(void) jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row,
+				    JSAMPARRAY output_array, int dest_row,
+				    int num_rows, JDIMENSION num_cols));
+EXTERN(void) jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row,
+				  JDIMENSION num_blocks));
+EXTERN(void) jzero_far JPP((void FAR * target, size_t bytestozero));
+/* Constant tables in jutils.c */
+#if 0				/* This table is not actually needed in v6a */
+extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */
+#endif
+extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */
+
+/* Suppress undefined-structure complaints if necessary. */
+
+#ifdef INCOMPLETE_TYPES_BROKEN
+#ifndef AM_MEMORY_MANAGER	/* only jmemmgr.c defines these */
+struct jvirt_sarray_control { long dummy; };
+struct jvirt_barray_control { long dummy; };
+#endif
+#endif /* INCOMPLETE_TYPES_BROKEN */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jpeglib.h b/Utilities/GDAL/frmts/jpeg/libjpeg/jpeglib.h
new file mode 100644
index 0000000000..d1be8ddeff
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jpeglib.h
@@ -0,0 +1,1096 @@
+/*
+ * jpeglib.h
+ *
+ * Copyright (C) 1991-1998, 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 defines the application interface for the JPEG library.
+ * Most applications using the library need only include this file,
+ * and perhaps jerror.h if they want to know the exact error codes.
+ */
+
+#ifndef JPEGLIB_H
+#define JPEGLIB_H
+
+/*
+ * First we include the configuration files that record how this
+ * installation of the JPEG library is set up.  jconfig.h can be
+ * generated automatically for many systems.  jmorecfg.h contains
+ * manual configuration options that most people need not worry about.
+ */
+
+#ifndef JCONFIG_INCLUDED	/* in case jinclude.h already did */
+#include "jconfig.h"		/* widely used configuration options */
+#endif
+#include "jmorecfg.h"		/* seldom changed options */
+
+
+/* Version ID for the JPEG library.
+ * Might be useful for tests like "#if JPEG_LIB_VERSION >= 60".
+ */
+
+#define JPEG_LIB_VERSION  62	/* Version 6b */
+
+
+/* Various constants determining the sizes of things.
+ * All of these are specified by the JPEG standard, so don't change them
+ * if you want to be compatible.
+ */
+
+#define DCTSIZE		    8	/* The basic DCT block is 8x8 samples */
+#define DCTSIZE2	    64	/* DCTSIZE squared; # of elements in a block */
+#define NUM_QUANT_TBLS      4	/* Quantization tables are numbered 0..3 */
+#define NUM_HUFF_TBLS       4	/* Huffman tables are numbered 0..3 */
+#define NUM_ARITH_TBLS      16	/* Arith-coding tables are numbered 0..15 */
+#define MAX_COMPS_IN_SCAN   4	/* JPEG limit on # of components in one scan */
+#define MAX_SAMP_FACTOR     4	/* JPEG limit on sampling factors */
+/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard;
+ * the PostScript DCT filter can emit files with many more than 10 blocks/MCU.
+ * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU
+ * to handle it.  We even let you do this from the jconfig.h file.  However,
+ * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe
+ * sometimes emits noncompliant files doesn't mean you should too.
+ */
+#define C_MAX_BLOCKS_IN_MCU   10 /* compressor's limit on blocks per MCU */
+#ifndef D_MAX_BLOCKS_IN_MCU
+#define D_MAX_BLOCKS_IN_MCU   10 /* decompressor's limit on blocks per MCU */
+#endif
+
+
+/* Data structures for images (arrays of samples and of DCT coefficients).
+ * On 80x86 machines, the image arrays are too big for near pointers,
+ * but the pointer arrays can fit in near memory.
+ */
+
+typedef JSAMPLE FAR *JSAMPROW;	/* ptr to one image row of pixel samples. */
+typedef JSAMPROW *JSAMPARRAY;	/* ptr to some rows (a 2-D sample array) */
+typedef JSAMPARRAY *JSAMPIMAGE;	/* a 3-D sample array: top index is color */
+
+typedef JCOEF JBLOCK[DCTSIZE2];	/* one block of coefficients */
+typedef JBLOCK FAR *JBLOCKROW;	/* pointer to one row of coefficient blocks */
+typedef JBLOCKROW *JBLOCKARRAY;		/* a 2-D array of coefficient blocks */
+typedef JBLOCKARRAY *JBLOCKIMAGE;	/* a 3-D array of coefficient blocks */
+
+typedef JCOEF FAR *JCOEFPTR;	/* useful in a couple of places */
+
+
+/* Types for JPEG compression parameters and working tables. */
+
+
+/* DCT coefficient quantization tables. */
+
+typedef struct {
+  /* This array gives the coefficient quantizers in natural array order
+   * (not the zigzag order in which they are stored in a JPEG DQT marker).
+   * CAUTION: IJG versions prior to v6a kept this array in zigzag order.
+   */
+  UINT16 quantval[DCTSIZE2];	/* quantization step for each coefficient */
+  /* This field is used only during compression.  It's initialized FALSE when
+   * the table is created, and set TRUE when it's been output to the file.
+   * You could suppress output of a table by setting this to TRUE.
+   * (See jpeg_suppress_tables for an example.)
+   */
+  boolean sent_table;		/* TRUE when table has been output */
+} JQUANT_TBL;
+
+
+/* Huffman coding tables. */
+
+typedef struct {
+  /* These two fields directly represent the contents of a JPEG DHT marker */
+  UINT8 bits[17];		/* bits[k] = # of symbols with codes of */
+				/* length k bits; bits[0] is unused */
+  UINT8 huffval[256];		/* The symbols, in order of incr code length */
+  /* This field is used only during compression.  It's initialized FALSE when
+   * the table is created, and set TRUE when it's been output to the file.
+   * You could suppress output of a table by setting this to TRUE.
+   * (See jpeg_suppress_tables for an example.)
+   */
+  boolean sent_table;		/* TRUE when table has been output */
+} JHUFF_TBL;
+
+
+/* Basic info about one component (color channel). */
+
+typedef struct {
+  /* These values are fixed over the whole image. */
+  /* For compression, they must be supplied by parameter setup; */
+  /* for decompression, they are read from the SOF marker. */
+  int component_id;		/* identifier for this component (0..255) */
+  int component_index;		/* its index in SOF or cinfo->comp_info[] */
+  int h_samp_factor;		/* horizontal sampling factor (1..4) */
+  int v_samp_factor;		/* vertical sampling factor (1..4) */
+  int quant_tbl_no;		/* quantization table selector (0..3) */
+  /* These values may vary between scans. */
+  /* For compression, they must be supplied by parameter setup; */
+  /* for decompression, they are read from the SOS marker. */
+  /* The decompressor output side may not use these variables. */
+  int dc_tbl_no;		/* DC entropy table selector (0..3) */
+  int ac_tbl_no;		/* AC entropy table selector (0..3) */
+  
+  /* Remaining fields should be treated as private by applications. */
+  
+  /* These values are computed during compression or decompression startup: */
+  /* Component's size in DCT blocks.
+   * Any dummy blocks added to complete an MCU are not counted; therefore
+   * these values do not depend on whether a scan is interleaved or not.
+   */
+  JDIMENSION width_in_blocks;
+  JDIMENSION height_in_blocks;
+  /* Size of a DCT block in samples.  Always DCTSIZE for compression.
+   * For decompression this is the size of the output from one DCT block,
+   * reflecting any scaling we choose to apply during the IDCT step.
+   * Values of 1,2,4,8 are likely to be supported.  Note that different
+   * components may receive different IDCT scalings.
+   */
+  int DCT_scaled_size;
+  /* The downsampled dimensions are the component's actual, unpadded number
+   * of samples at the main buffer (preprocessing/compression interface), thus
+   * downsampled_width = ceil(image_width * Hi/Hmax)
+   * and similarly for height.  For decompression, IDCT scaling is included, so
+   * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE)
+   */
+  JDIMENSION downsampled_width;	 /* actual width in samples */
+  JDIMENSION downsampled_height; /* actual height in samples */
+  /* This flag is used only for decompression.  In cases where some of the
+   * components will be ignored (eg grayscale output from YCbCr image),
+   * we can skip most computations for the unused components.
+   */
+  boolean component_needed;	/* do we need the value of this component? */
+
+  /* These values are computed before starting a scan of the component. */
+  /* The decompressor output side may not use these variables. */
+  int MCU_width;		/* number of blocks per MCU, horizontally */
+  int MCU_height;		/* number of blocks per MCU, vertically */
+  int MCU_blocks;		/* MCU_width * MCU_height */
+  int MCU_sample_width;		/* MCU width in samples, MCU_width*DCT_scaled_size */
+  int last_col_width;		/* # of non-dummy blocks across in last MCU */
+  int last_row_height;		/* # of non-dummy blocks down in last MCU */
+
+  /* Saved quantization table for component; NULL if none yet saved.
+   * See jdinput.c comments about the need for this information.
+   * This field is currently used only for decompression.
+   */
+  JQUANT_TBL * quant_table;
+
+  /* Private per-component storage for DCT or IDCT subsystem. */
+  void * dct_table;
+} jpeg_component_info;
+
+
+/* The script for encoding a multiple-scan file is an array of these: */
+
+typedef struct {
+  int comps_in_scan;		/* number of components encoded in this scan */
+  int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */
+  int Ss, Se;			/* progressive JPEG spectral selection parms */
+  int Ah, Al;			/* progressive JPEG successive approx. parms */
+} jpeg_scan_info;
+
+/* The decompressor can save APPn and COM markers in a list of these: */
+
+typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr;
+
+struct jpeg_marker_struct {
+  jpeg_saved_marker_ptr next;	/* next in list, or NULL */
+  UINT8 marker;			/* marker code: JPEG_COM, or JPEG_APP0+n */
+  unsigned int original_length;	/* # bytes of data in the file */
+  unsigned int data_length;	/* # bytes of data saved at data[] */
+  JOCTET FAR * data;		/* the data contained in the marker */
+  /* the marker length word is not counted in data_length or original_length */
+};
+
+/* Known color spaces. */
+
+typedef enum {
+	JCS_UNKNOWN,		/* error/unspecified */
+	JCS_GRAYSCALE,		/* monochrome */
+	JCS_RGB,		/* red/green/blue */
+	JCS_YCbCr,		/* Y/Cb/Cr (also known as YUV) */
+	JCS_CMYK,		/* C/M/Y/K */
+	JCS_YCCK		/* Y/Cb/Cr/K */
+} J_COLOR_SPACE;
+
+/* DCT/IDCT algorithm options. */
+
+typedef enum {
+	JDCT_ISLOW,		/* slow but accurate integer algorithm */
+	JDCT_IFAST,		/* faster, less accurate integer method */
+	JDCT_FLOAT		/* floating-point: accurate, fast on fast HW */
+} J_DCT_METHOD;
+
+#ifndef JDCT_DEFAULT		/* may be overridden in jconfig.h */
+#define JDCT_DEFAULT  JDCT_ISLOW
+#endif
+#ifndef JDCT_FASTEST		/* may be overridden in jconfig.h */
+#define JDCT_FASTEST  JDCT_IFAST
+#endif
+
+/* Dithering options for decompression. */
+
+typedef enum {
+	JDITHER_NONE,		/* no dithering */
+	JDITHER_ORDERED,	/* simple ordered dither */
+	JDITHER_FS		/* Floyd-Steinberg error diffusion dither */
+} J_DITHER_MODE;
+
+
+/* Common fields between JPEG compression and decompression master structs. */
+
+#define jpeg_common_fields \
+  struct jpeg_error_mgr * err;	/* Error handler module */\
+  struct jpeg_memory_mgr * mem;	/* Memory manager module */\
+  struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\
+  void * client_data;		/* Available for use by application */\
+  boolean is_decompressor;	/* So common code can tell which is which */\
+  int global_state		/* For checking call sequence validity */
+
+/* Routines that are to be used by both halves of the library are declared
+ * to receive a pointer to this structure.  There are no actual instances of
+ * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct.
+ */
+struct jpeg_common_struct {
+  jpeg_common_fields;		/* Fields common to both master struct types */
+  /* Additional fields follow in an actual jpeg_compress_struct or
+   * jpeg_decompress_struct.  All three structs must agree on these
+   * initial fields!  (This would be a lot cleaner in C++.)
+   */
+};
+
+typedef struct jpeg_common_struct * j_common_ptr;
+typedef struct jpeg_compress_struct * j_compress_ptr;
+typedef struct jpeg_decompress_struct * j_decompress_ptr;
+
+
+/* Master record for a compression instance */
+
+struct jpeg_compress_struct {
+  jpeg_common_fields;		/* Fields shared with jpeg_decompress_struct */
+
+  /* Destination for compressed data */
+  struct jpeg_destination_mgr * dest;
+
+  /* Description of source image --- these fields must be filled in by
+   * outer application before starting compression.  in_color_space must
+   * be correct before you can even call jpeg_set_defaults().
+   */
+
+  JDIMENSION image_width;	/* input image width */
+  JDIMENSION image_height;	/* input image height */
+  int input_components;		/* # of color components in input image */
+  J_COLOR_SPACE in_color_space;	/* colorspace of input image */
+
+  double input_gamma;		/* image gamma of input image */
+
+  /* Compression parameters --- these fields must be set before calling
+   * jpeg_start_compress().  We recommend calling jpeg_set_defaults() to
+   * initialize everything to reasonable defaults, then changing anything
+   * the application specifically wants to change.  That way you won't get
+   * burnt when new parameters are added.  Also note that there are several
+   * helper routines to simplify changing parameters.
+   */
+
+  int data_precision;		/* bits of precision in image data */
+
+  int num_components;		/* # of color components in JPEG image */
+  J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */
+
+  jpeg_component_info * comp_info;
+  /* comp_info[i] describes component that appears i'th in SOF */
+  
+  JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS];
+  /* ptrs to coefficient quantization tables, or NULL if not defined */
+  
+  JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS];
+  JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS];
+  /* ptrs to Huffman coding tables, or NULL if not defined */
+  
+  UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */
+  UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */
+  UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */
+
+  int num_scans;		/* # of entries in scan_info array */
+  const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */
+  /* The default value of scan_info is NULL, which causes a single-scan
+   * sequential JPEG file to be emitted.  To create a multi-scan file,
+   * set num_scans and scan_info to point to an array of scan definitions.
+   */
+
+  boolean raw_data_in;		/* TRUE=caller supplies downsampled data */
+  boolean arith_code;		/* TRUE=arithmetic coding, FALSE=Huffman */
+  boolean optimize_coding;	/* TRUE=optimize entropy encoding parms */
+  boolean CCIR601_sampling;	/* TRUE=first samples are cosited */
+  int smoothing_factor;		/* 1..100, or 0 for no input smoothing */
+  J_DCT_METHOD dct_method;	/* DCT algorithm selector */
+
+  /* The restart interval can be specified in absolute MCUs by setting
+   * restart_interval, or in MCU rows by setting restart_in_rows
+   * (in which case the correct restart_interval will be figured
+   * for each scan).
+   */
+  unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */
+  int restart_in_rows;		/* if > 0, MCU rows per restart interval */
+
+  /* Parameters controlling emission of special markers. */
+
+  boolean write_JFIF_header;	/* should a JFIF marker be written? */
+  UINT8 JFIF_major_version;	/* What to write for the JFIF version number */
+  UINT8 JFIF_minor_version;
+  /* These three values are not used by the JPEG code, merely copied */
+  /* into the JFIF APP0 marker.  density_unit can be 0 for unknown, */
+  /* 1 for dots/inch, or 2 for dots/cm.  Note that the pixel aspect */
+  /* ratio is defined by X_density/Y_density even when density_unit=0. */
+  UINT8 density_unit;		/* JFIF code for pixel size units */
+  UINT16 X_density;		/* Horizontal pixel density */
+  UINT16 Y_density;		/* Vertical pixel density */
+  boolean write_Adobe_marker;	/* should an Adobe marker be written? */
+  
+  /* State variable: index of next scanline to be written to
+   * jpeg_write_scanlines().  Application may use this to control its
+   * processing loop, e.g., "while (next_scanline < image_height)".
+   */
+
+  JDIMENSION next_scanline;	/* 0 .. image_height-1  */
+
+  /* Remaining fields are known throughout compressor, but generally
+   * should not be touched by a surrounding application.
+   */
+
+  /*
+   * These fields are computed during compression startup
+   */
+  boolean progressive_mode;	/* TRUE if scan script uses progressive mode */
+  int max_h_samp_factor;	/* largest h_samp_factor */
+  int max_v_samp_factor;	/* largest v_samp_factor */
+
+  JDIMENSION total_iMCU_rows;	/* # of iMCU rows to be input to coef ctlr */
+  /* The coefficient controller receives data in units of MCU rows as defined
+   * for fully interleaved scans (whether the JPEG file is interleaved or not).
+   * There are v_samp_factor * DCTSIZE sample rows of each component in an
+   * "iMCU" (interleaved MCU) row.
+   */
+  
+  /*
+   * These fields are valid during any one scan.
+   * They describe the components and MCUs actually appearing in the scan.
+   */
+  int comps_in_scan;		/* # of JPEG components in this scan */
+  jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN];
+  /* *cur_comp_info[i] describes component that appears i'th in SOS */
+  
+  JDIMENSION MCUs_per_row;	/* # of MCUs across the image */
+  JDIMENSION MCU_rows_in_scan;	/* # of MCU rows in the image */
+  
+  int blocks_in_MCU;		/* # of DCT blocks per MCU */
+  int MCU_membership[C_MAX_BLOCKS_IN_MCU];
+  /* MCU_membership[i] is index in cur_comp_info of component owning */
+  /* i'th block in an MCU */
+
+  int Ss, Se, Ah, Al;		/* progressive JPEG parameters for scan */
+
+  /*
+   * Links to compression subobjects (methods and private variables of modules)
+   */
+  struct jpeg_comp_master * master;
+  struct jpeg_c_main_controller * main;
+  struct jpeg_c_prep_controller * prep;
+  struct jpeg_c_coef_controller * coef;
+  struct jpeg_marker_writer * marker;
+  struct jpeg_color_converter * cconvert;
+  struct jpeg_downsampler * downsample;
+  struct jpeg_forward_dct * fdct;
+  struct jpeg_entropy_encoder * entropy;
+  jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */
+  int script_space_size;
+};
+
+
+/* Master record for a decompression instance */
+
+struct jpeg_decompress_struct {
+  jpeg_common_fields;		/* Fields shared with jpeg_compress_struct */
+
+  /* Source of compressed data */
+  struct jpeg_source_mgr * src;
+
+  /* Basic description of image --- filled in by jpeg_read_header(). */
+  /* Application may inspect these values to decide how to process image. */
+
+  JDIMENSION image_width;	/* nominal image width (from SOF marker) */
+  JDIMENSION image_height;	/* nominal image height */
+  int num_components;		/* # of color components in JPEG image */
+  J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */
+
+  /* Decompression processing parameters --- these fields must be set before
+   * calling jpeg_start_decompress().  Note that jpeg_read_header() initializes
+   * them to default values.
+   */
+
+  J_COLOR_SPACE out_color_space; /* colorspace for output */
+
+  unsigned int scale_num, scale_denom; /* fraction by which to scale image */
+
+  double output_gamma;		/* image gamma wanted in output */
+
+  boolean buffered_image;	/* TRUE=multiple output passes */
+  boolean raw_data_out;		/* TRUE=downsampled data wanted */
+
+  J_DCT_METHOD dct_method;	/* IDCT algorithm selector */
+  boolean do_fancy_upsampling;	/* TRUE=apply fancy upsampling */
+  boolean do_block_smoothing;	/* TRUE=apply interblock smoothing */
+
+  boolean quantize_colors;	/* TRUE=colormapped output wanted */
+  /* the following are ignored if not quantize_colors: */
+  J_DITHER_MODE dither_mode;	/* type of color dithering to use */
+  boolean two_pass_quantize;	/* TRUE=use two-pass color quantization */
+  int desired_number_of_colors;	/* max # colors to use in created colormap */
+  /* these are significant only in buffered-image mode: */
+  boolean enable_1pass_quant;	/* enable future use of 1-pass quantizer */
+  boolean enable_external_quant;/* enable future use of external colormap */
+  boolean enable_2pass_quant;	/* enable future use of 2-pass quantizer */
+
+  /* Description of actual output image that will be returned to application.
+   * These fields are computed by jpeg_start_decompress().
+   * You can also use jpeg_calc_output_dimensions() to determine these values
+   * in advance of calling jpeg_start_decompress().
+   */
+
+  JDIMENSION output_width;	/* scaled image width */
+  JDIMENSION output_height;	/* scaled image height */
+  int out_color_components;	/* # of color components in out_color_space */
+  int output_components;	/* # of color components returned */
+  /* output_components is 1 (a colormap index) when quantizing colors;
+   * otherwise it equals out_color_components.
+   */
+  int rec_outbuf_height;	/* min recommended height of scanline buffer */
+  /* If the buffer passed to jpeg_read_scanlines() is less than this many rows
+   * high, space and time will be wasted due to unnecessary data copying.
+   * Usually rec_outbuf_height will be 1 or 2, at most 4.
+   */
+
+  /* When quantizing colors, the output colormap is described by these fields.
+   * The application can supply a colormap by setting colormap non-NULL before
+   * calling jpeg_start_decompress; otherwise a colormap is created during
+   * jpeg_start_decompress or jpeg_start_output.
+   * The map has out_color_components rows and actual_number_of_colors columns.
+   */
+  int actual_number_of_colors;	/* number of entries in use */
+  JSAMPARRAY colormap;		/* The color map as a 2-D pixel array */
+
+  /* State variables: these variables indicate the progress of decompression.
+   * The application may examine these but must not modify them.
+   */
+
+  /* Row index of next scanline to be read from jpeg_read_scanlines().
+   * Application may use this to control its processing loop, e.g.,
+   * "while (output_scanline < output_height)".
+   */
+  JDIMENSION output_scanline;	/* 0 .. output_height-1  */
+
+  /* Current input scan number and number of iMCU rows completed in scan.
+   * These indicate the progress of the decompressor input side.
+   */
+  int input_scan_number;	/* Number of SOS markers seen so far */
+  JDIMENSION input_iMCU_row;	/* Number of iMCU rows completed */
+
+  /* The "output scan number" is the notional scan being displayed by the
+   * output side.  The decompressor will not allow output scan/row number
+   * to get ahead of input scan/row, but it can fall arbitrarily far behind.
+   */
+  int output_scan_number;	/* Nominal scan number being displayed */
+  JDIMENSION output_iMCU_row;	/* Number of iMCU rows read */
+
+  /* Current progression status.  coef_bits[c][i] indicates the precision
+   * with which component c's DCT coefficient i (in zigzag order) is known.
+   * It is -1 when no data has yet been received, otherwise it is the point
+   * transform (shift) value for the most recent scan of the coefficient
+   * (thus, 0 at completion of the progression).
+   * This pointer is NULL when reading a non-progressive file.
+   */
+  int (*coef_bits)[DCTSIZE2];	/* -1 or current Al value for each coef */
+
+  /* Internal JPEG parameters --- the application usually need not look at
+   * these fields.  Note that the decompressor output side may not use
+   * any parameters that can change between scans.
+   */
+
+  /* Quantization and Huffman tables are carried forward across input
+   * datastreams when processing abbreviated JPEG datastreams.
+   */
+
+  JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS];
+  /* ptrs to coefficient quantization tables, or NULL if not defined */
+
+  JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS];
+  JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS];
+  /* ptrs to Huffman coding tables, or NULL if not defined */
+
+  /* These parameters are never carried across datastreams, since they
+   * are given in SOF/SOS markers or defined to be reset by SOI.
+   */
+
+  int data_precision;		/* bits of precision in image data */
+
+  jpeg_component_info * comp_info;
+  /* comp_info[i] describes component that appears i'th in SOF */
+
+  boolean progressive_mode;	/* TRUE if SOFn specifies progressive mode */
+  boolean arith_code;		/* TRUE=arithmetic coding, FALSE=Huffman */
+
+  UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */
+  UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */
+  UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */
+
+  unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */
+
+  /* These fields record data obtained from optional markers recognized by
+   * the JPEG library.
+   */
+  boolean saw_JFIF_marker;	/* TRUE iff a JFIF APP0 marker was found */
+  /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */
+  UINT8 JFIF_major_version;	/* JFIF version number */
+  UINT8 JFIF_minor_version;
+  UINT8 density_unit;		/* JFIF code for pixel size units */
+  UINT16 X_density;		/* Horizontal pixel density */
+  UINT16 Y_density;		/* Vertical pixel density */
+  boolean saw_Adobe_marker;	/* TRUE iff an Adobe APP14 marker was found */
+  UINT8 Adobe_transform;	/* Color transform code from Adobe marker */
+
+  boolean CCIR601_sampling;	/* TRUE=first samples are cosited */
+
+  /* Aside from the specific data retained from APPn markers known to the
+   * library, the uninterpreted contents of any or all APPn and COM markers
+   * can be saved in a list for examination by the application.
+   */
+  jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */
+
+  /* Remaining fields are known throughout decompressor, but generally
+   * should not be touched by a surrounding application.
+   */
+
+  /*
+   * These fields are computed during decompression startup
+   */
+  int max_h_samp_factor;	/* largest h_samp_factor */
+  int max_v_samp_factor;	/* largest v_samp_factor */
+
+  int min_DCT_scaled_size;	/* smallest DCT_scaled_size of any component */
+
+  JDIMENSION total_iMCU_rows;	/* # of iMCU rows in image */
+  /* The coefficient controller's input and output progress is measured in
+   * units of "iMCU" (interleaved MCU) rows.  These are the same as MCU rows
+   * in fully interleaved JPEG scans, but are used whether the scan is
+   * interleaved or not.  We define an iMCU row as v_samp_factor DCT block
+   * rows of each component.  Therefore, the IDCT output contains
+   * v_samp_factor*DCT_scaled_size sample rows of a component per iMCU row.
+   */
+
+  JSAMPLE * sample_range_limit; /* table for fast range-limiting */
+
+  /*
+   * These fields are valid during any one scan.
+   * They describe the components and MCUs actually appearing in the scan.
+   * Note that the decompressor output side must not use these fields.
+   */
+  int comps_in_scan;		/* # of JPEG components in this scan */
+  jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN];
+  /* *cur_comp_info[i] describes component that appears i'th in SOS */
+
+  JDIMENSION MCUs_per_row;	/* # of MCUs across the image */
+  JDIMENSION MCU_rows_in_scan;	/* # of MCU rows in the image */
+
+  int blocks_in_MCU;		/* # of DCT blocks per MCU */
+  int MCU_membership[D_MAX_BLOCKS_IN_MCU];
+  /* MCU_membership[i] is index in cur_comp_info of component owning */
+  /* i'th block in an MCU */
+
+  int Ss, Se, Ah, Al;		/* progressive JPEG parameters for scan */
+
+  /* This field is shared between entropy decoder and marker parser.
+   * It is either zero or the code of a JPEG marker that has been
+   * read from the data source, but has not yet been processed.
+   */
+  int unread_marker;
+
+  /*
+   * Links to decompression subobjects (methods, private variables of modules)
+   */
+  struct jpeg_decomp_master * master;
+  struct jpeg_d_main_controller * main;
+  struct jpeg_d_coef_controller * coef;
+  struct jpeg_d_post_controller * post;
+  struct jpeg_input_controller * inputctl;
+  struct jpeg_marker_reader * marker;
+  struct jpeg_entropy_decoder * entropy;
+  struct jpeg_inverse_dct * idct;
+  struct jpeg_upsampler * upsample;
+  struct jpeg_color_deconverter * cconvert;
+  struct jpeg_color_quantizer * cquantize;
+};
+
+
+/* "Object" declarations for JPEG modules that may be supplied or called
+ * directly by the surrounding application.
+ * As with all objects in the JPEG library, these structs only define the
+ * publicly visible methods and state variables of a module.  Additional
+ * private fields may exist after the public ones.
+ */
+
+
+/* Error handler object */
+
+struct jpeg_error_mgr {
+  /* Error exit handler: does not return to caller */
+  JMETHOD(void, error_exit, (j_common_ptr cinfo));
+  /* Conditionally emit a trace or warning message */
+  JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level));
+  /* Routine that actually outputs a trace or error message */
+  JMETHOD(void, output_message, (j_common_ptr cinfo));
+  /* Format a message string for the most recent JPEG error or message */
+  JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer));
+#define JMSG_LENGTH_MAX  200	/* recommended size of format_message buffer */
+  /* Reset error state variables at start of a new image */
+  JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo));
+  
+  /* The message ID code and any parameters are saved here.
+   * A message can have one string parameter or up to 8 int parameters.
+   */
+  int msg_code;
+#define JMSG_STR_PARM_MAX  80
+  union {
+    int i[8];
+    char s[JMSG_STR_PARM_MAX];
+  } msg_parm;
+  
+  /* Standard state variables for error facility */
+  
+  int trace_level;		/* max msg_level that will be displayed */
+  
+  /* For recoverable corrupt-data errors, we emit a warning message,
+   * but keep going unless emit_message chooses to abort.  emit_message
+   * should count warnings in num_warnings.  The surrounding application
+   * can check for bad data by seeing if num_warnings is nonzero at the
+   * end of processing.
+   */
+  long num_warnings;		/* number of corrupt-data warnings */
+
+  /* These fields point to the table(s) of error message strings.
+   * An application can change the table pointer to switch to a different
+   * message list (typically, to change the language in which errors are
+   * reported).  Some applications may wish to add additional error codes
+   * that will be handled by the JPEG library error mechanism; the second
+   * table pointer is used for this purpose.
+   *
+   * First table includes all errors generated by JPEG library itself.
+   * Error code 0 is reserved for a "no such error string" message.
+   */
+  const char * const * jpeg_message_table; /* Library errors */
+  int last_jpeg_message;    /* Table contains strings 0..last_jpeg_message */
+  /* Second table can be added by application (see cjpeg/djpeg for example).
+   * It contains strings numbered first_addon_message..last_addon_message.
+   */
+  const char * const * addon_message_table; /* Non-library errors */
+  int first_addon_message;	/* code for first string in addon table */
+  int last_addon_message;	/* code for last string in addon table */
+};
+
+
+/* Progress monitor object */
+
+struct jpeg_progress_mgr {
+  JMETHOD(void, progress_monitor, (j_common_ptr cinfo));
+
+  long pass_counter;		/* work units completed in this pass */
+  long pass_limit;		/* total number of work units in this pass */
+  int completed_passes;		/* passes completed so far */
+  int total_passes;		/* total number of passes expected */
+};
+
+
+/* Data destination object for compression */
+
+struct jpeg_destination_mgr {
+  JOCTET * next_output_byte;	/* => next byte to write in buffer */
+  size_t free_in_buffer;	/* # of byte spaces remaining in buffer */
+
+  JMETHOD(void, init_destination, (j_compress_ptr cinfo));
+  JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo));
+  JMETHOD(void, term_destination, (j_compress_ptr cinfo));
+};
+
+
+/* Data source object for decompression */
+
+struct jpeg_source_mgr {
+  const JOCTET * next_input_byte; /* => next byte to read from buffer */
+  size_t bytes_in_buffer;	/* # of bytes remaining in buffer */
+
+  JMETHOD(void, init_source, (j_decompress_ptr cinfo));
+  JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo));
+  JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes));
+  JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired));
+  JMETHOD(void, term_source, (j_decompress_ptr cinfo));
+};
+
+
+/* Memory manager object.
+ * Allocates "small" objects (a few K total), "large" objects (tens of K),
+ * and "really big" objects (virtual arrays with backing store if needed).
+ * The memory manager does not allow individual objects to be freed; rather,
+ * each created object is assigned to a pool, and whole pools can be freed
+ * at once.  This is faster and more convenient than remembering exactly what
+ * to free, especially where malloc()/free() are not too speedy.
+ * NB: alloc routines never return NULL.  They exit to error_exit if not
+ * successful.
+ */
+
+#define JPOOL_PERMANENT	0	/* lasts until master record is destroyed */
+#define JPOOL_IMAGE	1	/* lasts until done with image/datastream */
+#define JPOOL_NUMPOOLS	2
+
+typedef struct jvirt_sarray_control * jvirt_sarray_ptr;
+typedef struct jvirt_barray_control * jvirt_barray_ptr;
+
+
+struct jpeg_memory_mgr {
+  /* Method pointers */
+  JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id,
+				size_t sizeofobject));
+  JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id,
+				     size_t sizeofobject));
+  JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id,
+				     JDIMENSION samplesperrow,
+				     JDIMENSION numrows));
+  JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id,
+				      JDIMENSION blocksperrow,
+				      JDIMENSION numrows));
+  JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo,
+						  int pool_id,
+						  boolean pre_zero,
+						  JDIMENSION samplesperrow,
+						  JDIMENSION numrows,
+						  JDIMENSION maxaccess));
+  JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo,
+						  int pool_id,
+						  boolean pre_zero,
+						  JDIMENSION blocksperrow,
+						  JDIMENSION numrows,
+						  JDIMENSION maxaccess));
+  JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo));
+  JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo,
+					   jvirt_sarray_ptr ptr,
+					   JDIMENSION start_row,
+					   JDIMENSION num_rows,
+					   boolean writable));
+  JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo,
+					    jvirt_barray_ptr ptr,
+					    JDIMENSION start_row,
+					    JDIMENSION num_rows,
+					    boolean writable));
+  JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id));
+  JMETHOD(void, self_destruct, (j_common_ptr cinfo));
+
+  /* Limit on memory allocation for this JPEG object.  (Note that this is
+   * merely advisory, not a guaranteed maximum; it only affects the space
+   * used for virtual-array buffers.)  May be changed by outer application
+   * after creating the JPEG object.
+   */
+  long max_memory_to_use;
+
+  /* Maximum allocation request accepted by alloc_large. */
+  long max_alloc_chunk;
+};
+
+
+/* Routine signature for application-supplied marker processing methods.
+ * Need not pass marker code since it is stored in cinfo->unread_marker.
+ */
+typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo));
+
+
+/* Declarations for routines called by application.
+ * The JPP macro hides prototype parameters from compilers that can't cope.
+ * Note JPP requires double parentheses.
+ */
+
+#ifdef HAVE_PROTOTYPES
+#define JPP(arglist)	arglist
+#else
+#define JPP(arglist)	()
+#endif
+
+
+/* Short forms of external names for systems with brain-damaged linkers.
+ * We shorten external names to be unique in the first six letters, which
+ * is good enough for all known systems.
+ * (If your compiler itself needs names to be unique in less than 15 
+ * characters, you are out of luck.  Get a better compiler.)
+ */
+
+#ifdef NEED_SHORT_EXTERNAL_NAMES
+#define jpeg_std_error		jStdError
+#define jpeg_CreateCompress	jCreaCompress
+#define jpeg_CreateDecompress	jCreaDecompress
+#define jpeg_destroy_compress	jDestCompress
+#define jpeg_destroy_decompress	jDestDecompress
+#define jpeg_stdio_dest		jStdDest
+#define jpeg_stdio_src		jStdSrc
+#define jpeg_set_defaults	jSetDefaults
+#define jpeg_set_colorspace	jSetColorspace
+#define jpeg_default_colorspace	jDefColorspace
+#define jpeg_set_quality	jSetQuality
+#define jpeg_set_linear_quality	jSetLQuality
+#define jpeg_add_quant_table	jAddQuantTable
+#define jpeg_quality_scaling	jQualityScaling
+#define jpeg_simple_progression	jSimProgress
+#define jpeg_suppress_tables	jSuppressTables
+#define jpeg_alloc_quant_table	jAlcQTable
+#define jpeg_alloc_huff_table	jAlcHTable
+#define jpeg_start_compress	jStrtCompress
+#define jpeg_write_scanlines	jWrtScanlines
+#define jpeg_finish_compress	jFinCompress
+#define jpeg_write_raw_data	jWrtRawData
+#define jpeg_write_marker	jWrtMarker
+#define jpeg_write_m_header	jWrtMHeader
+#define jpeg_write_m_byte	jWrtMByte
+#define jpeg_write_tables	jWrtTables
+#define jpeg_read_header	jReadHeader
+#define jpeg_start_decompress	jStrtDecompress
+#define jpeg_read_scanlines	jReadScanlines
+#define jpeg_finish_decompress	jFinDecompress
+#define jpeg_read_raw_data	jReadRawData
+#define jpeg_has_multiple_scans	jHasMultScn
+#define jpeg_start_output	jStrtOutput
+#define jpeg_finish_output	jFinOutput
+#define jpeg_input_complete	jInComplete
+#define jpeg_new_colormap	jNewCMap
+#define jpeg_consume_input	jConsumeInput
+#define jpeg_calc_output_dimensions	jCalcDimensions
+#define jpeg_save_markers	jSaveMarkers
+#define jpeg_set_marker_processor	jSetMarker
+#define jpeg_read_coefficients	jReadCoefs
+#define jpeg_write_coefficients	jWrtCoefs
+#define jpeg_copy_critical_parameters	jCopyCrit
+#define jpeg_abort_compress	jAbrtCompress
+#define jpeg_abort_decompress	jAbrtDecompress
+#define jpeg_abort		jAbort
+#define jpeg_destroy		jDestroy
+#define jpeg_resync_to_restart	jResyncRestart
+#endif /* NEED_SHORT_EXTERNAL_NAMES */
+
+
+/* Default error-management setup */
+EXTERN(struct jpeg_error_mgr *) jpeg_std_error
+	JPP((struct jpeg_error_mgr * err));
+
+/* Initialization of JPEG compression objects.
+ * jpeg_create_compress() and jpeg_create_decompress() are the exported
+ * names that applications should call.  These expand to calls on
+ * jpeg_CreateCompress and jpeg_CreateDecompress with additional information
+ * passed for version mismatch checking.
+ * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx.
+ */
+#define jpeg_create_compress(cinfo) \
+    jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \
+			(size_t) sizeof(struct jpeg_compress_struct))
+#define jpeg_create_decompress(cinfo) \
+    jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \
+			  (size_t) sizeof(struct jpeg_decompress_struct))
+EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo,
+				      int version, size_t structsize));
+EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo,
+					int version, size_t structsize));
+/* Destruction of JPEG compression objects */
+EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo));
+EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo));
+
+/* Standard data source and destination managers: stdio streams. */
+/* Caller is responsible for opening the file before and closing after. */
+EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile));
+EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile));
+
+/* Default parameter setup for compression */
+EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo));
+/* Compression parameter setup aids */
+EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo,
+				      J_COLOR_SPACE colorspace));
+EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo));
+EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality,
+				   boolean force_baseline));
+EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo,
+					  int scale_factor,
+					  boolean force_baseline));
+EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl,
+				       const unsigned int *basic_table,
+				       int scale_factor,
+				       boolean force_baseline));
+EXTERN(int) jpeg_quality_scaling JPP((int quality));
+EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo));
+EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo,
+				       boolean suppress));
+EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo));
+EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo));
+
+/* Main entry points for compression */
+EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo,
+				      boolean write_all_tables));
+EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo,
+					     JSAMPARRAY scanlines,
+					     JDIMENSION num_lines));
+EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo));
+
+/* Replaces jpeg_write_scanlines when writing raw downsampled data. */
+EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo,
+					    JSAMPIMAGE data,
+					    JDIMENSION num_lines));
+
+/* Write a special marker.  See libjpeg.doc concerning safe usage. */
+EXTERN(void) jpeg_write_marker
+	JPP((j_compress_ptr cinfo, int marker,
+	     const JOCTET * dataptr, unsigned int datalen));
+/* Same, but piecemeal. */
+EXTERN(void) jpeg_write_m_header
+	JPP((j_compress_ptr cinfo, int marker, unsigned int datalen));
+EXTERN(void) jpeg_write_m_byte
+	JPP((j_compress_ptr cinfo, int val));
+
+/* Alternate compression function: just write an abbreviated table file */
+EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo));
+
+/* Decompression startup: read start of JPEG datastream to see what's there */
+EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo,
+				  boolean require_image));
+/* Return value is one of: */
+#define JPEG_SUSPENDED		0 /* Suspended due to lack of input data */
+#define JPEG_HEADER_OK		1 /* Found valid image datastream */
+#define JPEG_HEADER_TABLES_ONLY	2 /* Found valid table-specs-only datastream */
+/* If you pass require_image = TRUE (normal case), you need not check for
+ * a TABLES_ONLY return code; an abbreviated file will cause an error exit.
+ * JPEG_SUSPENDED is only possible if you use a data source module that can
+ * give a suspension return (the stdio source module doesn't).
+ */
+
+/* Main entry points for decompression */
+EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo));
+EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo,
+					    JSAMPARRAY scanlines,
+					    JDIMENSION max_lines));
+EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo));
+
+/* Replaces jpeg_read_scanlines when reading raw downsampled data. */
+EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo,
+					   JSAMPIMAGE data,
+					   JDIMENSION max_lines));
+
+/* Additional entry points for buffered-image mode. */
+EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo));
+EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo,
+				       int scan_number));
+EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo));
+EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo));
+EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo));
+EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo));
+/* Return value is one of: */
+/* #define JPEG_SUSPENDED	0    Suspended due to lack of input data */
+#define JPEG_REACHED_SOS	1 /* Reached start of new scan */
+#define JPEG_REACHED_EOI	2 /* Reached end of image */
+#define JPEG_ROW_COMPLETED	3 /* Completed one iMCU row */
+#define JPEG_SCAN_COMPLETED	4 /* Completed last iMCU row of a scan */
+
+/* Precalculate output dimensions for current decompression parameters. */
+EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo));
+
+/* Control saving of COM and APPn markers into marker_list. */
+EXTERN(void) jpeg_save_markers
+	JPP((j_decompress_ptr cinfo, int marker_code,
+	     unsigned int length_limit));
+
+/* Install a special processing method for COM or APPn markers. */
+EXTERN(void) jpeg_set_marker_processor
+	JPP((j_decompress_ptr cinfo, int marker_code,
+	     jpeg_marker_parser_method routine));
+
+/* Read or write raw DCT coefficients --- useful for lossless transcoding. */
+EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo));
+EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo,
+					  jvirt_barray_ptr * coef_arrays));
+EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo,
+						j_compress_ptr dstinfo));
+
+/* If you choose to abort compression or decompression before completing
+ * jpeg_finish_(de)compress, then you need to clean up to release memory,
+ * temporary files, etc.  You can just call jpeg_destroy_(de)compress
+ * if you're done with the JPEG object, but if you want to clean it up and
+ * reuse it, call this:
+ */
+EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo));
+EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo));
+
+/* Generic versions of jpeg_abort and jpeg_destroy that work on either
+ * flavor of JPEG object.  These may be more convenient in some places.
+ */
+EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo));
+EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo));
+
+/* Default restart-marker-resync procedure for use by data source modules */
+EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo,
+					    int desired));
+
+
+/* These marker codes are exported since applications and data source modules
+ * are likely to want to use them.
+ */
+
+#define JPEG_RST0	0xD0	/* RST0 marker code */
+#define JPEG_EOI	0xD9	/* EOI marker code */
+#define JPEG_APP0	0xE0	/* APP0 marker code */
+#define JPEG_COM	0xFE	/* COM marker code */
+
+
+/* If we have a brain-damaged compiler that emits warnings (or worse, errors)
+ * for structure definitions that are never filled in, keep it quiet by
+ * supplying dummy definitions for the various substructures.
+ */
+
+#ifdef INCOMPLETE_TYPES_BROKEN
+#ifndef JPEG_INTERNALS		/* will be defined in jpegint.h */
+struct jvirt_sarray_control { long dummy; };
+struct jvirt_barray_control { long dummy; };
+struct jpeg_comp_master { long dummy; };
+struct jpeg_c_main_controller { long dummy; };
+struct jpeg_c_prep_controller { long dummy; };
+struct jpeg_c_coef_controller { long dummy; };
+struct jpeg_marker_writer { long dummy; };
+struct jpeg_color_converter { long dummy; };
+struct jpeg_downsampler { long dummy; };
+struct jpeg_forward_dct { long dummy; };
+struct jpeg_entropy_encoder { long dummy; };
+struct jpeg_decomp_master { long dummy; };
+struct jpeg_d_main_controller { long dummy; };
+struct jpeg_d_coef_controller { long dummy; };
+struct jpeg_d_post_controller { long dummy; };
+struct jpeg_input_controller { long dummy; };
+struct jpeg_marker_reader { long dummy; };
+struct jpeg_entropy_decoder { long dummy; };
+struct jpeg_inverse_dct { long dummy; };
+struct jpeg_upsampler { long dummy; };
+struct jpeg_color_deconverter { long dummy; };
+struct jpeg_color_quantizer { long dummy; };
+#endif /* JPEG_INTERNALS */
+#endif /* INCOMPLETE_TYPES_BROKEN */
+
+
+/*
+ * The JPEG library modules define JPEG_INTERNALS before including this file.
+ * The internal structure declarations are read only when that is true.
+ * Applications using the library should not include jpegint.h, but may wish
+ * to include jerror.h.
+ */
+
+#ifdef JPEG_INTERNALS
+#include "jpegint.h"		/* fetch private declarations */
+#include "jerror.h"		/* fetch error codes too */
+#endif
+
+#endif /* JPEGLIB_H */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jquant1.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jquant1.c
new file mode 100644
index 0000000000..b2f96aa15d
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jquant1.c
@@ -0,0 +1,856 @@
+/*
+ * jquant1.c
+ *
+ * Copyright (C) 1991-1996, 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 1-pass color quantization (color mapping) routines.
+ * These routines provide mapping to a fixed color map using equally spaced
+ * color values.  Optional Floyd-Steinberg or ordered dithering is available.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+#ifdef QUANT_1PASS_SUPPORTED
+
+
+/*
+ * The main purpose of 1-pass quantization is to provide a fast, if not very
+ * high quality, colormapped output capability.  A 2-pass quantizer usually
+ * gives better visual quality; however, for quantized grayscale output this
+ * quantizer is perfectly adequate.  Dithering is highly recommended with this
+ * quantizer, though you can turn it off if you really want to.
+ *
+ * In 1-pass quantization the colormap must be chosen in advance of seeing the
+ * image.  We use a map consisting of all combinations of Ncolors[i] color
+ * values for the i'th component.  The Ncolors[] values are chosen so that
+ * their product, the total number of colors, is no more than that requested.
+ * (In most cases, the product will be somewhat less.)
+ *
+ * Since the colormap is orthogonal, the representative value for each color
+ * component can be determined without considering the other components;
+ * then these indexes can be combined into a colormap index by a standard
+ * N-dimensional-array-subscript calculation.  Most of the arithmetic involved
+ * can be precalculated and stored in the lookup table colorindex[].
+ * colorindex[i][j] maps pixel value j in component i to the nearest
+ * representative value (grid plane) for that component; this index is
+ * multiplied by the array stride for component i, so that the
+ * index of the colormap entry closest to a given pixel value is just
+ *    sum( colorindex[component-number][pixel-component-value] )
+ * Aside from being fast, this scheme allows for variable spacing between
+ * representative values with no additional lookup cost.
+ *
+ * If gamma correction has been applied in color conversion, it might be wise
+ * to adjust the color grid spacing so that the representative colors are
+ * equidistant in linear space.  At this writing, gamma correction is not
+ * implemented by jdcolor, so nothing is done here.
+ */
+
+
+/* Declarations for ordered dithering.
+ *
+ * We use a standard 16x16 ordered dither array.  The basic concept of ordered
+ * dithering is described in many references, for instance Dale Schumacher's
+ * chapter II.2 of Graphics Gems II (James Arvo, ed. Academic Press, 1991).
+ * In place of Schumacher's comparisons against a "threshold" value, we add a
+ * "dither" value to the input pixel and then round the result to the nearest
+ * output value.  The dither value is equivalent to (0.5 - threshold) times
+ * the distance between output values.  For ordered dithering, we assume that
+ * the output colors are equally spaced; if not, results will probably be
+ * worse, since the dither may be too much or too little at a given point.
+ *
+ * The normal calculation would be to form pixel value + dither, range-limit
+ * this to 0..MAXJSAMPLE, and then index into the colorindex table as usual.
+ * We can skip the separate range-limiting step by extending the colorindex
+ * table in both directions.
+ */
+
+#define ODITHER_SIZE  16	/* dimension of dither matrix */
+/* NB: if ODITHER_SIZE is not a power of 2, ODITHER_MASK uses will break */
+#define ODITHER_CELLS (ODITHER_SIZE*ODITHER_SIZE)	/* # cells in matrix */
+#define ODITHER_MASK  (ODITHER_SIZE-1) /* mask for wrapping around counters */
+
+typedef int ODITHER_MATRIX[ODITHER_SIZE][ODITHER_SIZE];
+typedef int (*ODITHER_MATRIX_PTR)[ODITHER_SIZE];
+
+static const UINT8 base_dither_matrix[ODITHER_SIZE][ODITHER_SIZE] = {
+  /* Bayer's order-4 dither array.  Generated by the code given in
+   * Stephen Hawley's article "Ordered Dithering" in Graphics Gems I.
+   * The values in this array must range from 0 to ODITHER_CELLS-1.
+   */
+  {   0,192, 48,240, 12,204, 60,252,  3,195, 51,243, 15,207, 63,255 },
+  { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
+  {  32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
+  { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
+  {   8,200, 56,248,  4,196, 52,244, 11,203, 59,251,  7,199, 55,247 },
+  { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
+  {  40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
+  { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
+  {   2,194, 50,242, 14,206, 62,254,  1,193, 49,241, 13,205, 61,253 },
+  { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
+  {  34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
+  { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
+  {  10,202, 58,250,  6,198, 54,246,  9,201, 57,249,  5,197, 53,245 },
+  { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
+  {  42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
+  { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
+};
+
+
+/* Declarations for Floyd-Steinberg dithering.
+ *
+ * Errors are accumulated into the array fserrors[], at a resolution of
+ * 1/16th of a pixel count.  The error at a given pixel is propagated
+ * to its not-yet-processed neighbors using the standard F-S fractions,
+ *		...	(here)	7/16
+ *		3/16	5/16	1/16
+ * We work left-to-right on even rows, right-to-left on odd rows.
+ *
+ * We can get away with a single array (holding one row's worth of errors)
+ * by using it to store the current row's errors at pixel columns not yet
+ * processed, but the next row's errors at columns already processed.  We
+ * need only a few extra variables to hold the errors immediately around the
+ * current column.  (If we are lucky, those variables are in registers, but
+ * even if not, they're probably cheaper to access than array elements are.)
+ *
+ * The fserrors[] array is indexed [component#][position].
+ * We provide (#columns + 2) entries per component; the extra entry at each
+ * end saves us from special-casing the first and last pixels.
+ *
+ * Note: on a wide image, we might not have enough room in a PC's near data
+ * segment to hold the error array; so it is allocated with alloc_large.
+ */
+
+#if BITS_IN_JSAMPLE == 8
+typedef INT16 FSERROR;		/* 16 bits should be enough */
+typedef int LOCFSERROR;		/* use 'int' for calculation temps */
+#else
+typedef INT32 FSERROR;		/* may need more than 16 bits */
+typedef INT32 LOCFSERROR;	/* be sure calculation temps are big enough */
+#endif
+
+typedef FSERROR FAR *FSERRPTR;	/* pointer to error array (in FAR storage!) */
+
+
+/* Private subobject */
+
+#define MAX_Q_COMPS 4		/* max components I can handle */
+
+typedef struct {
+  struct jpeg_color_quantizer pub; /* public fields */
+
+  /* Initially allocated colormap is saved here */
+  JSAMPARRAY sv_colormap;	/* The color map as a 2-D pixel array */
+  int sv_actual;		/* number of entries in use */
+
+  JSAMPARRAY colorindex;	/* Precomputed mapping for speed */
+  /* colorindex[i][j] = index of color closest to pixel value j in component i,
+   * premultiplied as described above.  Since colormap indexes must fit into
+   * JSAMPLEs, the entries of this array will too.
+   */
+  boolean is_padded;		/* is the colorindex padded for odither? */
+
+  int Ncolors[MAX_Q_COMPS];	/* # of values alloced to each component */
+
+  /* Variables for ordered dithering */
+  int row_index;		/* cur row's vertical index in dither matrix */
+  ODITHER_MATRIX_PTR odither[MAX_Q_COMPS]; /* one dither array per component */
+
+  /* Variables for Floyd-Steinberg dithering */
+  FSERRPTR fserrors[MAX_Q_COMPS]; /* accumulated errors */
+  boolean on_odd_row;		/* flag to remember which row we are on */
+} my_cquantizer;
+
+typedef my_cquantizer * my_cquantize_ptr;
+
+
+/*
+ * Policy-making subroutines for create_colormap and create_colorindex.
+ * These routines determine the colormap to be used.  The rest of the module
+ * only assumes that the colormap is orthogonal.
+ *
+ *  * select_ncolors decides how to divvy up the available colors
+ *    among the components.
+ *  * output_value defines the set of representative values for a component.
+ *  * largest_input_value defines the mapping from input values to
+ *    representative values for a component.
+ * Note that the latter two routines may impose different policies for
+ * different components, though this is not currently done.
+ */
+
+
+LOCAL(int)
+select_ncolors (j_decompress_ptr cinfo, int Ncolors[])
+/* Determine allocation of desired colors to components, */
+/* and fill in Ncolors[] array to indicate choice. */
+/* Return value is total number of colors (product of Ncolors[] values). */
+{
+  int nc = cinfo->out_color_components; /* number of color components */
+  int max_colors = cinfo->desired_number_of_colors;
+  int total_colors, iroot, i, j;
+  boolean changed;
+  long temp;
+  static const int RGB_order[3] = { RGB_GREEN, RGB_RED, RGB_BLUE };
+
+  /* We can allocate at least the nc'th root of max_colors per component. */
+  /* Compute floor(nc'th root of max_colors). */
+  iroot = 1;
+  do {
+    iroot++;
+    temp = iroot;		/* set temp = iroot ** nc */
+    for (i = 1; i < nc; i++)
+      temp *= iroot;
+  } while (temp <= (long) max_colors); /* repeat till iroot exceeds root */
+  iroot--;			/* now iroot = floor(root) */
+
+  /* Must have at least 2 color values per component */
+  if (iroot < 2)
+    ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, (int) temp);
+
+  /* Initialize to iroot color values for each component */
+  total_colors = 1;
+  for (i = 0; i < nc; i++) {
+    Ncolors[i] = iroot;
+    total_colors *= iroot;
+  }
+  /* We may be able to increment the count for one or more components without
+   * exceeding max_colors, though we know not all can be incremented.
+   * Sometimes, the first component can be incremented more than once!
+   * (Example: for 16 colors, we start at 2*2*2, go to 3*2*2, then 4*2*2.)
+   * In RGB colorspace, try to increment G first, then R, then B.
+   */
+  do {
+    changed = FALSE;
+    for (i = 0; i < nc; i++) {
+      j = (cinfo->out_color_space == JCS_RGB ? RGB_order[i] : i);
+      /* calculate new total_colors if Ncolors[j] is incremented */
+      temp = total_colors / Ncolors[j];
+      temp *= Ncolors[j]+1;	/* done in long arith to avoid oflo */
+      if (temp > (long) max_colors)
+	break;			/* won't fit, done with this pass */
+      Ncolors[j]++;		/* OK, apply the increment */
+      total_colors = (int) temp;
+      changed = TRUE;
+    }
+  } while (changed);
+
+  return total_colors;
+}
+
+
+LOCAL(int)
+output_value (j_decompress_ptr cinfo, int ci, int j, int maxj)
+/* Return j'th output value, where j will range from 0 to maxj */
+/* The output values must fall in 0..MAXJSAMPLE in increasing order */
+{
+  /* We always provide values 0 and MAXJSAMPLE for each component;
+   * any additional values are equally spaced between these limits.
+   * (Forcing the upper and lower values to the limits ensures that
+   * dithering can't produce a color outside the selected gamut.)
+   */
+  return (int) (((INT32) j * MAXJSAMPLE + maxj/2) / maxj);
+}
+
+
+LOCAL(int)
+largest_input_value (j_decompress_ptr cinfo, int ci, int j, int maxj)
+/* Return largest input value that should map to j'th output value */
+/* Must have largest(j=0) >= 0, and largest(j=maxj) >= MAXJSAMPLE */
+{
+  /* Breakpoints are halfway between values returned by output_value */
+  return (int) (((INT32) (2*j + 1) * MAXJSAMPLE + maxj) / (2*maxj));
+}
+
+
+/*
+ * Create the colormap.
+ */
+
+LOCAL(void)
+create_colormap (j_decompress_ptr cinfo)
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  JSAMPARRAY colormap;		/* Created colormap */
+  int total_colors;		/* Number of distinct output colors */
+  int i,j,k, nci, blksize, blkdist, ptr, val;
+
+  /* Select number of colors for each component */
+  total_colors = select_ncolors(cinfo, cquantize->Ncolors);
+
+  /* Report selected color counts */
+  if (cinfo->out_color_components == 3)
+    TRACEMS4(cinfo, 1, JTRC_QUANT_3_NCOLORS,
+	     total_colors, cquantize->Ncolors[0],
+	     cquantize->Ncolors[1], cquantize->Ncolors[2]);
+  else
+    TRACEMS1(cinfo, 1, JTRC_QUANT_NCOLORS, total_colors);
+
+  /* Allocate and fill in the colormap. */
+  /* The colors are ordered in the map in standard row-major order, */
+  /* i.e. rightmost (highest-indexed) color changes most rapidly. */
+
+  colormap = (*cinfo->mem->alloc_sarray)
+    ((j_common_ptr) cinfo, JPOOL_IMAGE,
+     (JDIMENSION) total_colors, (JDIMENSION) cinfo->out_color_components);
+
+  /* blksize is number of adjacent repeated entries for a component */
+  /* blkdist is distance between groups of identical entries for a component */
+  blkdist = total_colors;
+
+  for (i = 0; i < cinfo->out_color_components; i++) {
+    /* fill in colormap entries for i'th color component */
+    nci = cquantize->Ncolors[i]; /* # of distinct values for this color */
+    blksize = blkdist / nci;
+    for (j = 0; j < nci; j++) {
+      /* Compute j'th output value (out of nci) for component */
+      val = output_value(cinfo, i, j, nci-1);
+      /* Fill in all colormap entries that have this value of this component */
+      for (ptr = j * blksize; ptr < total_colors; ptr += blkdist) {
+	/* fill in blksize entries beginning at ptr */
+	for (k = 0; k < blksize; k++)
+	  colormap[i][ptr+k] = (JSAMPLE) val;
+      }
+    }
+    blkdist = blksize;		/* blksize of this color is blkdist of next */
+  }
+
+  /* Save the colormap in private storage,
+   * where it will survive color quantization mode changes.
+   */
+  cquantize->sv_colormap = colormap;
+  cquantize->sv_actual = total_colors;
+}
+
+
+/*
+ * Create the color index table.
+ */
+
+LOCAL(void)
+create_colorindex (j_decompress_ptr cinfo)
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  JSAMPROW indexptr;
+  int i,j,k, nci, blksize, val, pad;
+
+  /* For ordered dither, we pad the color index tables by MAXJSAMPLE in
+   * each direction (input index values can be -MAXJSAMPLE .. 2*MAXJSAMPLE).
+   * This is not necessary in the other dithering modes.  However, we
+   * flag whether it was done in case user changes dithering mode.
+   */
+  if (cinfo->dither_mode == JDITHER_ORDERED) {
+    pad = MAXJSAMPLE*2;
+    cquantize->is_padded = TRUE;
+  } else {
+    pad = 0;
+    cquantize->is_padded = FALSE;
+  }
+
+  cquantize->colorindex = (*cinfo->mem->alloc_sarray)
+    ((j_common_ptr) cinfo, JPOOL_IMAGE,
+     (JDIMENSION) (MAXJSAMPLE+1 + pad),
+     (JDIMENSION) cinfo->out_color_components);
+
+  /* blksize is number of adjacent repeated entries for a component */
+  blksize = cquantize->sv_actual;
+
+  for (i = 0; i < cinfo->out_color_components; i++) {
+    /* fill in colorindex entries for i'th color component */
+    nci = cquantize->Ncolors[i]; /* # of distinct values for this color */
+    blksize = blksize / nci;
+
+    /* adjust colorindex pointers to provide padding at negative indexes. */
+    if (pad)
+      cquantize->colorindex[i] += MAXJSAMPLE;
+
+    /* in loop, val = index of current output value, */
+    /* and k = largest j that maps to current val */
+    indexptr = cquantize->colorindex[i];
+    val = 0;
+    k = largest_input_value(cinfo, i, 0, nci-1);
+    for (j = 0; j <= MAXJSAMPLE; j++) {
+      while (j > k)		/* advance val if past boundary */
+	k = largest_input_value(cinfo, i, ++val, nci-1);
+      /* premultiply so that no multiplication needed in main processing */
+      indexptr[j] = (JSAMPLE) (val * blksize);
+    }
+    /* Pad at both ends if necessary */
+    if (pad)
+      for (j = 1; j <= MAXJSAMPLE; j++) {
+	indexptr[-j] = indexptr[0];
+	indexptr[MAXJSAMPLE+j] = indexptr[MAXJSAMPLE];
+      }
+  }
+}
+
+
+/*
+ * Create an ordered-dither array for a component having ncolors
+ * distinct output values.
+ */
+
+LOCAL(ODITHER_MATRIX_PTR)
+make_odither_array (j_decompress_ptr cinfo, int ncolors)
+{
+  ODITHER_MATRIX_PTR odither;
+  int j,k;
+  INT32 num,den;
+
+  odither = (ODITHER_MATRIX_PTR)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(ODITHER_MATRIX));
+  /* The inter-value distance for this color is MAXJSAMPLE/(ncolors-1).
+   * Hence the dither value for the matrix cell with fill order f
+   * (f=0..N-1) should be (N-1-2*f)/(2*N) * MAXJSAMPLE/(ncolors-1).
+   * On 16-bit-int machine, be careful to avoid overflow.
+   */
+  den = 2 * ODITHER_CELLS * ((INT32) (ncolors - 1));
+  for (j = 0; j < ODITHER_SIZE; j++) {
+    for (k = 0; k < ODITHER_SIZE; k++) {
+      num = ((INT32) (ODITHER_CELLS-1 - 2*((int)base_dither_matrix[j][k])))
+	    * MAXJSAMPLE;
+      /* Ensure round towards zero despite C's lack of consistency
+       * about rounding negative values in integer division...
+       */
+      odither[j][k] = (int) (num<0 ? -((-num)/den) : num/den);
+    }
+  }
+  return odither;
+}
+
+
+/*
+ * Create the ordered-dither tables.
+ * Components having the same number of representative colors may 
+ * share a dither table.
+ */
+
+LOCAL(void)
+create_odither_tables (j_decompress_ptr cinfo)
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  ODITHER_MATRIX_PTR odither;
+  int i, j, nci;
+
+  for (i = 0; i < cinfo->out_color_components; i++) {
+    nci = cquantize->Ncolors[i]; /* # of distinct values for this color */
+    odither = NULL;		/* search for matching prior component */
+    for (j = 0; j < i; j++) {
+      if (nci == cquantize->Ncolors[j]) {
+	odither = cquantize->odither[j];
+	break;
+      }
+    }
+    if (odither == NULL)	/* need a new table? */
+      odither = make_odither_array(cinfo, nci);
+    cquantize->odither[i] = odither;
+  }
+}
+
+
+/*
+ * Map some rows of pixels to the output colormapped representation.
+ */
+
+METHODDEF(void)
+color_quantize (j_decompress_ptr cinfo, JSAMPARRAY input_buf,
+		JSAMPARRAY output_buf, int num_rows)
+/* General case, no dithering */
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  JSAMPARRAY colorindex = cquantize->colorindex;
+  register int pixcode, ci;
+  register JSAMPROW ptrin, ptrout;
+  int row;
+  JDIMENSION col;
+  JDIMENSION width = cinfo->output_width;
+  register int nc = cinfo->out_color_components;
+
+  for (row = 0; row < num_rows; row++) {
+    ptrin = input_buf[row];
+    ptrout = output_buf[row];
+    for (col = width; col > 0; col--) {
+      pixcode = 0;
+      for (ci = 0; ci < nc; ci++) {
+	pixcode += GETJSAMPLE(colorindex[ci][GETJSAMPLE(*ptrin++)]);
+      }
+      *ptrout++ = (JSAMPLE) pixcode;
+    }
+  }
+}
+
+
+METHODDEF(void)
+color_quantize3 (j_decompress_ptr cinfo, JSAMPARRAY input_buf,
+		 JSAMPARRAY output_buf, int num_rows)
+/* Fast path for out_color_components==3, no dithering */
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  register int pixcode;
+  register JSAMPROW ptrin, ptrout;
+  JSAMPROW colorindex0 = cquantize->colorindex[0];
+  JSAMPROW colorindex1 = cquantize->colorindex[1];
+  JSAMPROW colorindex2 = cquantize->colorindex[2];
+  int row;
+  JDIMENSION col;
+  JDIMENSION width = cinfo->output_width;
+
+  for (row = 0; row < num_rows; row++) {
+    ptrin = input_buf[row];
+    ptrout = output_buf[row];
+    for (col = width; col > 0; col--) {
+      pixcode  = GETJSAMPLE(colorindex0[GETJSAMPLE(*ptrin++)]);
+      pixcode += GETJSAMPLE(colorindex1[GETJSAMPLE(*ptrin++)]);
+      pixcode += GETJSAMPLE(colorindex2[GETJSAMPLE(*ptrin++)]);
+      *ptrout++ = (JSAMPLE) pixcode;
+    }
+  }
+}
+
+
+METHODDEF(void)
+quantize_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf,
+		     JSAMPARRAY output_buf, int num_rows)
+/* General case, with ordered dithering */
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  register JSAMPROW input_ptr;
+  register JSAMPROW output_ptr;
+  JSAMPROW colorindex_ci;
+  int * dither;			/* points to active row of dither matrix */
+  int row_index, col_index;	/* current indexes into dither matrix */
+  int nc = cinfo->out_color_components;
+  int ci;
+  int row;
+  JDIMENSION col;
+  JDIMENSION width = cinfo->output_width;
+
+  for (row = 0; row < num_rows; row++) {
+    /* Initialize output values to 0 so can process components separately */
+    jzero_far((void FAR *) output_buf[row],
+	      (size_t) (width * SIZEOF(JSAMPLE)));
+    row_index = cquantize->row_index;
+    for (ci = 0; ci < nc; ci++) {
+      input_ptr = input_buf[row] + ci;
+      output_ptr = output_buf[row];
+      colorindex_ci = cquantize->colorindex[ci];
+      dither = cquantize->odither[ci][row_index];
+      col_index = 0;
+
+      for (col = width; col > 0; col--) {
+	/* Form pixel value + dither, range-limit to 0..MAXJSAMPLE,
+	 * select output value, accumulate into output code for this pixel.
+	 * Range-limiting need not be done explicitly, as we have extended
+	 * the colorindex table to produce the right answers for out-of-range
+	 * inputs.  The maximum dither is +- MAXJSAMPLE; this sets the
+	 * required amount of padding.
+	 */
+	*output_ptr += colorindex_ci[GETJSAMPLE(*input_ptr)+dither[col_index]];
+	input_ptr += nc;
+	output_ptr++;
+	col_index = (col_index + 1) & ODITHER_MASK;
+      }
+    }
+    /* Advance row index for next row */
+    row_index = (row_index + 1) & ODITHER_MASK;
+    cquantize->row_index = row_index;
+  }
+}
+
+
+METHODDEF(void)
+quantize3_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf,
+		      JSAMPARRAY output_buf, int num_rows)
+/* Fast path for out_color_components==3, with ordered dithering */
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  register int pixcode;
+  register JSAMPROW input_ptr;
+  register JSAMPROW output_ptr;
+  JSAMPROW colorindex0 = cquantize->colorindex[0];
+  JSAMPROW colorindex1 = cquantize->colorindex[1];
+  JSAMPROW colorindex2 = cquantize->colorindex[2];
+  int * dither0;		/* points to active row of dither matrix */
+  int * dither1;
+  int * dither2;
+  int row_index, col_index;	/* current indexes into dither matrix */
+  int row;
+  JDIMENSION col;
+  JDIMENSION width = cinfo->output_width;
+
+  for (row = 0; row < num_rows; row++) {
+    row_index = cquantize->row_index;
+    input_ptr = input_buf[row];
+    output_ptr = output_buf[row];
+    dither0 = cquantize->odither[0][row_index];
+    dither1 = cquantize->odither[1][row_index];
+    dither2 = cquantize->odither[2][row_index];
+    col_index = 0;
+
+    for (col = width; col > 0; col--) {
+      pixcode  = GETJSAMPLE(colorindex0[GETJSAMPLE(*input_ptr++) +
+					dither0[col_index]]);
+      pixcode += GETJSAMPLE(colorindex1[GETJSAMPLE(*input_ptr++) +
+					dither1[col_index]]);
+      pixcode += GETJSAMPLE(colorindex2[GETJSAMPLE(*input_ptr++) +
+					dither2[col_index]]);
+      *output_ptr++ = (JSAMPLE) pixcode;
+      col_index = (col_index + 1) & ODITHER_MASK;
+    }
+    row_index = (row_index + 1) & ODITHER_MASK;
+    cquantize->row_index = row_index;
+  }
+}
+
+
+METHODDEF(void)
+quantize_fs_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf,
+		    JSAMPARRAY output_buf, int num_rows)
+/* General case, with Floyd-Steinberg dithering */
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  register LOCFSERROR cur;	/* current error or pixel value */
+  LOCFSERROR belowerr;		/* error for pixel below cur */
+  LOCFSERROR bpreverr;		/* error for below/prev col */
+  LOCFSERROR bnexterr;		/* error for below/next col */
+  LOCFSERROR delta;
+  register FSERRPTR errorptr;	/* => fserrors[] at column before current */
+  register JSAMPROW input_ptr;
+  register JSAMPROW output_ptr;
+  JSAMPROW colorindex_ci;
+  JSAMPROW colormap_ci;
+  int pixcode;
+  int nc = cinfo->out_color_components;
+  int dir;			/* 1 for left-to-right, -1 for right-to-left */
+  int dirnc;			/* dir * nc */
+  int ci;
+  int row;
+  JDIMENSION col;
+  JDIMENSION width = cinfo->output_width;
+  JSAMPLE *range_limit = cinfo->sample_range_limit;
+  SHIFT_TEMPS
+
+  for (row = 0; row < num_rows; row++) {
+    /* Initialize output values to 0 so can process components separately */
+    jzero_far((void FAR *) output_buf[row],
+	      (size_t) (width * SIZEOF(JSAMPLE)));
+    for (ci = 0; ci < nc; ci++) {
+      input_ptr = input_buf[row] + ci;
+      output_ptr = output_buf[row];
+      if (cquantize->on_odd_row) {
+	/* work right to left in this row */
+	input_ptr += (width-1) * nc; /* so point to rightmost pixel */
+	output_ptr += width-1;
+	dir = -1;
+	dirnc = -nc;
+	errorptr = cquantize->fserrors[ci] + (width+1); /* => entry after last column */
+      } else {
+	/* work left to right in this row */
+	dir = 1;
+	dirnc = nc;
+	errorptr = cquantize->fserrors[ci]; /* => entry before first column */
+      }
+      colorindex_ci = cquantize->colorindex[ci];
+      colormap_ci = cquantize->sv_colormap[ci];
+      /* Preset error values: no error propagated to first pixel from left */
+      cur = 0;
+      /* and no error propagated to row below yet */
+      belowerr = bpreverr = 0;
+
+      for (col = width; col > 0; col--) {
+	/* cur holds the error propagated from the previous pixel on the
+	 * current line.  Add the error propagated from the previous line
+	 * to form the complete error correction term for this pixel, and
+	 * round the error term (which is expressed * 16) to an integer.
+	 * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct
+	 * for either sign of the error value.
+	 * Note: errorptr points to *previous* column's array entry.
+	 */
+	cur = RIGHT_SHIFT(cur + errorptr[dir] + 8, 4);
+	/* Form pixel value + error, and range-limit to 0..MAXJSAMPLE.
+	 * The maximum error is +- MAXJSAMPLE; this sets the required size
+	 * of the range_limit array.
+	 */
+	cur += GETJSAMPLE(*input_ptr);
+	cur = GETJSAMPLE(range_limit[cur]);
+	/* Select output value, accumulate into output code for this pixel */
+	pixcode = GETJSAMPLE(colorindex_ci[cur]);
+	*output_ptr += (JSAMPLE) pixcode;
+	/* Compute actual representation error at this pixel */
+	/* Note: we can do this even though we don't have the final */
+	/* pixel code, because the colormap is orthogonal. */
+	cur -= GETJSAMPLE(colormap_ci[pixcode]);
+	/* Compute error fractions to be propagated to adjacent pixels.
+	 * Add these into the running sums, and simultaneously shift the
+	 * next-line error sums left by 1 column.
+	 */
+	bnexterr = cur;
+	delta = cur * 2;
+	cur += delta;		/* form error * 3 */
+	errorptr[0] = (FSERROR) (bpreverr + cur);
+	cur += delta;		/* form error * 5 */
+	bpreverr = belowerr + cur;
+	belowerr = bnexterr;
+	cur += delta;		/* form error * 7 */
+	/* At this point cur contains the 7/16 error value to be propagated
+	 * to the next pixel on the current line, and all the errors for the
+	 * next line have been shifted over. We are therefore ready to move on.
+	 */
+	input_ptr += dirnc;	/* advance input ptr to next column */
+	output_ptr += dir;	/* advance output ptr to next column */
+	errorptr += dir;	/* advance errorptr to current column */
+      }
+      /* Post-loop cleanup: we must unload the final error value into the
+       * final fserrors[] entry.  Note we need not unload belowerr because
+       * it is for the dummy column before or after the actual array.
+       */
+      errorptr[0] = (FSERROR) bpreverr; /* unload prev err into array */
+    }
+    cquantize->on_odd_row = (cquantize->on_odd_row ? FALSE : TRUE);
+  }
+}
+
+
+/*
+ * Allocate workspace for Floyd-Steinberg errors.
+ */
+
+LOCAL(void)
+alloc_fs_workspace (j_decompress_ptr cinfo)
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  size_t arraysize;
+  int i;
+
+  arraysize = (size_t) ((cinfo->output_width + 2) * SIZEOF(FSERROR));
+  for (i = 0; i < cinfo->out_color_components; i++) {
+    cquantize->fserrors[i] = (FSERRPTR)
+      (*cinfo->mem->alloc_large)((j_common_ptr) cinfo, JPOOL_IMAGE, arraysize);
+  }
+}
+
+
+/*
+ * Initialize for one-pass color quantization.
+ */
+
+METHODDEF(void)
+start_pass_1_quant (j_decompress_ptr cinfo, boolean is_pre_scan)
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  size_t arraysize;
+  int i;
+
+  /* Install my colormap. */
+  cinfo->colormap = cquantize->sv_colormap;
+  cinfo->actual_number_of_colors = cquantize->sv_actual;
+
+  /* Initialize for desired dithering mode. */
+  switch (cinfo->dither_mode) {
+  case JDITHER_NONE:
+    if (cinfo->out_color_components == 3)
+      cquantize->pub.color_quantize = color_quantize3;
+    else
+      cquantize->pub.color_quantize = color_quantize;
+    break;
+  case JDITHER_ORDERED:
+    if (cinfo->out_color_components == 3)
+      cquantize->pub.color_quantize = quantize3_ord_dither;
+    else
+      cquantize->pub.color_quantize = quantize_ord_dither;
+    cquantize->row_index = 0;	/* initialize state for ordered dither */
+    /* If user changed to ordered dither from another mode,
+     * we must recreate the color index table with padding.
+     * This will cost extra space, but probably isn't very likely.
+     */
+    if (! cquantize->is_padded)
+      create_colorindex(cinfo);
+    /* Create ordered-dither tables if we didn't already. */
+    if (cquantize->odither[0] == NULL)
+      create_odither_tables(cinfo);
+    break;
+  case JDITHER_FS:
+    cquantize->pub.color_quantize = quantize_fs_dither;
+    cquantize->on_odd_row = FALSE; /* initialize state for F-S dither */
+    /* Allocate Floyd-Steinberg workspace if didn't already. */
+    if (cquantize->fserrors[0] == NULL)
+      alloc_fs_workspace(cinfo);
+    /* Initialize the propagated errors to zero. */
+    arraysize = (size_t) ((cinfo->output_width + 2) * SIZEOF(FSERROR));
+    for (i = 0; i < cinfo->out_color_components; i++)
+      jzero_far((void FAR *) cquantize->fserrors[i], arraysize);
+    break;
+  default:
+    ERREXIT(cinfo, JERR_NOT_COMPILED);
+    break;
+  }
+}
+
+
+/*
+ * Finish up at the end of the pass.
+ */
+
+METHODDEF(void)
+finish_pass_1_quant (j_decompress_ptr cinfo)
+{
+  /* no work in 1-pass case */
+}
+
+
+/*
+ * Switch to a new external colormap between output passes.
+ * Shouldn't get to this module!
+ */
+
+METHODDEF(void)
+new_color_map_1_quant (j_decompress_ptr cinfo)
+{
+  ERREXIT(cinfo, JERR_MODE_CHANGE);
+}
+
+
+/*
+ * Module initialization routine for 1-pass color quantization.
+ */
+
+GLOBAL(void)
+jinit_1pass_quantizer (j_decompress_ptr cinfo)
+{
+  my_cquantize_ptr cquantize;
+
+  cquantize = (my_cquantize_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_cquantizer));
+  cinfo->cquantize = (struct jpeg_color_quantizer *) cquantize;
+  cquantize->pub.start_pass = start_pass_1_quant;
+  cquantize->pub.finish_pass = finish_pass_1_quant;
+  cquantize->pub.new_color_map = new_color_map_1_quant;
+  cquantize->fserrors[0] = NULL; /* Flag FS workspace not allocated */
+  cquantize->odither[0] = NULL;	/* Also flag odither arrays not allocated */
+
+  /* Make sure my internal arrays won't overflow */
+  if (cinfo->out_color_components > MAX_Q_COMPS)
+    ERREXIT1(cinfo, JERR_QUANT_COMPONENTS, MAX_Q_COMPS);
+  /* Make sure colormap indexes can be represented by JSAMPLEs */
+  if (cinfo->desired_number_of_colors > (MAXJSAMPLE+1))
+    ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXJSAMPLE+1);
+
+  /* Create the colormap and color index table. */
+  create_colormap(cinfo);
+  create_colorindex(cinfo);
+
+  /* Allocate Floyd-Steinberg workspace now if requested.
+   * We do this now since it is FAR storage and may affect the memory
+   * manager's space calculations.  If the user changes to FS dither
+   * mode in a later pass, we will allocate the space then, and will
+   * possibly overrun the max_memory_to_use setting.
+   */
+  if (cinfo->dither_mode == JDITHER_FS)
+    alloc_fs_workspace(cinfo);
+}
+
+#endif /* QUANT_1PASS_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jquant2.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jquant2.c
new file mode 100644
index 0000000000..af601e334b
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jquant2.c
@@ -0,0 +1,1310 @@
+/*
+ * jquant2.c
+ *
+ * Copyright (C) 1991-1996, 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 2-pass color quantization (color mapping) routines.
+ * These routines provide selection of a custom color map for an image,
+ * followed by mapping of the image to that color map, with optional
+ * Floyd-Steinberg dithering.
+ * It is also possible to use just the second pass to map to an arbitrary
+ * externally-given color map.
+ *
+ * Note: ordered dithering is not supported, since there isn't any fast
+ * way to compute intercolor distances; it's unclear that ordered dither's
+ * fundamental assumptions even hold with an irregularly spaced color map.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+#ifdef QUANT_2PASS_SUPPORTED
+
+
+/*
+ * This module implements the well-known Heckbert paradigm for color
+ * quantization.  Most of the ideas used here can be traced back to
+ * Heckbert's seminal paper
+ *   Heckbert, Paul.  "Color Image Quantization for Frame Buffer Display",
+ *   Proc. SIGGRAPH '82, Computer Graphics v.16 #3 (July 1982), pp 297-304.
+ *
+ * In the first pass over the image, we accumulate a histogram showing the
+ * usage count of each possible color.  To keep the histogram to a reasonable
+ * size, we reduce the precision of the input; typical practice is to retain
+ * 5 or 6 bits per color, so that 8 or 4 different input values are counted
+ * in the same histogram cell.
+ *
+ * Next, the color-selection step begins with a box representing the whole
+ * color space, and repeatedly splits the "largest" remaining box until we
+ * have as many boxes as desired colors.  Then the mean color in each
+ * remaining box becomes one of the possible output colors.
+ * 
+ * The second pass over the image maps each input pixel to the closest output
+ * color (optionally after applying a Floyd-Steinberg dithering correction).
+ * This mapping is logically trivial, but making it go fast enough requires
+ * considerable care.
+ *
+ * Heckbert-style quantizers vary a good deal in their policies for choosing
+ * the "largest" box and deciding where to cut it.  The particular policies
+ * used here have proved out well in experimental comparisons, but better ones
+ * may yet be found.
+ *
+ * In earlier versions of the IJG code, this module quantized in YCbCr color
+ * space, processing the raw upsampled data without a color conversion step.
+ * This allowed the color conversion math to be done only once per colormap
+ * entry, not once per pixel.  However, that optimization precluded other
+ * useful optimizations (such as merging color conversion with upsampling)
+ * and it also interfered with desired capabilities such as quantizing to an
+ * externally-supplied colormap.  We have therefore abandoned that approach.
+ * The present code works in the post-conversion color space, typically RGB.
+ *
+ * To improve the visual quality of the results, we actually work in scaled
+ * RGB space, giving G distances more weight than R, and R in turn more than
+ * B.  To do everything in integer math, we must use integer scale factors.
+ * The 2/3/1 scale factors used here correspond loosely to the relative
+ * weights of the colors in the NTSC grayscale equation.
+ * If you want to use this code to quantize a non-RGB color space, you'll
+ * probably need to change these scale factors.
+ */
+
+#define R_SCALE 2		/* scale R distances by this much */
+#define G_SCALE 3		/* scale G distances by this much */
+#define B_SCALE 1		/* and B by this much */
+
+/* Relabel R/G/B as components 0/1/2, respecting the RGB ordering defined
+ * in jmorecfg.h.  As the code stands, it will do the right thing for R,G,B
+ * and B,G,R orders.  If you define some other weird order in jmorecfg.h,
+ * you'll get compile errors until you extend this logic.  In that case
+ * you'll probably want to tweak the histogram sizes too.
+ */
+
+#if RGB_RED == 0
+#define C0_SCALE R_SCALE
+#endif
+#if RGB_BLUE == 0
+#define C0_SCALE B_SCALE
+#endif
+#if RGB_GREEN == 1
+#define C1_SCALE G_SCALE
+#endif
+#if RGB_RED == 2
+#define C2_SCALE R_SCALE
+#endif
+#if RGB_BLUE == 2
+#define C2_SCALE B_SCALE
+#endif
+
+
+/*
+ * First we have the histogram data structure and routines for creating it.
+ *
+ * The number of bits of precision can be adjusted by changing these symbols.
+ * We recommend keeping 6 bits for G and 5 each for R and B.
+ * If you have plenty of memory and cycles, 6 bits all around gives marginally
+ * better results; if you are short of memory, 5 bits all around will save
+ * some space but degrade the results.
+ * To maintain a fully accurate histogram, we'd need to allocate a "long"
+ * (preferably unsigned long) for each cell.  In practice this is overkill;
+ * we can get by with 16 bits per cell.  Few of the cell counts will overflow,
+ * and clamping those that do overflow to the maximum value will give close-
+ * enough results.  This reduces the recommended histogram size from 256Kb
+ * to 128Kb, which is a useful savings on PC-class machines.
+ * (In the second pass the histogram space is re-used for pixel mapping data;
+ * in that capacity, each cell must be able to store zero to the number of
+ * desired colors.  16 bits/cell is plenty for that too.)
+ * Since the JPEG code is intended to run in small memory model on 80x86
+ * machines, we can't just allocate the histogram in one chunk.  Instead
+ * of a true 3-D array, we use a row of pointers to 2-D arrays.  Each
+ * pointer corresponds to a C0 value (typically 2^5 = 32 pointers) and
+ * each 2-D array has 2^6*2^5 = 2048 or 2^6*2^6 = 4096 entries.  Note that
+ * on 80x86 machines, the pointer row is in near memory but the actual
+ * arrays are in far memory (same arrangement as we use for image arrays).
+ */
+
+#define MAXNUMCOLORS  (MAXJSAMPLE+1) /* maximum size of colormap */
+
+/* These will do the right thing for either R,G,B or B,G,R color order,
+ * but you may not like the results for other color orders.
+ */
+#define HIST_C0_BITS  5		/* bits of precision in R/B histogram */
+#define HIST_C1_BITS  6		/* bits of precision in G histogram */
+#define HIST_C2_BITS  5		/* bits of precision in B/R histogram */
+
+/* Number of elements along histogram axes. */
+#define HIST_C0_ELEMS  (1<<HIST_C0_BITS)
+#define HIST_C1_ELEMS  (1<<HIST_C1_BITS)
+#define HIST_C2_ELEMS  (1<<HIST_C2_BITS)
+
+/* These are the amounts to shift an input value to get a histogram index. */
+#define C0_SHIFT  (BITS_IN_JSAMPLE-HIST_C0_BITS)
+#define C1_SHIFT  (BITS_IN_JSAMPLE-HIST_C1_BITS)
+#define C2_SHIFT  (BITS_IN_JSAMPLE-HIST_C2_BITS)
+
+
+typedef UINT16 histcell;	/* histogram cell; prefer an unsigned type */
+
+typedef histcell FAR * histptr;	/* for pointers to histogram cells */
+
+typedef histcell hist1d[HIST_C2_ELEMS]; /* typedefs for the array */
+typedef hist1d FAR * hist2d;	/* type for the 2nd-level pointers */
+typedef hist2d * hist3d;	/* type for top-level pointer */
+
+
+/* Declarations for Floyd-Steinberg dithering.
+ *
+ * Errors are accumulated into the array fserrors[], at a resolution of
+ * 1/16th of a pixel count.  The error at a given pixel is propagated
+ * to its not-yet-processed neighbors using the standard F-S fractions,
+ *		...	(here)	7/16
+ *		3/16	5/16	1/16
+ * We work left-to-right on even rows, right-to-left on odd rows.
+ *
+ * We can get away with a single array (holding one row's worth of errors)
+ * by using it to store the current row's errors at pixel columns not yet
+ * processed, but the next row's errors at columns already processed.  We
+ * need only a few extra variables to hold the errors immediately around the
+ * current column.  (If we are lucky, those variables are in registers, but
+ * even if not, they're probably cheaper to access than array elements are.)
+ *
+ * The fserrors[] array has (#columns + 2) entries; the extra entry at
+ * each end saves us from special-casing the first and last pixels.
+ * Each entry is three values long, one value for each color component.
+ *
+ * Note: on a wide image, we might not have enough room in a PC's near data
+ * segment to hold the error array; so it is allocated with alloc_large.
+ */
+
+#if BITS_IN_JSAMPLE == 8
+typedef INT16 FSERROR;		/* 16 bits should be enough */
+typedef int LOCFSERROR;		/* use 'int' for calculation temps */
+#else
+typedef INT32 FSERROR;		/* may need more than 16 bits */
+typedef INT32 LOCFSERROR;	/* be sure calculation temps are big enough */
+#endif
+
+typedef FSERROR FAR *FSERRPTR;	/* pointer to error array (in FAR storage!) */
+
+
+/* Private subobject */
+
+typedef struct {
+  struct jpeg_color_quantizer pub; /* public fields */
+
+  /* Space for the eventually created colormap is stashed here */
+  JSAMPARRAY sv_colormap;	/* colormap allocated at init time */
+  int desired;			/* desired # of colors = size of colormap */
+
+  /* Variables for accumulating image statistics */
+  hist3d histogram;		/* pointer to the histogram */
+
+  boolean needs_zeroed;		/* TRUE if next pass must zero histogram */
+
+  /* Variables for Floyd-Steinberg dithering */
+  FSERRPTR fserrors;		/* accumulated errors */
+  boolean on_odd_row;		/* flag to remember which row we are on */
+  int * error_limiter;		/* table for clamping the applied error */
+} my_cquantizer;
+
+typedef my_cquantizer * my_cquantize_ptr;
+
+
+/*
+ * Prescan some rows of pixels.
+ * In this module the prescan simply updates the histogram, which has been
+ * initialized to zeroes by start_pass.
+ * An output_buf parameter is required by the method signature, but no data
+ * is actually output (in fact the buffer controller is probably passing a
+ * NULL pointer).
+ */
+
+METHODDEF(void)
+prescan_quantize (j_decompress_ptr cinfo, JSAMPARRAY input_buf,
+		  JSAMPARRAY output_buf, int num_rows)
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  register JSAMPROW ptr;
+  register histptr histp;
+  register hist3d histogram = cquantize->histogram;
+  int row;
+  JDIMENSION col;
+  JDIMENSION width = cinfo->output_width;
+
+  for (row = 0; row < num_rows; row++) {
+    ptr = input_buf[row];
+    for (col = width; col > 0; col--) {
+      /* get pixel value and index into the histogram */
+      histp = & histogram[GETJSAMPLE(ptr[0]) >> C0_SHIFT]
+			 [GETJSAMPLE(ptr[1]) >> C1_SHIFT]
+			 [GETJSAMPLE(ptr[2]) >> C2_SHIFT];
+      /* increment, check for overflow and undo increment if so. */
+      if (++(*histp) <= 0)
+	(*histp)--;
+      ptr += 3;
+    }
+  }
+}
+
+
+/*
+ * Next we have the really interesting routines: selection of a colormap
+ * given the completed histogram.
+ * These routines work with a list of "boxes", each representing a rectangular
+ * subset of the input color space (to histogram precision).
+ */
+
+typedef struct {
+  /* The bounds of the box (inclusive); expressed as histogram indexes */
+  int c0min, c0max;
+  int c1min, c1max;
+  int c2min, c2max;
+  /* The volume (actually 2-norm) of the box */
+  INT32 volume;
+  /* The number of nonzero histogram cells within this box */
+  long colorcount;
+} box;
+
+typedef box * boxptr;
+
+
+LOCAL(boxptr)
+find_biggest_color_pop (boxptr boxlist, int numboxes)
+/* Find the splittable box with the largest color population */
+/* Returns NULL if no splittable boxes remain */
+{
+  register boxptr boxp;
+  register int i;
+  register long maxc = 0;
+  boxptr which = NULL;
+  
+  for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) {
+    if (boxp->colorcount > maxc && boxp->volume > 0) {
+      which = boxp;
+      maxc = boxp->colorcount;
+    }
+  }
+  return which;
+}
+
+
+LOCAL(boxptr)
+find_biggest_volume (boxptr boxlist, int numboxes)
+/* Find the splittable box with the largest (scaled) volume */
+/* Returns NULL if no splittable boxes remain */
+{
+  register boxptr boxp;
+  register int i;
+  register INT32 maxv = 0;
+  boxptr which = NULL;
+  
+  for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) {
+    if (boxp->volume > maxv) {
+      which = boxp;
+      maxv = boxp->volume;
+    }
+  }
+  return which;
+}
+
+
+LOCAL(void)
+update_box (j_decompress_ptr cinfo, boxptr boxp)
+/* Shrink the min/max bounds of a box to enclose only nonzero elements, */
+/* and recompute its volume and population */
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  hist3d histogram = cquantize->histogram;
+  histptr histp;
+  int c0,c1,c2;
+  int c0min,c0max,c1min,c1max,c2min,c2max;
+  INT32 dist0,dist1,dist2;
+  long ccount;
+  
+  c0min = boxp->c0min;  c0max = boxp->c0max;
+  c1min = boxp->c1min;  c1max = boxp->c1max;
+  c2min = boxp->c2min;  c2max = boxp->c2max;
+  
+  if (c0max > c0min)
+    for (c0 = c0min; c0 <= c0max; c0++)
+      for (c1 = c1min; c1 <= c1max; c1++) {
+	histp = & histogram[c0][c1][c2min];
+	for (c2 = c2min; c2 <= c2max; c2++)
+	  if (*histp++ != 0) {
+	    boxp->c0min = c0min = c0;
+	    goto have_c0min;
+	  }
+      }
+ have_c0min:
+  if (c0max > c0min)
+    for (c0 = c0max; c0 >= c0min; c0--)
+      for (c1 = c1min; c1 <= c1max; c1++) {
+	histp = & histogram[c0][c1][c2min];
+	for (c2 = c2min; c2 <= c2max; c2++)
+	  if (*histp++ != 0) {
+	    boxp->c0max = c0max = c0;
+	    goto have_c0max;
+	  }
+      }
+ have_c0max:
+  if (c1max > c1min)
+    for (c1 = c1min; c1 <= c1max; c1++)
+      for (c0 = c0min; c0 <= c0max; c0++) {
+	histp = & histogram[c0][c1][c2min];
+	for (c2 = c2min; c2 <= c2max; c2++)
+	  if (*histp++ != 0) {
+	    boxp->c1min = c1min = c1;
+	    goto have_c1min;
+	  }
+      }
+ have_c1min:
+  if (c1max > c1min)
+    for (c1 = c1max; c1 >= c1min; c1--)
+      for (c0 = c0min; c0 <= c0max; c0++) {
+	histp = & histogram[c0][c1][c2min];
+	for (c2 = c2min; c2 <= c2max; c2++)
+	  if (*histp++ != 0) {
+	    boxp->c1max = c1max = c1;
+	    goto have_c1max;
+	  }
+      }
+ have_c1max:
+  if (c2max > c2min)
+    for (c2 = c2min; c2 <= c2max; c2++)
+      for (c0 = c0min; c0 <= c0max; c0++) {
+	histp = & histogram[c0][c1min][c2];
+	for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS)
+	  if (*histp != 0) {
+	    boxp->c2min = c2min = c2;
+	    goto have_c2min;
+	  }
+      }
+ have_c2min:
+  if (c2max > c2min)
+    for (c2 = c2max; c2 >= c2min; c2--)
+      for (c0 = c0min; c0 <= c0max; c0++) {
+	histp = & histogram[c0][c1min][c2];
+	for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS)
+	  if (*histp != 0) {
+	    boxp->c2max = c2max = c2;
+	    goto have_c2max;
+	  }
+      }
+ have_c2max:
+
+  /* Update box volume.
+   * We use 2-norm rather than real volume here; this biases the method
+   * against making long narrow boxes, and it has the side benefit that
+   * a box is splittable iff norm > 0.
+   * Since the differences are expressed in histogram-cell units,
+   * we have to shift back to JSAMPLE units to get consistent distances;
+   * after which, we scale according to the selected distance scale factors.
+   */
+  dist0 = ((c0max - c0min) << C0_SHIFT) * C0_SCALE;
+  dist1 = ((c1max - c1min) << C1_SHIFT) * C1_SCALE;
+  dist2 = ((c2max - c2min) << C2_SHIFT) * C2_SCALE;
+  boxp->volume = dist0*dist0 + dist1*dist1 + dist2*dist2;
+  
+  /* Now scan remaining volume of box and compute population */
+  ccount = 0;
+  for (c0 = c0min; c0 <= c0max; c0++)
+    for (c1 = c1min; c1 <= c1max; c1++) {
+      histp = & histogram[c0][c1][c2min];
+      for (c2 = c2min; c2 <= c2max; c2++, histp++)
+	if (*histp != 0) {
+	  ccount++;
+	}
+    }
+  boxp->colorcount = ccount;
+}
+
+
+LOCAL(int)
+median_cut (j_decompress_ptr cinfo, boxptr boxlist, int numboxes,
+	    int desired_colors)
+/* Repeatedly select and split the largest box until we have enough boxes */
+{
+  int n,lb;
+  int c0,c1,c2,cmax;
+  register boxptr b1,b2;
+
+  while (numboxes < desired_colors) {
+    /* Select box to split.
+     * Current algorithm: by population for first half, then by volume.
+     */
+    if (numboxes*2 <= desired_colors) {
+      b1 = find_biggest_color_pop(boxlist, numboxes);
+    } else {
+      b1 = find_biggest_volume(boxlist, numboxes);
+    }
+    if (b1 == NULL)		/* no splittable boxes left! */
+      break;
+    b2 = &boxlist[numboxes];	/* where new box will go */
+    /* Copy the color bounds to the new box. */
+    b2->c0max = b1->c0max; b2->c1max = b1->c1max; b2->c2max = b1->c2max;
+    b2->c0min = b1->c0min; b2->c1min = b1->c1min; b2->c2min = b1->c2min;
+    /* Choose which axis to split the box on.
+     * Current algorithm: longest scaled axis.
+     * See notes in update_box about scaling distances.
+     */
+    c0 = ((b1->c0max - b1->c0min) << C0_SHIFT) * C0_SCALE;
+    c1 = ((b1->c1max - b1->c1min) << C1_SHIFT) * C1_SCALE;
+    c2 = ((b1->c2max - b1->c2min) << C2_SHIFT) * C2_SCALE;
+    /* We want to break any ties in favor of green, then red, blue last.
+     * This code does the right thing for R,G,B or B,G,R color orders only.
+     */
+#if RGB_RED == 0
+    cmax = c1; n = 1;
+    if (c0 > cmax) { cmax = c0; n = 0; }
+    if (c2 > cmax) { n = 2; }
+#else
+    cmax = c1; n = 1;
+    if (c2 > cmax) { cmax = c2; n = 2; }
+    if (c0 > cmax) { n = 0; }
+#endif
+    /* Choose split point along selected axis, and update box bounds.
+     * Current algorithm: split at halfway point.
+     * (Since the box has been shrunk to minimum volume,
+     * any split will produce two nonempty subboxes.)
+     * Note that lb value is max for lower box, so must be < old max.
+     */
+    switch (n) {
+    case 0:
+      lb = (b1->c0max + b1->c0min) / 2;
+      b1->c0max = lb;
+      b2->c0min = lb+1;
+      break;
+    case 1:
+      lb = (b1->c1max + b1->c1min) / 2;
+      b1->c1max = lb;
+      b2->c1min = lb+1;
+      break;
+    case 2:
+      lb = (b1->c2max + b1->c2min) / 2;
+      b1->c2max = lb;
+      b2->c2min = lb+1;
+      break;
+    }
+    /* Update stats for boxes */
+    update_box(cinfo, b1);
+    update_box(cinfo, b2);
+    numboxes++;
+  }
+  return numboxes;
+}
+
+
+LOCAL(void)
+compute_color (j_decompress_ptr cinfo, boxptr boxp, int icolor)
+/* Compute representative color for a box, put it in colormap[icolor] */
+{
+  /* Current algorithm: mean weighted by pixels (not colors) */
+  /* Note it is important to get the rounding correct! */
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  hist3d histogram = cquantize->histogram;
+  histptr histp;
+  int c0,c1,c2;
+  int c0min,c0max,c1min,c1max,c2min,c2max;
+  long count;
+  long total = 0;
+  long c0total = 0;
+  long c1total = 0;
+  long c2total = 0;
+  
+  c0min = boxp->c0min;  c0max = boxp->c0max;
+  c1min = boxp->c1min;  c1max = boxp->c1max;
+  c2min = boxp->c2min;  c2max = boxp->c2max;
+  
+  for (c0 = c0min; c0 <= c0max; c0++)
+    for (c1 = c1min; c1 <= c1max; c1++) {
+      histp = & histogram[c0][c1][c2min];
+      for (c2 = c2min; c2 <= c2max; c2++) {
+	if ((count = *histp++) != 0) {
+	  total += count;
+	  c0total += ((c0 << C0_SHIFT) + ((1<<C0_SHIFT)>>1)) * count;
+	  c1total += ((c1 << C1_SHIFT) + ((1<<C1_SHIFT)>>1)) * count;
+	  c2total += ((c2 << C2_SHIFT) + ((1<<C2_SHIFT)>>1)) * count;
+	}
+      }
+    }
+  
+  cinfo->colormap[0][icolor] = (JSAMPLE) ((c0total + (total>>1)) / total);
+  cinfo->colormap[1][icolor] = (JSAMPLE) ((c1total + (total>>1)) / total);
+  cinfo->colormap[2][icolor] = (JSAMPLE) ((c2total + (total>>1)) / total);
+}
+
+
+LOCAL(void)
+select_colors (j_decompress_ptr cinfo, int desired_colors)
+/* Master routine for color selection */
+{
+  boxptr boxlist;
+  int numboxes;
+  int i;
+
+  /* Allocate workspace for box list */
+  boxlist = (boxptr) (*cinfo->mem->alloc_small)
+    ((j_common_ptr) cinfo, JPOOL_IMAGE, desired_colors * SIZEOF(box));
+  /* Initialize one box containing whole space */
+  numboxes = 1;
+  boxlist[0].c0min = 0;
+  boxlist[0].c0max = MAXJSAMPLE >> C0_SHIFT;
+  boxlist[0].c1min = 0;
+  boxlist[0].c1max = MAXJSAMPLE >> C1_SHIFT;
+  boxlist[0].c2min = 0;
+  boxlist[0].c2max = MAXJSAMPLE >> C2_SHIFT;
+  /* Shrink it to actually-used volume and set its statistics */
+  update_box(cinfo, & boxlist[0]);
+  /* Perform median-cut to produce final box list */
+  numboxes = median_cut(cinfo, boxlist, numboxes, desired_colors);
+  /* Compute the representative color for each box, fill colormap */
+  for (i = 0; i < numboxes; i++)
+    compute_color(cinfo, & boxlist[i], i);
+  cinfo->actual_number_of_colors = numboxes;
+  TRACEMS1(cinfo, 1, JTRC_QUANT_SELECTED, numboxes);
+}
+
+
+/*
+ * These routines are concerned with the time-critical task of mapping input
+ * colors to the nearest color in the selected colormap.
+ *
+ * We re-use the histogram space as an "inverse color map", essentially a
+ * cache for the results of nearest-color searches.  All colors within a
+ * histogram cell will be mapped to the same colormap entry, namely the one
+ * closest to the cell's center.  This may not be quite the closest entry to
+ * the actual input color, but it's almost as good.  A zero in the cache
+ * indicates we haven't found the nearest color for that cell yet; the array
+ * is cleared to zeroes before starting the mapping pass.  When we find the
+ * nearest color for a cell, its colormap index plus one is recorded in the
+ * cache for future use.  The pass2 scanning routines call fill_inverse_cmap
+ * when they need to use an unfilled entry in the cache.
+ *
+ * Our method of efficiently finding nearest colors is based on the "locally
+ * sorted search" idea described by Heckbert and on the incremental distance
+ * calculation described by Spencer W. Thomas in chapter III.1 of Graphics
+ * Gems II (James Arvo, ed.  Academic Press, 1991).  Thomas points out that
+ * the distances from a given colormap entry to each cell of the histogram can
+ * be computed quickly using an incremental method: the differences between
+ * distances to adjacent cells themselves differ by a constant.  This allows a
+ * fairly fast implementation of the "brute force" approach of computing the
+ * distance from every colormap entry to every histogram cell.  Unfortunately,
+ * it needs a work array to hold the best-distance-so-far for each histogram
+ * cell (because the inner loop has to be over cells, not colormap entries).
+ * The work array elements have to be INT32s, so the work array would need
+ * 256Kb at our recommended precision.  This is not feasible in DOS machines.
+ *
+ * To get around these problems, we apply Thomas' method to compute the
+ * nearest colors for only the cells within a small subbox of the histogram.
+ * The work array need be only as big as the subbox, so the memory usage
+ * problem is solved.  Furthermore, we need not fill subboxes that are never
+ * referenced in pass2; many images use only part of the color gamut, so a
+ * fair amount of work is saved.  An additional advantage of this
+ * approach is that we can apply Heckbert's locality criterion to quickly
+ * eliminate colormap entries that are far away from the subbox; typically
+ * three-fourths of the colormap entries are rejected by Heckbert's criterion,
+ * and we need not compute their distances to individual cells in the subbox.
+ * The speed of this approach is heavily influenced by the subbox size: too
+ * small means too much overhead, too big loses because Heckbert's criterion
+ * can't eliminate as many colormap entries.  Empirically the best subbox
+ * size seems to be about 1/512th of the histogram (1/8th in each direction).
+ *
+ * Thomas' article also describes a refined method which is asymptotically
+ * faster than the brute-force method, but it is also far more complex and
+ * cannot efficiently be applied to small subboxes.  It is therefore not
+ * useful for programs intended to be portable to DOS machines.  On machines
+ * with plenty of memory, filling the whole histogram in one shot with Thomas'
+ * refined method might be faster than the present code --- but then again,
+ * it might not be any faster, and it's certainly more complicated.
+ */
+
+
+/* log2(histogram cells in update box) for each axis; this can be adjusted */
+#define BOX_C0_LOG  (HIST_C0_BITS-3)
+#define BOX_C1_LOG  (HIST_C1_BITS-3)
+#define BOX_C2_LOG  (HIST_C2_BITS-3)
+
+#define BOX_C0_ELEMS  (1<<BOX_C0_LOG) /* # of hist cells in update box */
+#define BOX_C1_ELEMS  (1<<BOX_C1_LOG)
+#define BOX_C2_ELEMS  (1<<BOX_C2_LOG)
+
+#define BOX_C0_SHIFT  (C0_SHIFT + BOX_C0_LOG)
+#define BOX_C1_SHIFT  (C1_SHIFT + BOX_C1_LOG)
+#define BOX_C2_SHIFT  (C2_SHIFT + BOX_C2_LOG)
+
+
+/*
+ * The next three routines implement inverse colormap filling.  They could
+ * all be folded into one big routine, but splitting them up this way saves
+ * some stack space (the mindist[] and bestdist[] arrays need not coexist)
+ * and may allow some compilers to produce better code by registerizing more
+ * inner-loop variables.
+ */
+
+LOCAL(int)
+find_nearby_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2,
+		    JSAMPLE colorlist[])
+/* Locate the colormap entries close enough to an update box to be candidates
+ * for the nearest entry to some cell(s) in the update box.  The update box
+ * is specified by the center coordinates of its first cell.  The number of
+ * candidate colormap entries is returned, and their colormap indexes are
+ * placed in colorlist[].
+ * This routine uses Heckbert's "locally sorted search" criterion to select
+ * the colors that need further consideration.
+ */
+{
+  int numcolors = cinfo->actual_number_of_colors;
+  int maxc0, maxc1, maxc2;
+  int centerc0, centerc1, centerc2;
+  int i, x, ncolors;
+  INT32 minmaxdist, min_dist, max_dist, tdist;
+  INT32 mindist[MAXNUMCOLORS];	/* min distance to colormap entry i */
+
+  /* Compute true coordinates of update box's upper corner and center.
+   * Actually we compute the coordinates of the center of the upper-corner
+   * histogram cell, which are the upper bounds of the volume we care about.
+   * Note that since ">>" rounds down, the "center" values may be closer to
+   * min than to max; hence comparisons to them must be "<=", not "<".
+   */
+  maxc0 = minc0 + ((1 << BOX_C0_SHIFT) - (1 << C0_SHIFT));
+  centerc0 = (minc0 + maxc0) >> 1;
+  maxc1 = minc1 + ((1 << BOX_C1_SHIFT) - (1 << C1_SHIFT));
+  centerc1 = (minc1 + maxc1) >> 1;
+  maxc2 = minc2 + ((1 << BOX_C2_SHIFT) - (1 << C2_SHIFT));
+  centerc2 = (minc2 + maxc2) >> 1;
+
+  /* For each color in colormap, find:
+   *  1. its minimum squared-distance to any point in the update box
+   *     (zero if color is within update box);
+   *  2. its maximum squared-distance to any point in the update box.
+   * Both of these can be found by considering only the corners of the box.
+   * We save the minimum distance for each color in mindist[];
+   * only the smallest maximum distance is of interest.
+   */
+  minmaxdist = 0x7FFFFFFFL;
+
+  for (i = 0; i < numcolors; i++) {
+    /* We compute the squared-c0-distance term, then add in the other two. */
+    x = GETJSAMPLE(cinfo->colormap[0][i]);
+    if (x < minc0) {
+      tdist = (x - minc0) * C0_SCALE;
+      min_dist = tdist*tdist;
+      tdist = (x - maxc0) * C0_SCALE;
+      max_dist = tdist*tdist;
+    } else if (x > maxc0) {
+      tdist = (x - maxc0) * C0_SCALE;
+      min_dist = tdist*tdist;
+      tdist = (x - minc0) * C0_SCALE;
+      max_dist = tdist*tdist;
+    } else {
+      /* within cell range so no contribution to min_dist */
+      min_dist = 0;
+      if (x <= centerc0) {
+	tdist = (x - maxc0) * C0_SCALE;
+	max_dist = tdist*tdist;
+      } else {
+	tdist = (x - minc0) * C0_SCALE;
+	max_dist = tdist*tdist;
+      }
+    }
+
+    x = GETJSAMPLE(cinfo->colormap[1][i]);
+    if (x < minc1) {
+      tdist = (x - minc1) * C1_SCALE;
+      min_dist += tdist*tdist;
+      tdist = (x - maxc1) * C1_SCALE;
+      max_dist += tdist*tdist;
+    } else if (x > maxc1) {
+      tdist = (x - maxc1) * C1_SCALE;
+      min_dist += tdist*tdist;
+      tdist = (x - minc1) * C1_SCALE;
+      max_dist += tdist*tdist;
+    } else {
+      /* within cell range so no contribution to min_dist */
+      if (x <= centerc1) {
+	tdist = (x - maxc1) * C1_SCALE;
+	max_dist += tdist*tdist;
+      } else {
+	tdist = (x - minc1) * C1_SCALE;
+	max_dist += tdist*tdist;
+      }
+    }
+
+    x = GETJSAMPLE(cinfo->colormap[2][i]);
+    if (x < minc2) {
+      tdist = (x - minc2) * C2_SCALE;
+      min_dist += tdist*tdist;
+      tdist = (x - maxc2) * C2_SCALE;
+      max_dist += tdist*tdist;
+    } else if (x > maxc2) {
+      tdist = (x - maxc2) * C2_SCALE;
+      min_dist += tdist*tdist;
+      tdist = (x - minc2) * C2_SCALE;
+      max_dist += tdist*tdist;
+    } else {
+      /* within cell range so no contribution to min_dist */
+      if (x <= centerc2) {
+	tdist = (x - maxc2) * C2_SCALE;
+	max_dist += tdist*tdist;
+      } else {
+	tdist = (x - minc2) * C2_SCALE;
+	max_dist += tdist*tdist;
+      }
+    }
+
+    mindist[i] = min_dist;	/* save away the results */
+    if (max_dist < minmaxdist)
+      minmaxdist = max_dist;
+  }
+
+  /* Now we know that no cell in the update box is more than minmaxdist
+   * away from some colormap entry.  Therefore, only colors that are
+   * within minmaxdist of some part of the box need be considered.
+   */
+  ncolors = 0;
+  for (i = 0; i < numcolors; i++) {
+    if (mindist[i] <= minmaxdist)
+      colorlist[ncolors++] = (JSAMPLE) i;
+  }
+  return ncolors;
+}
+
+
+LOCAL(void)
+find_best_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2,
+		  int numcolors, JSAMPLE colorlist[], JSAMPLE bestcolor[])
+/* Find the closest colormap entry for each cell in the update box,
+ * given the list of candidate colors prepared by find_nearby_colors.
+ * Return the indexes of the closest entries in the bestcolor[] array.
+ * This routine uses Thomas' incremental distance calculation method to
+ * find the distance from a colormap entry to successive cells in the box.
+ */
+{
+  int ic0, ic1, ic2;
+  int i, icolor;
+  register INT32 * bptr;	/* pointer into bestdist[] array */
+  JSAMPLE * cptr;		/* pointer into bestcolor[] array */
+  INT32 dist0, dist1;		/* initial distance values */
+  register INT32 dist2;		/* current distance in inner loop */
+  INT32 xx0, xx1;		/* distance increments */
+  register INT32 xx2;
+  INT32 inc0, inc1, inc2;	/* initial values for increments */
+  /* This array holds the distance to the nearest-so-far color for each cell */
+  INT32 bestdist[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS];
+
+  /* Initialize best-distance for each cell of the update box */
+  bptr = bestdist;
+  for (i = BOX_C0_ELEMS*BOX_C1_ELEMS*BOX_C2_ELEMS-1; i >= 0; i--)
+    *bptr++ = 0x7FFFFFFFL;
+  
+  /* For each color selected by find_nearby_colors,
+   * compute its distance to the center of each cell in the box.
+   * If that's less than best-so-far, update best distance and color number.
+   */
+  
+  /* Nominal steps between cell centers ("x" in Thomas article) */
+#define STEP_C0  ((1 << C0_SHIFT) * C0_SCALE)
+#define STEP_C1  ((1 << C1_SHIFT) * C1_SCALE)
+#define STEP_C2  ((1 << C2_SHIFT) * C2_SCALE)
+  
+  for (i = 0; i < numcolors; i++) {
+    icolor = GETJSAMPLE(colorlist[i]);
+    /* Compute (square of) distance from minc0/c1/c2 to this color */
+    inc0 = (minc0 - GETJSAMPLE(cinfo->colormap[0][icolor])) * C0_SCALE;
+    dist0 = inc0*inc0;
+    inc1 = (minc1 - GETJSAMPLE(cinfo->colormap[1][icolor])) * C1_SCALE;
+    dist0 += inc1*inc1;
+    inc2 = (minc2 - GETJSAMPLE(cinfo->colormap[2][icolor])) * C2_SCALE;
+    dist0 += inc2*inc2;
+    /* Form the initial difference increments */
+    inc0 = inc0 * (2 * STEP_C0) + STEP_C0 * STEP_C0;
+    inc1 = inc1 * (2 * STEP_C1) + STEP_C1 * STEP_C1;
+    inc2 = inc2 * (2 * STEP_C2) + STEP_C2 * STEP_C2;
+    /* Now loop over all cells in box, updating distance per Thomas method */
+    bptr = bestdist;
+    cptr = bestcolor;
+    xx0 = inc0;
+    for (ic0 = BOX_C0_ELEMS-1; ic0 >= 0; ic0--) {
+      dist1 = dist0;
+      xx1 = inc1;
+      for (ic1 = BOX_C1_ELEMS-1; ic1 >= 0; ic1--) {
+	dist2 = dist1;
+	xx2 = inc2;
+	for (ic2 = BOX_C2_ELEMS-1; ic2 >= 0; ic2--) {
+	  if (dist2 < *bptr) {
+	    *bptr = dist2;
+	    *cptr = (JSAMPLE) icolor;
+	  }
+	  dist2 += xx2;
+	  xx2 += 2 * STEP_C2 * STEP_C2;
+	  bptr++;
+	  cptr++;
+	}
+	dist1 += xx1;
+	xx1 += 2 * STEP_C1 * STEP_C1;
+      }
+      dist0 += xx0;
+      xx0 += 2 * STEP_C0 * STEP_C0;
+    }
+  }
+}
+
+
+LOCAL(void)
+fill_inverse_cmap (j_decompress_ptr cinfo, int c0, int c1, int c2)
+/* Fill the inverse-colormap entries in the update box that contains */
+/* histogram cell c0/c1/c2.  (Only that one cell MUST be filled, but */
+/* we can fill as many others as we wish.) */
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  hist3d histogram = cquantize->histogram;
+  int minc0, minc1, minc2;	/* lower left corner of update box */
+  int ic0, ic1, ic2;
+  register JSAMPLE * cptr;	/* pointer into bestcolor[] array */
+  register histptr cachep;	/* pointer into main cache array */
+  /* This array lists the candidate colormap indexes. */
+  JSAMPLE colorlist[MAXNUMCOLORS];
+  int numcolors;		/* number of candidate colors */
+  /* This array holds the actually closest colormap index for each cell. */
+  JSAMPLE bestcolor[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS];
+
+  /* Convert cell coordinates to update box ID */
+  c0 >>= BOX_C0_LOG;
+  c1 >>= BOX_C1_LOG;
+  c2 >>= BOX_C2_LOG;
+
+  /* Compute true coordinates of update box's origin corner.
+   * Actually we compute the coordinates of the center of the corner
+   * histogram cell, which are the lower bounds of the volume we care about.
+   */
+  minc0 = (c0 << BOX_C0_SHIFT) + ((1 << C0_SHIFT) >> 1);
+  minc1 = (c1 << BOX_C1_SHIFT) + ((1 << C1_SHIFT) >> 1);
+  minc2 = (c2 << BOX_C2_SHIFT) + ((1 << C2_SHIFT) >> 1);
+  
+  /* Determine which colormap entries are close enough to be candidates
+   * for the nearest entry to some cell in the update box.
+   */
+  numcolors = find_nearby_colors(cinfo, minc0, minc1, minc2, colorlist);
+
+  /* Determine the actually nearest colors. */
+  find_best_colors(cinfo, minc0, minc1, minc2, numcolors, colorlist,
+		   bestcolor);
+
+  /* Save the best color numbers (plus 1) in the main cache array */
+  c0 <<= BOX_C0_LOG;		/* convert ID back to base cell indexes */
+  c1 <<= BOX_C1_LOG;
+  c2 <<= BOX_C2_LOG;
+  cptr = bestcolor;
+  for (ic0 = 0; ic0 < BOX_C0_ELEMS; ic0++) {
+    for (ic1 = 0; ic1 < BOX_C1_ELEMS; ic1++) {
+      cachep = & histogram[c0+ic0][c1+ic1][c2];
+      for (ic2 = 0; ic2 < BOX_C2_ELEMS; ic2++) {
+	*cachep++ = (histcell) (GETJSAMPLE(*cptr++) + 1);
+      }
+    }
+  }
+}
+
+
+/*
+ * Map some rows of pixels to the output colormapped representation.
+ */
+
+METHODDEF(void)
+pass2_no_dither (j_decompress_ptr cinfo,
+		 JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows)
+/* This version performs no dithering */
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  hist3d histogram = cquantize->histogram;
+  register JSAMPROW inptr, outptr;
+  register histptr cachep;
+  register int c0, c1, c2;
+  int row;
+  JDIMENSION col;
+  JDIMENSION width = cinfo->output_width;
+
+  for (row = 0; row < num_rows; row++) {
+    inptr = input_buf[row];
+    outptr = output_buf[row];
+    for (col = width; col > 0; col--) {
+      /* get pixel value and index into the cache */
+      c0 = GETJSAMPLE(*inptr++) >> C0_SHIFT;
+      c1 = GETJSAMPLE(*inptr++) >> C1_SHIFT;
+      c2 = GETJSAMPLE(*inptr++) >> C2_SHIFT;
+      cachep = & histogram[c0][c1][c2];
+      /* If we have not seen this color before, find nearest colormap entry */
+      /* and update the cache */
+      if (*cachep == 0)
+	fill_inverse_cmap(cinfo, c0,c1,c2);
+      /* Now emit the colormap index for this cell */
+      *outptr++ = (JSAMPLE) (*cachep - 1);
+    }
+  }
+}
+
+
+METHODDEF(void)
+pass2_fs_dither (j_decompress_ptr cinfo,
+		 JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows)
+/* This version performs Floyd-Steinberg dithering */
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  hist3d histogram = cquantize->histogram;
+  register LOCFSERROR cur0, cur1, cur2;	/* current error or pixel value */
+  LOCFSERROR belowerr0, belowerr1, belowerr2; /* error for pixel below cur */
+  LOCFSERROR bpreverr0, bpreverr1, bpreverr2; /* error for below/prev col */
+  register FSERRPTR errorptr;	/* => fserrors[] at column before current */
+  JSAMPROW inptr;		/* => current input pixel */
+  JSAMPROW outptr;		/* => current output pixel */
+  histptr cachep;
+  int dir;			/* +1 or -1 depending on direction */
+  int dir3;			/* 3*dir, for advancing inptr & errorptr */
+  int row;
+  JDIMENSION col;
+  JDIMENSION width = cinfo->output_width;
+  JSAMPLE *range_limit = cinfo->sample_range_limit;
+  int *error_limit = cquantize->error_limiter;
+  JSAMPROW colormap0 = cinfo->colormap[0];
+  JSAMPROW colormap1 = cinfo->colormap[1];
+  JSAMPROW colormap2 = cinfo->colormap[2];
+  SHIFT_TEMPS
+
+  for (row = 0; row < num_rows; row++) {
+    inptr = input_buf[row];
+    outptr = output_buf[row];
+    if (cquantize->on_odd_row) {
+      /* work right to left in this row */
+      inptr += (width-1) * 3;	/* so point to rightmost pixel */
+      outptr += width-1;
+      dir = -1;
+      dir3 = -3;
+      errorptr = cquantize->fserrors + (width+1)*3; /* => entry after last column */
+      cquantize->on_odd_row = FALSE; /* flip for next time */
+    } else {
+      /* work left to right in this row */
+      dir = 1;
+      dir3 = 3;
+      errorptr = cquantize->fserrors; /* => entry before first real column */
+      cquantize->on_odd_row = TRUE; /* flip for next time */
+    }
+    /* Preset error values: no error propagated to first pixel from left */
+    cur0 = cur1 = cur2 = 0;
+    /* and no error propagated to row below yet */
+    belowerr0 = belowerr1 = belowerr2 = 0;
+    bpreverr0 = bpreverr1 = bpreverr2 = 0;
+
+    for (col = width; col > 0; col--) {
+      /* curN holds the error propagated from the previous pixel on the
+       * current line.  Add the error propagated from the previous line
+       * to form the complete error correction term for this pixel, and
+       * round the error term (which is expressed * 16) to an integer.
+       * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct
+       * for either sign of the error value.
+       * Note: errorptr points to *previous* column's array entry.
+       */
+      cur0 = RIGHT_SHIFT(cur0 + errorptr[dir3+0] + 8, 4);
+      cur1 = RIGHT_SHIFT(cur1 + errorptr[dir3+1] + 8, 4);
+      cur2 = RIGHT_SHIFT(cur2 + errorptr[dir3+2] + 8, 4);
+      /* Limit the error using transfer function set by init_error_limit.
+       * See comments with init_error_limit for rationale.
+       */
+      cur0 = error_limit[cur0];
+      cur1 = error_limit[cur1];
+      cur2 = error_limit[cur2];
+      /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE.
+       * The maximum error is +- MAXJSAMPLE (or less with error limiting);
+       * this sets the required size of the range_limit array.
+       */
+      cur0 += GETJSAMPLE(inptr[0]);
+      cur1 += GETJSAMPLE(inptr[1]);
+      cur2 += GETJSAMPLE(inptr[2]);
+      cur0 = GETJSAMPLE(range_limit[cur0]);
+      cur1 = GETJSAMPLE(range_limit[cur1]);
+      cur2 = GETJSAMPLE(range_limit[cur2]);
+      /* Index into the cache with adjusted pixel value */
+      cachep = & histogram[cur0>>C0_SHIFT][cur1>>C1_SHIFT][cur2>>C2_SHIFT];
+      /* If we have not seen this color before, find nearest colormap */
+      /* entry and update the cache */
+      if (*cachep == 0)
+	fill_inverse_cmap(cinfo, cur0>>C0_SHIFT,cur1>>C1_SHIFT,cur2>>C2_SHIFT);
+      /* Now emit the colormap index for this cell */
+      { register int pixcode = *cachep - 1;
+	*outptr = (JSAMPLE) pixcode;
+	/* Compute representation error for this pixel */
+	cur0 -= GETJSAMPLE(colormap0[pixcode]);
+	cur1 -= GETJSAMPLE(colormap1[pixcode]);
+	cur2 -= GETJSAMPLE(colormap2[pixcode]);
+      }
+      /* Compute error fractions to be propagated to adjacent pixels.
+       * Add these into the running sums, and simultaneously shift the
+       * next-line error sums left by 1 column.
+       */
+      { register LOCFSERROR bnexterr, delta;
+
+	bnexterr = cur0;	/* Process component 0 */
+	delta = cur0 * 2;
+	cur0 += delta;		/* form error * 3 */
+	errorptr[0] = (FSERROR) (bpreverr0 + cur0);
+	cur0 += delta;		/* form error * 5 */
+	bpreverr0 = belowerr0 + cur0;
+	belowerr0 = bnexterr;
+	cur0 += delta;		/* form error * 7 */
+	bnexterr = cur1;	/* Process component 1 */
+	delta = cur1 * 2;
+	cur1 += delta;		/* form error * 3 */
+	errorptr[1] = (FSERROR) (bpreverr1 + cur1);
+	cur1 += delta;		/* form error * 5 */
+	bpreverr1 = belowerr1 + cur1;
+	belowerr1 = bnexterr;
+	cur1 += delta;		/* form error * 7 */
+	bnexterr = cur2;	/* Process component 2 */
+	delta = cur2 * 2;
+	cur2 += delta;		/* form error * 3 */
+	errorptr[2] = (FSERROR) (bpreverr2 + cur2);
+	cur2 += delta;		/* form error * 5 */
+	bpreverr2 = belowerr2 + cur2;
+	belowerr2 = bnexterr;
+	cur2 += delta;		/* form error * 7 */
+      }
+      /* At this point curN contains the 7/16 error value to be propagated
+       * to the next pixel on the current line, and all the errors for the
+       * next line have been shifted over.  We are therefore ready to move on.
+       */
+      inptr += dir3;		/* Advance pixel pointers to next column */
+      outptr += dir;
+      errorptr += dir3;		/* advance errorptr to current column */
+    }
+    /* Post-loop cleanup: we must unload the final error values into the
+     * final fserrors[] entry.  Note we need not unload belowerrN because
+     * it is for the dummy column before or after the actual array.
+     */
+    errorptr[0] = (FSERROR) bpreverr0; /* unload prev errs into array */
+    errorptr[1] = (FSERROR) bpreverr1;
+    errorptr[2] = (FSERROR) bpreverr2;
+  }
+}
+
+
+/*
+ * Initialize the error-limiting transfer function (lookup table).
+ * The raw F-S error computation can potentially compute error values of up to
+ * +- MAXJSAMPLE.  But we want the maximum correction applied to a pixel to be
+ * much less, otherwise obviously wrong pixels will be created.  (Typical
+ * effects include weird fringes at color-area boundaries, isolated bright
+ * pixels in a dark area, etc.)  The standard advice for avoiding this problem
+ * is to ensure that the "corners" of the color cube are allocated as output
+ * colors; then repeated errors in the same direction cannot cause cascading
+ * error buildup.  However, that only prevents the error from getting
+ * completely out of hand; Aaron Giles reports that error limiting improves
+ * the results even with corner colors allocated.
+ * A simple clamping of the error values to about +- MAXJSAMPLE/8 works pretty
+ * well, but the smoother transfer function used below is even better.  Thanks
+ * to Aaron Giles for this idea.
+ */
+
+LOCAL(void)
+init_error_limit (j_decompress_ptr cinfo)
+/* Allocate and fill in the error_limiter table */
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  int * table;
+  int in, out;
+
+  table = (int *) (*cinfo->mem->alloc_small)
+    ((j_common_ptr) cinfo, JPOOL_IMAGE, (MAXJSAMPLE*2+1) * SIZEOF(int));
+  table += MAXJSAMPLE;		/* so can index -MAXJSAMPLE .. +MAXJSAMPLE */
+  cquantize->error_limiter = table;
+
+#define STEPSIZE ((MAXJSAMPLE+1)/16)
+  /* Map errors 1:1 up to +- MAXJSAMPLE/16 */
+  out = 0;
+  for (in = 0; in < STEPSIZE; in++, out++) {
+    table[in] = out; table[-in] = -out;
+  }
+  /* Map errors 1:2 up to +- 3*MAXJSAMPLE/16 */
+  for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1) {
+    table[in] = out; table[-in] = -out;
+  }
+  /* Clamp the rest to final out value (which is (MAXJSAMPLE+1)/8) */
+  for (; in <= MAXJSAMPLE; in++) {
+    table[in] = out; table[-in] = -out;
+  }
+#undef STEPSIZE
+}
+
+
+/*
+ * Finish up at the end of each pass.
+ */
+
+METHODDEF(void)
+finish_pass1 (j_decompress_ptr cinfo)
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+
+  /* Select the representative colors and fill in cinfo->colormap */
+  cinfo->colormap = cquantize->sv_colormap;
+  select_colors(cinfo, cquantize->desired);
+  /* Force next pass to zero the color index table */
+  cquantize->needs_zeroed = TRUE;
+}
+
+
+METHODDEF(void)
+finish_pass2 (j_decompress_ptr cinfo)
+{
+  /* no work */
+}
+
+
+/*
+ * Initialize for each processing pass.
+ */
+
+METHODDEF(void)
+start_pass_2_quant (j_decompress_ptr cinfo, boolean is_pre_scan)
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+  hist3d histogram = cquantize->histogram;
+  int i;
+
+  /* Only F-S dithering or no dithering is supported. */
+  /* If user asks for ordered dither, give him F-S. */
+  if (cinfo->dither_mode != JDITHER_NONE)
+    cinfo->dither_mode = JDITHER_FS;
+
+  if (is_pre_scan) {
+    /* Set up method pointers */
+    cquantize->pub.color_quantize = prescan_quantize;
+    cquantize->pub.finish_pass = finish_pass1;
+    cquantize->needs_zeroed = TRUE; /* Always zero histogram */
+  } else {
+    /* Set up method pointers */
+    if (cinfo->dither_mode == JDITHER_FS)
+      cquantize->pub.color_quantize = pass2_fs_dither;
+    else
+      cquantize->pub.color_quantize = pass2_no_dither;
+    cquantize->pub.finish_pass = finish_pass2;
+
+    /* Make sure color count is acceptable */
+    i = cinfo->actual_number_of_colors;
+    if (i < 1)
+      ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 1);
+    if (i > MAXNUMCOLORS)
+      ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS);
+
+    if (cinfo->dither_mode == JDITHER_FS) {
+      size_t arraysize = (size_t) ((cinfo->output_width + 2) *
+				   (3 * SIZEOF(FSERROR)));
+      /* Allocate Floyd-Steinberg workspace if we didn't already. */
+      if (cquantize->fserrors == NULL)
+	cquantize->fserrors = (FSERRPTR) (*cinfo->mem->alloc_large)
+	  ((j_common_ptr) cinfo, JPOOL_IMAGE, arraysize);
+      /* Initialize the propagated errors to zero. */
+      jzero_far((void FAR *) cquantize->fserrors, arraysize);
+      /* Make the error-limit table if we didn't already. */
+      if (cquantize->error_limiter == NULL)
+	init_error_limit(cinfo);
+      cquantize->on_odd_row = FALSE;
+    }
+
+  }
+  /* Zero the histogram or inverse color map, if necessary */
+  if (cquantize->needs_zeroed) {
+    for (i = 0; i < HIST_C0_ELEMS; i++) {
+      jzero_far((void FAR *) histogram[i],
+		HIST_C1_ELEMS*HIST_C2_ELEMS * SIZEOF(histcell));
+    }
+    cquantize->needs_zeroed = FALSE;
+  }
+}
+
+
+/*
+ * Switch to a new external colormap between output passes.
+ */
+
+METHODDEF(void)
+new_color_map_2_quant (j_decompress_ptr cinfo)
+{
+  my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+
+  /* Reset the inverse color map */
+  cquantize->needs_zeroed = TRUE;
+}
+
+
+/*
+ * Module initialization routine for 2-pass color quantization.
+ */
+
+GLOBAL(void)
+jinit_2pass_quantizer (j_decompress_ptr cinfo)
+{
+  my_cquantize_ptr cquantize;
+  int i;
+
+  cquantize = (my_cquantize_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_cquantizer));
+  cinfo->cquantize = (struct jpeg_color_quantizer *) cquantize;
+  cquantize->pub.start_pass = start_pass_2_quant;
+  cquantize->pub.new_color_map = new_color_map_2_quant;
+  cquantize->fserrors = NULL;	/* flag optional arrays not allocated */
+  cquantize->error_limiter = NULL;
+
+  /* Make sure jdmaster didn't give me a case I can't handle */
+  if (cinfo->out_color_components != 3)
+    ERREXIT(cinfo, JERR_NOTIMPL);
+
+  /* Allocate the histogram/inverse colormap storage */
+  cquantize->histogram = (hist3d) (*cinfo->mem->alloc_small)
+    ((j_common_ptr) cinfo, JPOOL_IMAGE, HIST_C0_ELEMS * SIZEOF(hist2d));
+  for (i = 0; i < HIST_C0_ELEMS; i++) {
+    cquantize->histogram[i] = (hist2d) (*cinfo->mem->alloc_large)
+      ((j_common_ptr) cinfo, JPOOL_IMAGE,
+       HIST_C1_ELEMS*HIST_C2_ELEMS * SIZEOF(histcell));
+  }
+  cquantize->needs_zeroed = TRUE; /* histogram is garbage now */
+
+  /* Allocate storage for the completed colormap, if required.
+   * We do this now since it is FAR storage and may affect
+   * the memory manager's space calculations.
+   */
+  if (cinfo->enable_2pass_quant) {
+    /* Make sure color count is acceptable */
+    int desired = cinfo->desired_number_of_colors;
+    /* Lower bound on # of colors ... somewhat arbitrary as long as > 0 */
+    if (desired < 8)
+      ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 8);
+    /* Make sure colormap indexes can be represented by JSAMPLEs */
+    if (desired > MAXNUMCOLORS)
+      ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS);
+    cquantize->sv_colormap = (*cinfo->mem->alloc_sarray)
+      ((j_common_ptr) cinfo,JPOOL_IMAGE, (JDIMENSION) desired, (JDIMENSION) 3);
+    cquantize->desired = desired;
+  } else
+    cquantize->sv_colormap = NULL;
+
+  /* Only F-S dithering or no dithering is supported. */
+  /* If user asks for ordered dither, give him F-S. */
+  if (cinfo->dither_mode != JDITHER_NONE)
+    cinfo->dither_mode = JDITHER_FS;
+
+  /* Allocate Floyd-Steinberg workspace if necessary.
+   * This isn't really needed until pass 2, but again it is FAR storage.
+   * Although we will cope with a later change in dither_mode,
+   * we do not promise to honor max_memory_to_use if dither_mode changes.
+   */
+  if (cinfo->dither_mode == JDITHER_FS) {
+    cquantize->fserrors = (FSERRPTR) (*cinfo->mem->alloc_large)
+      ((j_common_ptr) cinfo, JPOOL_IMAGE,
+       (size_t) ((cinfo->output_width + 2) * (3 * SIZEOF(FSERROR))));
+    /* Might as well create the error-limiting table too. */
+    init_error_limit(cinfo);
+  }
+}
+
+#endif /* QUANT_2PASS_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jutils.c b/Utilities/GDAL/frmts/jpeg/libjpeg/jutils.c
new file mode 100644
index 0000000000..d18a955562
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jutils.c
@@ -0,0 +1,179 @@
+/*
+ * jutils.c
+ *
+ * Copyright (C) 1991-1996, 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 tables and miscellaneous utility routines needed
+ * for both compression and decompression.
+ * Note we prefix all global names with "j" to minimize conflicts with
+ * a surrounding application.
+ */
+
+#define JPEG_INTERNALS
+#include "jinclude.h"
+#include "jpeglib.h"
+
+
+/*
+ * jpeg_zigzag_order[i] is the zigzag-order position of the i'th element
+ * of a DCT block read in natural order (left to right, top to bottom).
+ */
+
+#if 0				/* This table is not actually needed in v6a */
+
+const int jpeg_zigzag_order[DCTSIZE2] = {
+   0,  1,  5,  6, 14, 15, 27, 28,
+   2,  4,  7, 13, 16, 26, 29, 42,
+   3,  8, 12, 17, 25, 30, 41, 43,
+   9, 11, 18, 24, 31, 40, 44, 53,
+  10, 19, 23, 32, 39, 45, 52, 54,
+  20, 22, 33, 38, 46, 51, 55, 60,
+  21, 34, 37, 47, 50, 56, 59, 61,
+  35, 36, 48, 49, 57, 58, 62, 63
+};
+
+#endif
+
+/*
+ * jpeg_natural_order[i] is the natural-order position of the i'th element
+ * of zigzag order.
+ *
+ * When reading corrupted data, the Huffman decoders could attempt
+ * to reference an entry beyond the end of this array (if the decoded
+ * zero run length reaches past the end of the block).  To prevent
+ * wild stores without adding an inner-loop test, we put some extra
+ * "63"s after the real entries.  This will cause the extra coefficient
+ * to be stored in location 63 of the block, not somewhere random.
+ * The worst case would be a run-length of 15, which means we need 16
+ * fake entries.
+ */
+
+const int jpeg_natural_order[DCTSIZE2+16] = {
+  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,
+ 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */
+ 63, 63, 63, 63, 63, 63, 63, 63
+};
+
+
+/*
+ * Arithmetic utilities
+ */
+
+GLOBAL(long)
+jdiv_round_up (long a, long b)
+/* Compute a/b rounded up to next integer, ie, ceil(a/b) */
+/* Assumes a >= 0, b > 0 */
+{
+  return (a + b - 1L) / b;
+}
+
+
+GLOBAL(long)
+jround_up (long a, long b)
+/* Compute a rounded up to next multiple of b, ie, ceil(a/b)*b */
+/* Assumes a >= 0, b > 0 */
+{
+  a += b - 1L;
+  return a - (a % b);
+}
+
+
+/* On normal machines we can apply MEMCOPY() and MEMZERO() to sample arrays
+ * and coefficient-block arrays.  This won't work on 80x86 because the arrays
+ * are FAR and we're assuming a small-pointer memory model.  However, some
+ * DOS compilers provide far-pointer versions of memcpy() and memset() even
+ * in the small-model libraries.  These will be used if USE_FMEM is defined.
+ * Otherwise, the routines below do it the hard way.  (The performance cost
+ * is not all that great, because these routines aren't very heavily used.)
+ */
+
+#ifndef NEED_FAR_POINTERS	/* normal case, same as regular macros */
+#define FMEMCOPY(dest,src,size)	MEMCOPY(dest,src,size)
+#define FMEMZERO(target,size)	MEMZERO(target,size)
+#else				/* 80x86 case, define if we can */
+#ifdef USE_FMEM
+#define FMEMCOPY(dest,src,size)	_fmemcpy((void FAR *)(dest), (const void FAR *)(src), (size_t)(size))
+#define FMEMZERO(target,size)	_fmemset((void FAR *)(target), 0, (size_t)(size))
+#endif
+#endif
+
+
+GLOBAL(void)
+jcopy_sample_rows (JSAMPARRAY input_array, int source_row,
+		   JSAMPARRAY output_array, int dest_row,
+		   int num_rows, JDIMENSION num_cols)
+/* Copy some rows of samples from one place to another.
+ * num_rows rows are copied from input_array[source_row++]
+ * to output_array[dest_row++]; these areas may overlap for duplication.
+ * The source and destination arrays must be at least as wide as num_cols.
+ */
+{
+  register JSAMPROW inptr, outptr;
+#ifdef FMEMCOPY
+  register size_t count = (size_t) (num_cols * SIZEOF(JSAMPLE));
+#else
+  register JDIMENSION count;
+#endif
+  register int row;
+
+  input_array += source_row;
+  output_array += dest_row;
+
+  for (row = num_rows; row > 0; row--) {
+    inptr = *input_array++;
+    outptr = *output_array++;
+#ifdef FMEMCOPY
+    FMEMCOPY(outptr, inptr, count);
+#else
+    for (count = num_cols; count > 0; count--)
+      *outptr++ = *inptr++;	/* needn't bother with GETJSAMPLE() here */
+#endif
+  }
+}
+
+
+GLOBAL(void)
+jcopy_block_row (JBLOCKROW input_row, JBLOCKROW output_row,
+		 JDIMENSION num_blocks)
+/* Copy a row of coefficient blocks from one place to another. */
+{
+#ifdef FMEMCOPY
+  FMEMCOPY(output_row, input_row, num_blocks * (DCTSIZE2 * SIZEOF(JCOEF)));
+#else
+  register JCOEFPTR inptr, outptr;
+  register long count;
+
+  inptr = (JCOEFPTR) input_row;
+  outptr = (JCOEFPTR) output_row;
+  for (count = (long) num_blocks * DCTSIZE2; count > 0; count--) {
+    *outptr++ = *inptr++;
+  }
+#endif
+}
+
+
+GLOBAL(void)
+jzero_far (void FAR * target, size_t bytestozero)
+/* Zero out a chunk of FAR memory. */
+/* This might be sample-array data, block-array data, or alloc_large data. */
+{
+#ifdef FMEMZERO
+  FMEMZERO(target, bytestozero);
+#else
+  register char FAR * ptr = (char FAR *) target;
+  register size_t count;
+
+  for (count = bytestozero; count > 0; count--) {
+    *ptr++ = 0;
+  }
+#endif
+}
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/jversion.h b/Utilities/GDAL/frmts/jpeg/libjpeg/jversion.h
new file mode 100644
index 0000000000..6472c58d35
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/jversion.h
@@ -0,0 +1,14 @@
+/*
+ * jversion.h
+ *
+ * Copyright (C) 1991-1998, 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 software version identification.
+ */
+
+
+#define JVERSION	"6b  27-Mar-1998"
+
+#define JCOPYRIGHT	"Copyright (C) 1998, Thomas G. Lane"
diff --git a/Utilities/GDAL/frmts/jpeg/libjpeg/makefile.vc b/Utilities/GDAL/frmts/jpeg/libjpeg/makefile.vc
new file mode 100644
index 0000000000..04bb761935
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/libjpeg/makefile.vc
@@ -0,0 +1,24 @@
+GDAL_ROOT	=	..\..\..
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+
+OBJ	=	\
+	jcapimin.obj jcapistd.obj jccoefct.obj jccolor.obj jcdctmgr.obj jchuff.obj \
+        jcinit.obj jcmainct.obj jcmarker.obj jcmaster.obj jcomapi.obj jcparam.obj \
+        jcphuff.obj jcprepct.obj jcsample.obj jctrans.obj jdapimin.obj jdapistd.obj \
+        jdatadst.obj jdatasrc.obj jdcoefct.obj jdcolor.obj jddctmgr.obj jdhuff.obj \
+        jdinput.obj jdmainct.obj jdmarker.obj jdmaster.obj jdmerge.obj jdphuff.obj \
+        jdpostct.obj jdsample.obj jdtrans.obj jerror.obj jfdctflt.obj jfdctfst.obj \
+        jfdctint.obj jidctflt.obj jidctfst.obj jidctint.obj jidctred.obj jquant1.obj \
+        jquant2.obj jutils.obj jmemmgr.obj jmemansi.obj 
+
+GDAL_ROOT	=	..\..\..
+
+
+default:	$(OBJ)
+        lib /out:libjpeg.lib $(OBJ)
+	copy *.obj ..\..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/jpeg/makefile.vc b/Utilities/GDAL/frmts/jpeg/makefile.vc
new file mode 100644
index 0000000000..f852c61209
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/makefile.vc
@@ -0,0 +1,28 @@
+GDAL_ROOT	=	..\..
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+
+OBJ	=    jpgdataset.obj vsidataio.obj
+
+
+!IFDEF JPEG_EXTERNAL_LIB
+EXTRAFLAGS      = -I$(JPEGDIR)
+!ELSE
+EXTRAFLAGS = 	-Ilibjpeg
+!ENDIF
+
+
+default:	$(OBJ)
+	copy *.obj ..\o
+!IFNDEF JPEG_EXTERNAL_LIB
+	cd libjpeg
+	$(MAKE) /f makefile.vc
+	cd ..
+!ENDIF
+
+clean:
+	-del *.obj
+        cd libjpeg
+	$(MAKE) /f makefile.vc clean
+	cd ..
+
diff --git a/Utilities/GDAL/frmts/jpeg/vsidataio.cpp b/Utilities/GDAL/frmts/jpeg/vsidataio.cpp
new file mode 100644
index 0000000000..488b547695
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg/vsidataio.cpp
@@ -0,0 +1,374 @@
+/******************************************************************************
+ * $Id: vsidataio.cpp,v 1.3 2005/09/14 14:19:55 dron Exp $
+ *
+ * Project:  JPEG JFIF Driver
+ * Purpose:  Implement JPEG read/write io indirection through VSI.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *           Code partially derived from libjpeg jdatasrc.c and jdatadst.c.
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: vsidataio.cpp,v $
+ * Revision 1.3  2005/09/14 14:19:55  dron
+ * Use sizeof() function instead of SIZEOF macro.
+ *
+ * Revision 1.2  2005/09/11 17:59:01  fwarmerdam
+ * Fixed use of ferror().
+ *
+ * Revision 1.1  2005/09/11 17:15:16  fwarmerdam
+ * New
+ *
+ */
+
+#include "cpl_vsi.h"
+
+CPL_CVSID("$Id: vsidataio.cpp,v 1.3 2005/09/14 14:19:55 dron Exp $");
+
+CPL_C_START
+#include "jpeglib.h"
+#include "jerror.h"
+CPL_C_END
+
+
+/* Expanded data source object for stdio input */
+
+typedef struct {
+  struct jpeg_source_mgr pub;	/* public fields */
+
+  FILE * infile;		/* source stream */
+  JOCTET * buffer;		/* start of buffer */
+  boolean start_of_file;	/* have we gotten any data yet? */
+} my_source_mgr;
+
+typedef my_source_mgr * my_src_ptr;
+
+#define INPUT_BUF_SIZE  4096	/* choose an efficiently fread'able size */
+
+
+/*
+ * Initialize source --- called by jpeg_read_header
+ * before any data is actually read.
+ */
+
+METHODDEF(void)
+init_source (j_decompress_ptr cinfo)
+{
+  my_src_ptr src = (my_src_ptr) cinfo->src;
+
+  /* We reset the empty-input-file flag for each image,
+   * but we don't clear the input buffer.
+   * This is correct behavior for reading a series of images from one source.
+   */
+  src->start_of_file = TRUE;
+}
+
+
+/*
+ * Fill the input buffer --- called whenever buffer is emptied.
+ *
+ * In typical applications, this should read fresh data into the buffer
+ * (ignoring the current state of next_input_byte & bytes_in_buffer),
+ * reset the pointer & count to the start of the buffer, and return TRUE
+ * indicating that the buffer has been reloaded.  It is not necessary to
+ * fill the buffer entirely, only to obtain at least one more byte.
+ *
+ * There is no such thing as an EOF return.  If the end of the file has been
+ * reached, the routine has a choice of ERREXIT() or inserting fake data into
+ * the buffer.  In most cases, generating a warning message and inserting a
+ * fake EOI marker is the best course of action --- this will allow the
+ * decompressor to output however much of the image is there.  However,
+ * the resulting error message is misleading if the real problem is an empty
+ * input file, so we handle that case specially.
+ *
+ * In applications that need to be able to suspend compression due to input
+ * not being available yet, a FALSE return indicates that no more data can be
+ * obtained right now, but more may be forthcoming later.  In this situation,
+ * the decompressor will return to its caller (with an indication of the
+ * number of scanlines it has read, if any).  The application should resume
+ * decompression after it has loaded more data into the input buffer.  Note
+ * that there are substantial restrictions on the use of suspension --- see
+ * the documentation.
+ *
+ * When suspending, the decompressor will back up to a convenient restart point
+ * (typically the start of the current MCU). next_input_byte & bytes_in_buffer
+ * indicate where the restart point will be if the current call returns FALSE.
+ * Data beyond this point must be rescanned after resumption, so move it to
+ * the front of the buffer rather than discarding it.
+ */
+
+METHODDEF(boolean)
+fill_input_buffer (j_decompress_ptr cinfo)
+{
+  my_src_ptr src = (my_src_ptr) cinfo->src;
+  size_t nbytes;
+
+  nbytes = VSIFReadL(src->buffer, 1, INPUT_BUF_SIZE, src->infile);
+
+  if (nbytes <= 0) {
+    if (src->start_of_file)	/* Treat empty input file as fatal error */
+      ERREXIT(cinfo, JERR_INPUT_EMPTY);
+    WARNMS(cinfo, JWRN_JPEG_EOF);
+    /* Insert a fake EOI marker */
+    src->buffer[0] = (JOCTET) 0xFF;
+    src->buffer[1] = (JOCTET) JPEG_EOI;
+    nbytes = 2;
+  }
+
+  src->pub.next_input_byte = src->buffer;
+  src->pub.bytes_in_buffer = nbytes;
+  src->start_of_file = FALSE;
+
+  return TRUE;
+}
+
+
+/*
+ * Skip data --- used to skip over a potentially large amount of
+ * uninteresting data (such as an APPn marker).
+ *
+ * Writers of suspendable-input applications must note that skip_input_data
+ * is not granted the right to give a suspension return.  If the skip extends
+ * beyond the data currently in the buffer, the buffer can be marked empty so
+ * that the next read will cause a fill_input_buffer call that can suspend.
+ * Arranging for additional bytes to be discarded before reloading the input
+ * buffer is the application writer's problem.
+ */
+
+METHODDEF(void)
+skip_input_data (j_decompress_ptr cinfo, long num_bytes)
+{
+  my_src_ptr src = (my_src_ptr) cinfo->src;
+
+  /* Just a dumb implementation for now.  Could use fseek() except
+   * it doesn't work on pipes.  Not clear that being smart is worth
+   * any trouble anyway --- large skips are infrequent.
+   */
+  if (num_bytes > 0) {
+    while (num_bytes > (long) src->pub.bytes_in_buffer) {
+      num_bytes -= (long) src->pub.bytes_in_buffer;
+      (void) fill_input_buffer(cinfo);
+      /* note we assume that fill_input_buffer will never return FALSE,
+       * so suspension need not be handled.
+       */
+    }
+    src->pub.next_input_byte += (size_t) num_bytes;
+    src->pub.bytes_in_buffer -= (size_t) num_bytes;
+  }
+}
+
+
+/*
+ * An additional method that can be provided by data source modules is the
+ * resync_to_restart method for error recovery in the presence of RST markers.
+ * For the moment, this source module just uses the default resync method
+ * provided by the JPEG library.  That method assumes that no backtracking
+ * is possible.
+ */
+
+
+/*
+ * Terminate source --- called by jpeg_finish_decompress
+ * after all data has been read.  Often a no-op.
+ *
+ * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
+ * application must deal with any cleanup that should happen even
+ * for error exit.
+ */
+
+METHODDEF(void)
+term_source (j_decompress_ptr cinfo)
+{
+  /* no work necessary here */
+}
+
+
+/*
+ * Prepare for input from a stdio stream.
+ * The caller must have already opened the stream, and is responsible
+ * for closing it after finishing decompression.
+ */
+
+void jpeg_vsiio_src (j_decompress_ptr cinfo, FILE * infile)
+{
+  my_src_ptr src;
+
+  /* The source object and input buffer are made permanent so that a series
+   * of JPEG images can be read from the same file by calling jpeg_stdio_src
+   * only before the first one.  (If we discarded the buffer at the end of
+   * one image, we'd likely lose the start of the next one.)
+   * This makes it unsafe to use this manager and a different source
+   * manager serially with the same JPEG object.  Caveat programmer.
+   */
+  if (cinfo->src == NULL) {	/* first time for this JPEG object? */
+    cinfo->src = (struct jpeg_source_mgr *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+				  sizeof(my_source_mgr));
+    src = (my_src_ptr) cinfo->src;
+    src->buffer = (JOCTET *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+				  INPUT_BUF_SIZE * sizeof(JOCTET));
+  }
+
+  src = (my_src_ptr) cinfo->src;
+  src->pub.init_source = init_source;
+  src->pub.fill_input_buffer = fill_input_buffer;
+  src->pub.skip_input_data = skip_input_data;
+  src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
+  src->pub.term_source = term_source;
+  src->infile = infile;
+  src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
+  src->pub.next_input_byte = NULL; /* until buffer loaded */
+}
+
+
+/* ==================================================================== */
+/*      The rest was derived from jdatadst.c                            */
+/* ==================================================================== */
+
+/* Expanded data destination object for stdio output */
+
+typedef struct {
+  struct jpeg_destination_mgr pub; /* public fields */
+
+  FILE * outfile;		/* target stream */
+  JOCTET * buffer;		/* start of buffer */
+} my_destination_mgr;
+
+typedef my_destination_mgr * my_dest_ptr;
+
+#define OUTPUT_BUF_SIZE  4096	/* choose an efficiently fwrite'able size */
+
+
+/*
+ * Initialize destination --- called by jpeg_start_compress
+ * before any data is actually written.
+ */
+
+METHODDEF(void)
+init_destination (j_compress_ptr cinfo)
+{
+  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+
+  /* Allocate the output buffer --- it will be released when done with image */
+  dest->buffer = (JOCTET *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				  OUTPUT_BUF_SIZE * sizeof(JOCTET));
+
+  dest->pub.next_output_byte = dest->buffer;
+  dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
+}
+
+
+/*
+ * Empty the output buffer --- called whenever buffer fills up.
+ *
+ * In typical applications, this should write the entire output buffer
+ * (ignoring the current state of next_output_byte & free_in_buffer),
+ * reset the pointer & count to the start of the buffer, and return TRUE
+ * indicating that the buffer has been dumped.
+ *
+ * In applications that need to be able to suspend compression due to output
+ * overrun, a FALSE return indicates that the buffer cannot be emptied now.
+ * In this situation, the compressor will return to its caller (possibly with
+ * an indication that it has not accepted all the supplied scanlines).  The
+ * application should resume compression after it has made more room in the
+ * output buffer.  Note that there are substantial restrictions on the use of
+ * suspension --- see the documentation.
+ *
+ * When suspending, the compressor will back up to a convenient restart point
+ * (typically the start of the current MCU). next_output_byte & free_in_buffer
+ * indicate where the restart point will be if the current call returns FALSE.
+ * Data beyond this point will be regenerated after resumption, so do not
+ * write it out when emptying the buffer externally.
+ */
+
+METHODDEF(boolean)
+empty_output_buffer (j_compress_ptr cinfo)
+{
+  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+
+  if (VSIFWriteL(dest->buffer, 1, OUTPUT_BUF_SIZE, dest->outfile) !=
+      (size_t) OUTPUT_BUF_SIZE)
+    ERREXIT(cinfo, JERR_FILE_WRITE);
+
+  dest->pub.next_output_byte = dest->buffer;
+  dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
+
+  return TRUE;
+}
+
+
+/*
+ * Terminate destination --- called by jpeg_finish_compress
+ * after all data has been written.  Usually needs to flush buffer.
+ *
+ * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
+ * application must deal with any cleanup that should happen even
+ * for error exit.
+ */
+
+METHODDEF(void)
+term_destination (j_compress_ptr cinfo)
+{
+  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+  size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
+
+  /* Write any data remaining in the buffer */
+  if (datacount > 0) {
+    if (VSIFWriteL(dest->buffer, 1, datacount, dest->outfile) != datacount)
+      ERREXIT(cinfo, JERR_FILE_WRITE);
+  }
+  if( VSIFFlushL(dest->outfile) != 0 )
+    ERREXIT(cinfo, JERR_FILE_WRITE);
+}
+
+
+/*
+ * Prepare for output to a stdio stream.
+ * The caller must have already opened the stream, and is responsible
+ * for closing it after finishing compression.
+ */
+
+void
+jpeg_vsiio_dest (j_compress_ptr cinfo, FILE * outfile)
+{
+  my_dest_ptr dest;
+
+  /* The destination object is made permanent so that multiple JPEG images
+   * can be written to the same file without re-executing jpeg_stdio_dest.
+   * This makes it dangerous to use this manager and a different destination
+   * manager serially with the same JPEG object, because their private object
+   * sizes may be different.  Caveat programmer.
+   */
+  if (cinfo->dest == NULL) {	/* first time for this JPEG object? */
+    cinfo->dest = (struct jpeg_destination_mgr *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+				  sizeof(my_destination_mgr));
+  }
+
+  dest = (my_dest_ptr) cinfo->dest;
+  dest->pub.init_destination = init_destination;
+  dest->pub.empty_output_buffer = empty_output_buffer;
+  dest->pub.term_destination = term_destination;
+  dest->outfile = outfile;
+}
diff --git a/Utilities/GDAL/frmts/jpeg2000/GNUmakefile b/Utilities/GDAL/frmts/jpeg2000/GNUmakefile
new file mode 100644
index 0000000000..97a623df6f
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg2000/GNUmakefile
@@ -0,0 +1,14 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	jpeg2000dataset.o
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(JASPER_INCLUDE) $(JASPER_FLAGS) $(CPPFLAGS)
+CXXFLAGS	+=	-g -O2
+
+default:	$(OBJ)
+
+clean:
+	rm -f $(OBJ) $(O_OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/jpeg2000/frmt_jpeg2000.html b/Utilities/GDAL/frmts/jpeg2000/frmt_jpeg2000.html
new file mode 100644
index 0000000000..e2ee6accd1
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg2000/frmt_jpeg2000.html
@@ -0,0 +1,258 @@
+<html>
+<head>
+<title>JPEG2000 --- Implementation of the JPEG-2000 part 1
+(ISO/IEC 15444-1) standard</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>JPEG2000 --- Implementation of the JPEG-2000 part 1</h1>
+
+This implementation based on JasPer software (see below).
+
+<h2>Creation Options</h2>
+<ul>
+	<li> <b>WORLDFILE=ON</b>: Force the generation of an associated
+	ESRI world file (.wld).<p>
+
+	<li> <b>FORMAT=JP2|JPC</b>: Specify output file format.<p>
+
+	<li> Encoding parameters, directly delivered to the JasPer library
+	described in the JasPer documentation. Quoted from the docs:<p>
+
+	``The following options are supported by the encoder:
+	<table>
+	<tr>
+		<td>imgareatlx=x</td>
+		<td> Set the x-coordinate of the top-left corner
+			of the image area to x.</td>
+	</tr>
+
+	<tr>
+		<td>imgareatly=y</td>
+		<td>Set the y-coordinate of the top-left corner
+			of the image area to y.</td>
+	</tr>
+
+	<tr>
+		<td>tilegrdtlx=x</td>
+		<td>Set the x-coordinate of the top-left corner
+			of the tiling grid to x.</td>
+	</tr>
+
+	<tr>
+		<td>tilegrdtly=y</td>
+		<td>Set the y-coordinate of the top-left corner
+			of the tiling grid to y.</td>
+	</tr>
+
+	<tr>
+		<td>tilewidth=w</td>
+		<td>Set the nominal tile width to w.</td>
+	</tr>
+
+	<tr>
+		<td>tileheight=h</td>
+		<td>Set the nominal tile height to h.</td>
+	</tr>
+
+	<tr>
+		<td>prcwidth=w</td>
+		<td>Set the precinct width to w. The argument w must be an
+			integer power of two. The default value is 32768.</td>
+	</tr>
+
+	<tr>
+		<td>prcheight=h</td>
+		<td>Set the precinct height to h. The argument h must be an
+			integer power of two. The default value is 32768.</td>
+	</tr>
+
+	<tr>
+		<td>cblkwidth=w</td>
+		<td>Set the nominal code block width to w. The argument
+			w must be an integer power of two.
+			The default value is 64.</td>
+	</tr>
+
+	<tr>
+		<td>cblkheight=h</td>
+		<td>Set the nominal code block height to h. The argument
+			h must be an integer power of two.
+			The default value is 64.</td>
+	</tr>
+
+	<tr>
+		<td>mode=m</td>
+		<td>Set the coding mode to m. The argument m must have
+			one of the following values:
+			<center><table border>
+			<tr>
+				<th>Value</th>	<th>Description</th>
+			</tr>
+
+			<tr>
+				<td>int</td>	<td>integer mode</td>
+			</tr>
+			<tr>
+				<td>real</td>	<td>real mode</td>
+			</tr>
+			</table></center>
+		If lossless coding is desired, the integer mode must
+		be used. By default, the integer mode is employed. The
+		choice of mode also determines which multicomponent
+		and wavelet transforms (if any) are employed.</td>
+	</tr>
+
+	<tr>
+		<td>rate=r</td>
+		<td>Specify the target rate. The argument r is a positive
+			real number. Since a rate of one corresponds
+			to no compression, one should never need
+			to explicitly specify a rate greater than one.
+			By default, the target rate is considered
+			to be infinite.</td>
+	</tr>
+
+	<tr>
+		<td>ilyrrates=[, ,. . . ,      ]</td>
+		
+		<td>Specify the rates for any intermediate layers.
+			The argument to this option is a comma separated
+			list of N rates. Each rate is a positive real number.
+			The rates must increase monotonically. The last	rate
+			in the list should be less than or equal to the
+			overall rate (as specified with the rate option).</td>
+	</tr>
+	
+	<tr>
+		<td>prg=p</td>
+		<td>Set the progression order to p. The argument
+			p must have one of the following values:
+			<center><table border>
+			<tr>
+				<th>Value</th>	<th>Description</th>
+			</tr>
+
+			<tr>
+				<td>lrcp</td>
+				<td>layer-resolution-component-position	(LRCP)
+				progressive (i.e., rate scalable)</td>
+			</tr>
+			<tr>
+				<td>rlcp</td>
+				<td>resolution-layer-component-position	(RLCP)
+				progressive (i.e., resolution scalable)</td>
+			</tr>
+			<tr>
+				<td>rpcl</td>
+				<td>resolution-position-component-layer (RPCL)
+					progressive</td>
+			</tr>
+			<tr>
+				<td>pcrl</td>
+				<td>position-component-resolution-layer (PCRL)
+					progressive</td>
+			</tr>
+			<tr>
+				<td>cprl</td>
+				<td>component-position-resolution-layer (CPRL)
+					progressive</td>
+			</tr>
+			</table></center>
+		By default, LRCP progressive ordering is employed.
+		Note that the RPCL and PCRL progressions are not valid
+		for all possible image geometries.
+		(See standard for more details.)</td>
+	</tr>
+	
+	<tr>
+		<td>nomct</td>
+		<td>Disallow the use of any multicomponent transform.</td>
+	</tr>
+
+	<tr>
+		<td>numrlvls=n</td>
+		<td>Set the number of resolution levels to n. The argument
+			n must be an integer that is greater than or equal
+			to one. The default value is 6.</td>
+	</tr>
+
+	<tr>
+		<td>sop</td>
+		<td>Generate SOP marker segments.</td>
+	</tr>
+
+	<tr>
+		<td>eph</td>
+		<td>Generate EPH marker segments.</td>
+	</tr>
+
+	<tr>
+		<td>lazy</td>
+		<td>Enable lazy coding mode (a.k.a. arithmetic coding
+			bypass).</td>
+	</tr>
+
+	<tr>
+		<td>termall</td>
+		<td>Terminate all coding passes.</td>
+	</tr>
+
+	<tr>
+		<td>segsym</td>
+		<td>Use segmentation symbols.</td>
+	</tr>
+
+	<tr>
+		<td>vcausal</td>
+		<td>Use vertically stripe causal contexts.</td>
+	</tr>
+
+	<tr>
+		<td>pterm</td>
+		<td>Use predictable termination.</td>
+	</tr>
+
+	<tr>
+		<td>resetprob</td>
+		<td>Reset the probability models after each coding pass.<td>
+	</tr>
+	
+	<tr>
+		<td>numgbits=n</td>
+		<td>Set the number of guard bits to n.''</td>
+	</tr>
+
+	</table>
+
+</ul>
+
+<h2>See Also:</h2>
+
+<ul>
+<li> Implemented as <tt>gdal/frmts/jpeg2000/jpeg2000dataset.cpp</tt>.<p>
+
+<li> You need modified JasPer library to build this driver with GeoJP2 support
+enabled. Modified version can be downloaded from
+<a href="ftp://ftp.remotesensing.org/gdal/jasper-1.701.0.uuid.tar.gz">
+ftp://ftp.remotesensing.org/gdal/jasper-1.701.0.uuid.tar.gz</a><p>
+
+<li> <a href="http://www.remotesensing.org/jpeg2000/">JPEG2000 for Geospatial
+Applications</a> page, includes GeoJP2(tm) discussion.<p>
+
+<li> <a href="http://www.jpeg.org/JPEG2000.html">
+Official JPEG-2000 page 
+<a><p>
+
+<li> <a href="http://www.ece.uvic.ca/~mdadams/jasper/">
+The JasPer Project Home Page
+</a><p>
+
+<li> Another implementation of the JPEG-2000 standard:
+<a href="http://j2000.org/">J2000</a><p>
+
+</ul>
+
+</body>
+</html>
diff --git a/Utilities/GDAL/frmts/jpeg2000/jpeg2000dataset.cpp b/Utilities/GDAL/frmts/jpeg2000/jpeg2000dataset.cpp
new file mode 100644
index 0000000000..798e662238
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg2000/jpeg2000dataset.cpp
@@ -0,0 +1,1172 @@
+/******************************************************************************
+ * $Id: jpeg2000dataset.cpp,v 1.21 2006/04/07 05:37:51 fwarmerdam Exp $
+ *
+ * Project:  JPEG-2000
+ * Purpose:  Partial implementation of the ISO/IEC 15444-1 standard
+ * Author:   Andrey Kiselev, dron@remotesensing.org
+ *
+ ******************************************************************************
+ * Copyright (c) 2002, Andrey Kiselev <dron@remotesensing.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: jpeg2000dataset.cpp,v $
+ * Revision 1.21  2006/04/07 05:37:51  fwarmerdam
+ * modify to use GDALJP2Metadata class
+ *
+ * Revision 1.20  2005/05/05 15:54:48  fwarmerdam
+ * PAM Enabled
+ *
+ * Revision 1.19  2004/01/20 16:23:44  dron
+ * Don't close streams before deleting the JPEG2000Dataset object.
+ *
+ * Revision 1.18  2003/05/13 14:00:44  warmerda
+ * added mimetype and extension
+ *
+ * Revision 1.17  2003/04/06 10:13:23  dron
+ * Support for pseudocoloured images with LUT.
+ *
+ * Revision 1.16  2003/03/06 18:08:39  dron
+ * Unneeded macro removed.
+ *
+ * Revision 1.15  2003/02/24 11:06:11  dron
+ * Updated to JasPer 1.700.2.
+ *
+ * Revision 1.14  2003/02/14 11:26:04  dron
+ * Colour interpretation improved.
+ *
+ * Revision 1.13  2003/02/13 21:59:24  dron
+ * Fixed problem with cleaning formats before using.
+ *
+ * Revision 1.12  2003/02/03 19:37:45  dron
+ * Fixed problem with component type determining.
+ *
+ * Revision 1.11  2003/02/03 15:13:53  warmerda
+ * avoid warning
+ *
+ * Revision 1.10  2003/01/30 16:25:35  dron
+ * Support for reading and writing GeoJP2 information via hacked JasPer.
+ *
+ * Revision 1.9  2003/01/30 11:59:15  dron
+ * Fixes in component color space handling; several memory leaks removed.
+ *
+ * Revision 1.8  2003/01/28 14:44:11  dron
+ * Temporary hack for reading JP2 boxes (due to lack of appropriate JasPer API).
+ *
+ * Revision 1.7  2002/11/23 18:54:17  warmerda
+ * added CREATIONDATATYPES metadata for drivers
+ *
+ * Revision 1.6  2002/10/10 11:31:12  dron
+ * Fix for buiding GDAL with JasPer software under Windows.
+ *
+ * Revision 1.5  2002/10/08 07:58:05  dron
+ * Added encoding options.
+ *
+ * Revision 1.4  2002/10/04 15:13:25  dron
+ * WORLDFILE support
+ *
+ * Revision 1.3  2002/09/23 15:47:00  dron
+ * CreateCopy() enabled.
+ *
+ * Revision 1.2  2002/09/19 14:50:02  warmerda
+ * added debug statement
+ *
+ * Revision 1.1  2002/09/18 16:49:01  dron
+ * Initial release
+ *
+ */
+
+#include "gdal_pam.h"
+#include "cpl_string.h"
+#include "gdaljp2metadata.h"
+
+#include <jasper/jasper.h>
+
+CPL_CVSID("$Id: jpeg2000dataset.cpp,v 1.21 2006/04/07 05:37:51 fwarmerdam Exp $");
+
+CPL_C_START
+void    GDALRegister_JPEG2000(void);
+#ifdef HAVE_JASPER_UUID
+CPLErr CPL_DLL GTIFMemBufFromWkt( const char *pszWKT, 
+                                  const double *padfGeoTransform,
+                                  int nGCPCount, const GDAL_GCP *pasGCPList,
+                                  int *pnSize, unsigned char **ppabyBuffer );
+#endif
+CPL_C_END
+
+// XXX: Part of code below extracted from the JasPer internal headers and
+// must be in sync with JasPer version (this one works with JasPer 1.700.2)
+#define JP2_FTYP_MAXCOMPATCODES 32
+#define JP2_BOX_IHDR    0x69686472      /* Image Header */
+#define JP2_BOX_BPCC    0x62706363      /* Bits Per Component */
+#define	JP2_BOX_PCLR	0x70636c72	/* Palette */
+#define JP2_BOX_UUID    0x75756964      /* UUID */
+extern "C" {
+typedef struct {
+        uint_fast32_t magic;
+} jp2_jp_t;
+typedef struct {
+        uint_fast32_t majver;
+        uint_fast32_t minver;
+        uint_fast32_t numcompatcodes;
+        uint_fast32_t compatcodes[JP2_FTYP_MAXCOMPATCODES];
+} jp2_ftyp_t;
+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;
+typedef struct {
+        uint_fast16_t numcmpts;
+        uint_fast8_t *bpcs;
+} jp2_bpcc_t;
+typedef struct {
+        uint_fast8_t method;
+        uint_fast8_t pri;
+        uint_fast8_t approx;
+        uint_fast32_t csid;
+        uint_fast8_t *iccp;
+        int iccplen;
+} jp2_colr_t;
+typedef struct {
+        uint_fast16_t numlutents;
+        uint_fast8_t numchans;
+        int_fast32_t *lutdata;
+        uint_fast8_t *bpc;
+} jp2_pclr_t;
+typedef struct {
+        uint_fast16_t channo;
+        uint_fast16_t type;
+        uint_fast16_t assoc;
+} jp2_cdefchan_t;
+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;
+
+#ifdef HAVE_JASPER_UUID
+typedef struct {
+        uint_fast32_t data_len;
+        uint_fast8_t uuid[16];
+        uint_fast8_t *data;
+} jp2_uuid_t;
+#endif
+
+struct jp2_boxops_s;
+typedef struct {
+
+        struct jp2_boxops_s *ops;
+        struct jp2_boxinfo_s *info;
+
+        uint_fast32_t type;
+        uint_fast32_t len;
+#ifdef HAVE_JASPER_UUID
+        uint_fast32_t data_len;
+#endif
+
+        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;
+#ifdef HAVE_JASPER_UUID
+                jp2_uuid_t uuid;
+#endif
+        } 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;
+
+extern jp2_box_t *jp2_box_create(int type);
+extern void jp2_box_destroy(jp2_box_t *box);
+extern jp2_box_t *jp2_box_get(jas_stream_t *in);
+extern int jp2_box_put(jp2_box_t *box, jas_stream_t *out);
+#ifdef HAVE_JASPER_UUID
+int jp2_encode_uuid(jas_image_t *image, jas_stream_t *out,
+                    char *optstr, jp2_box_t *uuid);
+#endif
+}
+// XXX: End of JasPer header.
+
+#ifdef HAVE_JASPER_UUID
+// Magick sequence for GeoJP2 box
+static unsigned char msi_uuid2[16] =
+        {0xb1,0x4b,0xf8,0xbd,0x08,0x3d,0x4b,0x43,
+         0xa5,0xae,0x8c,0xd7,0xd5,0xa6,0xce,0x03}; 
+#endif
+
+/************************************************************************/
+/* ==================================================================== */
+/*                              JPEG2000Dataset                         */
+/* ==================================================================== */
+/************************************************************************/
+
+class JPEG2000Dataset : public GDALPamDataset
+{
+    friend class JPEG2000RasterBand;
+
+    FILE        *fp;
+    jas_stream_t *psStream;
+    jas_image_t *psImage;
+    int         iFormat;
+
+    char        *pszProjection;
+    int         bGeoTransformValid;
+    double      adfGeoTransform[6];
+    int         nGCPCount;
+    GDAL_GCP    *pasGCPList;
+
+  public:
+                JPEG2000Dataset();
+                ~JPEG2000Dataset();
+    
+    static GDALDataset  *Open( GDALOpenInfo * );
+
+    CPLErr              GetGeoTransform( double* );
+    virtual const char  *GetProjectionRef(void);
+    virtual int         GetGCPCount();
+    virtual const char  *GetGCPProjection();
+    virtual const GDAL_GCP *GetGCPs();
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            JPEG2000RasterBand                        */
+/* ==================================================================== */
+/************************************************************************/
+
+class JPEG2000RasterBand : public GDALPamRasterBand
+{
+    friend class JPEG2000Dataset;
+
+    jas_matrix_t        *psMatrix;
+
+  public:
+
+                JPEG2000RasterBand( JPEG2000Dataset *, int, int, int );
+                ~JPEG2000RasterBand();
+                
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual GDALColorInterp GetColorInterpretation();
+};
+
+
+/************************************************************************/
+/*                           JPEG2000RasterBand()                       */
+/************************************************************************/
+
+JPEG2000RasterBand::JPEG2000RasterBand( JPEG2000Dataset *poDS, int nBand,
+                int iDepth, int bSignedness )
+
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+
+    // XXX: JasPer can't handle data with depth > 32 bits
+    // Maximum possible depth for JPEG2000 is 38!
+    switch ( bSignedness )
+    {
+        case 1:                         // Signed component
+        if (iDepth <= 8)
+            this->eDataType = GDT_Byte; // FIXME: should be signed,
+                                        // but we haven't signed byte
+                                        // data type in GDAL
+        else if (iDepth <= 16)
+            this->eDataType = GDT_Int16;
+        else if (iDepth <= 32)
+            this->eDataType = GDT_Int32;
+        break;
+        case 0:                         // Unsigned component
+        default:
+        if (iDepth <= 8)
+            this->eDataType = GDT_Byte;
+        else if (iDepth <= 16)
+            this->eDataType = GDT_UInt16;
+        else if (iDepth <= 32)
+            this->eDataType = GDT_UInt32;
+        break;
+    }
+
+    nBlockXSize = poDS->GetRasterXSize();
+    nBlockYSize = poDS->GetRasterYSize();
+    psMatrix = jas_matrix_create(nBlockYSize, nBlockXSize);
+    
+}
+
+/************************************************************************/
+/*                         ~JPEG2000RasterBand()                        */
+/************************************************************************/
+
+JPEG2000RasterBand::~JPEG2000RasterBand()
+{
+    jas_matrix_destroy( psMatrix );
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr JPEG2000RasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                      void * pImage )
+{
+    int             i, j;
+    JPEG2000Dataset *poGDS = (JPEG2000Dataset *) poDS;
+
+    // Decode image from the stream, if not yet
+    if ( !poGDS->psImage )
+    {
+        poGDS->psImage = jas_image_decode(poGDS->psStream, poGDS->iFormat, 0);
+        if ( !poGDS->psImage )
+        {
+            CPLDebug( "JPEG2000", "Unable to decode image. Format: %s, %d",
+                      jas_image_fmttostr( poGDS->iFormat ), poGDS->iFormat );
+            return CE_Failure;
+        }
+    }
+    
+    jas_image_readcmpt( poGDS->psImage, nBand - 1, nBlockXOff, nBlockYOff,
+        nBlockXSize, nBlockYSize, psMatrix );
+
+    for( i = 0; i < jas_matrix_numrows(psMatrix); i++ )
+        for( j = 0; j < jas_matrix_numcols(psMatrix); j++ )
+            // XXX: We need casting because matrix element always
+            // has 32 bit depth in JasPer
+            // FIXME: what about float values?
+            switch( eDataType )
+            {
+                case GDT_Int16:
+                {
+                    GInt16* ptr = (GInt16*)pImage;
+                    *ptr = (GInt16)jas_matrix_get(psMatrix, i, j);
+                    ptr++;
+                    pImage = ptr;
+                }
+                break;
+                case GDT_Int32:
+                {
+                    GInt32* ptr = (GInt32*)pImage;
+                    *ptr = (GInt32)jas_matrix_get(psMatrix, i, j);
+                    ptr++;
+                    pImage = ptr;
+                }
+                break;
+                case GDT_UInt16:
+                {
+                    GUInt16* ptr = (GUInt16*)pImage;
+                    *ptr = (GUInt16)jas_matrix_get(psMatrix, i, j);
+                    ptr++;
+                    pImage = ptr;
+                }
+                break;
+                case GDT_UInt32:
+                {
+                    GUInt32* ptr = (GUInt32*)pImage;
+                    *ptr = (GUInt32)jas_matrix_get(psMatrix, i, j);
+                    ptr++;
+                    pImage = ptr;
+                }
+                break;
+                case GDT_Byte:
+                default:
+                {
+                    GByte* ptr = (GByte*)pImage;
+                    *ptr = (GByte)jas_matrix_get(psMatrix, i, j);
+                    ptr++;
+                    pImage = ptr;
+                }
+                break;
+            }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp JPEG2000RasterBand::GetColorInterpretation()
+{
+    JPEG2000Dataset *poGDS = (JPEG2000Dataset *) poDS;
+
+    // Decode image from the stream, if not yet
+    if ( !poGDS->psImage )
+    {
+        if ( !( poGDS->psImage =
+                jas_image_decode(poGDS->psStream, poGDS->iFormat, 0) ) )
+        {
+            CPLDebug( "JPEG2000", "Unable to decode image. Format: %s, %d",
+                      jas_image_fmttostr( poGDS->iFormat ), poGDS->iFormat );
+            return GCI_Undefined;
+        }
+    }
+    
+    if ( jas_clrspc_fam( jas_image_clrspc( poGDS->psImage ) ) ==
+         JAS_CLRSPC_FAM_GRAY )
+        return GCI_GrayIndex;
+    else if ( jas_clrspc_fam( jas_image_clrspc( poGDS->psImage ) ) == 
+              JAS_CLRSPC_FAM_RGB )
+    {
+        switch ( jas_image_cmpttype( poGDS->psImage, nBand - 1 ) )
+        {
+            case JAS_IMAGE_CT_RGB_R:
+                return GCI_RedBand;
+            case JAS_IMAGE_CT_RGB_G:
+                return GCI_GreenBand;
+            case JAS_IMAGE_CT_RGB_B:
+                return GCI_BlueBand;
+            case JAS_IMAGE_CT_OPACITY:
+                return GCI_AlphaBand;
+            default:
+                return GCI_Undefined;
+        }
+    }
+    else
+        return GCI_Undefined;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                              JPEG2000Dataset                         */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                           JPEG2000Dataset()                          */
+/************************************************************************/
+
+JPEG2000Dataset::JPEG2000Dataset()
+
+{
+    fp = NULL;
+    psStream = NULL;
+    psImage = NULL;
+    nBands = 0;
+    pszProjection = CPLStrdup("");
+    nGCPCount = 0;
+    pasGCPList = NULL;
+    bGeoTransformValid = FALSE;
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+}
+
+/************************************************************************/
+/*                            ~JPEG2000Dataset()                         */
+/************************************************************************/
+
+JPEG2000Dataset::~JPEG2000Dataset()
+
+{
+    FlushCache();
+
+    if ( psStream )
+        jas_stream_close( psStream );
+    if ( psImage )
+        jas_image_destroy( psImage );
+    jas_image_clearfmts();
+    if ( pszProjection )
+        CPLFree( pszProjection );
+    if( nGCPCount > 0 )
+    {
+        for( int i = 0; i < nGCPCount; i++ )
+            if ( pasGCPList[i].pszId )
+                CPLFree( pasGCPList[i].pszId );
+
+        CPLFree( pasGCPList );
+    }
+    if( fp != NULL )
+        VSIFClose( fp );
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *JPEG2000Dataset::GetProjectionRef()
+
+{
+    return( pszProjection );
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr JPEG2000Dataset::GetGeoTransform( double * padfTransform )
+{
+    if( bGeoTransformValid )
+    {
+        memcpy( padfTransform, adfGeoTransform, sizeof(adfGeoTransform[0]) * 6 );
+        return CE_None;
+    }
+    else
+        return CE_Failure;
+}
+
+/************************************************************************/
+/*                            GetGCPCount()                             */
+/************************************************************************/
+
+int JPEG2000Dataset::GetGCPCount()
+
+{
+    return nGCPCount;
+}
+
+/************************************************************************/
+/*                          GetGCPProjection()                          */
+/************************************************************************/
+
+const char *JPEG2000Dataset::GetGCPProjection()
+
+{
+    if( nGCPCount > 0 )
+        return pszProjection;
+    else
+        return "";
+}
+
+/************************************************************************/
+/*                               GetGCP()                               */
+/************************************************************************/
+
+const GDAL_GCP *JPEG2000Dataset::GetGCPs()
+
+{
+    return pasGCPList;
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *JPEG2000Dataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    int         iFormat;
+    char        *pszFormatName = NULL;
+    jas_stream_t *sS;
+
+    if( poOpenInfo->fp == NULL )
+        return NULL;
+   
+    jas_init();
+    if( !(sS = jas_stream_fopen( poOpenInfo->pszFilename, "rb" )) )
+    {
+        jas_image_clearfmts();
+        return NULL;
+    }
+    iFormat = jas_image_getfmt( sS );
+    if ( !(pszFormatName = jas_image_fmttostr( iFormat )) )
+    {
+        jas_stream_close( sS );
+        jas_image_clearfmts();
+        return NULL;
+    }
+    if ( strlen( pszFormatName ) < 3 ||
+        (!EQUALN( pszFormatName, "jp2", 3 ) &&
+         !EQUALN( pszFormatName, "jpc", 3 ) &&
+         !EQUALN( pszFormatName, "pgx", 3 )) )
+    {
+        CPLDebug( "JPEG2000", "JasPer reports file is format type `%s'.", 
+                  pszFormatName );
+        jas_stream_close( sS );
+        jas_image_clearfmts();
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    JPEG2000Dataset     *poDS;
+    int                 *paiDepth = NULL, *pabSignedness = NULL;
+    int                 iBand;
+
+    poDS = new JPEG2000Dataset();
+
+    poDS->fp = poOpenInfo->fp;
+    poOpenInfo->fp = NULL;
+    poDS->psStream = sS;
+    poDS->iFormat = iFormat;
+
+    if ( EQUALN( pszFormatName, "jp2", 3 ) )
+    {
+// XXX: Hack to read JP2 boxes from input file. JasPer hasn't public API
+// call for such things, so we use internal JasPer functions.
+        jp2_box_t *box;
+        box = 0;
+        while ( ( box = jp2_box_get(poDS->psStream) ) )
+        {
+            switch (box->type)
+            {
+                case JP2_BOX_IHDR:
+                poDS->nBands = box->data.ihdr.numcmpts;
+                poDS->nRasterXSize = box->data.ihdr.width;
+                poDS->nRasterYSize = box->data.ihdr.height;
+                CPLDebug( "JPEG2000",
+                          "IHDR box found. Dump: "
+                          "width=%d, height=%d, numcmpts=%d, bpp=%d",
+                          box->data.ihdr.width, box->data.ihdr.height,
+                          box->data.ihdr.numcmpts, (box->data.ihdr.bpc & 0x7F) + 1 );
+                if ( box->data.ihdr.bpc )
+                {
+                    paiDepth = (int *)CPLMalloc(poDS->nBands * sizeof(int));
+                    pabSignedness = (int *)CPLMalloc(poDS->nBands * sizeof(int));
+                    for ( iBand = 0; iBand < poDS->nBands; iBand++ )
+                    {
+                        paiDepth[iBand] = (box->data.ihdr.bpc & 0x7F) + 1;
+                        pabSignedness[iBand] = box->data.ihdr.bpc >> 7;
+                        CPLDebug( "JPEG2000",
+                                  "Component %d: bpp=%d, signedness=%d",
+                                  iBand, paiDepth[iBand], pabSignedness[iBand] );
+                    }
+                }
+                break;
+                case JP2_BOX_BPCC:
+                CPLDebug( "JPEG2000", "BPCC box found. Dump:" );
+                if ( !paiDepth && !pabSignedness )
+                {
+                    paiDepth = (int *)
+                        CPLMalloc( box->data.bpcc.numcmpts * sizeof(int) );
+                    pabSignedness = (int *)
+                        CPLMalloc( box->data.bpcc.numcmpts * sizeof(int) );
+                    for( iBand = 0; iBand < (int)box->data.bpcc.numcmpts; iBand++ )
+                    {
+                        paiDepth[iBand] = box->data.bpcc.bpcs[iBand] && 0x7F;
+                        pabSignedness[iBand] = box->data.bpcc.bpcs[iBand] >> 7;
+                        CPLDebug( "JPEG2000",
+                                  "Component %d: bpp=%d, signedness=%d",
+                                  iBand, paiDepth[iBand], pabSignedness[iBand] );
+                    }
+                }
+                break;
+                case JP2_BOX_PCLR:
+                CPLDebug( "JPEG2000",
+                          "PCLR box found. Dump: number of LUT entries=%d, "
+                          "number of resulting channels=%d",
+                          box->data.pclr.numlutents, box->data.pclr.numchans );
+                poDS->nBands = box->data.pclr.numchans;
+                if ( paiDepth )
+                    CPLFree( paiDepth );
+                if ( pabSignedness )
+                    CPLFree( pabSignedness );
+                paiDepth = (int *)
+                        CPLMalloc( box->data.pclr.numchans * sizeof(int) );
+                pabSignedness = (int *)
+                        CPLMalloc( box->data.pclr.numchans * sizeof(int) );
+                for( iBand = 0; iBand < (int)box->data.pclr.numchans; iBand++ )
+                {
+                    paiDepth[iBand] = box->data.pclr.bpc[iBand] && 0x7F;
+                    pabSignedness[iBand] = box->data.pclr.bpc[iBand] >> 7;
+                    CPLDebug( "JPEG2000",
+                              "Component %d: bpp=%d, signedness=%d",
+                              iBand, paiDepth[iBand], pabSignedness[iBand] );
+                }
+                break;
+            }
+            jp2_box_destroy( box );
+            box = 0;
+        }
+	if( !paiDepth || !pabSignedness )
+	{
+	    delete poDS;
+	    CPLDebug( "JPEG2000", "Unable to read JP2 header boxes.\n" );
+	    return NULL;
+	}
+        if ( jas_stream_rewind( poDS->psStream ) < 0 )
+        {
+            delete poDS;
+            CPLDebug( "JPEG2000", "Unable to rewind input stream.\n" );
+            return NULL;
+        }
+    }
+    else
+    {
+        poDS->psImage = jas_image_decode(poDS->psStream, poDS->iFormat, 0);
+        if ( !poDS->psImage )
+        {
+            delete poDS;
+            CPLDebug( "JPEG2000", "Unable to decode image %s. Format: %s, %d",
+                      poOpenInfo->pszFilename,
+                      jas_image_fmttostr( poDS->iFormat ), poDS->iFormat );
+            return NULL;
+        }
+
+        poDS->nBands = jas_image_numcmpts( poDS->psImage );
+        poDS->nRasterXSize = jas_image_cmptwidth( poDS->psImage, 0 );
+        poDS->nRasterYSize = jas_image_cmptheight( poDS->psImage, 0 );
+        paiDepth = (int *)CPLMalloc( poDS->nBands * sizeof(int) );
+        pabSignedness = (int *)CPLMalloc( poDS->nBands * sizeof(int) );
+        for ( iBand = 0; iBand < poDS->nBands; iBand++ )
+        {
+            paiDepth[iBand] = jas_image_cmptprec( poDS->psImage, iBand );
+            pabSignedness[iBand] = jas_image_cmptsgnd( poDS->psImage, iBand );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    for( iBand = 1; iBand <= poDS->nBands; iBand++ )
+    {
+        poDS->SetBand( iBand, new JPEG2000RasterBand( poDS, iBand,
+            paiDepth[iBand - 1], pabSignedness[iBand - 1] ) );
+        
+    }
+    
+    if ( paiDepth )
+        CPLFree( paiDepth );
+    if ( pabSignedness )
+        CPLFree( pabSignedness );
+
+/* -------------------------------------------------------------------- */
+/*      Check for georeferencing information.                           */
+/* -------------------------------------------------------------------- */
+    GDALJP2Metadata oJP2Geo;
+    
+    if( oJP2Geo.ReadAndParse( poOpenInfo->pszFilename ) )
+    {
+        poDS->pszProjection = CPLStrdup(oJP2Geo.pszProjection);
+        poDS->bGeoTransformValid = oJP2Geo.bHaveGeoTransform;
+        memcpy( poDS->adfGeoTransform, oJP2Geo.adfGeoTransform, 
+                sizeof(double) * 6 );
+        poDS->nGCPCount = oJP2Geo.nGCPCount;
+        poDS->pasGCPList = oJP2Geo.pasGCPList;
+        oJP2Geo.pasGCPList = NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                      JPEG2000CreateCopy()                            */
+/************************************************************************/
+
+static GDALDataset *
+JPEG2000CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, 
+                    int bStrict, char ** papszOptions, 
+                    GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    int  nBands = poSrcDS->GetRasterCount();
+    int  nXSize = poSrcDS->GetRasterXSize();
+    int  nYSize = poSrcDS->GetRasterYSize();
+
+    if( !pfnProgress( 0.0, NULL, pProgressData ) )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Create the dataset.                                             */
+/* -------------------------------------------------------------------- */
+    int                 iBand;
+    jas_stream_t        *psStream;
+    jas_image_t         *psImage;
+
+    jas_init();
+    if( !(psStream = jas_stream_fopen( pszFilename, "w+b" )) )
+    {
+        CPLError( CE_Failure, CPLE_FileIO, "Unable to create file %s.\n", 
+                  pszFilename );
+        return NULL;
+    }
+    
+    if ( !(psImage = jas_image_create0()) )
+    {
+        CPLError( CE_Failure, CPLE_OutOfMemory, "Unable to create image %s.\n", 
+                  pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Loop over image, copying image data.                            */
+/* -------------------------------------------------------------------- */
+    GDALRasterBand      *poBand;
+    GUInt32             *paiScanline;
+    int                 iLine, iPixel;
+    CPLErr              eErr = CE_None;
+    jas_matrix_t        *psMatrix;
+    jas_image_cmptparm_t *sComps; // Array of pointers to image components
+
+    sComps = (jas_image_cmptparm_t*)
+        CPLMalloc( nBands * sizeof(jas_image_cmptparm_t) );
+  
+    if ( !(psMatrix = jas_matrix_create( 1, nXSize )) )
+    {
+        CPLError( CE_Failure, CPLE_OutOfMemory, 
+                  "Unable to create matrix with size %dx%d.\n", 1, nYSize );
+        CPLFree( sComps );
+        jas_image_destroy( psImage );
+        return NULL;
+    }
+    paiScanline = (GUInt32 *) CPLMalloc( nXSize *
+                            GDALGetDataTypeSize(GDT_UInt32) / 8 );
+    
+    for ( iBand = 0; iBand < nBands; iBand++ )
+    {
+        poBand = poSrcDS->GetRasterBand( iBand + 1);
+        
+        sComps[iBand].tlx = sComps[iBand].tly = 0;
+        sComps[iBand].hstep = sComps[iBand].vstep = 1;
+        sComps[iBand].width = nXSize;
+        sComps[iBand].height = nYSize;
+        sComps[iBand].prec = GDALGetDataTypeSize( poBand->GetRasterDataType() );
+        switch ( poBand->GetRasterDataType() )
+        {
+            case GDT_Int16:
+            case GDT_Int32:
+            case GDT_Float32:
+            case GDT_Float64:
+            sComps[iBand].sgnd = 1;
+            break;
+            case GDT_Byte:
+            case GDT_UInt16:
+            case GDT_UInt32:
+            default:
+            sComps[iBand].sgnd = 0;
+            break;
+        }
+        jas_image_addcmpt(psImage, iBand, sComps);
+
+        for( iLine = 0; eErr == CE_None && iLine < nYSize; iLine++ )
+        {
+            eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, 
+                              paiScanline, nXSize, 1, GDT_UInt32,
+                              sizeof(GUInt32), sizeof(GUInt32) * nXSize );
+            for ( iPixel = 0; iPixel < nXSize; iPixel++ )
+                jas_matrix_setv( psMatrix, iPixel, paiScanline[iPixel] );
+            
+            if( (jas_image_writecmpt(psImage, iBand, 0, iLine,
+                              nXSize, 1, psMatrix)) < 0 )
+            {
+                CPLError( CE_Failure, CPLE_AppDefined, 
+                    "Unable to write scanline %d of the component %d.\n", 
+                    iLine, iBand );
+                jas_matrix_destroy( psMatrix );
+                CPLFree( paiScanline );
+                CPLFree( sComps );
+                jas_image_destroy( psImage );
+                return NULL;
+            }
+            
+            if( eErr == CE_None &&
+            !pfnProgress( ((iLine + 1) + iBand * nYSize) /
+                          ((double) nYSize * nBands),
+                         NULL, pProgressData) )
+            {
+                eErr = CE_Failure;
+                CPLError( CE_Failure, CPLE_UserInterrupt, 
+                      "User terminated CreateCopy()" );
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*       Read compression parameters and encode the image.              */
+/* -------------------------------------------------------------------- */
+    int             i, j;
+    const int       OPTSMAX = 4096;
+    const char      *pszFormatName;
+    char            pszOptionBuf[OPTSMAX + 1];
+
+    const char  *apszComprOptions[]=
+    {
+        "imgareatlx",
+        "imgareatly",
+        "tilegrdtlx",
+        "tilegrdtly",
+        "tilewidth",
+        "tileheight",
+        "prcwidth",
+        "prcheight",
+        "cblkwidth",
+        "cblkheight",
+        "mode",
+        "rate",
+        "ilyrrates",
+        "prg",
+        "numrlvls",
+        "sop",
+        "eph",
+        "lazy",
+        "termall",
+        "segsym",
+        "vcausal",
+        "pterm",
+        "resetprob",
+        "numgbits",
+        NULL
+    };
+    
+    pszFormatName = CSLFetchNameValue( papszOptions, "FORMAT" );
+    if ( !pszFormatName ||
+         !EQUALN( pszFormatName, "jp2", 3 ) ||
+         !EQUALN( pszFormatName, "jpc", 3 ) )
+        pszFormatName = "jp2";
+    
+    pszOptionBuf[0] = '\0';
+    if ( papszOptions )
+    {
+        CPLDebug( "JPEG2000", "User supplied parameters:" );
+        for ( i = 0; papszOptions[i] != NULL; i++ )
+        {
+            CPLDebug( "JPEG2000", "%s\n", papszOptions[i] );
+            for ( j = 0; apszComprOptions[j] != NULL; j++ )
+                if( EQUALN( apszComprOptions[j], papszOptions[i],
+                            strlen(apszComprOptions[j]) ) )
+                {
+                    int m, n;
+
+                    n = strlen( pszOptionBuf );
+                    m = n + strlen( papszOptions[i] ) + 1;
+                    if ( m > OPTSMAX )
+                        break;
+                    if ( n > 0 )
+                    {
+                        strcat( pszOptionBuf, "\n" );
+                    }
+                    strcat( pszOptionBuf, papszOptions[i] );
+                }
+        }
+    }
+    CPLDebug( "JPEG2000", "Parameters, delivered to the JasPer library:" );
+    CPLDebug( "JPEG2000", "%s", pszOptionBuf );
+
+    if ( nBands == 1 )                      // Grayscale
+    {
+        jas_image_setclrspc( psImage, JAS_CLRSPC_SGRAY );
+        jas_image_setcmpttype( psImage, 0, JAS_IMAGE_CT_GRAY_Y );
+    }
+    else if ( nBands == 3 || nBands == 4 )  // Assume as RGB(A)
+    {
+        jas_image_setclrspc( psImage, JAS_CLRSPC_SRGB );
+        for ( iBand = 0; iBand < nBands; iBand++ )
+        {
+            poBand = poSrcDS->GetRasterBand( iBand + 1);
+            switch ( poBand->GetColorInterpretation() )
+            {
+                case GCI_RedBand:
+                jas_image_setcmpttype( psImage, iBand, JAS_IMAGE_CT_RGB_R );
+                break;
+                case GCI_GreenBand:
+                jas_image_setcmpttype( psImage, iBand, JAS_IMAGE_CT_RGB_G );
+                break;
+                case GCI_BlueBand:
+                jas_image_setcmpttype( psImage, iBand, JAS_IMAGE_CT_RGB_B );
+                break;
+                case GCI_AlphaBand:
+                jas_image_setcmpttype( psImage, iBand, JAS_IMAGE_CT_OPACITY );
+                break;
+                default:
+                jas_image_setcmpttype( psImage, iBand, JAS_IMAGE_CT_UNKNOWN );
+                break;
+            }
+        }
+    }
+    else                                    // Unknown
+    {
+        jas_image_setclrspc( psImage, JAS_CLRSPC_UNKNOWN );
+        for ( iBand = 0; iBand < nBands; iBand++ )
+            jas_image_setcmpttype( psImage, iBand, JAS_IMAGE_CT_UNKNOWN );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Set the GeoTIFF box if georeferencing is available, and this    */
+/*      is a JP2 file.                                                  */
+/* -------------------------------------------------------------------- */
+    if ( EQUALN( pszFormatName, "jp2", 3 ) )
+    {
+#ifdef HAVE_JASPER_UUID
+        double  adfGeoTransform[6];
+        if( ((poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None
+                 && (adfGeoTransform[0] != 0.0 
+                     || adfGeoTransform[1] != 1.0 
+                     || adfGeoTransform[2] != 0.0 
+                     || adfGeoTransform[3] != 0.0 
+                     || adfGeoTransform[4] != 0.0 
+                     || ABS(adfGeoTransform[5]) != 1.0))
+                || poSrcDS->GetGCPCount() > 0) )
+        {
+            // Prepare the memory buffer containing the degenerate GeoTIFF file.
+            const char  *pszWKT;
+            int         nGTBufSize = 0;
+            unsigned char *pabyGTBuf = NULL;
+            jp2_box_t   *box = jp2_box_create( JP2_BOX_UUID );
+
+            if( GDALGetGCPCount( poSrcDS ) > 0 )
+                pszWKT = poSrcDS->GetGCPProjection();
+            else
+                pszWKT = poSrcDS->GetProjectionRef();
+
+            GTIFMemBufFromWkt( pszWKT, adfGeoTransform,
+                               poSrcDS->GetGCPCount(), poSrcDS->GetGCPs(),
+                               &nGTBufSize, &pabyGTBuf );
+ 
+            memcpy( box->data.uuid.uuid, msi_uuid2, sizeof(msi_uuid2) );
+            box->data.uuid.data_len = nGTBufSize;
+            box->data.uuid.data = (uint_fast8_t *)jas_malloc( nGTBufSize );
+            memcpy( box->data.uuid.data, pabyGTBuf, nGTBufSize );
+            CPLFree( pabyGTBuf );
+            if ( jp2_encode_uuid( psImage, psStream, pszOptionBuf, box) < 0 )
+            {
+                CPLError( CE_Failure, CPLE_FileIO,
+                          "Unable to encode image %s.", pszFilename );
+                jp2_box_destroy( box );
+                jas_matrix_destroy( psMatrix );
+                CPLFree( paiScanline );
+                CPLFree( sComps );
+                jas_image_destroy( psImage );
+                jas_image_clearfmts();
+                return NULL;
+            }
+        jp2_box_destroy( box );
+        }
+        else
+        {
+#endif
+            if ( jp2_encode( psImage, psStream, pszOptionBuf) < 0 )
+            {
+                CPLError( CE_Failure, CPLE_FileIO,
+                          "Unable to encode image %s.", pszFilename );
+                jas_matrix_destroy( psMatrix );
+                CPLFree( paiScanline );
+                CPLFree( sComps );
+                jas_image_destroy( psImage );
+                jas_image_clearfmts();
+                return NULL;
+            }
+#ifdef HAVE_JASPER_UUID
+        }
+#endif
+    }
+    else    // Write JPC code stream
+    {
+        if ( jpc_encode(psImage, psStream, pszOptionBuf) < 0 )
+        {
+            CPLError( CE_Failure, CPLE_FileIO,
+                      "Unable to encode image %s.\n", pszFilename );
+            jas_matrix_destroy( psMatrix );
+            CPLFree( paiScanline );
+            CPLFree( sComps );
+            jas_image_destroy( psImage );
+            jas_image_clearfmts();
+            return NULL;
+        }
+    }
+
+    jas_stream_flush( psStream );
+    
+    jas_matrix_destroy( psMatrix );
+    CPLFree( paiScanline );
+    CPLFree( sComps );
+    jas_image_destroy( psImage );
+    jas_image_clearfmts();
+    if ( jas_stream_close( psStream ) )
+    {
+        CPLError( CE_Failure, CPLE_FileIO, "Unable to close file %s.\n",
+                  pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Do we need a world file?                                        */
+/* -------------------------------------------------------------------- */
+    if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) )
+    {
+        double      adfGeoTransform[6];
+        
+        poSrcDS->GetGeoTransform( adfGeoTransform );
+        GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Re-open dataset, and copy any auxilary pam information.         */
+/* -------------------------------------------------------------------- */
+    GDALPamDataset *poDS = (GDALPamDataset *) 
+        GDALOpen( pszFilename, GA_ReadOnly );
+
+    if( poDS )
+        poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                        GDALRegister_JPEG2000()                       */
+/************************************************************************/
+
+void GDALRegister_JPEG2000()
+
+{
+    GDALDriver  *poDriver;
+
+    if( GDALGetDriverByName( "JPEG2000" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "JPEG2000" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "JPEG-2000 part 1 (ISO/IEC 15444-1)" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_jpeg2000.html" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte Int16 UInt16 Int32 UInt32" );
+        poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/jp2" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "jp2" );
+
+        poDriver->pfnOpen = JPEG2000Dataset::Open;
+        poDriver->pfnCreateCopy = JPEG2000CreateCopy;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/jpeg2000/makefile.vc b/Utilities/GDAL/frmts/jpeg2000/makefile.vc
new file mode 100644
index 0000000000..9505567dbb
--- /dev/null
+++ b/Utilities/GDAL/frmts/jpeg2000/makefile.vc
@@ -0,0 +1,14 @@
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+OBJ	=	jpeg2000dataset.obj
+EXTRAFLAGS = 	$(JASPER_INCLUDE) /DWIN32 -DFRMT_jpeg2000
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/l1b/GNUmakefile b/Utilities/GDAL/frmts/l1b/GNUmakefile
new file mode 100644
index 0000000000..b606f73d20
--- /dev/null
+++ b/Utilities/GDAL/frmts/l1b/GNUmakefile
@@ -0,0 +1,13 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	l1bdataset.o
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/l1b/frmt_l1b.html b/Utilities/GDAL/frmts/l1b/frmt_l1b.html
new file mode 100644
index 0000000000..2418b2cf6f
--- /dev/null
+++ b/Utilities/GDAL/frmts/l1b/frmt_l1b.html
@@ -0,0 +1,107 @@
+<html>
+<head>
+<title>L1B -- NOAA Polar Orbiter Level 1b Data Set (AVHRR)</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>L1B -- NOAA Polar Orbiter Level 1b Data Set (AVHRR)</h1>
+
+GDAL supports NOAA Polar Orbiter Level 1b Data Set format for reading. Now it can
+read NOAA-9(F) --- NOAA-17(M) datasets. NOTE: only AVHRR instrument supported
+now, if you want read data from other instruments, write to me (Andrey Kiselev, 
+<a href=mailto:dron@at1895.spb.edu>dron@at1895.spb.edu</a>). AVHRR
+LAC/HRPT (1 km resolution) and GAC (4 km resolution) should be processed
+correctly.
+
+<h2>Georeference</h2>
+
+Note, that GDAL simple affine georeference model completely unsuitable for
+the NOAA data. So you should not rely on it. It is recommended to use the
+thin plate spline warper (tps). Automatic image rectification can be done with
+ground control points (GCPs) from the input file.
+
+NOAA stores 51 GCPs per scanline both in the LAC and GAC datasets. In fact
+you may get less than 51 GCPs, especially at end of scanlines. Another approach
+to rectification is manual selection of the GCPs using external source of
+georeference information.
+<br>
+Precision of the GCPs determination depends from the satellite type. In the
+NOAA-9 -- NOAA-14 datasets geographic coordinates of the GCPs stored in
+integer values as a 128th of a degree. So we can't determine positions more
+precise than 1/128=0.0078125 of degree (~28"). In NOAA-15 -- NOAA-17 datasets
+we have much more precise positions, they are stored as 10000th of degree.
+<br>
+Image will be always returned with most northern scanline located at the top of
+image. If you want determine actual direction of the satellite moving you
+should look at <b>LOCATION</b> metadata record.
+
+<h2>Data</h2>
+
+In case of NOAA-10 in channel 5 you will get repeated channel 4 data.
+<br>
+AVHRR/3 instrument (NOAA-15 -- NOAA-17) is a six channel radiometer, but only
+five channels are transmitted to the ground at any given time. Channels 3A and
+3B cannot operate simultaneously. Look at channel description field reported
+by <tt>gdalinfo</tt> to determine what kind of channel contained in processed file.
+
+<h2>Metadata</h2>
+
+Several parameters, obtained from the dataset stored as metadata records. <p>
+
+Metadata records:<p>
+
+<ul>
+
+<li> <b>SATELLITE</b>: Satellite name<p>
+
+<li> <b>DATA_TYPE</b>: Type of the data, stored in the Level 1b dataset (AVHRR
+HRPT/LAC/GAC). <p>
+
+<li> <b>REVOLUTION</b>: Orbit number. Note that it can be 1 to 2 off the
+correct orbit number (according to documentation).<p>
+
+<li> <b>SOURCE</b>: Receiving station name.<p>
+
+<li> <b>PROCESSING_CENTER</b>: Name of data processing center.<p>
+
+<li> <b>START</b>: Time of first scanline acquisition (year, day of year,
+millisecond of day).<p>
+
+<li> <b>STOP</b>: Time of last scanline acquisition (year, day of year,
+millisecond of day).<p>
+
+<li> <b>LOCATION</b>: AVHRR Earth location indication. Will be <b>Ascending</b>
+when satellite moves from low latitudes to high latitudes and <b>Descending</b>
+in other case.<p>
+
+</ul>
+
+<h2>See Also:</h2>
+
+<ul>
+<li> Implemented as <tt>gdal/frmts/l1b/l1bdataset.cpp</tt>.<p>
+
+<li>  NOAA Polar Orbiter Level 1b Data Set documented in the ``POD User's
+Guide'' (TIROS-N -- NOAA-14 satellites) and in the ``NOAA KLM User's Guide''
+(NOAA-15 -- NOAA-16 satellites). You can find this manuals at
+<a href="http://www2.ncdc.noaa.gov/docs/intro.htm">
+NOAA Technical Documentation Introduction Page
+</a><p>
+
+<li>  Excellent and complete review contained in the printed book ``The
+Advanced Very High Resolution Radiometer (AVHRR)'' by Arthur P. Cracknell,
+Taylor and Francis Ltd., 1997, ISBN 0-7484-0209-8.
+</a><p>
+
+<li> NOAA data can be downloaded from the 
+<a href="http://www.class.noaa.gov/">Comprehensive Large Array-data Stewardship System (CLASS)</a>
+(former SAA). Actually it is only source of Level 1b datasets for me, so my implementation
+tested with that files only. <p>
+
+<li> <a href="http://www.oso.noaa.gov/poesstatus/">NOAA spacecrafts status page</a>
+
+</ul>
+
+</body>
+</html>
diff --git a/Utilities/GDAL/frmts/l1b/l1bdataset.cpp b/Utilities/GDAL/frmts/l1b/l1bdataset.cpp
new file mode 100644
index 0000000000..a2827c9d7c
--- /dev/null
+++ b/Utilities/GDAL/frmts/l1b/l1bdataset.cpp
@@ -0,0 +1,1401 @@
+/******************************************************************************
+ * $Id: l1bdataset.cpp,v 1.27 2006/02/16 05:14:46 fwarmerdam Exp $
+ *
+ * Project:  NOAA Polar Orbiter Level 1b Dataset Reader (AVHRR)
+ * Purpose:  Can read NOAA-9(F)-NOAA-17(M) AVHRR datasets
+ * Author:   Andrey Kiselev, dron@remotesensing.org
+ *
+ * Some format info at: http://www.sat.dundee.ac.uk/noaa1b.html
+ *
+ ******************************************************************************
+ * Copyright (c) 2002, Andrey Kiselev <dron@remotesensing.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: l1bdataset.cpp,v $
+ * Revision 1.27  2006/02/16 05:14:46  fwarmerdam
+ * Fixed byte swapping of NOAA9 GCPS.
+ *
+ * Revision 1.26  2005/07/31 01:00:08  fwarmerdam
+ * Moved CPL_LSB variables into #ifdef.
+ *
+ * Revision 1.25  2005/05/05 15:54:48  fwarmerdam
+ * PAM Enabled
+ *
+ * Revision 1.24  2005/02/07 20:19:11  fwarmerdam
+ * Parametrized sampling rates.
+ *
+ * Revision 1.23  2005/02/07 19:22:35  fwarmerdam
+ * Patched problem in parsing negative lat and long values in GCP handling.
+ *
+ * Revision 1.22  2005/02/07 17:22:14  fwarmerdam
+ * Hacked to emit only 11x20 GCPs.  Eventually we should make this
+ * controllable.
+ *
+ * Revision 1.21  2005/01/28 20:29:37  fwarmerdam
+ * added format info url.
+ *
+ * Revision 1.20  2003/07/08 21:23:23  warmerda
+ * avoid warnings
+ *
+ * Revision 1.19  2003/06/19 12:28:45  dron
+ * More fixes in GCP extraction.
+ *
+ * Revision 1.18  2003/06/19 09:36:13  dron
+ * Fixed reading number of meaningful GCPs for NOAA-9/NOAA-14 data format.
+ *
+ * Revision 1.17  2003/04/17 12:54:21  dron
+ * Fixed small memory leak.
+ *
+ * Revision 1.16  2003/04/05 15:16:29  dron
+ * Simplified process of georeference determination.
+ *
+ * Revision 1.15  2002/11/27 13:45:45  dron
+ * Fixed problem with L1B signature handling.
+ *
+ * Revision 1.14  2002/10/04 16:23:56  dron
+ * Memory leak fixed.
+ *
+ * Revision 1.13  2002/09/04 06:50:37  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.12  2002/08/31 10:01:36  dron
+ * Fixes in function declarations
+ *
+ * Revision 1.11  2002/08/29 17:05:22  dron
+ * Fixes in channels description.
+ *
+ * Revision 1.10  2002/08/28 15:16:31  dron
+ * Band descriptions added
+ *
+ * Revision 1.9  2002/06/26 12:28:36  dron
+ * 8-bit selective extract subsets supported
+ *
+ * Revision 1.8  2002/06/25 18:11:26  dron
+ * Added support for 16-bit selective extract subsets
+ *
+ * Revision 1.7  2002/06/12 21:12:25  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.6  2002/06/07 18:02:41  warmerda
+ * don't redeclare i
+ *
+ * Revision 1.5  2002/06/04 16:02:28  dron
+ * Fixes in georeferencing, metadata, NOAA-K/L/M support.
+ * Many thanks to Markus Neteler for intensive testing.
+ *
+ * Revision 1.4  2002/05/21 17:37:09  dron
+ * Additional meteadata.
+ *
+ * Revision 1.3  2002/05/18 14:01:21  dron
+ * NOAA-15 fixes, georeferencing
+ *
+ * Revision 1.2  2002/05/16 01:26:57  warmerda
+ * move up variable declaration to avoid VC++ error
+ *
+ * Revision 1.1  2002/05/08 16:32:20  dron
+ * NOAA Polar Orbiter Dataset reader added (not full implementation yet).
+ *
+ *
+ */
+
+#include "gdal_pam.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: l1bdataset.cpp,v 1.27 2006/02/16 05:14:46 fwarmerdam Exp $");
+
+CPL_C_START
+void    GDALRegister_L1B(void);
+CPL_C_END
+
+enum {          // Spacecrafts:
+    TIROSN,     // TIROS-N
+    NOAA6,      // NOAA-6(A)
+    NOAAB,      // NOAA-B
+    NOAA7,      // NOAA-7(C)
+    NOAA8,      // NOAA-8(E)
+    NOAA9,      // NOAA-9(F)
+    NOAA10,     // NOAA-10(G)
+    NOAA11,     // NOAA-11(H)
+    NOAA12,     // NOAA-12(D)
+    NOAA13,     // NOAA-13(I)
+    NOAA14,     // NOAA-14(J)
+    NOAA15,     // NOAA-15(K)
+    NOAA16,     // NOAA-16(L)
+    NOAA17      // NOAA-17(M)
+};
+
+enum {          // Types of datasets
+    HRPT,
+    LAC,
+    GAC
+};
+
+enum {          // Data format
+    PACKED10BIT,
+    UNPACKED8BIT,
+    UNPACKED16BIT
+};
+
+enum {          // Receiving stations names:
+    DU,         // Dundee, Scotland, UK
+    GC,         // Fairbanks, Alaska, USA (formerly Gilmore Creek)
+    HO,         // Honolulu, Hawaii, USA
+    MO,         // Monterey, California, USA
+    WE,         // Western Europe CDA, Lannion, France
+    SO,         // SOCC (Satellite Operations Control Center), Suitland, Maryland, USA
+    WI,         // Wallops Island, Virginia, USA
+    UNKNOWN_STATION
+};
+
+enum {          // Data processing centers:
+    CMS,        // Centre de Meteorologie Spatiale - Lannion, France
+    DSS,        // Dundee Satellite Receiving Station - Dundee, Scotland, UK
+    NSS,        // NOAA/NESDIS - Suitland, Maryland, USA
+    UKM,        // United Kingdom Meteorological Office - Bracknell, England, UK
+    UNKNOWN_CENTER
+};
+
+enum {          // AVHRR Earth location indication
+        ASCEND,
+        DESCEND
+};
+
+const char *paszChannelsDesc[] =                            // AVHRR band widths
+{                                                           // NOAA-7 -- NOAA-17 channels
+"AVHRR Channel 1:  0.58  micrometers -- 0.68 micrometers",
+"AVHRR Channel 2:  0.725 micrometers -- 1.10 micrometers",
+"AVHRR Channel 3:  3.55  micrometers -- 3.93 micrometers",
+"AVHRR Channel 4:  10.3  micrometers -- 11.3 micrometers",
+"AVHRR Channel 5:  11.5  micrometers -- 12.5 micrometers",  // not in NOAA-6,-8,-10
+"AVHRR Channel 5:  11.4  micrometers -- 12.4 micrometers",  // NOAA-13
+                                                            // NOAA-15 -- NOAA-17
+"AVHRR Channel 3A: 1.58  micrometers -- 1.64 micrometers",
+"AVHRR Channel 3B: 3.55  micrometers -- 3.93 micrometers"
+};
+
+#define TBM_HEADER_SIZE 122
+
+#define DESIRED_GCPS_PER_LINE 11
+#define DESIRED_LINES_OF_GCPS 20
+
+/************************************************************************/
+/* ==================================================================== */
+/*                      TimeCode (helper class)                         */
+/* ==================================================================== */
+/************************************************************************/
+
+class TimeCode {
+    long        lYear;
+    long        lDay;
+    long        lMillisecond;
+    char        pszString[100];
+
+  public:
+    void SetYear(long year)
+    {
+        lYear = year;
+    }
+    void SetDay(long day)
+    {
+        lDay = day;
+    }
+    void SetMillisecond(long millisecond)
+    {
+        lMillisecond = millisecond;
+    }
+    char* PrintTime()
+    {
+        sprintf(pszString, "year: %ld, day: %ld, millisecond: %ld", lYear, lDay, lMillisecond);
+        return pszString;
+    }
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                              L1BDataset                              */
+/* ==================================================================== */
+/************************************************************************/
+
+class L1BDataset : public GDALPamDataset
+{
+    friend class L1BRasterBand;
+
+    GByte        pabyTBMHeader[TBM_HEADER_SIZE];
+    char        pszRevolution[6]; // Five-digit number identifying spacecraft revolution
+    int         iSource;        // Source of data (receiving station name)
+    int         iProcCenter;    // Data processing center
+    TimeCode    sStartTime;
+    TimeCode    sStopTime;
+
+    GDAL_GCP    *pasGCPList;
+    int         nGCPCount;
+    int         iGCPOffset;
+    int         iGCPCodeOffset;
+    int         nGCPsPerLine;
+    int         iLocationIndicator;
+    double      dfGCPStart, dfGCPStep;
+
+    int         nBufferSize;
+    int         iSpacecraftID;
+    int         iDataType;      // LAC, GAC, HRPT
+    int         iDataFormat;    // 10-bit packed or 16-bit unpacked
+    int         nRecordDataStart;
+    int         nRecordDataEnd;
+    int         nDataStartOffset;
+    int         nRecordSize;
+    GUInt16     iInstrumentStatus;
+    GUInt32     iChannels;
+
+    char        *pszGCPProjection;
+
+    FILE        *fp;
+
+    void        ProcessRecordHeaders();
+    void        FetchNOAA9GCPs(GDAL_GCP *pasGCPList, GInt16 *piRecordHeader, int iLine);
+    void        FetchNOAA15GCPs(GDAL_GCP *pasGCPList, GInt32 *piRecordHeader, int iLine);
+    void        FetchNOAA9TimeCode(TimeCode *psTime, GByte *piRecordHeader, int *iLocInd);
+    void        FetchNOAA15TimeCode(TimeCode *psTime, GUInt16 *piRecordHeader, int *intLocInd);
+    void        ProcessDatasetHeader();
+    
+  public:
+                L1BDataset();
+                ~L1BDataset();
+    
+    virtual int   GetGCPCount();
+    virtual const char *GetGCPProjection();
+    virtual const GDAL_GCP *GetGCPs();
+    static GDALDataset *Open( GDALOpenInfo * );
+
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            L1BRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class L1BRasterBand : public GDALPamRasterBand
+{
+    friend class L1BDataset;
+
+  public:
+
+                L1BRasterBand( L1BDataset *, int );
+    
+//    virtual double GetNoDataValue( int *pbSuccess = NULL );
+    virtual CPLErr IReadBlock( int, int, void * );
+};
+
+
+/************************************************************************/
+/*                           L1BRasterBand()                            */
+/************************************************************************/
+
+L1BRasterBand::L1BRasterBand( L1BDataset *poDS, int nBand )
+
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+    eDataType = GDT_UInt16;
+
+    nBlockXSize = poDS->GetRasterXSize();
+    nBlockYSize = 1;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr L1BRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                      void * pImage )
+{
+    L1BDataset  *poGDS = (L1BDataset *) poDS;
+    GUInt32     iword, jword;
+    GUInt32     *iRawScan = NULL;               // Packed scanline buffer
+    GUInt16     *iScan1 = NULL, *iScan2 = NULL; // Unpacked 16-bit scanline buffers
+    GByte       *byScan = NULL;                 // Unpacked 8-bit scanline buffer
+    int         iDataOffset, i, j;
+            
+/* -------------------------------------------------------------------- */
+/*      Seek to data.                                                   */
+/* -------------------------------------------------------------------- */
+    iDataOffset = (poGDS->iLocationIndicator == DESCEND)?
+            poGDS->nDataStartOffset + nBlockYOff * poGDS->nRecordSize:
+            poGDS->nDataStartOffset +
+            (poGDS->GetRasterYSize() - nBlockYOff - 1) * poGDS->nRecordSize;
+    VSIFSeek(poGDS->fp, iDataOffset, SEEK_SET);
+
+/* -------------------------------------------------------------------- */
+/*      Read data into the buffer.                                      */
+/* -------------------------------------------------------------------- */
+    switch (poGDS->iDataFormat)
+    {
+        case PACKED10BIT:
+        // Read packed scanline
+        iRawScan = (GUInt32 *)CPLMalloc(poGDS->nRecordSize);
+        VSIFRead(iRawScan, 1, poGDS->nRecordSize, poGDS->fp);
+        iScan1 = (GUInt16 *)CPLMalloc(poGDS->nBufferSize);
+        j = 0;
+        for(i = poGDS->nRecordDataStart / (int)sizeof(iRawScan[0]);
+            i < poGDS->nRecordDataEnd / (int)sizeof(iRawScan[0]); i++)
+        {
+            iword = iRawScan[i];
+#ifdef CPL_LSB
+            CPL_SWAP32PTR(&iword);
+#endif
+            jword = iword & 0x3FF00000;
+            iScan1[j++] = (GUInt16) (jword >> 20);
+            jword = iword & 0x000FFC00;
+            iScan1[j++] = (GUInt16) (jword >> 10);
+            iScan1[j++] = (GUInt16) (iword & 0x000003FF);
+        }
+        CPLFree(iRawScan);
+        break;
+        case UNPACKED16BIT:
+        iScan1 = (GUInt16 *)CPLMalloc(poGDS->GetRasterXSize()
+                                      * poGDS->nBands * sizeof(GUInt16));
+        iScan2 = (GUInt16 *)CPLMalloc(poGDS->nRecordSize);
+        VSIFRead(iScan2, 1, poGDS->nRecordSize, poGDS->fp);
+        for (i = 0; i < poGDS->GetRasterXSize() * poGDS->nBands; i++)
+        {
+            iScan1[i] = iScan2[poGDS->nRecordDataStart / (int)sizeof(iScan2[0]) + i];
+#ifdef CPL_LSB
+            CPL_SWAP16PTR(&iScan1[i]);
+#endif
+        }
+        CPLFree(iScan2);
+        break;
+        case UNPACKED8BIT:
+        iScan1 = (GUInt16 *)CPLMalloc(poGDS->GetRasterXSize()
+                                      * poGDS->nBands * sizeof(GUInt16));
+        byScan = (GByte *)CPLMalloc(poGDS->nRecordSize);
+        VSIFRead(byScan, 1, poGDS->nRecordSize, poGDS->fp);
+        for (i = 0; i < poGDS->GetRasterXSize() * poGDS->nBands; i++)
+            iScan1[i] = byScan[poGDS->nRecordDataStart / (int)sizeof(byScan[0]) + i];
+        CPLFree(byScan);
+        break;
+        default: // NOTREACHED
+        break;
+    }
+    
+    int nBlockSize = nBlockXSize * nBlockYSize;
+    if (poGDS->iLocationIndicator == DESCEND)
+        for( i = 0, j = 0; i < nBlockSize; i++ )
+        {
+            ((GUInt16 *) pImage)[i] = iScan1[j + nBand - 1];
+            j += poGDS->nBands;
+        }
+    else
+        for ( i = nBlockSize - 1, j = 0; i >= 0; i-- )
+        {
+            ((GUInt16 *) pImage)[i] = iScan1[j + nBand - 1];
+            j += poGDS->nBands;
+        }
+    
+    CPLFree(iScan1);
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           L1BDataset()                               */
+/************************************************************************/
+
+L1BDataset::L1BDataset()
+
+{
+    fp = NULL;
+    nGCPCount = 0;
+    pasGCPList = NULL;
+    pszGCPProjection = CPLStrdup( "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\",SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",7043]],TOWGS84[0,0,4.5,0,0,0.554,0.2263],AUTHORITY[\"EPSG\",6322]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AXIS[\"Lat\",\"NORTH\"],AXIS[\"Long\",\"EAST\"],AUTHORITY[\"EPSG\",4322]]" );
+    nBands = 0;
+    iLocationIndicator = DESCEND; // XXX: should be initialised
+    iChannels = 0;
+    iInstrumentStatus = 0;
+}
+
+/************************************************************************/
+/*                            ~L1BDataset()                             */
+/************************************************************************/
+
+L1BDataset::~L1BDataset()
+
+{
+    FlushCache();
+
+    if( nGCPCount > 0 )
+    {
+        for( int i = 0; i < nGCPCount; i++ )
+        {
+            if ( pasGCPList[i].pszId )
+                CPLFree( pasGCPList[i].pszId );
+            if ( pasGCPList[i].pszInfo )
+                CPLFree( pasGCPList[i].pszInfo );
+        }
+
+        CPLFree( pasGCPList );
+    }
+    if ( pszGCPProjection )
+        CPLFree( pszGCPProjection );
+    if( fp != NULL )
+        VSIFClose( fp );
+}
+
+/************************************************************************/
+/*                            GetGCPCount()                             */
+/************************************************************************/
+
+int L1BDataset::GetGCPCount()
+
+{
+    return nGCPCount;
+}
+
+/************************************************************************/
+/*                          GetGCPProjection()                          */
+/************************************************************************/
+
+const char *L1BDataset::GetGCPProjection()
+
+{
+    if( nGCPCount > 0 )
+        return pszGCPProjection;
+    else
+        return "";
+}
+
+/************************************************************************/
+/*                               GetGCPs()                              */
+/************************************************************************/
+
+const GDAL_GCP *L1BDataset::GetGCPs()
+{
+    return pasGCPList;
+}
+
+/************************************************************************/
+/*      Fetch timecode from the record header (NOAA9-NOAA14 version)    */
+/************************************************************************/
+
+void L1BDataset::FetchNOAA9TimeCode( TimeCode *psTime, GByte *piRecordHeader,
+                                     int *iLocInd )
+{
+    GUInt32 lTemp;
+
+    lTemp = ((piRecordHeader[2] >> 1) & 0x7F);
+    psTime->SetYear((lTemp > 77)?(lTemp + 1900):(lTemp + 2000)); // Avoid `Year 2000' problem
+    psTime->SetDay((GUInt32)(piRecordHeader[2] & 0x01) << 8 | (GUInt32)piRecordHeader[3]);
+    psTime->SetMillisecond(
+        ((GUInt32)(piRecordHeader[4] & 0x07) << 24) | ((GUInt32)piRecordHeader[5] << 16) |
+        ((GUInt32)piRecordHeader[6] << 8) | (GUInt32)piRecordHeader[7]);
+    *iLocInd = ((piRecordHeader[8] & 0x02) == 0) ? ASCEND : DESCEND;
+}
+
+/************************************************************************/
+/*      Fetch timecode from the record header (NOAA15-NOAA17 version)   */
+/************************************************************************/
+
+void L1BDataset::FetchNOAA15TimeCode( TimeCode *psTime,
+                                      GUInt16 *piRecordHeader, int *iLocInd )
+{
+#ifdef CPL_LSB
+    GUInt16 iTemp;
+    GUInt32 lTemp;
+
+    iTemp = piRecordHeader[1];
+    psTime->SetYear(CPL_SWAP16(iTemp));
+    iTemp = piRecordHeader[2];
+    psTime->SetDay(CPL_SWAP16(iTemp));
+    lTemp = (GUInt32)CPL_SWAP16(piRecordHeader[4]) << 16 |
+        (GUInt32)CPL_SWAP16(piRecordHeader[5]);
+    psTime->SetMillisecond(lTemp);
+    *iLocInd = ((CPL_SWAP16(piRecordHeader[6]) & 0x8000) == 0)?ASCEND:DESCEND; // FIXME: hemisphere
+#else
+    psTime->SetYear(piRecordHeader[1]);
+    psTime->SetDay(piRecordHeader[2]);
+    psTime->SetMillisecond((GUInt32)piRecordHeader[4] << 16 | (GUInt32)piRecordHeader[5]);
+    *iLocInd = ((piRecordHeader[6] & 0x8000) == 0)?ASCEND:DESCEND;
+#endif
+}
+
+/************************************************************************/
+/* Fetch the GCPs from the individual scanlines (NOAA9-NOAA14 version)  */
+/************************************************************************/
+
+void L1BDataset::FetchNOAA9GCPs( GDAL_GCP *pasGCPList,
+                                 GInt16 *piRecordHeader, int iLine )
+{
+    int         nGoodGCPs, iGCPPos, j;
+    double      dfPixel;
+    
+    nGoodGCPs = (*((GByte *)piRecordHeader + iGCPCodeOffset) <= nGCPsPerLine)?
+            *((GByte *)piRecordHeader + iGCPCodeOffset):nGCPsPerLine;
+
+#ifdef DEBUG
+    CPLDebug( "L1B", "iGCPCodeOffset=%d, nGCPsPerLine=%d, nGoodGCPs=%d",
+              iGCPCodeOffset, nGCPsPerLine, nGoodGCPs );
+#endif
+
+    dfPixel = (iLocationIndicator == DESCEND)?
+        dfGCPStart:(GetRasterXSize() - dfGCPStart);
+    j = iGCPOffset / (int)sizeof(piRecordHeader[0]);
+    iGCPPos = iGCPOffset / (int)sizeof(piRecordHeader[0]) + 2 * nGoodGCPs;
+    while ( j < iGCPPos )
+    {
+        GInt16  nRawY = piRecordHeader[j++];
+        GInt16  nRawX = piRecordHeader[j++];
+
+#ifdef CPL_LSB
+        CPL_SWAP16PTR( &nRawX );
+        CPL_SWAP16PTR( &nRawY );
+#endif
+        pasGCPList[nGCPCount].dfGCPY = nRawY / 128.0;
+        pasGCPList[nGCPCount].dfGCPX = nRawX / 128.0;
+
+        if (pasGCPList[nGCPCount].dfGCPX < -180
+            || pasGCPList[nGCPCount].dfGCPX > 180
+            || pasGCPList[nGCPCount].dfGCPY < -90
+            || pasGCPList[nGCPCount].dfGCPY > 90)
+            continue;
+
+        pasGCPList[nGCPCount].dfGCPZ = 0.0;
+        pasGCPList[nGCPCount].dfGCPPixel = dfPixel;
+        dfPixel += (iLocationIndicator == DESCEND)?dfGCPStep:-dfGCPStep;
+        pasGCPList[nGCPCount].dfGCPLine =
+            (double)((iLocationIndicator == DESCEND)?
+            iLine:GetRasterYSize() - iLine - 1) + 0.5;
+        nGCPCount++;
+    }
+}
+
+/************************************************************************/
+/* Fetch the GCPs from the individual scanlines (NOAA15-NOAA17 version) */
+/************************************************************************/
+
+void L1BDataset::FetchNOAA15GCPs( GDAL_GCP *pasGCPList,
+                                  GInt32 *piRecordHeader, int iLine)
+{
+    int         j, iGCPPos;
+    double      dfPixel;
+    
+    dfPixel = (iLocationIndicator == DESCEND)?
+        dfGCPStart:(GetRasterXSize() - dfGCPStart);
+    j = iGCPOffset / (int)sizeof(piRecordHeader[0]);
+    iGCPPos = iGCPOffset / (int)sizeof(piRecordHeader[0]) + 2 * nGCPsPerLine;
+    while ( j < iGCPPos )
+    {
+        GInt32  nRawY = piRecordHeader[j++];
+        GInt32  nRawX = piRecordHeader[j++];
+
+#ifdef CPL_LSB
+        CPL_SWAP32PTR( &nRawX );
+        CPL_SWAP32PTR( &nRawY );
+#endif
+        pasGCPList[nGCPCount].dfGCPY = nRawY / 10000.0;
+        pasGCPList[nGCPCount].dfGCPX = nRawX / 10000.0;
+
+        if ( pasGCPList[nGCPCount].dfGCPX < -180
+             || pasGCPList[nGCPCount].dfGCPX > 180
+             || pasGCPList[nGCPCount].dfGCPY < -90
+             || pasGCPList[nGCPCount].dfGCPY > 90 )
+        {
+            continue;
+        }
+        pasGCPList[nGCPCount].dfGCPZ = 0.0;
+        pasGCPList[nGCPCount].dfGCPPixel = dfPixel;
+        dfPixel += (iLocationIndicator == DESCEND)?dfGCPStep:-dfGCPStep;
+        pasGCPList[nGCPCount].dfGCPLine =
+            (double)((iLocationIndicator == DESCEND)?
+            iLine:GetRasterYSize() - iLine - 1) + 0.5;
+        nGCPCount++;
+    }
+}
+
+/************************************************************************/
+/*                      ProcessRecordHeaders()                          */
+/************************************************************************/
+
+void L1BDataset::ProcessRecordHeaders()
+{
+    int         iLine, iLocInd;
+    void        *piRecordHeader;
+
+    piRecordHeader = CPLMalloc(nRecordDataStart);
+    VSIFSeek(fp, nDataStartOffset, SEEK_SET);
+    VSIFRead(piRecordHeader, 1, nRecordDataStart, fp);
+
+    if (iSpacecraftID <= NOAA14)
+        FetchNOAA9TimeCode(&sStartTime, (GByte *) piRecordHeader, &iLocInd);
+    else
+        FetchNOAA15TimeCode(&sStartTime, (GUInt16 *) piRecordHeader, &iLocInd);
+    iLocationIndicator = iLocInd;
+    VSIFSeek( fp, nDataStartOffset + (GetRasterYSize() - 1) * nRecordSize,
+              SEEK_SET);
+    VSIFRead( piRecordHeader, 1, nRecordDataStart, fp );
+    if (iSpacecraftID <= NOAA14)
+        FetchNOAA9TimeCode(&sStopTime, (GByte *) piRecordHeader, &iLocInd);
+    else
+        FetchNOAA15TimeCode(&sStopTime, (GUInt16 *) piRecordHeader, &iLocInd);
+
+/* -------------------------------------------------------------------- */
+/*      Pick a skip factor so that we will get roughly 20 lines         */
+/*      worth of GCPs.  That should give respectible coverage on all    */
+/*      but the longest swaths.                                         */
+/* -------------------------------------------------------------------- */
+    int nTargetLines = DESIRED_LINES_OF_GCPS;
+    int nLineSkip = GetRasterYSize() / (nTargetLines-1);
+    
+/* -------------------------------------------------------------------- */
+/*      Initialize the GCP list.                                        */
+/* -------------------------------------------------------------------- */
+    pasGCPList = (GDAL_GCP *)CPLCalloc( nTargetLines * nGCPsPerLine,
+                                        sizeof(GDAL_GCP) );
+    GDALInitGCPs( nTargetLines * nGCPsPerLine, pasGCPList );
+
+/* -------------------------------------------------------------------- */
+/*      Fetch the GCPs for each selected line.  We force the last       */
+/*      line sampled to be the last line in the dataset even if that    */
+/*      leaves a bigger than expected gap.                              */
+/* -------------------------------------------------------------------- */
+    int iStep;
+
+    for( iStep = 0; iStep < nTargetLines; iStep++ )
+    {
+        int nOrigGCPs = nGCPCount;
+
+        if( iStep == nTargetLines - 1 )
+            iLine = GetRasterYSize() - 1;
+        else
+            iLine = nLineSkip * iStep;
+
+        VSIFSeek( fp, nDataStartOffset + iLine * nRecordSize, SEEK_SET );
+        VSIFRead( piRecordHeader, 1, nRecordDataStart, fp );
+
+        if (iSpacecraftID <= NOAA14)
+            FetchNOAA9GCPs( pasGCPList, (GInt16 *)piRecordHeader, iLine );
+        else
+            FetchNOAA15GCPs( pasGCPList, (GInt32 *)piRecordHeader, iLine );
+
+/* -------------------------------------------------------------------- */
+/*      We don't really want too many GCPs per line.  Downsample to     */
+/*      11 per line.                                                    */
+/* -------------------------------------------------------------------- */
+        int iGCP;
+        int nGCPsOnThisLine = nGCPCount - nOrigGCPs;
+        int nDesiredGCPsPerLine = MIN(DESIRED_GCPS_PER_LINE,nGCPsOnThisLine);
+        int nGCPStep = (nGCPsOnThisLine - 1) / (nDesiredGCPsPerLine-1);
+
+        if( nGCPStep == 0 )
+            nGCPStep = 1;
+
+        for( iGCP = 0; iGCP < nDesiredGCPsPerLine; iGCP++ )
+        {
+            int iSrcGCP = nOrigGCPs + iGCP * nGCPStep;
+            int iDstGCP = nOrigGCPs + iGCP;
+
+            pasGCPList[iDstGCP].dfGCPX = pasGCPList[iSrcGCP].dfGCPX;
+            pasGCPList[iDstGCP].dfGCPY = pasGCPList[iSrcGCP].dfGCPY;
+            pasGCPList[iDstGCP].dfGCPPixel = pasGCPList[iSrcGCP].dfGCPPixel;
+            pasGCPList[iDstGCP].dfGCPLine = pasGCPList[iSrcGCP].dfGCPLine;
+        }
+
+        nGCPCount = nOrigGCPs + nDesiredGCPsPerLine;
+    }
+
+    CPLFree( piRecordHeader );
+}
+
+/************************************************************************/
+/*                      ProcessDatasetHeader()                          */
+/************************************************************************/
+
+void L1BDataset::ProcessDatasetHeader()
+{
+    GUInt16 *piHeader;
+    piHeader = (GUInt16 *)CPLMalloc(nDataStartOffset);
+    VSIFSeek( fp, 0, SEEK_SET );
+    VSIFRead( piHeader, 1, nDataStartOffset, fp );
+    if (iSpacecraftID > NOAA14)
+    {
+        iInstrumentStatus = (piHeader + 512)[58];
+#ifdef CPL_LSB
+        CPL_SWAP16PTR(&iInstrumentStatus);
+#endif
+    }
+    CPLFree( piHeader );
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *L1BDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    int i = 0;
+
+    if( poOpenInfo->fp == NULL )
+        return NULL;
+
+    // XXX: Signature is not very good
+    if( !EQUALN((const char *) poOpenInfo->pabyHeader + 33, ".", 1) ||
+        !EQUALN((const char *) poOpenInfo->pabyHeader + 38, ".", 1) || 
+        !EQUALN((const char *) poOpenInfo->pabyHeader + 41, ".", 1) || 
+        !EQUALN((const char *) poOpenInfo->pabyHeader + 48, ".", 1) || 
+        !EQUALN((const char *) poOpenInfo->pabyHeader + 54, ".", 1) ||
+        !EQUALN((const char *) poOpenInfo->pabyHeader + 60, ".", 1) ||
+        !EQUALN((const char *) poOpenInfo->pabyHeader + 69, ".", 1) )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    L1BDataset  *poDS;
+    VSIStatBuf  sStat;
+
+    poDS = new L1BDataset();
+
+    poDS->fp = poOpenInfo->fp;
+    poOpenInfo->fp = NULL;
+    
+/* -------------------------------------------------------------------- */
+/*      Read the header.                                                */
+/* -------------------------------------------------------------------- */
+    VSIFSeek( poDS->fp, 0, SEEK_SET );
+    VSIFRead( poDS->pabyTBMHeader, 1, TBM_HEADER_SIZE, poDS->fp );
+
+    // Determine processing center where the dataset was created
+    if ( EQUALN((const char *) poDS->pabyTBMHeader + 30, "CMS", 3) )
+         poDS->iProcCenter = CMS;
+    else if ( EQUALN((const char *) poDS->pabyTBMHeader + 30, "DSS", 3) )
+         poDS->iProcCenter = DSS;
+    else if ( EQUALN((const char *) poDS->pabyTBMHeader + 30, "NSS", 3) )
+         poDS->iProcCenter = NSS;
+    else if ( EQUALN((const char *) poDS->pabyTBMHeader + 30, "UKM", 3) )
+         poDS->iProcCenter = UKM;
+    else
+         poDS->iProcCenter = UNKNOWN_CENTER;
+    
+    // Determine spacecraft type
+    if ( EQUALN((const char *)poDS->pabyTBMHeader + 39, "NA", 2) )
+         poDS->iSpacecraftID = NOAA6;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 39, "NB", 2) )
+         poDS->iSpacecraftID = NOAAB;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 39, "NC", 2) )
+         poDS->iSpacecraftID = NOAA7;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 39, "NE", 2) )
+         poDS->iSpacecraftID = NOAA8;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 39, "NF", 2) )
+         poDS->iSpacecraftID = NOAA9;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 39, "NG", 2) )
+         poDS->iSpacecraftID = NOAA10;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 39, "NH", 2) )
+         poDS->iSpacecraftID = NOAA11;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 39, "ND", 2) )
+         poDS->iSpacecraftID = NOAA12;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 39, "NI", 2) )
+         poDS->iSpacecraftID = NOAA13;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 39, "NJ", 2) )
+         poDS->iSpacecraftID = NOAA14;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 39, "NK", 2) )
+         poDS->iSpacecraftID = NOAA15;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 39, "NL", 2) )
+         poDS->iSpacecraftID = NOAA16;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 39, "NM", 2) )
+         poDS->iSpacecraftID = NOAA17;
+    else
+         goto bad;
+           
+    // Determine dataset type
+    if ( EQUALN((const char *)poDS->pabyTBMHeader + 34, "HRPT", 4) )
+         poDS->iDataType = HRPT;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 34, "LHRR", 4) )
+         poDS->iDataType = LAC;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 34, "GHRR", 4) )
+         poDS->iDataType = GAC;
+    else
+         goto bad;
+
+    // Get revolution number (as string, we don't need this value for processing)
+    memcpy(poDS->pszRevolution, poDS->pabyTBMHeader + 62, 5);
+    poDS->pszRevolution[5] = 0;
+
+    // Get receiving station name
+    if ( EQUALN((const char *)poDS->pabyTBMHeader + 70, "DU", 2) )
+         poDS->iSource = DU;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 70, "GC", 2) )
+         poDS->iSource = GC;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 70, "HO", 2) )
+         poDS->iSource = HO;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 70, "MO", 2) )
+         poDS->iSource = MO;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 70, "WE", 2) )
+         poDS->iSource = WE;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 70, "SO", 2) )
+         poDS->iSource = SO;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 70, "WI", 2) )
+         poDS->iSource = WI;
+    else
+         poDS->iSource = UNKNOWN_STATION;
+
+    // Determine number of bands and data format
+    // (10-bit packed or 16-bit unpacked)
+    for ( i = 97; i < 117; i++ )
+        if (poDS->pabyTBMHeader[i] == 1 || poDS->pabyTBMHeader[i] == 'Y')
+        {
+            poDS->nBands++;
+            poDS->iChannels |= (1 << (i - 97));
+        }
+    if (poDS->nBands == 0 || poDS->nBands > 5)
+    {
+        poDS->nBands = 5;
+        poDS->iChannels = 0x1F;
+    }
+    if ( EQUALN((const char *)poDS->pabyTBMHeader + 117, "10", 2) ||
+         EQUALN((const char *)poDS->pabyTBMHeader + 117, "  ", 2) )
+        poDS->iDataFormat = PACKED10BIT;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 117, "16", 2) )
+        poDS->iDataFormat = UNPACKED16BIT;
+    else if ( EQUALN((const char *)poDS->pabyTBMHeader + 117, "08", 2) )
+        poDS->iDataFormat = UNPACKED8BIT;
+    else
+        goto bad;
+
+    switch( poDS->iDataType )
+    {
+        case HRPT:
+        case LAC:
+            poDS->nRasterXSize = 2048;
+            poDS->nBufferSize = 20484;
+            poDS->dfGCPStart = 25.5; // GCPs are located at center of pixel
+            poDS->dfGCPStep = 40;
+            poDS->nGCPsPerLine = 51;
+            if (poDS->iSpacecraftID <= NOAA14)
+            {
+                if (poDS->iDataFormat == PACKED10BIT)
+                {
+                    poDS->nRecordSize = 14800;
+                    poDS->nRecordDataEnd = 14104;
+                }
+                else if (poDS->iDataFormat == UNPACKED16BIT)
+                {
+                    switch(poDS->nBands)
+                    {
+                        case 1:
+                        poDS->nRecordSize = 4544;
+                        poDS->nRecordDataEnd = 4544;
+                        break;
+                        case 2:
+                        poDS->nRecordSize = 8640;
+                        poDS->nRecordDataEnd = 8640;
+                        break;
+                        case 3:
+                        poDS->nRecordSize = 12736;
+                        poDS->nRecordDataEnd = 12736;
+                        break;
+                        case 4:
+                        poDS->nRecordSize = 16832;
+                        poDS->nRecordDataEnd = 16832;
+                        break;
+                        case 5:
+                        poDS->nRecordSize = 20928;
+                        poDS->nRecordDataEnd = 20928;
+                        break;
+                    }
+                }
+                else // UNPACKED8BIT
+                {
+                    switch(poDS->nBands)
+                    {
+                        case 1:
+                        poDS->nRecordSize = 2496;
+                        poDS->nRecordDataEnd = 2496;
+                        break;
+                        case 2:
+                        poDS->nRecordSize = 4544;
+                        poDS->nRecordDataEnd = 4544;
+                        break;
+                        case 3:
+                        poDS->nRecordSize = 6592;
+                        poDS->nRecordDataEnd = 6592;
+                        break;
+                        case 4:
+                        poDS->nRecordSize = 8640;
+                        poDS->nRecordDataEnd = 8640;
+                        break;
+                        case 5:
+                        poDS->nRecordSize = 10688;
+                        poDS->nRecordDataEnd = 10688;
+                        break;
+                    }
+                }
+                poDS->nDataStartOffset = poDS->nRecordSize + 122;
+                poDS->nRecordDataStart = 448;
+                poDS->iGCPCodeOffset = 52;
+                poDS->iGCPOffset = 104;
+            }
+            else if (poDS->iSpacecraftID <= NOAA17)
+            {
+                if (poDS->iDataFormat == PACKED10BIT)
+                {
+                    poDS->nRecordSize = 15872;
+                    poDS->nRecordDataEnd = 14920;
+                }
+                else if (poDS->iDataFormat == UNPACKED16BIT)
+                {
+                    switch(poDS->nBands)
+                    {
+                        case 1:
+                        poDS->nRecordSize = 6144;
+                        poDS->nRecordDataEnd = 5360;
+                        break;
+                        case 2:
+                        poDS->nRecordSize = 10240;
+                        poDS->nRecordDataEnd = 9456;
+                        break;
+                        case 3:
+                        poDS->nRecordSize = 14336;
+                        poDS->nRecordDataEnd = 13552;
+                        break;
+                        case 4:
+                        poDS->nRecordSize = 18432;
+                        poDS->nRecordDataEnd = 17648;
+                        break;
+                        case 5:
+                        poDS->nRecordSize = 22528;
+                        poDS->nRecordDataEnd = 21744;
+                        break;
+                    }
+                }
+                else // UNPACKED8BIT
+                {
+                    switch(poDS->nBands)
+                    {
+                        case 1:
+                        poDS->nRecordSize = 4096;
+                        poDS->nRecordDataEnd = 3312;
+                        break;
+                        case 2:
+                        poDS->nRecordSize = 6144;
+                        poDS->nRecordDataEnd = 5360;
+                        break;
+                        case 3:
+                        poDS->nRecordSize = 8192;
+                        poDS->nRecordDataEnd = 7408;
+                        break;
+                        case 4:
+                        poDS->nRecordSize = 10240;
+                        poDS->nRecordDataEnd = 9456;
+                        break;
+                        case 5:
+                        poDS->nRecordSize = 12288;
+                        poDS->nRecordDataEnd = 11504;
+                        break;
+                    }
+                }
+                poDS->nDataStartOffset = poDS->nRecordSize + 512;
+                poDS->nRecordDataStart = 1264;
+                poDS->iGCPCodeOffset = 0; // XXX: not exist for NOAA15?
+                poDS->iGCPOffset = 640;
+            }
+            else
+                goto bad;
+        break;
+        case GAC:
+            poDS->nRasterXSize = 409;
+            poDS->nBufferSize = 4092;
+            poDS->dfGCPStart = 5.5; // FIXME: depends to scan direction
+            poDS->dfGCPStep = 8;
+            poDS->nGCPsPerLine = 51;
+            if (poDS->iSpacecraftID <= NOAA14)
+            {
+                if (poDS->iDataFormat == PACKED10BIT)
+                {
+                    poDS->nRecordSize = 3220;
+                    poDS->nRecordDataEnd = 3176;
+                }
+                else if (poDS->iDataFormat == UNPACKED16BIT)
+                    switch(poDS->nBands)
+                    {
+                        case 1:
+                        poDS->nRecordSize = 1268;
+                        poDS->nRecordDataEnd = 1266;
+                        break;
+                        case 2:
+                        poDS->nRecordSize = 2084;
+                        poDS->nRecordDataEnd = 2084;
+                        break;
+                        case 3:
+                        poDS->nRecordSize = 2904;
+                        poDS->nRecordDataEnd = 2902;
+                        break;
+                        case 4:
+                        poDS->nRecordSize = 3720;
+                        poDS->nRecordDataEnd = 3720;
+                        break;
+                        case 5:
+                        poDS->nRecordSize = 4540;
+                        poDS->nRecordDataEnd = 4538;
+                        break;
+                    }
+                else // UNPACKED8BIT
+                {
+                    switch(poDS->nBands)
+                    {
+                        case 1:
+                        poDS->nRecordSize = 860;
+                        poDS->nRecordDataEnd = 858;
+                        break;
+                        case 2:
+                        poDS->nRecordSize = 1268;
+                        poDS->nRecordDataEnd = 1266;
+                        break;
+                        case 3:
+                        poDS->nRecordSize = 1676;
+                        poDS->nRecordDataEnd = 1676;
+                        break;
+                        case 4:
+                        poDS->nRecordSize = 2084;
+                        poDS->nRecordDataEnd = 2084;
+                        break;
+                        case 5:
+                        poDS->nRecordSize = 2496;
+                        poDS->nRecordDataEnd = 2494;
+                        break;
+                    }
+                }
+                poDS->nDataStartOffset = poDS->nRecordSize * 2 + 122;
+                poDS->nRecordDataStart = 448;
+                poDS->iGCPCodeOffset = 52;
+                poDS->iGCPOffset = 104;
+            }
+            else if (poDS->iSpacecraftID <= NOAA17)
+            {
+                if (poDS->iDataFormat == PACKED10BIT)
+                {
+                    poDS->nRecordSize = 4608;
+                    poDS->nRecordDataEnd = 3992;
+                }
+                else if (poDS->iDataFormat == UNPACKED16BIT)
+                {
+                    switch(poDS->nBands)
+                    {
+                        case 1:
+                        poDS->nRecordSize = 2360;
+                        poDS->nRecordDataEnd = 2082;
+                        break;
+                        case 2:
+                        poDS->nRecordSize = 3176;
+                        poDS->nRecordDataEnd = 2900;
+                        break;
+                        case 3:
+                        poDS->nRecordSize = 3992;
+                        poDS->nRecordDataEnd = 3718;
+                        break;
+                        case 4:
+                        poDS->nRecordSize = 4816;
+                        poDS->nRecordDataEnd = 4536;
+                        break;
+                        case 5:
+                        poDS->nRecordSize = 5632;
+                        poDS->nRecordDataEnd = 5354;
+                        break;
+                    }
+                }
+                else // UNPACKED8BIT
+                {
+                    switch(poDS->nBands)
+                    {
+                        case 1:
+                        poDS->nRecordSize = 1952;
+                        poDS->nRecordDataEnd = 1673;
+                        break;
+                        case 2:
+                        poDS->nRecordSize = 2360;
+                        poDS->nRecordDataEnd = 2082;
+                        break;
+                        case 3:
+                        poDS->nRecordSize = 2768;
+                        poDS->nRecordDataEnd = 2491;
+                        break;
+                        case 4:
+                        poDS->nRecordSize = 3176;
+                        poDS->nRecordDataEnd = 2900;
+                        break;
+                        case 5:
+                        poDS->nRecordSize = 3584;
+                        poDS->nRecordDataEnd = 3309;
+                        break;
+                    }
+                }
+                poDS->nDataStartOffset = poDS->nRecordSize + 512;
+                poDS->nRecordDataStart = 1264;
+                poDS->iGCPCodeOffset = 0; // XXX: not exist for NOAA15?
+                poDS->iGCPOffset = 640;
+            }
+            else
+                goto bad;
+        break;
+        default:
+            goto bad;
+    }
+    // Compute number of lines dinamycally, so we can read partially
+    // downloaded files
+    CPLStat(poOpenInfo->pszFilename, &sStat);
+    poDS->nRasterYSize =
+        (sStat.st_size - poDS->nDataStartOffset) / poDS->nRecordSize;
+
+/* -------------------------------------------------------------------- */
+/*      Load some info from header.                                     */
+/* -------------------------------------------------------------------- */
+    poDS->ProcessDatasetHeader();
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    int iBand;
+    
+    for( iBand = 1, i = 0; iBand <= poDS->nBands; iBand++ )
+    {
+        poDS->SetBand( iBand, new L1BRasterBand( poDS, iBand ));
+        
+        // Channels descriptions
+        if ( poDS->iSpacecraftID >= NOAA6 && poDS->iSpacecraftID <= NOAA17 )
+        {
+            if ( !(i & 0x01) && poDS->iChannels & 0x01 )
+            {
+                poDS->GetRasterBand(iBand)->SetDescription( paszChannelsDesc[0] );
+                i |= 0x01;
+                continue;
+            }
+            if ( !(i & 0x02) && poDS->iChannels & 0x02 )
+            {
+                poDS->GetRasterBand(iBand)->SetDescription( paszChannelsDesc[1] );
+                i |= 0x02;
+                continue;
+            }
+            if ( !(i & 0x04) && poDS->iChannels & 0x04 )
+            {
+                if (poDS->iSpacecraftID >= NOAA15 && poDS->iSpacecraftID <= NOAA17)
+                    if (poDS->iInstrumentStatus & 0x0400)
+                        poDS->GetRasterBand(iBand)->SetDescription( paszChannelsDesc[7] );
+                    else
+                        poDS->GetRasterBand(iBand)->SetDescription( paszChannelsDesc[6] );
+                else    
+                    poDS->GetRasterBand(iBand)->SetDescription( paszChannelsDesc[2] );
+                i |= 0x04;
+                continue;
+            }
+            if ( !(i & 0x08) && poDS->iChannels & 0x08 )
+            {
+                poDS->GetRasterBand(iBand)->SetDescription( paszChannelsDesc[3] );
+                i |= 0x08;
+                continue;
+            }
+            if ( !(i & 0x10) && poDS->iChannels & 0x10 )
+            {
+                if (poDS->iSpacecraftID == NOAA13)              // 5 NOAA-13
+                    poDS->GetRasterBand(iBand)->SetDescription( paszChannelsDesc[5] );
+                else if (poDS->iSpacecraftID == NOAA6 ||
+                         poDS->iSpacecraftID == NOAA8 ||
+                         poDS->iSpacecraftID == NOAA10)         // 4 NOAA-6,-8,-10
+                    poDS->GetRasterBand(iBand)->SetDescription( paszChannelsDesc[3] );
+                else
+                    poDS->GetRasterBand(iBand)->SetDescription( paszChannelsDesc[4] );
+                i |= 0x10;
+                continue;
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Do we have GCPs?                                                */
+/* -------------------------------------------------------------------- */
+    if ( EQUALN((const char *)poDS->pabyTBMHeader + 96, "Y", 1) )
+    {
+        poDS->ProcessRecordHeaders();
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get and set other important information as metadata             */
+/* -------------------------------------------------------------------- */
+    char *pszText;
+    switch( poDS->iSpacecraftID )
+    {
+        case TIROSN:
+            pszText = "TIROS-N";
+        break;
+        case NOAA6:
+            pszText = "NOAA-6(A)";
+        break;
+        case NOAAB:
+            pszText = "NOAA-B";
+        break;
+        case NOAA7:
+            pszText = "NOAA-7(C)";
+        break;
+        case NOAA8:
+            pszText = "NOAA-8(E)";
+        break;
+        case NOAA9:
+            pszText = "NOAA-9(F)";
+        break;
+        case NOAA10:
+            pszText = "NOAA-10(G)";
+        break;
+        case NOAA11:
+            pszText = "NOAA-11(H)";
+        break;
+        case NOAA12:
+            pszText = "NOAA-12(D)";
+        break;
+        case NOAA13:
+            pszText = "NOAA-13(I)";
+        break;
+        case NOAA14:
+            pszText = "NOAA-14(J)";
+        break;
+        case NOAA15:
+            pszText = "NOAA-15(K)";
+        break;
+        case NOAA16:
+            pszText = "NOAA-16(L)";
+        break;
+        case NOAA17:
+            pszText = "NOAA-17(M)";
+        break;
+        default:
+            pszText = "Unknown";
+    }
+    poDS->SetMetadataItem( "SATELLITE",  pszText );
+    switch( poDS->iDataType )
+    {
+        case LAC:
+            pszText = "AVHRR LAC";
+        break;
+        case HRPT:
+            pszText = "AVHRR HRPT";
+        break;
+        case GAC:
+            pszText = "AVHRR GAC";
+        break;
+        default:
+            pszText = "Unknown";
+    }
+    poDS->SetMetadataItem( "DATA_TYPE",  pszText );
+    poDS->SetMetadataItem( "REVOLUTION",  poDS->pszRevolution );
+    switch( poDS->iSource )
+    {
+        case DU:
+            pszText = "Dundee, Scotland, UK";
+        break;
+        case GC:
+            pszText = "Fairbanks, Alaska, USA (formerly Gilmore Creek)";
+        break;
+        case HO:
+            pszText = "Honolulu, Hawaii, USA";
+        break;
+        case MO:
+            pszText = "Monterey, California, USA";
+        break;
+        case WE:
+            pszText = "Western Europe CDA, Lannion, France";
+        break;
+        case SO:
+            pszText = "SOCC (Satellite Operations Control Center), Suitland, Maryland, USA";
+        break;
+        case WI:
+            pszText = "Wallops Island, Virginia, USA";
+        break;
+        default:
+            pszText = "Unknown receiving station";
+    }
+    poDS->SetMetadataItem( "SOURCE",  pszText );
+    switch( poDS->iProcCenter )
+    {
+        case CMS:
+            pszText = "Centre de Meteorologie Spatiale - Lannion, France";
+        break;
+        case DSS:
+            pszText = "Dundee Satellite Receiving Station - Dundee, Scotland, UK";
+        break;
+        case NSS:
+            pszText = "NOAA/NESDIS - Suitland, Maryland, USA";
+        break;
+        case UKM:
+            pszText = "United Kingdom Meteorological Office - Bracknell, England, UK";
+        break;
+        default:
+            pszText = "Unknown processing center";
+    }
+    poDS->SetMetadataItem( "PROCESSING_CENTER",  pszText );
+    // Time of first scanline
+    poDS->SetMetadataItem( "START",  poDS->sStartTime.PrintTime() );
+    // Time of last scanline
+    poDS->SetMetadataItem( "STOP",  poDS->sStopTime.PrintTime() );
+    // AVHRR Earth location indication
+    switch(poDS->iLocationIndicator)
+    {
+        case ASCEND:
+        poDS->SetMetadataItem( "LOCATION", "Ascending" );
+        break;
+        case DESCEND:
+        default:
+        poDS->SetMetadataItem( "LOCATION", "Descending" );
+        break;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+bad:
+    delete poDS;
+    return NULL;
+}
+
+/************************************************************************/
+/*                        GDALRegister_L1B()                            */
+/************************************************************************/
+
+void GDALRegister_L1B()
+
+{
+    GDALDriver  *poDriver;
+
+    if( GDALGetDriverByName( "L1B" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "L1B" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "NOAA Polar Orbiter Level 1b Data Set" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_l1b.html" );
+
+        poDriver->pfnOpen = L1BDataset::Open;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/l1b/makefile.vc b/Utilities/GDAL/frmts/l1b/makefile.vc
new file mode 100644
index 0000000000..773e828bad
--- /dev/null
+++ b/Utilities/GDAL/frmts/l1b/makefile.vc
@@ -0,0 +1,13 @@
+
+OBJ	=	l1bdataset.obj
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/mem/GNUmakefile b/Utilities/GDAL/frmts/mem/GNUmakefile
new file mode 100644
index 0000000000..370f04f741
--- /dev/null
+++ b/Utilities/GDAL/frmts/mem/GNUmakefile
@@ -0,0 +1,17 @@
+
+
+include ../../GDALmake.opt
+
+OBJ	=	memdataset.o
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+install-obj:	$(O_OBJ)
+
+install:
+	$(INSTALL_DATA) memdataset.h $(INST_INCLUDE)
diff --git a/Utilities/GDAL/frmts/mem/frmt_mem.html b/Utilities/GDAL/frmts/mem/frmt_mem.html
new file mode 100644
index 0000000000..9dc8086f78
--- /dev/null
+++ b/Utilities/GDAL/frmts/mem/frmt_mem.html
@@ -0,0 +1,56 @@
+<html>
+<head>
+<title>MEM -- In Memory Raster</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>MEM -- In Memory Raster</h1>
+
+GDAL supports the ability to hold rasters in a temporary in-memory format.
+This is primarily useful for temporary datasets in scripts or internal
+to applications.  It is not generally of any use to application end-users.<p>
+
+Memory datasets should support support for most kinds of auxilary
+information including metadata, coordinate systems, georeferencing, GCPs, 
+color interpretation, nodata, color tables and all pixel data types.<p>
+
+<h2>Dataset Name Format</h2>
+
+It is possible to open an existing array in memory.  To do so, construct
+a dataset name with the following format:
+
+<pre>
+  MEM:::option=value[,option=value...]
+</pre>
+
+For example:
+
+<pre>
+  MEM:::DATAPOINTER=342343408,PIXELS=100,LINES=100,BANDS=3,DATATYPE=Byte,
+       PIXELOFFSET=3,LINEOFFSET=300,BANDOFFSET=1
+</pre>
+
+<ul>
+<li> DATAPOINTER: pointer to the first pixel of the first band represented as 
+a long integer.  NOTE!  This may not work on platforms where a long is 32 bits and a pointer is 64 bits. (required)
+<li> PIXELS: Width of raster in pixels. (required)
+<li> LINES: Height of raster in lines. (required)
+<li> BANDS: Number of bands, defaults to 1. (optional)
+<li> DATATYPE: Name of the data type, as returned by GDALGetDataTypeName() (eg. Byte, Int16)  Defaults to Byte.  (optional)
+<li> PIXELOFFSET: Offset in bytes between the start of one pixel and the next on the same scanline. (optional)
+<li> LINEOFFSET: Offset in bytes between the start of one scanline and the next. (optional)
+<li> BANDOFFSET: Offset in bytes between the start of one bands data and the next.
+</ul>
+
+<h2>Creation Options</h2>
+
+There are no supported creation options.<p>
+
+The MEM format is one of the few that supports the AddBand() method.
+The AddBand() method supports DATAPOINTER, PIXELOFFSET and LINEOFFSET 
+options to reference an existing memory array.<p>
+
+</body>
+</html>
+
diff --git a/Utilities/GDAL/frmts/mem/makefile.vc b/Utilities/GDAL/frmts/mem/makefile.vc
new file mode 100644
index 0000000000..f0a1dcf00c
--- /dev/null
+++ b/Utilities/GDAL/frmts/mem/makefile.vc
@@ -0,0 +1,13 @@
+
+OBJ	=	memdataset.obj
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/mem/memdataset.cpp b/Utilities/GDAL/frmts/mem/memdataset.cpp
new file mode 100644
index 0000000000..b3c377e382
--- /dev/null
+++ b/Utilities/GDAL/frmts/mem/memdataset.cpp
@@ -0,0 +1,814 @@
+/******************************************************************************
+ * $Id: memdataset.cpp,v 1.22 2006/04/04 04:42:39 fwarmerdam Exp $
+ *
+ * Project:  Memory Array Translator
+ * Purpose:  Complete implementation.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: memdataset.cpp,v $
+ * Revision 1.22  2006/04/04 04:42:39  fwarmerdam
+ * update contact info
+ *
+ * Revision 1.21  2005/03/08 16:35:05  fwarmerdam
+ * Fixed memory leak.
+ *
+ * Revision 1.20  2005/03/08 16:31:08  fwarmerdam
+ * Added handling of DATATYPE as a name, not just a number.
+ *
+ * Revision 1.19  2004/11/21 22:13:29  fwarmerdam
+ * use new pointer encode/decode functions
+ *
+ * Revision 1.18  2004/08/30 18:50:27  warmerda
+ * Fixed so that SetProjection() succeeds.
+ *
+ * Revision 1.17  2004/04/15 18:54:10  warmerda
+ * added UnitType, Offset, Scale and CategoryNames support
+ *
+ * Revision 1.16  2003/04/16 14:37:54  warmerda
+ * Comment out debug messages ... too noisy.
+ *
+ * Revision 1.15  2003/02/03 17:57:30  warmerda
+ * Fix for last fix.
+ *
+ * Revision 1.14  2003/02/03 16:28:35  warmerda
+ * fixed fatal bug with nWordSize for unpacked arrays in read/write block
+ *
+ * Revision 1.13  2002/12/21 21:13:04  warmerda
+ * fixed memory leak of colortable
+ *
+ * Revision 1.12  2002/11/23 18:54:17  warmerda
+ * added CREATIONDATATYPES metadata for drivers
+ *
+ * Revision 1.11  2002/11/20 05:18:09  warmerda
+ * added AddBand() implementation
+ *
+ * Revision 1.10  2002/09/04 06:50:37  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.9  2002/06/12 21:12:25  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.8  2002/06/10 21:31:57  warmerda
+ * preserve projection and geotransform
+ *
+ * Revision 1.7  2002/05/29 16:01:54  warmerda
+ * fixed SetColorInterpretation
+ *
+ * Revision 1.6  2002/04/12 17:37:31  warmerda
+ * added colortable support
+ *
+ * Revision 1.5  2002/03/01 16:45:53  warmerda
+ * added support for retaining nodata value
+ *
+ * Revision 1.4  2001/10/26 20:03:28  warmerda
+ * added C entry point for creating MEMRasterBand
+ *
+ * Revision 1.3  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.2  2000/07/19 19:07:04  warmerda
+ * break linkage between MEMDataset and MEMRasterBand
+ *
+ * Revision 1.1  2000/07/19 15:55:11  warmerda
+ * New
+ *
+ */
+
+#include "memdataset.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: memdataset.cpp,v 1.22 2006/04/04 04:42:39 fwarmerdam Exp $");
+
+/************************************************************************/
+/*                        MEMCreateRasterBand()                         */
+/************************************************************************/
+
+GDALRasterBandH MEMCreateRasterBand( GDALDataset *poDS, int nBand, 
+                                    GByte *pabyData, GDALDataType eType, 
+                                    int nPixelOffset, int nLineOffset, 
+                                    int bAssumeOwnership )
+
+{
+    return (GDALRasterBandH) 
+        new MEMRasterBand( poDS, nBand, pabyData, eType, nPixelOffset, 
+                           nLineOffset, bAssumeOwnership );
+}
+
+/************************************************************************/
+/*                           MEMRasterBand()                            */
+/************************************************************************/
+
+MEMRasterBand::MEMRasterBand( GDALDataset *poDS, int nBand,
+                              GByte *pabyDataIn, GDALDataType eTypeIn, 
+                              int nPixelOffsetIn, int nLineOffsetIn,
+                              int bAssumeOwnership )
+
+{
+    //CPLDebug( "MEM", "MEMRasterBand(%p)", this );
+
+    this->poDS = poDS;
+    this->nBand = nBand;
+
+    this->eAccess = poDS->GetAccess();
+
+    eDataType = eTypeIn;
+
+    nBlockXSize = poDS->GetRasterXSize();
+    nBlockYSize = 1;
+
+    if( nPixelOffsetIn == 0 )
+        nPixelOffsetIn = GDALGetDataTypeSize(eTypeIn) / 8;
+
+    if( nLineOffsetIn == 0 )
+        nLineOffsetIn = nPixelOffsetIn * nBlockXSize;
+
+    nPixelOffset = nPixelOffsetIn;
+    nLineOffset = nLineOffsetIn;
+    bOwnData = bAssumeOwnership;
+
+    pabyData = pabyDataIn;
+
+    bNoDataSet  = FALSE;
+
+    poColorTable = NULL;
+    
+    eColorInterp = GCI_Undefined;
+
+    papszCategoryNames = NULL;
+    dfOffset = 0.0;
+    dfScale = 1.0;
+    pszUnitType = NULL;
+}
+
+/************************************************************************/
+/*                           ~MEMRasterBand()                           */
+/************************************************************************/
+
+MEMRasterBand::~MEMRasterBand()
+
+{
+    //CPLDebug( "MEM", "~MEMRasterBand(%p)", this );
+    if( bOwnData )
+    {
+        //CPLDebug( "MEM", "~MEMRasterBand() - free raw data." );
+        VSIFree( pabyData );
+    }
+
+    if( poColorTable != NULL )
+        delete poColorTable;
+
+    CPLFree( pszUnitType );
+    CSLDestroy( papszCategoryNames );
+}
+
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr MEMRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                   void * pImage )
+
+{
+    int     nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
+    CPLAssert( nBlockXOff == 0 );
+
+    if( nPixelOffset == nWordSize )
+    {
+        memcpy( pImage, 
+                pabyData+nLineOffset*nBlockYOff, 
+                nPixelOffset * nBlockXSize );
+    }
+    else
+    {
+        GByte *pabyCur = pabyData + nLineOffset*nBlockYOff;
+
+        for( int iPixel = 0; iPixel < nBlockXSize; iPixel++ )
+        {
+            memcpy( ((GByte *) pImage) + iPixel*nWordSize, 
+                    pabyCur + iPixel*nPixelOffset, 
+                    nWordSize );
+        }
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                            IWriteBlock()                             */
+/************************************************************************/
+
+CPLErr MEMRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
+                                     void * pImage )
+
+{
+    int     nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
+    CPLAssert( nBlockXOff == 0 );
+
+    if( nPixelOffset == nWordSize )
+    {
+        memcpy( pabyData+nLineOffset*nBlockYOff, 
+                pImage, 
+                nPixelOffset * nBlockXSize );
+    }
+    else
+    {
+        GByte *pabyCur = pabyData + nLineOffset*nBlockYOff;
+
+        for( int iPixel = 0; iPixel < nBlockXSize; iPixel++ )
+        {
+            memcpy( pabyCur + iPixel*nPixelOffset, 
+                    ((GByte *) pImage) + iPixel*nWordSize, 
+                    nWordSize );
+        }
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                            GetNoDataValue()                          */
+/************************************************************************/
+double MEMRasterBand::GetNoDataValue( int *pbSuccess )
+
+{
+    if( pbSuccess )
+        *pbSuccess = bNoDataSet;
+
+    if( bNoDataSet )
+        return dfNoData;
+    else
+        return 0.0;
+}
+
+/************************************************************************/
+/*                            SetNoDataValue()                          */
+/************************************************************************/
+CPLErr MEMRasterBand::SetNoDataValue( double dfNewValue )
+{
+    dfNoData = dfNewValue;
+    bNoDataSet = TRUE;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp MEMRasterBand::GetColorInterpretation()
+
+{
+    if( poColorTable != NULL )
+        return GCI_PaletteIndex;
+    else
+        return eColorInterp;
+}
+
+/************************************************************************/
+/*                       SetColorInterpretation()                       */
+/************************************************************************/
+
+CPLErr MEMRasterBand::SetColorInterpretation( GDALColorInterp eGCI )
+
+{
+    eColorInterp = eGCI;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *MEMRasterBand::GetColorTable()
+
+{
+    return poColorTable;
+}
+
+/************************************************************************/
+/*                           SetColorTable()                            */
+/************************************************************************/
+
+CPLErr MEMRasterBand::SetColorTable( GDALColorTable *poCT )
+
+{
+    if( poColorTable != NULL )
+        delete poColorTable;
+
+    if( poCT == NULL )
+        poColorTable = NULL;
+    else
+        poColorTable = poCT->Clone();
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                            GetUnitType()                             */
+/************************************************************************/
+
+const char *MEMRasterBand::GetUnitType()
+
+{
+    if( pszUnitType == NULL )
+        return "";
+    else
+        return pszUnitType;
+}
+
+/************************************************************************/
+/*                            SetUnitType()                             */
+/************************************************************************/
+
+CPLErr MEMRasterBand::SetUnitType( const char *pszNewValue )
+
+{
+    CPLFree( pszUnitType );
+    
+    if( pszNewValue == NULL )
+        pszUnitType = NULL;
+    else
+        pszUnitType = CPLStrdup(pszNewValue);
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                             GetOffset()                              */
+/************************************************************************/
+
+double MEMRasterBand::GetOffset( int *pbSuccess )
+
+{
+    if( pbSuccess != NULL )
+        *pbSuccess = TRUE;
+
+    return dfOffset;
+}
+
+/************************************************************************/
+/*                             SetOffset()                              */
+/************************************************************************/
+
+CPLErr MEMRasterBand::SetOffset( double dfNewOffset )
+
+{
+    dfOffset = dfNewOffset;
+    return CE_None;
+}
+
+/************************************************************************/
+/*                              GetScale()                              */
+/************************************************************************/
+
+double MEMRasterBand::GetScale( int *pbSuccess )
+
+{
+    if( pbSuccess != NULL )
+        *pbSuccess = TRUE;
+
+    return dfScale;
+}
+
+/************************************************************************/
+/*                              SetScale()                              */
+/************************************************************************/
+
+CPLErr MEMRasterBand::SetScale( double dfNewScale )
+
+{
+    dfScale = dfNewScale;
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetCategoryNames()                          */
+/************************************************************************/
+
+char **MEMRasterBand::GetCategoryNames()
+
+{
+    return papszCategoryNames;
+}
+
+/************************************************************************/
+/*                          SetCategoryNames()                          */
+/************************************************************************/
+
+CPLErr MEMRasterBand::SetCategoryNames( char ** papszNewNames )
+
+{
+    CSLDestroy( papszCategoryNames );
+    papszCategoryNames = CSLDuplicate( papszNewNames );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*      MEMDataset                                                     */
+/* ==================================================================== */
+/************************************************************************/
+
+
+/************************************************************************/
+/*                            MEMDataset()                             */
+/************************************************************************/
+
+MEMDataset::MEMDataset()
+
+{
+    pszProjection = NULL;
+    bGeoTransformSet = FALSE;
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = -1.0;
+}
+
+/************************************************************************/
+/*                            ~MEMDataset()                            */
+/************************************************************************/
+
+MEMDataset::~MEMDataset()
+
+{
+    FlushCache();
+    CPLFree( pszProjection );
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *MEMDataset::GetProjectionRef()
+
+{
+    if( pszProjection == NULL )
+        return "";
+    else
+        return pszProjection;
+}
+
+/************************************************************************/
+/*                           SetProjection()                            */
+/************************************************************************/
+
+CPLErr MEMDataset::SetProjection( const char *pszProjectionIn )
+
+{
+    CPLFree( pszProjection );
+    pszProjection = CPLStrdup( pszProjectionIn );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr MEMDataset::GetGeoTransform( double *padfGeoTransform )
+
+{
+    memcpy( padfGeoTransform, adfGeoTransform, sizeof(double) * 6 );
+    if( bGeoTransformSet )
+        return CE_None;
+    else
+        return CE_Failure;
+}
+
+/************************************************************************/
+/*                          SetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr MEMDataset::SetGeoTransform( double *padfGeoTransform )
+
+{
+    memcpy( adfGeoTransform, padfGeoTransform, sizeof(double) * 6 );
+    bGeoTransformSet = TRUE;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                              AddBand()                               */
+/*                                                                      */
+/*      Add a new band to the dataset, allowing creation options to     */
+/*      specify the existing memory to use, otherwise create new        */
+/*      memory.                                                         */
+/************************************************************************/
+
+CPLErr MEMDataset::AddBand( GDALDataType eType, char **papszOptions )
+
+{
+    int nBandId = GetRasterCount() + 1;
+    GByte *pData;
+    int   nPixelSize = (GDALGetDataTypeSize(eType) / 8);
+
+/* -------------------------------------------------------------------- */
+/*      Do we need to allocate the memory ourselves?  This is the       */
+/*      simple case.                                                    */
+/* -------------------------------------------------------------------- */
+    if( CSLFetchNameValue( papszOptions, "DATAPOINTER" ) == NULL )
+    {
+
+        pData = (GByte *) 
+            CPLCalloc(nPixelSize, GetRasterXSize() * GetRasterYSize() );
+
+        if( pData == NULL )
+        {
+            CPLError( CE_Failure, CPLE_OutOfMemory,
+                      "Unable to create band arrays ... out of memory." );
+            return CE_Failure;
+        }
+
+        SetBand( nBandId,
+                 new MEMRasterBand( this, nBandId, pData, eType, nPixelSize, 
+                                    nPixelSize * GetRasterXSize(), TRUE ) );
+
+        return CE_None;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get layout of memory and other flags.                           */
+/* -------------------------------------------------------------------- */
+    const char *pszOption;
+    int nPixelOffset, nLineOffset;
+    const char *pszDataPointer;
+
+    pszDataPointer = CSLFetchNameValue(papszOptions,"DATAPOINTER");
+    pData = (GByte *) CPLScanPointer(pszDataPointer,
+                                     strlen(pszDataPointer));
+    
+    pszOption = CSLFetchNameValue(papszOptions,"PIXELOFFSET");
+    if( pszOption == NULL )
+        nPixelOffset = nPixelSize;
+    else
+        nPixelOffset = atoi(pszOption);
+
+    pszOption = CSLFetchNameValue(papszOptions,"LINEOFFSET");
+    if( pszOption == NULL )
+        nLineOffset = GetRasterXSize() * nPixelOffset;
+    else
+        nLineOffset = atoi(pszOption);
+
+    SetBand( nBandId,
+             new MEMRasterBand( this, nBandId, pData, eType, 
+                                nPixelOffset, nLineOffset, FALSE ) );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *MEMDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    char    **papszOptions;
+
+/* -------------------------------------------------------------------- */
+/*      Do we have the special filename signature for MEM format        */
+/*      description strings?                                            */
+/* -------------------------------------------------------------------- */
+    if( !EQUALN(poOpenInfo->pszFilename,"MEM:::",6) 
+        || poOpenInfo->fp != NULL )
+        return NULL;
+
+    papszOptions = CSLTokenizeStringComplex(poOpenInfo->pszFilename+6, ",",
+                                            TRUE, FALSE );
+
+/* -------------------------------------------------------------------- */
+/*      Verify we have all required fields                              */
+/* -------------------------------------------------------------------- */
+    if( CSLFetchNameValue( papszOptions, "PIXELS" ) == NULL
+        || CSLFetchNameValue( papszOptions, "LINES" ) == NULL
+        || CSLFetchNameValue( papszOptions, "DATAPOINTER" ) == NULL )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+              "Missing required field (one of PIXELS, LINES or DATAPOINTER)\n"
+              "Unable to access in-memory array." );
+
+        CSLDestroy( papszOptions );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create the new GTiffDataset object.                             */
+/* -------------------------------------------------------------------- */
+    MEMDataset *poDS;
+
+    poDS = new MEMDataset();
+
+    poDS->nRasterXSize = atoi(CSLFetchNameValue(papszOptions,"PIXELS"));
+    poDS->nRasterYSize = atoi(CSLFetchNameValue(papszOptions,"LINES"));
+
+    poDS->eAccess = GA_Update;
+
+/* -------------------------------------------------------------------- */
+/*      Extract other information.                                      */
+/* -------------------------------------------------------------------- */
+    const char *pszOption;
+    GDALDataType eType;
+    int nBands, nPixelOffset, nLineOffset, nBandOffset;
+    const char *pszDataPointer;
+    GByte *pabyData;
+
+    pszOption = CSLFetchNameValue(papszOptions,"BANDS");
+    if( pszOption == NULL )
+        nBands = 1;
+    else
+        nBands = atoi(pszOption);
+
+    pszOption = CSLFetchNameValue(papszOptions,"DATATYPE");
+    if( pszOption == NULL )
+        eType = GDT_Byte;
+    else
+    {
+        if( atoi(pszOption) > 0 && atoi(pszOption) < GDT_TypeCount )
+            eType = (GDALDataType) atoi(pszOption);
+        else
+        {
+            int iType;
+            
+            eType = GDT_Unknown;
+            for( iType = 0; iType < GDT_TypeCount; iType++ )
+            {
+                if( EQUAL(GDALGetDataTypeName((GDALDataType) iType),
+                          pszOption) )
+                {
+                    eType = (GDALDataType) iType;
+                    break;
+                }
+            }
+            
+            if( eType == GDT_Unknown )
+            {
+                CPLError( CE_Failure, CPLE_AppDefined,
+                          "DATATYPE=%s not recognised.", 
+                          pszOption );
+                delete poDS;
+                return NULL;
+            }
+        }
+    }
+
+    pszOption = CSLFetchNameValue(papszOptions,"PIXELOFFSET");
+    if( pszOption == NULL )
+        nPixelOffset = GDALGetDataTypeSize(eType) / 8;
+    else
+        nPixelOffset = atoi(pszOption);
+
+    pszOption = CSLFetchNameValue(papszOptions,"LINEOFFSET");
+    if( pszOption == NULL )
+        nLineOffset = poDS->nRasterXSize * nPixelOffset;
+    else
+        nLineOffset = atoi(pszOption);
+
+    pszOption = CSLFetchNameValue(papszOptions,"BANDOFFSET");
+    if( pszOption == NULL )
+        nBandOffset = nLineOffset * poDS->nRasterYSize;
+    else
+        nBandOffset = atoi(pszOption);
+
+    pszDataPointer = CSLFetchNameValue(papszOptions,"DATAPOINTER");
+    pabyData = (GByte *) CPLScanPointer( pszDataPointer, 
+                                         strlen(pszDataPointer) );
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    for( int iBand = 0; iBand < nBands; iBand++ )
+    {
+        poDS->SetBand( iBand+1, 
+                       new MEMRasterBand( poDS, iBand+1, 
+                                          pabyData + iBand * nBandOffset,
+                                          eType, nPixelOffset, nLineOffset, 
+                                          FALSE ) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try to return a regular handle on the file.                     */
+/* -------------------------------------------------------------------- */
+    CSLDestroy( papszOptions );
+    return poDS;
+}
+
+/************************************************************************/
+/*                               Create()                               */
+/************************************************************************/
+
+GDALDataset *MEMDataset::Create( const char * pszFilename,
+                                 int nXSize, int nYSize, int nBands,
+                                 GDALDataType eType,
+                                 char ** /* notdef: papszParmList */ )
+
+{
+/* -------------------------------------------------------------------- */
+/*      First allocate band data, verifying that we can get enough      */
+/*      memory.                                                         */
+/* -------------------------------------------------------------------- */
+    GByte  	**papBandData;
+    int   	iBand;
+    int         nWordSize = GDALGetDataTypeSize(eType) / 8;
+
+    papBandData = (GByte **) CPLCalloc(sizeof(void *),nBands);
+    for( iBand = 0; iBand < nBands; iBand++ )
+    {
+        papBandData[iBand] = (GByte *) VSICalloc( nWordSize, nXSize * nYSize );
+        if( papBandData[iBand] == NULL )
+        {
+            for( iBand = 0; iBand < nBands; iBand++ )
+            {
+                if( papBandData[iBand] )
+                    VSIFree( papBandData[iBand] );
+            }
+
+            CPLError( CE_Failure, CPLE_OutOfMemory,
+                      "Unable to create band arrays ... out of memory." );
+            return NULL;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create the new GTiffDataset object.                             */
+/* -------------------------------------------------------------------- */
+    MEMDataset *poDS;
+
+    poDS = new MEMDataset();
+
+    poDS->nRasterXSize = nXSize;
+    poDS->nRasterYSize = nYSize;
+    poDS->eAccess = GA_Update;
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    for( iBand = 0; iBand < nBands; iBand++ )
+    {
+        poDS->SetBand( iBand+1, 
+                       new MEMRasterBand( poDS, iBand+1, papBandData[iBand],
+                                          eType, 0, 0, TRUE ) );
+    }
+
+    CPLFree( papBandData );
+
+/* -------------------------------------------------------------------- */
+/*      Try to return a regular handle on the file.                     */
+/* -------------------------------------------------------------------- */
+    return poDS;
+}
+
+/************************************************************************/
+/*                          GDALRegister_MEM()                          */
+/************************************************************************/
+
+void GDALRegister_MEM()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "MEM" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "MEM" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "In Memory Raster" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte Int16 UInt16 Int32 UInt32 Float32 Float64 CInt16 CInt32 CFloat32 CFloat64" );
+
+        poDriver->pfnOpen = MEMDataset::Open;
+        poDriver->pfnCreate = MEMDataset::Create;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/mem/memdataset.h b/Utilities/GDAL/frmts/mem/memdataset.h
new file mode 100644
index 0000000000..fbb27d9023
--- /dev/null
+++ b/Utilities/GDAL/frmts/mem/memdataset.h
@@ -0,0 +1,176 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  Memory Array Translator
+ * Purpose:  Declaration of MEMDataset, and MEMRasterBand.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: memdataset.h,v $
+ * Revision 1.13  2006/04/04 04:42:39  fwarmerdam
+ * update contact info
+ *
+ * Revision 1.12  2005/05/19 20:39:54  dron
+ * Derive the MEMRasterBand from GDALPamRasterBand.
+ *
+ * Revision 1.11  2005/04/19 19:56:11  fwarmerdam
+ * Explicitly mark destructor as virtual.
+ *
+ * Revision 1.10  2004/04/15 18:54:10  warmerda
+ * added UnitType, Offset, Scale and CategoryNames support
+ *
+ * Revision 1.9  2002/11/20 05:18:09  warmerda
+ * added AddBand() implementation
+ *
+ * Revision 1.8  2002/06/10 21:31:57  warmerda
+ * preserve projection and geotransform
+ *
+ * Revision 1.7  2002/05/29 16:01:54  warmerda
+ * fixed SetColorInterpretation
+ *
+ * Revision 1.6  2002/04/12 17:37:31  warmerda
+ * added colortable support
+ *
+ * Revision 1.5  2002/03/01 16:45:53  warmerda
+ * added support for retaining nodata value
+ *
+ * Revision 1.4  2001/10/26 20:03:28  warmerda
+ * added C entry point for creating MEMRasterBand
+ *
+ * Revision 1.3  2000/07/20 13:38:48  warmerda
+ * make classes public with CPL_DLL
+ *
+ * Revision 1.2  2000/07/19 19:07:04  warmerda
+ * break linkage between MEMDataset and MEMRasterBand
+ *
+ * Revision 1.1  2000/07/19 15:55:11  warmerda
+ * New
+ *
+ */
+
+#ifndef MEMDATASET_H_INCLUDED
+#define MEMDATASET_H_INCLUDED
+
+#include "gdal_pam.h"
+#include "gdal_priv.h"
+
+CPL_C_START
+void	GDALRegister_MEM(void);
+GDALRasterBandH CPL_DLL MEMCreateRasterBand( GDALDataset *, int, GByte *,
+                                             GDALDataType, int, int, int );
+CPL_C_END
+
+/************************************************************************/
+/*				MEMDataset				*/
+/************************************************************************/
+
+class MEMRasterBand;
+
+class CPL_DLL MEMDataset : public GDALDataset
+{
+    int         bGeoTransformSet;
+    double	adfGeoTransform[6];
+
+    char        *pszProjection;
+
+  public:
+                 MEMDataset();
+    virtual      ~MEMDataset();
+
+    virtual const char *GetProjectionRef(void);
+    virtual CPLErr SetProjection( const char * );
+
+    virtual CPLErr GetGeoTransform( double * );
+    virtual CPLErr SetGeoTransform( double * );
+
+    virtual CPLErr        AddBand( GDALDataType eType, 
+                                   char **papszOptions=NULL );
+
+    static GDALDataset *Open( GDALOpenInfo * );
+    static GDALDataset *Create( const char * pszFilename,
+                                int nXSize, int nYSize, int nBands,
+                                GDALDataType eType, char ** papszParmList );
+};
+
+/************************************************************************/
+/*                            MEMRasterBand                             */
+/************************************************************************/
+
+class CPL_DLL MEMRasterBand : public GDALPamRasterBand
+{
+  protected:
+
+    GByte      *pabyData;
+    int         nPixelOffset;
+    int         nLineOffset;
+    int         bOwnData;
+
+    int         bNoDataSet;
+    double      dfNoData;
+
+    GDALColorTable *poColorTable;
+    GDALColorInterp eColorInterp;
+
+    char           *pszUnitType;
+    char           **papszCategoryNames;
+    
+    double         dfOffset;
+    double         dfScale;
+
+  public:
+
+                   MEMRasterBand( GDALDataset *poDS, int nBand,
+                                  GByte *pabyData, GDALDataType eType,
+                                  int nPixelOffset, int nLineOffset,
+                                  int bAssumeOwnership );
+    virtual        ~MEMRasterBand();
+
+    // should override RasterIO eventually.
+
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual CPLErr IWriteBlock( int, int, void * );
+
+    virtual double GetNoDataValue( int *pbSuccess = NULL );
+    virtual CPLErr SetNoDataValue( double );
+
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual GDALColorTable *GetColorTable();
+    virtual CPLErr SetColorTable( GDALColorTable * ); 
+
+    virtual CPLErr SetColorInterpretation( GDALColorInterp );
+
+    virtual const char *GetUnitType();
+    CPLErr SetUnitType( const char * ); 
+
+    virtual char **GetCategoryNames();
+    virtual CPLErr SetCategoryNames( char ** );
+
+    virtual double GetOffset( int *pbSuccess = NULL );
+    CPLErr SetOffset( double );
+    virtual double GetScale( int *pbSuccess = NULL );
+    CPLErr SetScale( double );
+};
+
+#endif /* ndef MEMDATASET_H_INCLUDED */
+
diff --git a/Utilities/GDAL/frmts/mrsid/GNUmakefile b/Utilities/GDAL/frmts/mrsid/GNUmakefile
new file mode 100644
index 0000000000..53753fd417
--- /dev/null
+++ b/Utilities/GDAL/frmts/mrsid/GNUmakefile
@@ -0,0 +1,17 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	mrsiddataset.o
+
+ifeq ($(GEOTIFF_SETTING),internal)
+GEOTIFF_INCLUDE	=	-I../../frmts/gtiff/libgeotiff
+endif
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(MRSID_FLAGS) $(MRSID_INCLUDE) $(GEOTIFF_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/mrsid/frmt_jp2mrsid.html b/Utilities/GDAL/frmts/mrsid/frmt_jp2mrsid.html
new file mode 100644
index 0000000000..c69df16819
--- /dev/null
+++ b/Utilities/GDAL/frmts/mrsid/frmt_jp2mrsid.html
@@ -0,0 +1,80 @@
+<html>
+<head>
+	<title>JP2MrSID --- JPEG2000 via MrSID SDK</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>JP2MrSID --- JPEG2000 via MrSID SDK</h1>
+
+JPEG2000 file format is supported for reading with the MrSID DSDK.  It
+is also supported for writing with the MrSID ESDK.<p>
+
+JPEG2000 MrSID support is only available with the version 5.x or newer
+DSDK and ESDK.<p>
+
+<h2>Creation Options</h2>
+
+If you have the MrSID ESDK (5.x or newer), it can be used to write JPEG2000
+files.  The following creation options are supported.
+
+<ul>
+<li> WORLDFILE: Yes to write an ESRI world file (with the extension .j2w). <p>
+<li> COMPRESSION: Indicates the desired compression ratio.  Zero indicates
+lossless compression.  Twenty would indicate a 20:1 compression ratio (the
+image would be compressed to 1/20 its original size).<p>
+<li> XMLPROFILE: Indicates a path to a LizardTech-specific XML profile that
+can be used to set JPEG2000 encoding parameters.  They can be created using
+the MrSID ESDK, or with GeoExpress, or by hand using the following example as
+a template:<p>
+<pre>
+&lt;?xml version="1.0"?&gt;
+&lt;Jp2Profile version="1.0"&gt;
+  &lt;Header&gt;
+    &lt;name&gt;Default&lt;/name&gt; 
+    &lt;description&gt;LizardTech preferred settings (20051216)&lt;/description&gt;
+  &lt;/Header&gt;
+  &lt;Codestream&gt;
+    &lt;layers&gt;
+      8
+    &lt;/layers&gt;
+    &lt;levels&gt;
+      99
+    &lt;/levels&gt;
+    &lt;tileSize&gt;
+      0 0
+    &lt;/tileSize&gt;
+    &lt;progressionOrder&gt;
+      RPCL
+    &lt;/progressionOrder&gt;
+    &lt;codeblockSize&gt;
+      64 64
+    &lt;/codeblockSize&gt;
+    &lt;pltMarkers&gt;
+      true
+    &lt;/pltMarkers&gt;
+    &lt;wavelet97&gt;
+      false
+    &lt;/wavelet97&gt;
+    &lt;precinctSize&gt;
+      256 256
+    &lt;/precinctSize&gt;
+  &lt;/Codestream&gt;
+&lt;/Jp2Profile&gt;
+
+</pre>
+
+</ul>
+
+<h2>See Also:</h2>
+
+<ul>
+<li> Implemented as <tt>gdal/frmts/mrsid/mrsiddataset.cpp</tt>.<p>
+
+<li> <a href="http://www.lizardtech.com">LizardTech's Web site<a><p>
+
+</ul>
+
+</body>
+</html>
+
diff --git a/Utilities/GDAL/frmts/mrsid/frmt_mrsid.html b/Utilities/GDAL/frmts/mrsid/frmt_mrsid.html
new file mode 100644
index 0000000000..6ee4466c7c
--- /dev/null
+++ b/Utilities/GDAL/frmts/mrsid/frmt_mrsid.html
@@ -0,0 +1,84 @@
+<html>
+<head>
+	<title>MrSID --- Multi-resolution Seamless Image Database</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>MrSID --- Multi-resolution Seamless Image Database</h1>
+
+MrSID is a wavelet-based image compression technology which can utilise both
+lossy and lossless encoding. This technology was acquired in its original
+form from Los Alamos National Laboratories (LANL), where it was developed
+under the aegis of the U.S. government for storing fingerprints for the FBI.
+Now is is developed and distributed by the LizardTech, Inc.<p>
+
+This driver supports reading of MrSID image files using LizardTech's decoding
+software development kit (DSDK). <b>This DSDK is not free software, you should
+contact LizardTech to obtain it (see link at end of this page).</b> If you are
+using GCC, please, ensure that you have the same compiler as was used
+for DSDK compilation. It is C++ library, so you may get incompatibilities
+in C++ name mangling between different GCC versions (2.95.x and 3.x).<p>
+
+Latest versions of the DSDK also support decoding JPEG2000 file format, so this driver can be used for JPEG2000 too.<p>
+
+<h2>Metadata</h2>
+
+MrSID metadata transparently translated into GDAL metadata strings. Files
+in MrSID format contain a set of standard metadata tags such as:
+IMAGE__WIDTH (contains the width of the image), IMAGE__HEIGHT (contains the
+height of the image), IMAGE__XY_ORIGIN (contains the x and y coordinates
+of the origin), IMAGE__INPUT_NAME (contains the name or names of the files
+used to create the MrSID image) etc.  GDAL's metadata keys cannot contain
+characters `:' and `=', but standard MrSID tags always contain double colons
+in tag names. These characters replaced in GDAL with `_' during translation.
+So if you are using other software to work with MrSID be ready that names
+of metadata keys will be shown differently in GDAL.<p>
+
+<h2>Georeference</h2>
+
+MrSID images may contain georeference and coordinate system information in
+form of GeoTIFF GeoKeys, translated in metadata records. All those GeoKeys
+properly extracted and used by the driver. Unfortunately, there is one
+caveat: old MrSID encoders has a bug which resulted in wrong GeoKeys, stored
+in MrSID files. This bug was fixed in MrSID software version 1.5, but if you
+have older encoders or files, created with older encoders, you cannot use
+georeference information from them.<p>
+
+<h2>Creation Options</h2>
+
+MrSID writing is only supported if GDAL is built against a MrSID ESDK
+5.x or newer.  This is normally only licensed by Lizardtech under controlled
+circumstances (whereas the DSDK is free for anyone to use within the 
+constraints of a license agreement).  If you do have the ESDK, it can
+be used to write MrSID files.  The following creation options are supported.
+
+<ul>
+<li> WORLDFILE: Yes to write an ESRI world file (with the extension .sdw). <p>
+<li> VERSION: Can be 2 for MrSID version 2 files, or 3 for MrSID version 3
+files.<p>
+<li> COMPRESSION: Used only for version 2 MrSID files.  Indicates the desired
+compression ratio.  MrSID generation 2 cannot compress an image losslessly; so
+zero does <i>not</i> indicate numerically lossless compression.  A value of
+one may be used for highest fidelity, but a value of twenty will generally
+yield the best results (a "visually lossless" compression).  Twenty would
+indicate a 20:1 compression ratio (the image would be compressed to 1/20 its
+original size).<p>
+<li> FILESIZE: Used only for version 3 MrSID files.  Indicates the desired
+output file size in bytes.  Use "0" for lossless compression.<p>
+<li> TWOPASS: Used only with 3 MrSID files.  Indicates that a two pass 
+optimizer algorithm should be used during compression.<p>
+</ul>
+
+<h2>See Also:</h2>
+
+<ul>
+<li> Implemented as <tt>gdal/frmts/mrsid/mrsiddataset.cpp</tt>.<p>
+
+<li> <a href="http://www.lizardtech.com">LizardTech's Web site<a><p>
+
+</ul>
+
+</body>
+</html>
+
diff --git a/Utilities/GDAL/frmts/mrsid/makefile.vc b/Utilities/GDAL/frmts/mrsid/makefile.vc
new file mode 100644
index 0000000000..6f2c209334
--- /dev/null
+++ b/Utilities/GDAL/frmts/mrsid/makefile.vc
@@ -0,0 +1,24 @@
+
+OBJ	=	mrsiddataset.obj
+
+EXTRAFLAGS = 	$(MRSID_INCLUDE) -I..\gtiff\libgeotiff $(MRSID_FLAGS)
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+	-del *.dll
+
+
+plugin:	gdal_MrSID.dll
+
+gdal_MrSID.dll:	$(OBJ)
+	link /dll /out:gdal_MrSID.dll $(OBJ) \
+		$(GDAL_ROOT)/gdal_i.lib $(MRSID_LIB) $(GEOTIFF_LIB)
+
+
diff --git a/Utilities/GDAL/frmts/mrsid/mrsidcommon.h b/Utilities/GDAL/frmts/mrsid/mrsidcommon.h
new file mode 100644
index 0000000000..5b8ee5e79e
--- /dev/null
+++ b/Utilities/GDAL/frmts/mrsid/mrsidcommon.h
@@ -0,0 +1,1302 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  Multi-resolution Seamless Image Database (MrSID)
+ * Purpose:  Shared code between MrSID 3 and 4 drivers.
+ * Author:   Andrey Kiselev, dron@remotesensing.org
+ *
+ ******************************************************************************
+ * Copyright (c) 2003, Andrey Kiselev <dron@remotesensing.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: mrsidcommon.h,v $
+ * Revision 1.1  2005/02/16 22:07:29  fwarmerdam
+ * New
+ *
+ */
+
+/************************************************************************/
+/*                    EPSGProjMethodToCTProjMethod()                    */
+/*                                                                      */
+/*      Convert between the EPSG enumeration for projection methods,    */
+/*      and the GeoTIFF CT codes.                                       */
+/*      Explicitly copied from geo_normalize.c of the GeoTIFF package   */
+/************************************************************************/
+
+static int EPSGProjMethodToCTProjMethod( int nEPSG )
+
+{
+    /* see trf_method.csv for list of EPSG codes */
+    
+    switch( nEPSG )
+    {
+      case 9801:
+        return( CT_LambertConfConic_1SP );
+
+      case 9802:
+        return( CT_LambertConfConic_2SP );
+
+      case 9803:
+        return( CT_LambertConfConic_2SP ); /* Belgian variant not supported */
+
+      case 9804:
+        return( CT_Mercator );  /* 1SP and 2SP not differentiated */
+
+      case 9805:
+        return( CT_Mercator );  /* 1SP and 2SP not differentiated */
+
+      case 9806:
+        return( CT_CassiniSoldner );
+
+      case 9807:
+        return( CT_TransverseMercator );
+
+      case 9808:
+        return( CT_TransvMercator_SouthOriented );
+
+      case 9809:
+        return( CT_ObliqueStereographic );
+
+      case 9810:
+        return( CT_PolarStereographic );
+
+      case 9811:
+        return( CT_NewZealandMapGrid );
+
+      case 9812:
+        return( CT_ObliqueMercator ); /* is hotine actually different? */
+
+      case 9813:
+        return( CT_ObliqueMercator_Laborde );
+
+      case 9814:
+        return( CT_ObliqueMercator_Rosenmund ); /* swiss  */
+
+      case 9815:
+        return( CT_ObliqueMercator );
+
+      case 9816: /* tunesia mining grid has no counterpart */
+        return( KvUserDefined );
+    }
+
+    return( KvUserDefined );
+}
+
+/* EPSG Codes for projection parameters.  Unfortunately, these bear no
+   relationship to the GeoTIFF codes even though the names are so similar. */
+
+#define EPSGNatOriginLat         8801
+#define EPSGNatOriginLong        8802
+#define EPSGNatOriginScaleFactor 8805
+#define EPSGFalseEasting         8806
+#define EPSGFalseNorthing        8807
+#define EPSGProjCenterLat        8811
+#define EPSGProjCenterLong       8812
+#define EPSGAzimuth              8813
+#define EPSGAngleRectifiedToSkewedGrid 8814
+#define EPSGInitialLineScaleFactor 8815
+#define EPSGProjCenterEasting    8816
+#define EPSGProjCenterNorthing   8817
+#define EPSGPseudoStdParallelLat 8818
+#define EPSGPseudoStdParallelScaleFactor 8819
+#define EPSGFalseOriginLat       8821
+#define EPSGFalseOriginLong      8822
+#define EPSGStdParallel1Lat      8823
+#define EPSGStdParallel2Lat      8824
+#define EPSGFalseOriginEasting   8826
+#define EPSGFalseOriginNorthing  8827
+#define EPSGSphericalOriginLat   8828
+#define EPSGSphericalOriginLong  8829
+#define EPSGInitialLongitude     8830
+#define EPSGZoneWidth            8831
+
+/************************************************************************/
+/*                            SetGTParmIds()                            */
+/*                                                                      */
+/*      This is hardcoded logic to set the GeoTIFF parameter            */
+/*      identifiers for all the EPSG supported projections.  As the     */
+/*      trf_method.csv table grows with new projections, this code      */
+/*      will need to be updated.                                        */
+/*      Explicitly copied from geo_normalize.c of the GeoTIFF package.  */
+/************************************************************************/
+
+static int SetGTParmIds( int nCTProjection, 
+                         int *panProjParmId, 
+                         int *panEPSGCodes )
+
+{
+    int anWorkingDummy[7];
+
+    if( panEPSGCodes == NULL )
+        panEPSGCodes = anWorkingDummy;
+    if( panProjParmId == NULL )
+        panProjParmId = anWorkingDummy;
+
+    memset( panEPSGCodes, 0, sizeof(int) * 7 );
+
+    /* psDefn->nParms = 7; */
+    
+    switch( nCTProjection )
+    {
+      case CT_CassiniSoldner:
+      case CT_NewZealandMapGrid:
+        panProjParmId[0] = ProjNatOriginLatGeoKey;
+        panProjParmId[1] = ProjNatOriginLongGeoKey;
+        panProjParmId[5] = ProjFalseEastingGeoKey;
+        panProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        panEPSGCodes[0] = EPSGNatOriginLat;
+        panEPSGCodes[1] = EPSGNatOriginLong;
+        panEPSGCodes[5] = EPSGFalseEasting;
+        panEPSGCodes[6] = EPSGFalseNorthing;
+        return TRUE;
+
+      case CT_ObliqueMercator:
+        panProjParmId[0] = ProjCenterLatGeoKey;
+        panProjParmId[1] = ProjCenterLongGeoKey;
+        panProjParmId[2] = ProjAzimuthAngleGeoKey;
+        panProjParmId[3] = ProjRectifiedGridAngleGeoKey;
+        panProjParmId[4] = ProjScaleAtCenterGeoKey;
+        panProjParmId[5] = ProjFalseEastingGeoKey;
+        panProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        panEPSGCodes[0] = EPSGProjCenterLat;
+        panEPSGCodes[1] = EPSGProjCenterLong;
+        panEPSGCodes[2] = EPSGAzimuth;
+        panEPSGCodes[3] = EPSGAngleRectifiedToSkewedGrid;
+        panEPSGCodes[4] = EPSGInitialLineScaleFactor;
+        panEPSGCodes[5] = EPSGProjCenterEasting;
+        panEPSGCodes[6] = EPSGProjCenterNorthing;
+        return TRUE;
+
+      case CT_ObliqueMercator_Laborde:
+        panProjParmId[0] = ProjCenterLatGeoKey;
+        panProjParmId[1] = ProjCenterLongGeoKey;
+        panProjParmId[2] = ProjAzimuthAngleGeoKey;
+        panProjParmId[4] = ProjScaleAtCenterGeoKey;
+        panProjParmId[5] = ProjFalseEastingGeoKey;
+        panProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        panEPSGCodes[0] = EPSGProjCenterLat;
+        panEPSGCodes[1] = EPSGProjCenterLong;
+        panEPSGCodes[2] = EPSGAzimuth;
+        panEPSGCodes[4] = EPSGInitialLineScaleFactor;
+        panEPSGCodes[5] = EPSGProjCenterEasting;
+        panEPSGCodes[6] = EPSGProjCenterNorthing;
+        return TRUE;
+        
+      case CT_LambertConfConic_1SP:
+      case CT_Mercator:
+      case CT_ObliqueStereographic:
+      case CT_PolarStereographic:
+      case CT_TransverseMercator:
+      case CT_TransvMercator_SouthOriented:
+        panProjParmId[0] = ProjNatOriginLatGeoKey;
+        panProjParmId[1] = ProjNatOriginLongGeoKey;
+        panProjParmId[4] = ProjScaleAtNatOriginGeoKey;
+        panProjParmId[5] = ProjFalseEastingGeoKey;
+        panProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        panEPSGCodes[0] = EPSGNatOriginLat;
+        panEPSGCodes[1] = EPSGNatOriginLong;
+        panEPSGCodes[4] = EPSGNatOriginScaleFactor;
+        panEPSGCodes[5] = EPSGFalseEasting;
+        panEPSGCodes[6] = EPSGFalseNorthing;
+        return TRUE;
+
+      case CT_LambertConfConic_2SP:
+        panProjParmId[0] = ProjFalseOriginLatGeoKey;
+        panProjParmId[1] = ProjFalseOriginLongGeoKey;
+        panProjParmId[2] = ProjStdParallel1GeoKey;
+        panProjParmId[3] = ProjStdParallel2GeoKey;
+        panProjParmId[5] = ProjFalseEastingGeoKey;
+        panProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        panEPSGCodes[0] = EPSGFalseOriginLat;
+        panEPSGCodes[1] = EPSGFalseOriginLong;
+        panEPSGCodes[2] = EPSGStdParallel1Lat;
+        panEPSGCodes[3] = EPSGStdParallel2Lat;
+        panEPSGCodes[5] = EPSGFalseOriginEasting;
+        panEPSGCodes[6] = EPSGFalseOriginNorthing;
+        return TRUE;
+
+      case CT_SwissObliqueCylindrical:
+        panProjParmId[0] = ProjCenterLatGeoKey;
+        panProjParmId[1] = ProjCenterLongGeoKey;
+        panProjParmId[5] = ProjFalseEastingGeoKey;
+        panProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        /* EPSG codes? */
+        return TRUE;
+
+      default:
+        return( FALSE );
+    }
+}
+
+static char *papszDatumEquiv[] =
+{
+    "Militar_Geographische_Institut",
+    "Militar_Geographische_Institute",
+    "World_Geodetic_System_1984",
+    "WGS_1984",
+    "WGS_72_Transit_Broadcast_Ephemeris",
+    "WGS_1972_Transit_Broadcast_Ephemeris",
+    "World_Geodetic_System_1972",
+    "WGS_1972",
+    "European_Terrestrial_Reference_System_89",
+    "European_Reference_System_1989",
+    NULL
+};
+
+/************************************************************************/
+/*                          WKTMassageDatum()                           */
+/*                                                                      */
+/*      Massage an EPSG datum name into WMT format.  Also transform     */
+/*      specific exception cases into WKT versions.                     */
+/*      Explicitly copied from the gt_wkt_srs.cpp.                      */
+/************************************************************************/
+
+static void WKTMassageDatum( char ** ppszDatum )
+
+{
+    int		i, j;
+    char	*pszDatum;
+
+/* -------------------------------------------------------------------- */
+/*      First copy string and allocate with our CPLStrdup() to so we    */
+/*      know when we are done this function we will have a CPL          */
+/*      string, not a GTIF one.                                         */
+/* -------------------------------------------------------------------- */
+    pszDatum = CPLStrdup(*ppszDatum);
+    GTIFFreeMemory( *ppszDatum );
+    *ppszDatum = pszDatum;
+
+/* -------------------------------------------------------------------- */
+/*      Translate non-alphanumeric values to underscores.               */
+/* -------------------------------------------------------------------- */
+    for( i = 0; pszDatum[i] != '\0'; i++ )
+    {
+        if( !(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z')
+            && !(pszDatum[i] >= 'a' && pszDatum[i] <= 'z')
+            && !(pszDatum[i] >= '0' && pszDatum[i] <= '9') )
+        {
+            pszDatum[i] = '_';
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Remove repeated and trailing underscores.                       */
+/* -------------------------------------------------------------------- */
+    for( i = 1, j = 0; pszDatum[i] != '\0'; i++ )
+    {
+        if( pszDatum[j] == '_' && pszDatum[i] == '_' )
+            continue;
+
+        pszDatum[++j] = pszDatum[i];
+    }
+    if( pszDatum[j] == '_' )
+        pszDatum[j] = '\0';
+    else
+        pszDatum[j+1] = '\0';
+    
+/* -------------------------------------------------------------------- */
+/*      Search for datum equivelences.  Specific massaged names get     */
+/*      mapped to OpenGIS specified names.                              */
+/* -------------------------------------------------------------------- */
+    for( i = 0; papszDatumEquiv[i] != NULL; i += 2 )
+    {
+        if( EQUAL(*ppszDatum,papszDatumEquiv[i]) )
+        {
+            CPLFree( *ppszDatum );
+            *ppszDatum = CPLStrdup( papszDatumEquiv[i+1] );
+            return;
+        }
+    }
+}
+
+/************************************************************************/
+/*                           FetchProjParms()                           */
+/*                                                                      */
+/*      Fetch the projection parameters for a particular projection     */
+/*      from MrSID metadata, and fill the GTIFDefn structure out        */
+/*      with them.                                                      */
+/*      Copied from geo_normalize.c of the GeoTIFF package.             */
+/************************************************************************/
+
+void MrSIDDataset::FetchProjParms()
+{
+    double dfNatOriginLong = 0.0, dfNatOriginLat = 0.0, dfRectGridAngle = 0.0;
+    double dfFalseEasting = 0.0, dfFalseNorthing = 0.0, dfNatOriginScale = 1.0;
+    double dfStdParallel1 = 0.0, dfStdParallel2 = 0.0, dfAzimuth = 0.0;
+
+/* -------------------------------------------------------------------- */
+/*      Get the false easting, and northing if available.               */
+/* -------------------------------------------------------------------- */
+    if( !GetMetadataElement( "GEOTIFF_NUM::3082::ProjFalseEastingGeoKey",
+                             &dfFalseEasting )
+        && !GetMetadataElement( "GEOTIFF_NUM::3090:ProjCenterEastingGeoKey",
+                                &dfFalseEasting ) )
+        dfFalseEasting = 0.0;
+        
+    if( !GetMetadataElement( "GEOTIFF_NUM::3083::ProjFalseNorthingGeoKey",
+                             &dfFalseNorthing )
+        && !GetMetadataElement( "GEOTIFF_NUM::3091::ProjCenterNorthingGeoKey",
+                                &dfFalseNorthing ) )
+        dfFalseNorthing = 0.0;
+
+    switch( psDefn->CTProjection )
+    {
+/* -------------------------------------------------------------------- */
+      case CT_Stereographic:
+/* -------------------------------------------------------------------- */
+        if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey",
+                                &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey",
+                                   &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey",
+                                   &dfNatOriginLong ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey",
+                                &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey",
+                                   &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey",
+                                   &dfNatOriginLat ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3092::ProjScaleAtNatOriginGeoKey",
+                                &dfNatOriginScale ) == 0 )
+            dfNatOriginScale = 1.0;
+            
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfNatOriginLat;
+        psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
+        psDefn->ProjParm[4] = dfNatOriginScale;
+        psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_LambertConfConic_1SP:
+      case CT_Mercator:
+      case CT_ObliqueStereographic:
+      case CT_TransverseMercator:
+      case CT_TransvMercator_SouthOriented:
+/* -------------------------------------------------------------------- */
+        if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey",
+                                &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey",
+                                   &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", 
+                                   &dfNatOriginLong ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey",
+                                &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey",
+                                   &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey",
+                                   &dfNatOriginLat ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3092::ProjScaleAtNatOriginGeoKey",
+                                &dfNatOriginScale ) == 0 )
+            dfNatOriginScale = 1.0;
+
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfNatOriginLat;
+        psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
+        psDefn->ProjParm[4] = dfNatOriginScale;
+        psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_ObliqueMercator: /* hotine */
+/* -------------------------------------------------------------------- */
+        if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey",
+                                &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey",
+                                   &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", 
+                                   &dfNatOriginLong ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey",
+                                &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey",
+                                   &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey",
+                                   &dfNatOriginLat ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3094::ProjAzimuthAngleGeoKey",
+                                &dfAzimuth ) == 0 )
+            dfAzimuth = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3096::ProjRectifiedGridAngleGeoKey",
+                                &dfRectGridAngle ) == 0 )
+            dfRectGridAngle = 90.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3092::ProjScaleAtNatOriginGeoKey",
+                                &dfNatOriginScale ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3093::ProjScaleAtCenterGeoKey",
+                                   &dfNatOriginScale ) == 0 )
+            dfNatOriginScale = 1.0;
+            
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfNatOriginLat;
+        psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
+        psDefn->ProjParm[2] = dfAzimuth;
+        psDefn->ProjParmId[2] = ProjAzimuthAngleGeoKey;
+        psDefn->ProjParm[3] = dfRectGridAngle;
+        psDefn->ProjParmId[3] = ProjRectifiedGridAngleGeoKey;
+        psDefn->ProjParm[4] = dfNatOriginScale;
+        psDefn->ProjParmId[4] = ProjScaleAtCenterGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_CassiniSoldner:
+      case CT_Polyconic:
+/* -------------------------------------------------------------------- */
+        if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey",
+                                &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey",
+                                   &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", 
+                                   &dfNatOriginLong ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey",
+                                &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey",
+                                   &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey",
+                                   &dfNatOriginLat ) == 0 )
+            dfNatOriginLat = 0.0;
+
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3092::ProjScaleAtNatOriginGeoKey",
+                                &dfNatOriginScale ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3093::ProjScaleAtCenterGeoKey",
+                                   &dfNatOriginScale ) == 0 )
+            dfNatOriginScale = 1.0;
+            
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfNatOriginLat;
+        psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
+        psDefn->ProjParm[4] = dfNatOriginScale;
+        psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_AzimuthalEquidistant:
+      case CT_MillerCylindrical:
+      case CT_Equirectangular:
+      case CT_Gnomonic:
+      case CT_LambertAzimEqualArea:
+      case CT_Orthographic:
+/* -------------------------------------------------------------------- */
+        if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey",
+                                &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey",
+                                   &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", 
+                                   &dfNatOriginLong ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey",
+                                &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey",
+                                   &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey",
+                                   &dfNatOriginLat ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfNatOriginLat;
+        psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_Robinson:
+      case CT_Sinusoidal:
+      case CT_VanDerGrinten:
+/* -------------------------------------------------------------------- */
+        if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey",
+                                &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey",
+                                   &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", 
+                                   &dfNatOriginLong ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_PolarStereographic:
+/* -------------------------------------------------------------------- */
+        if( GetMetadataElement( "GEOTIFF_NUM::3095::ProjStraightVertPoleLongGeoKey",
+                                &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey",
+                                   &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey",
+                                   &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey",
+                                   &dfNatOriginLong ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey",
+                                &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey",
+                                   &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey",
+                                   &dfNatOriginLat ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3092::ProjScaleAtNatOriginGeoKey",
+                                &dfNatOriginScale ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3093::ProjScaleAtCenterGeoKey",
+                                   &dfNatOriginScale ) == 0 )
+            dfNatOriginScale = 1.0;
+            
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfNatOriginLat;
+        psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjStraightVertPoleLongGeoKey;
+        psDefn->ProjParm[4] = dfNatOriginScale;
+        psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_LambertConfConic_2SP:
+/* -------------------------------------------------------------------- */
+        if( GetMetadataElement( "GEOTIFF_NUM::3078::ProjStdParallel1GeoKey",
+                                &dfStdParallel1 ) == 0 )
+            dfStdParallel1 = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3079::ProjStdParallel2GeoKey",
+                                &dfStdParallel2 ) == 0 )
+            dfStdParallel1 = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey",
+                                &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey",
+                                   &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", 
+                                   &dfNatOriginLong ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey",
+                                &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey",
+                                   &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey",
+                                   &dfNatOriginLat ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfNatOriginLat;
+        psDefn->ProjParmId[0] = ProjFalseOriginLatGeoKey;
+        psDefn->ProjParm[1] = dfNatOriginLong;
+        psDefn->ProjParmId[1] = ProjFalseOriginLongGeoKey;
+        psDefn->ProjParm[2] = dfStdParallel1;
+        psDefn->ProjParmId[2] = ProjStdParallel1GeoKey;
+        psDefn->ProjParm[3] = dfStdParallel2;
+        psDefn->ProjParmId[3] = ProjStdParallel2GeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+
+/* -------------------------------------------------------------------- */
+      case CT_AlbersEqualArea:
+      case CT_EquidistantConic:
+/* -------------------------------------------------------------------- */
+        if( GetMetadataElement( "GEOTIFF_NUM::3078::ProjStdParallel1GeoKey",
+                                &dfStdParallel1 ) == 0 )
+            dfStdParallel1 = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3079::ProjStdParallel2GeoKey",
+                                &dfStdParallel2 ) == 0 )
+            dfStdParallel1 = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey",
+                                &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey",
+                                   &dfNatOriginLong ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", 
+                                   &dfNatOriginLong ) == 0 )
+            dfNatOriginLong = 0.0;
+
+        if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey",
+                                &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey",
+                                   &dfNatOriginLat ) == 0
+            && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey",
+                                   &dfNatOriginLat ) == 0 )
+            dfNatOriginLat = 0.0;
+
+        /* notdef: should transform to decimal degrees at this point */
+
+        psDefn->ProjParm[0] = dfStdParallel1;
+        psDefn->ProjParmId[0] = ProjStdParallel1GeoKey;
+        psDefn->ProjParm[1] = dfStdParallel2;
+        psDefn->ProjParmId[1] = ProjStdParallel2GeoKey;
+        psDefn->ProjParm[2] = dfNatOriginLat;
+        psDefn->ProjParmId[2] = ProjNatOriginLatGeoKey;
+        psDefn->ProjParm[3] = dfNatOriginLong;
+        psDefn->ProjParmId[3] = ProjNatOriginLongGeoKey;
+        psDefn->ProjParm[5] = dfFalseEasting;
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[6] = dfFalseNorthing;
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        psDefn->nParms = 7;
+        break;
+    }
+}
+
+/************************************************************************/
+/*                            GetGTIFDefn()                             */
+/*      This function borrowed from the GTIFGetDefn() function.         */
+/*      See geo_normalize.c from the GeoTIFF package.                   */
+/************************************************************************/
+
+void MrSIDDataset::GetGTIFDefn()
+{
+    double	dfInvFlattening;
+
+/* -------------------------------------------------------------------- */
+/*      Initially we default all the information we can.                */
+/* -------------------------------------------------------------------- */
+    psDefn = new( GTIFDefn );
+    psDefn->Model = KvUserDefined;
+    psDefn->PCS = KvUserDefined;
+    psDefn->GCS = KvUserDefined;
+    psDefn->UOMLength = KvUserDefined;
+    psDefn->UOMLengthInMeters = 1.0;
+    psDefn->UOMAngle = KvUserDefined;
+    psDefn->UOMAngleInDegrees = 1.0;
+    psDefn->Datum = KvUserDefined;
+    psDefn->Ellipsoid = KvUserDefined;
+    psDefn->SemiMajor = 0.0;
+    psDefn->SemiMinor = 0.0;
+    psDefn->PM = KvUserDefined;
+    psDefn->PMLongToGreenwich = 0.0;
+
+    psDefn->ProjCode = KvUserDefined;
+    psDefn->Projection = KvUserDefined;
+    psDefn->CTProjection = KvUserDefined;
+
+    psDefn->nParms = 0;
+    for( int i = 0; i < MAX_GTIF_PROJPARMS; i++ )
+    {
+        psDefn->ProjParm[i] = 0.0;
+        psDefn->ProjParmId[i] = 0;
+    }
+
+    psDefn->MapSys = KvUserDefined;
+    psDefn->Zone = 0;
+
+/* -------------------------------------------------------------------- */
+/*	Try to get the overall model type.				*/
+/* -------------------------------------------------------------------- */
+    GetMetadataElement( "GEOTIFF_NUM::1024::GTModelTypeGeoKey",
+                        &(psDefn->Model) );
+
+/* -------------------------------------------------------------------- */
+/*      Try to get a PCS.                                               */
+/* -------------------------------------------------------------------- */
+    if( GetMetadataElement( "GEOTIFF_NUM::3072::ProjectedCSTypeGeoKey",
+                            &(psDefn->PCS) )
+        && psDefn->PCS != KvUserDefined )
+    {
+        /*
+         * Translate this into useful information.
+         */
+        GTIFGetPCSInfo( psDefn->PCS, NULL, &(psDefn->ProjCode),
+                        &(psDefn->UOMLength), &(psDefn->GCS) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*       If we have the PCS code, but didn't find it in the CSV files   */
+/*      (likely because we can't find them) we will try some ``jiffy    */
+/*      rules'' for UTM and state plane.                                */
+/* -------------------------------------------------------------------- */
+    if( psDefn->PCS != KvUserDefined && psDefn->ProjCode == KvUserDefined )
+    {
+        int	nMapSys, nZone;
+        int	nGCS = psDefn->GCS;
+
+        nMapSys = GTIFPCSToMapSys( psDefn->PCS, &nGCS, &nZone );
+        if( nMapSys != KvUserDefined )
+        {
+            psDefn->ProjCode = (short) GTIFMapSysToProj( nMapSys, nZone );
+            psDefn->GCS = (short) nGCS;
+        }
+    }
+   
+/* -------------------------------------------------------------------- */
+/*      If the Proj_ code is specified directly, use that.              */
+/* -------------------------------------------------------------------- */
+    if( psDefn->ProjCode == KvUserDefined )
+        GetMetadataElement( "GEOTIFF_NUM::3074::ProjectionGeoKey",
+                            &(psDefn->ProjCode) );
+    
+    if( psDefn->ProjCode != KvUserDefined )
+    {
+        /*
+         * We have an underlying projection transformation value.  Look
+         * this up.  For a PCS of ``WGS 84 / UTM 11'' the transformation
+         * would be Transverse Mercator, with a particular set of options.
+         * The nProjTRFCode itself would correspond to the name
+         * ``UTM zone 11N'', and doesn't include datum info.
+         */
+        GTIFGetProjTRFInfo( psDefn->ProjCode, NULL, &(psDefn->Projection),
+                            psDefn->ProjParm );
+        
+        /*
+         * Set the GeoTIFF identity of the parameters.
+         */
+        psDefn->CTProjection = (short)
+            EPSGProjMethodToCTProjMethod( psDefn->Projection );
+
+        SetGTParmIds( psDefn->CTProjection, psDefn->ProjParmId, NULL);
+        psDefn->nParms = 7;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try to get a GCS.  If found, it will override any implied by    */
+/*      the PCS.                                                        */
+/* -------------------------------------------------------------------- */
+    GetMetadataElement( "GEOTIFF_NUM::2048::GeographicTypeGeoKey",
+                        &(psDefn->GCS) );
+
+/* -------------------------------------------------------------------- */
+/*      Derive the datum, and prime meridian from the GCS.              */
+/* -------------------------------------------------------------------- */
+    if( psDefn->GCS != KvUserDefined )
+    {
+        GTIFGetGCSInfo( psDefn->GCS, NULL, &(psDefn->Datum), &(psDefn->PM),
+                        &(psDefn->UOMAngle) );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Handle the GCS angular units.  GeogAngularUnitsGeoKey           */
+/*      overrides the GCS or PCS setting.                               */
+/* -------------------------------------------------------------------- */
+    GetMetadataElement( "GEOTIFF_NUM::2054::GeogAngularUnitsGeoKey",
+                        &(psDefn->UOMAngle) );
+    if( psDefn->UOMAngle != KvUserDefined )
+    {
+        GTIFGetUOMAngleInfo( psDefn->UOMAngle, NULL,
+                             &(psDefn->UOMAngleInDegrees) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for a datum setting, and then use the datum to derive     */
+/*      an ellipsoid.                                                   */
+/* -------------------------------------------------------------------- */
+    GetMetadataElement( "GEOTIFF_NUM::2050::GeogGeodeticDatumGeoKey",
+                        &(psDefn->Datum) );
+
+    if( psDefn->Datum != KvUserDefined )
+    {
+        GTIFGetDatumInfo( psDefn->Datum, NULL, &(psDefn->Ellipsoid) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for an explicit ellipsoid.  Use the ellipsoid to          */
+/*      derive the ellipsoid characteristics, if possible.              */
+/* -------------------------------------------------------------------- */
+    GetMetadataElement( "GEOTIFF_NUM::2056::GeogEllipsoidGeoKey",
+                        &(psDefn->Ellipsoid) );
+
+    if( psDefn->Ellipsoid != KvUserDefined )
+    {
+        GTIFGetEllipsoidInfo( psDefn->Ellipsoid, NULL,
+                              &(psDefn->SemiMajor), &(psDefn->SemiMinor) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for overridden ellipsoid parameters.  It would be nice    */
+/*      to warn if they conflict with provided information, but for     */
+/*      now we just override.                                           */
+/* -------------------------------------------------------------------- */
+    GetMetadataElement( "GEOTIFF_NUM::2057::GeogSemiMajorAxisGeoKey",
+                        &(psDefn->SemiMajor) );
+    GetMetadataElement( "GEOTIFF_NUM::2058::GeogSemiMinorAxisGeoKey",
+                        &(psDefn->SemiMinor) );
+    
+    if( GetMetadataElement( "GEOTIFF_NUM::2059::GeogInvFlatteningGeoKey",
+                            &dfInvFlattening ) == 1 )
+    {
+        if( dfInvFlattening != 0.0 )
+            psDefn->SemiMinor = 
+                psDefn->SemiMajor * (1 - 1.0/dfInvFlattening);
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get the prime meridian info.                                    */
+/* -------------------------------------------------------------------- */
+    GetMetadataElement( "GEOTIFF_NUM::2051::GeogPrimeMeridianGeoKey",
+                        &(psDefn->PM) );
+
+    if( psDefn->PM != KvUserDefined )
+    {
+        GTIFGetPMInfo( psDefn->PM, NULL, &(psDefn->PMLongToGreenwich) );
+    }
+    else
+    {
+        GetMetadataElement( "GEOTIFF_NUM::2061::GeogPrimeMeridianLongGeoKey",
+                            &(psDefn->PMLongToGreenwich) );
+
+        psDefn->PMLongToGreenwich =
+            GTIFAngleToDD( psDefn->PMLongToGreenwich,
+                           psDefn->UOMAngle );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Have the projection units of measure been overridden?  We       */
+/*      should likely be doing something about angular units too,       */
+/*      but these are very rarely not decimal degrees for actual        */
+/*      file coordinates.                                               */
+/* -------------------------------------------------------------------- */
+    GetMetadataElement( "GEOTIFF_NUM::3076::ProjLinearUnitsGeoKey",
+                        &(psDefn->UOMLength) );
+
+    if( psDefn->UOMLength != KvUserDefined )
+    {
+        GTIFGetUOMLengthInfo( psDefn->UOMLength, NULL,
+                              &(psDefn->UOMLengthInMeters) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Handle a variety of user defined transform types.               */
+/* -------------------------------------------------------------------- */
+    if( GetMetadataElement( "GEOTIFF_NUM::3075::ProjCoordTransGeoKey",
+                            &(psDefn->CTProjection) ) )
+    {
+        FetchProjParms();
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try to set the zoned map system information.                    */
+/* -------------------------------------------------------------------- */
+    psDefn->MapSys = GTIFProjToMapSys( psDefn->ProjCode, &(psDefn->Zone) );
+
+/* -------------------------------------------------------------------- */
+/*      If this is UTM, and we were unable to extract the projection    */
+/*      parameters from the CSV file, just set them directly now,       */
+/*      since it's pretty easy, and a common case.                      */
+/* -------------------------------------------------------------------- */
+    if( (psDefn->MapSys == MapSys_UTM_North
+         || psDefn->MapSys == MapSys_UTM_South)
+        && psDefn->CTProjection == KvUserDefined )
+    {
+        psDefn->CTProjection = CT_TransverseMercator;
+        psDefn->nParms = 7;
+        psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
+        psDefn->ProjParm[0] = 0.0;
+            
+        psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
+        psDefn->ProjParm[1] = psDefn->Zone*6 - 183.0;
+        
+        psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
+        psDefn->ProjParm[4] = 0.9996;
+        
+        psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
+        psDefn->ProjParm[5] = 500000.0;
+        
+        psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
+
+        if( psDefn->MapSys == MapSys_UTM_North )
+            psDefn->ProjParm[6] = 0.0;
+        else
+            psDefn->ProjParm[6] = 10000000.0;
+    }
+
+    if ( pszProjection )
+        CPLFree( pszProjection );
+    pszProjection = GetOGISDefn( psDefn );
+}
+
+/************************************************************************/
+/*                          GetOGISDefn()                               */
+/*  Copied from the gt_wkt_srs.cpp.                                     */
+/************************************************************************/
+
+char *MrSIDDataset::GetOGISDefn( GTIFDefn *psDefn )
+{
+    OGRSpatialReference	oSRS;
+
+    if( psDefn->Model != ModelTypeProjected 
+        && psDefn->Model != ModelTypeGeographic )
+        return CPLStrdup("");
+    
+/* -------------------------------------------------------------------- */
+/*      If this is a projected SRS we set the PROJCS keyword first      */
+/*      to ensure that the GEOGCS will be a child.                      */
+/* -------------------------------------------------------------------- */
+    if( psDefn->Model == ModelTypeProjected )
+    {
+        char	*pszPCSName = "unnamed";
+        int         bNeedFree = FALSE;
+
+        if( psDefn->PCS != KvUserDefined )
+        {
+
+            if( GTIFGetPCSInfo( psDefn->PCS, &pszPCSName, NULL, NULL, NULL ) )
+                bNeedFree = TRUE;
+            
+            oSRS.SetNode( "PROJCS", pszPCSName );
+            if( bNeedFree )
+                GTIFFreeMemory( pszPCSName );
+
+            oSRS.SetAuthority( "PROJCS", "EPSG", psDefn->PCS );
+        }
+        else
+        {
+            char szPCSName[200];
+            strcpy( szPCSName, "unnamed" );
+            if ( GetMetadataElement( "GEOTIFF_NUM::1026::GTCitationGeoKey",
+                                     szPCSName, sizeof(szPCSName) ) )
+                oSRS.SetNode( "PROJCS", szPCSName );
+        }
+    }
+    
+/* ==================================================================== */
+/*      Setup the GeogCS                                                */
+/* ==================================================================== */
+    char	*pszGeogName = NULL;
+    char	*pszDatumName = NULL;
+    char	*pszPMName = NULL;
+    char	*pszSpheroidName = NULL;
+    char	*pszAngularUnits = NULL;
+    double	dfInvFlattening, dfSemiMajor;
+    char        szGCSName[200];
+    
+    if( GetMetadataElement( "GEOTIFF_NUM::2049::GeogCitationGeoKey",
+                            szGCSName, sizeof(szGCSName) ) )
+        pszGeogName = CPLStrdup(szGCSName);
+    GTIFGetGCSInfo( psDefn->GCS, &pszGeogName, NULL, NULL, NULL );
+    GTIFGetDatumInfo( psDefn->Datum, &pszDatumName, NULL );
+    GTIFGetPMInfo( psDefn->PM, &pszPMName, NULL );
+    GTIFGetEllipsoidInfo( psDefn->Ellipsoid, &pszSpheroidName, NULL, NULL );
+    
+    GTIFGetUOMAngleInfo( psDefn->UOMAngle, &pszAngularUnits, NULL );
+    if( pszAngularUnits == NULL )
+        pszAngularUnits = CPLStrdup("unknown");
+
+    if( pszDatumName != NULL )
+        WKTMassageDatum( &pszDatumName );
+
+    dfSemiMajor = psDefn->SemiMajor;
+    if( psDefn->SemiMajor == 0.0 )
+    {
+        pszSpheroidName = CPLStrdup("unretrievable - using WGS84");
+        dfSemiMajor = SRS_WGS84_SEMIMAJOR;
+        dfInvFlattening = SRS_WGS84_INVFLATTENING;
+    }
+    else if( (psDefn->SemiMinor / psDefn->SemiMajor) < 0.99999999999999999
+             || (psDefn->SemiMinor / psDefn->SemiMajor) > 1.00000000000000001 )
+        dfInvFlattening = -1.0 / (psDefn->SemiMinor/psDefn->SemiMajor - 1.0);
+    else
+        dfInvFlattening = 0.0; /* special flag for infinity */
+
+    oSRS.SetGeogCS( pszGeogName, pszDatumName, 
+                    pszSpheroidName, dfSemiMajor, dfInvFlattening,
+                    pszPMName,
+                    psDefn->PMLongToGreenwich / psDefn->UOMAngleInDegrees,
+                    pszAngularUnits,
+                    psDefn->UOMAngleInDegrees * 0.0174532925199433 );
+
+    if( psDefn->GCS != KvUserDefined )
+        oSRS.SetAuthority( "GEOGCS", "EPSG", psDefn->GCS );
+
+    if( psDefn->Datum != KvUserDefined )
+        oSRS.SetAuthority( "DATUM", "EPSG", psDefn->Datum );
+
+    if( psDefn->Ellipsoid != KvUserDefined )
+        oSRS.SetAuthority( "SPHEROID", "EPSG", psDefn->Ellipsoid );
+
+    GTIFFreeMemory( pszGeogName );
+    CPLFree( pszDatumName );
+    GTIFFreeMemory( pszPMName );
+    GTIFFreeMemory( pszSpheroidName );
+    GTIFFreeMemory( pszAngularUnits );
+        
+/* ==================================================================== */
+/*      Handle projection parameters.                                   */
+/* ==================================================================== */
+    if( psDefn->Model == ModelTypeProjected )
+    {
+/* -------------------------------------------------------------------- */
+/*      Make a local copy of parms, and convert back into the           */
+/*      angular units of the GEOGCS and the linear units of the         */
+/*      projection.                                                     */
+/* -------------------------------------------------------------------- */
+        double		adfParm[10];
+        int		i;
+
+        for( i = 0; i < MIN(10,psDefn->nParms); i++ )
+            adfParm[i] = psDefn->ProjParm[i];
+
+        adfParm[0] /= psDefn->UOMAngleInDegrees;
+        adfParm[1] /= psDefn->UOMAngleInDegrees;
+        adfParm[2] /= psDefn->UOMAngleInDegrees;
+        adfParm[3] /= psDefn->UOMAngleInDegrees;
+        
+        adfParm[5] /= psDefn->UOMLengthInMeters;
+        adfParm[6] /= psDefn->UOMLengthInMeters;
+        
+/* -------------------------------------------------------------------- */
+/*      Translation the fundamental projection.                         */
+/* -------------------------------------------------------------------- */
+        switch( psDefn->CTProjection )
+        {
+          case CT_TransverseMercator:
+            oSRS.SetTM( adfParm[0], adfParm[1],
+                        adfParm[4],
+                        adfParm[5], adfParm[6] );
+            break;
+
+          case CT_TransvMercator_SouthOriented:
+            oSRS.SetTMSO( adfParm[0], adfParm[1],
+                          adfParm[4],
+                          adfParm[5], adfParm[6] );
+            break;
+
+          case CT_Mercator:
+            oSRS.SetMercator( adfParm[0], adfParm[1],
+                              adfParm[4],
+                              adfParm[5], adfParm[6] );
+            break;
+
+          case CT_ObliqueStereographic:
+            oSRS.SetOS( adfParm[0], adfParm[1],
+                        adfParm[4],
+                        adfParm[5], adfParm[6] );
+            break;
+
+          case CT_Stereographic:
+            oSRS.SetOS( adfParm[0], adfParm[1],
+                        adfParm[4],
+                        adfParm[5], adfParm[6] );
+            break;
+
+          case CT_ObliqueMercator: /* hotine */
+            oSRS.SetHOM( adfParm[0], adfParm[1],
+                         adfParm[2], adfParm[3],
+                         adfParm[4],
+                         adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_EquidistantConic: 
+            oSRS.SetEC( adfParm[0], adfParm[1],
+                        adfParm[2], adfParm[3],
+                        adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_CassiniSoldner:
+            oSRS.SetCS( adfParm[0], adfParm[1],
+                        adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_Polyconic:
+            oSRS.SetPolyconic( adfParm[0], adfParm[1],
+                               adfParm[5], adfParm[6] );
+            break;
+
+          case CT_AzimuthalEquidistant:
+            oSRS.SetAE( adfParm[0], adfParm[1],
+                        adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_MillerCylindrical:
+            oSRS.SetMC( adfParm[0], adfParm[1],
+                        adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_Equirectangular:
+            oSRS.SetEquirectangular( adfParm[0], adfParm[1],
+                                     adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_Gnomonic:
+            oSRS.SetGnomonic( adfParm[0], adfParm[1],
+                              adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_LambertAzimEqualArea:
+            oSRS.SetLAEA( adfParm[0], adfParm[1],
+                          adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_Orthographic:
+            oSRS.SetOrthographic( adfParm[0], adfParm[1],
+                                  adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_Robinson:
+            oSRS.SetRobinson( adfParm[1],
+                              adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_Sinusoidal:
+            oSRS.SetSinusoidal( adfParm[1],
+                                adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_VanDerGrinten:
+            oSRS.SetVDG( adfParm[1],
+                         adfParm[5], adfParm[6] );
+            break;
+
+          case CT_PolarStereographic:
+            oSRS.SetPS( adfParm[0], adfParm[1],
+                        adfParm[4],
+                        adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_LambertConfConic_2SP:
+            oSRS.SetLCC( adfParm[2], adfParm[3],
+                         adfParm[0], adfParm[1],
+                         adfParm[5], adfParm[6] );
+            break;
+
+          case CT_LambertConfConic_1SP:
+            oSRS.SetLCC1SP( adfParm[0], adfParm[1],
+                            adfParm[4],
+                            adfParm[5], adfParm[6] );
+            break;
+        
+          case CT_AlbersEqualArea:
+            oSRS.SetACEA( adfParm[0], adfParm[1],
+                          adfParm[2], adfParm[3],
+                          adfParm[5], adfParm[6] );
+            break;
+
+          case CT_NewZealandMapGrid:
+            oSRS.SetNZMG( adfParm[0], adfParm[1],
+                          adfParm[5], adfParm[6] );
+            break;
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Set projection units.                                           */
+/* -------------------------------------------------------------------- */
+        char	*pszUnitsName = NULL;
+        
+        GTIFGetUOMLengthInfo( psDefn->UOMLength, &pszUnitsName, NULL );
+
+        if( pszUnitsName != NULL && psDefn->UOMLength != KvUserDefined )
+        {
+            oSRS.SetLinearUnits( pszUnitsName, psDefn->UOMLengthInMeters );
+            oSRS.SetAuthority( "PROJCS|UNIT", "EPSG", psDefn->UOMLength );
+        }
+        else
+            oSRS.SetLinearUnits( "unknown", psDefn->UOMLengthInMeters );
+
+        GTIFFreeMemory( pszUnitsName );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Return the WKT serialization of the object.                     */
+/* -------------------------------------------------------------------- */
+    char	*pszWKT;
+
+    oSRS.FixupOrdering();
+
+    if( oSRS.exportToWkt( &pszWKT ) == OGRERR_NONE )
+        return pszWKT;
+    else
+        return NULL;
+}
+
diff --git a/Utilities/GDAL/frmts/mrsid/mrsiddataset.cpp b/Utilities/GDAL/frmts/mrsid/mrsiddataset.cpp
new file mode 100644
index 0000000000..97ec411694
--- /dev/null
+++ b/Utilities/GDAL/frmts/mrsid/mrsiddataset.cpp
@@ -0,0 +1,1920 @@
+/******************************************************************************
+ * $Id: mrsiddataset.cpp,v 1.53 2006/04/28 18:56:21 fwarmerdam Exp $
+ *
+ * Project:  Multi-resolution Seamless Image Database (MrSID)
+ * Purpose:  Read/write LizardTech's MrSID file format - Version 4+ SDK.
+ * Author:   Andrey Kiselev, dron@remotesensing.org
+ *
+ ******************************************************************************
+ * Copyright (c) 2003, Andrey Kiselev <dron@remotesensing.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: mrsiddataset.cpp,v $
+ * Revision 1.53  2006/04/28 18:56:21  fwarmerdam
+ * fixed up v5/v6 compatibility
+ *
+ * Revision 1.52  2006/03/03 04:30:13  fwarmerdam
+ * added XMLPROFILE creation option
+ *
+ * Revision 1.51  2006/02/28 19:30:55  fwarmerdam
+ * Use LTI_COLORSPACE_MULTISPECTRAL for anything other than 1 or 3 bands.
+ *
+ * Revision 1.50  2006/02/21 22:42:11  fwarmerdam
+ * Added ESDK 6 support.
+ *
+ * Revision 1.49  2006/02/19 03:01:24  fwarmerdam
+ * Improved error checking in writer code.
+ *
+ * Revision 1.48  2006/01/31 17:44:46  fwarmerdam
+ * jpc_header used even when j2k support disabled.
+ *
+ * Revision 1.47  2006/01/09 18:59:33  fwarmerdam
+ * ensure jp2mrsid driver support jpc (non-jP2 JPEG2000 files)
+ *
+ * Revision 1.46  2005/11/10 21:05:01  fwarmerdam
+ * Applied Rostics fix for slight overview size inaccuracies
+ *
+ * Revision 1.45  2005/10/17 19:30:47  fwarmerdam
+ * Fixed serious bug in MrSID overview access.
+ *
+ * Revision 1.44  2005/09/16 22:09:18  fwarmerdam
+ * Ensure SetDescription() is called before OpenZoom.
+ *
+ * Revision 1.43  2005/09/15 02:17:34  fwarmerdam
+ * note MRSID_HAVE_GETWKT macro.
+ *
+ * Revision 1.42  2005/09/15 00:46:21  fwarmerdam
+ * Fixed help topic link.
+ *
+ * Revision 1.41  2005/09/15 00:31:27  fwarmerdam
+ * added JP2MrSID driver, fixedup ESDK 5.x support
+ *
+ * Revision 1.40  2005/09/09 02:27:28  fwarmerdam
+ * Fallback to PAM if projection string is empty.
+ *
+ * Revision 1.39  2005/08/26 14:48:33  fwarmerdam
+ * Do not return nodata on bands since MrSID NODATA semantics are
+ * across all bands.   Note that IMAGE__TRANSPARENT_DATA_VALUE metadata
+ * on the dataset holds the set of values for application use if needed.
+ *
+ * Revision 1.38  2005/07/22 15:50:10  fwarmerdam
+ * Use blocked io for very small request buffers as well as small windows.
+ *
+ * Revision 1.37  2005/07/21 17:30:13  fwarmerdam
+ * Added preliminary worldfile support.
+ *
+ * Revision 1.36  2005/07/14 23:40:31  fwarmerdam
+ * getNumLevels() is the number of overviews
+ *
+ * Revision 1.35  2005/06/24 15:54:30  dron
+ * Properly fetch NODATA taking in account data type of the value.
+ *
+ * Revision 1.34  2005/05/13 01:12:25  fwarmerdam
+ * Fixed bug with block oriented access to 16bit data.
+ *
+ * Revision 1.33  2005/05/05 15:54:49  fwarmerdam
+ * PAM Enabled
+ *
+ * Revision 1.32  2005/05/04 17:21:00  fwarmerdam
+ * Avoid 32bit overflow when testing xsize*ysize.
+ *
+ * Revision 1.31  2005/04/21 16:43:26  fwarmerdam
+ * Fixed bug with RasterIO() at overview levels higher than is available
+ * in the file.  Now the code limits itself to available overview levels.
+ *
+ * Revision 1.30  2005/04/11 17:03:07  fwarmerdam
+ * Improved initial open checking to ensure it works properly with no header
+ * data at all.
+ *
+ * Revision 1.29  2005/04/11 14:41:14  fwarmerdam
+ * Only call getWKT() if HAVE_MRSID_GETWKT is defined.
+ *
+ * Revision 1.28  2005/04/06 22:03:17  kmelero
+ * Updated for CPLStrdup and mod to pointers for large file support per
+ * LizardTech Mrsid Team.  (kmelero@sanz.com)
+ *
+ * Revision 1.27  2005/04/02 19:26:08  kmelero
+ * Added read support for WKT.  (kmelero@sanz.com)
+ *
+ * Revision 1.26  2005/04/02 19:04:00  kmelero
+ * Added setting WKT in MrSID file.  (kmelero@sanz.com)
+ *
+ * Revision 1.25  2005/03/26 19:29:21  kmelero
+ * Added flag to set 64-bit fwrite in support of large MrSID files.  (kmelero@sanz.com)
+ *
+ * Revision 1.24  2005/03/25 15:02:22  kmelero
+ * Removed misplaced < character.  Was causing compiler error. (kmelero@sanz.com)
+ *
+ * Revision 1.23  2005/02/17 22:17:36  fwarmerdam
+ * some adjustments to deciding whether to use direct io or not
+ *
+ * Revision 1.22  2005/02/16 22:08:21  fwarmerdam
+ * split out v3 driver, added dataset::IRasterIO
+ *
+ * Revision 1.21  2005/02/12 17:52:28  kmelero
+ * Rewrote CreateCopy to support v2 and v3 of MrSID.  (kmelero@sanz.com)
+ *
+ * Revision 1.20  2005/02/06 18:40:05  dron
+ * Fixes in colorspace handling when the new file creating; use WORLDFILE option.
+ *
+ * Revision 1.19  2005/02/02 14:58:46  fwarmerdam
+ * Added a few debug statements.
+ *
+ * Revision 1.18  2004/11/14 21:18:22  dron
+ * Initial support for MrSID Encoding SDK.
+ */
+
+#include "gdal_pam.h"
+#include "ogr_spatialref.h"
+#include "cpl_string.h"
+#include <string>
+
+#include <geo_normalize.h>
+#include <geovalues.h>
+
+CPL_CVSID("$Id: mrsiddataset.cpp,v 1.53 2006/04/28 18:56:21 fwarmerdam Exp $");
+
+CPL_C_START
+double GTIFAngleToDD( double dfAngle, int nUOMAngle );
+CPL_C_END
+
+// Key Macros from Makefile:
+//   MRSID_ESDK: Means we have the encoding SDK (version 5 or newer required)
+//   MRSID_J2K: Means we are enabling MrSID SDK JPEG2000 support. 
+
+#include "lt_types.h"
+#include "lt_base.h"
+#include "lt_fileSpec.h"
+#include "lt_ioFileStream.h"
+#include "lt_utilStatusStrings.h"
+#include "lti_geoCoord.h"
+#include "lti_pixel.h"
+#include "lti_navigator.h"
+#include "lti_sceneBuffer.h"
+#include "lti_metadataDatabase.h"
+#include "lti_metadataRecord.h"
+#include "lti_utils.h"
+#include "lt_utilStatus.h"
+#include "MrSIDImageReader.h"
+
+#ifdef MRSID_J2K
+#  include "J2KImageReader.h"
+#endif
+
+// It seems that LT_STS_UTIL_TimeUnknown was added in version 6, also
+// the first version with lti_version.h
+#ifdef LT_STS_UTIL_TimeUnknown
+#  include "lti_version.h"
+#endif
+
+// Are we using version 6 or newer?
+#if defined(LTI_SDK_MAJOR) && LTI_SDK_MAJOR >= 6
+#  define MRSID_POST5
+#endif
+
+#ifdef MRSID_ESDK
+# include "MG3ImageWriter.h"
+# include "MG3WriterParams.h"
+# include "MG2ImageWriter.h"
+# include "MG2WriterParams.h"
+# ifdef MRSID_J2K
+#   ifdef MRSID_POST5
+#     include "JP2WriterManager.h"
+#     include "JPCWriterParams.h"
+#   else
+#     include "J2KImageWriter.h"
+#     include "J2KWriterParams.h"
+#   endif
+# endif
+#endif /* MRSID_ESDK */
+
+#ifdef MRSID_POST5
+#  define MRSID_HAVE_GETWKT
+#endif
+
+LT_USE_NAMESPACE(LizardTech)
+
+/************************************************************************/
+/* ==================================================================== */
+/*                              MrSIDDataset                            */
+/* ==================================================================== */
+/************************************************************************/
+
+class MrSIDDataset : public GDALPamDataset
+{
+    friend class MrSIDRasterBand;
+
+    LTIImageReader      *poImageReader;
+
+#ifdef MRSID_ESDK
+    LTIGeoFileImageWriter *poImageWriter;
+#endif
+
+    LTINavigator        *poLTINav;
+    LTIMetadataDatabase *poMetadata;
+    const LTIPixel      *poNDPixel;
+
+    LTISceneBuffer      *poBuffer;
+    int                 nBlockXSize, nBlockYSize;
+    int                 bPrevBlockRead;
+    int                 nPrevBlockXOff, nPrevBlockYOff;
+
+    LTIDataType         eSampleType;
+    GDALDataType        eDataType;
+    LTIColorSpace       eColorSpace;
+
+    double              dfCurrentMag;
+
+    int                 bHasGeoTransform;
+    double              adfGeoTransform[6];
+    char                *pszProjection;
+    GTIFDefn            *psDefn;
+
+    MrSIDDataset       *poParentDS;
+    int                 bIsOverview;
+    int                 nOverviewCount;
+    MrSIDDataset        **papoOverviewDS;
+
+    CPLErr              OpenZoomLevel( lt_int32 iZoom );
+    char                *SerializeMetadataRec( const LTIMetadataRecord* );
+    int                 GetMetadataElement( const char *, void *, int=0 );
+    void                FetchProjParms();
+    void                GetGTIFDefn();
+    char                *GetOGISDefn( GTIFDefn * );
+
+    virtual CPLErr      IRasterIO( GDALRWFlag, int, int, int, int, void *,
+                                   int, int, GDALDataType, int, int *,int,
+                                   int, int );
+
+  public:
+                MrSIDDataset();
+                ~MrSIDDataset();
+
+    static GDALDataset  *Open( GDALOpenInfo * );
+    virtual CPLErr      GetGeoTransform( double * padfTransform );
+    const char          *GetProjectionRef();
+
+#ifdef MRSID_ESDK
+    static GDALDataset  *Create( const char * pszFilename,
+                                 int nXSize, int nYSize, int nBands,
+                                 GDALDataType eType, char ** papszParmList );
+    virtual void        FlushCache( void );
+#endif
+};
+
+#include "mrsidcommon.h"
+
+/************************************************************************/
+/* ==================================================================== */
+/*                           MrSIDRasterBand                            */
+/* ==================================================================== */
+/************************************************************************/
+
+class MrSIDRasterBand : public GDALPamRasterBand
+{
+    friend class MrSIDDataset;
+
+    LTIPixel        *poPixel;
+
+    int             nBlockSize;
+
+    int             bNoDataSet;
+    double          dfNoDataValue;
+
+  public:
+
+                MrSIDRasterBand( MrSIDDataset *, int );
+                ~MrSIDRasterBand();
+
+    virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
+                              void *, int, int, GDALDataType,
+                              int, int );
+
+    virtual CPLErr          IReadBlock( int, int, void * );
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual double	    GetNoDataValue( int * );
+    virtual int             GetOverviewCount();
+    virtual GDALRasterBand  *GetOverview( int );
+
+#ifdef MRSID_ESDK
+    virtual CPLErr          IWriteBlock( int, int, void * );
+#endif
+};
+
+/************************************************************************/
+/*                           MrSIDRasterBand()                          */
+/************************************************************************/
+
+MrSIDRasterBand::MrSIDRasterBand( MrSIDDataset *poDS, int nBand )
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+    this->eDataType = poDS->eDataType;
+
+/* -------------------------------------------------------------------- */
+/*      Set the block sizes and buffer parameters.                      */
+/* -------------------------------------------------------------------- */
+    nBlockXSize = poDS->nBlockXSize;
+    nBlockYSize = poDS->nBlockYSize;
+//#ifdef notdef
+    if( poDS->GetRasterXSize() > 2048 )
+        nBlockXSize = 1024;
+    if( poDS->GetRasterYSize() > 128 )
+        nBlockYSize = 128;
+    else
+        nBlockYSize = poDS->GetRasterYSize();
+//#endif
+
+    nBlockSize = nBlockXSize * nBlockYSize;
+    poPixel = new LTIPixel( poDS->eColorSpace, poDS->nBands,
+                            poDS->eSampleType );
+
+
+/* -------------------------------------------------------------------- */
+/*      Set NoData values.                                              */
+/*                                                                      */
+/*      This logic is disabled for now since the MrSID nodata           */
+/*      semantics are different than GDAL.  In MrSID all bands must     */
+/*      match the nodata value for that band in order for the pixel     */
+/*      to be considered nodata, otherwise all values are valid.        */
+/* -------------------------------------------------------------------- */
+#ifdef notdef
+     if ( poDS->poNDPixel )
+     {
+         switch( poDS->eSampleType )
+         {
+             case LTI_DATATYPE_UINT8:
+             case LTI_DATATYPE_SINT8:
+                 dfNoDataValue = (double)
+                     poDS->poNDPixel->getSampleValueUint8( nBand - 1 );
+                 break;
+             case LTI_DATATYPE_UINT16:
+                 dfNoDataValue = (double)
+                     poDS->poNDPixel->getSampleValueUint16( nBand - 1 );
+                 break;
+             case LTI_DATATYPE_FLOAT32:
+                 dfNoDataValue =
+                     poDS->poNDPixel->getSampleValueFloat32( nBand - 1 );
+                 break;
+             case LTI_DATATYPE_SINT16:
+                 dfNoDataValue = (double)
+                     *(GInt16 *)poDS->poNDPixel->getSampleValueAddr( nBand - 1 );
+                 break;
+             case LTI_DATATYPE_UINT32:
+                 dfNoDataValue = (double)
+                     *(GUInt32 *)poDS->poNDPixel->getSampleValueAddr( nBand - 1 );
+                 break;
+             case LTI_DATATYPE_SINT32:
+                 dfNoDataValue = (double)
+                     *(GInt32 *)poDS->poNDPixel->getSampleValueAddr( nBand - 1 );
+                 break;
+             case LTI_DATATYPE_FLOAT64:
+                 dfNoDataValue =
+                     *(double *)poDS->poNDPixel->getSampleValueAddr( nBand - 1 );
+                 break;
+
+             case LTI_DATATYPE_INVALID:
+                 CPLAssert( FALSE );
+                 break;
+         }
+	 bNoDataSet = TRUE;
+     }
+     else
+#endif
+     {
+	dfNoDataValue = 0.0;
+        bNoDataSet = FALSE;
+     }
+}
+
+/************************************************************************/
+/*                            ~MrSIDRasterBand()                        */
+/************************************************************************/
+
+MrSIDRasterBand::~MrSIDRasterBand()
+{
+    if ( poPixel )
+        delete poPixel;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr MrSIDRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                    void * pImage )
+{
+    MrSIDDataset    *poGDS = (MrSIDDataset *)poDS;
+
+#ifdef MRSID_ESDK
+    if( poGDS->eAccess == GA_Update )
+    {
+        CPLDebug( "MrSID", 
+                  "IReadBlock() - DSDK - read on updatable file fails." );
+        memset( pImage, 0, nBlockSize * GDALGetDataTypeSize(eDataType) / 8 );
+        return CE_None;
+    }
+#endif /* MRSID_ESDK */
+
+    CPLDebug( "MrSID", "IReadBlock(%d,%d)", nBlockXOff, nBlockYOff );
+
+    if ( !poGDS->bPrevBlockRead
+         || poGDS->nPrevBlockXOff != nBlockXOff
+         || poGDS->nPrevBlockYOff != nBlockYOff )
+    {
+        GInt32      nLine = nBlockYOff * nBlockYSize;
+        GInt32      nCol = nBlockXOff * nBlockXSize;
+
+        // XXX: The scene, passed to LTIImageStage::read() call must be
+        // inside the image boundaries. So we should detect the last strip and
+        // form the scene properly.
+        CPLDebug( "MrSID", 
+                  "IReadBlock - read() %dx%d block at %d,%d.", 
+                  nBlockXSize, nBlockYSize, nCol, nLine );
+                  
+        if(!LT_SUCCESS( poGDS->poLTINav->setSceneAsULWH(
+                            nCol, nLine,
+                            (nCol+nBlockXSize>poGDS->GetRasterXSize())?
+                            (poGDS->GetRasterXSize()-nCol):nBlockXSize,
+                            (nLine+nBlockYSize>poGDS->GetRasterYSize())?
+                            (poGDS->GetRasterYSize()-nLine):nBlockYSize,
+                            poGDS->dfCurrentMag) ))
+            
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+            "MrSIDRasterBand::IReadBlock(): Failed to set scene position." );
+            return CE_Failure;
+        }
+
+        if ( !poGDS->poBuffer )
+        {
+            poGDS->poBuffer =
+                new LTISceneBuffer( *poPixel, nBlockXSize, nBlockYSize, NULL );
+        }
+
+        if(!LT_SUCCESS(poGDS->poImageReader->read(poGDS->poLTINav->getScene(),
+                                                  *poGDS->poBuffer)))
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "MrSIDRasterBand::IReadBlock(): Failed to load image." );
+            return CE_Failure;
+        }
+
+        poGDS->bPrevBlockRead = TRUE;
+        poGDS->nPrevBlockXOff = nBlockXOff;
+        poGDS->nPrevBlockYOff = nBlockYOff;
+    }
+
+    memcpy( pImage, poGDS->poBuffer->getTotalBandData(nBand - 1), 
+            nBlockSize * (GDALGetDataTypeSize(poGDS->eDataType) / 8) );
+
+    return CE_None;
+}
+
+#ifdef MRSID_ESDK
+
+/************************************************************************/
+/*                            IWriteBlock()                             */
+/************************************************************************/
+
+CPLErr MrSIDRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
+                                     void * pImage )
+{
+    MrSIDDataset    *poGDS = (MrSIDDataset *)poDS;
+
+    CPLAssert( poGDS != NULL
+               && nBlockXOff >= 0
+               && nBlockYOff >= 0
+               && pImage != NULL );
+
+#if DEBUG
+    CPLDebug( "MrSID", "IWriteBlock(): nBlockXOff=%d, nBlockYOff=%d",
+              nBlockXOff, nBlockYOff );
+#endif
+
+    LTIScene        oScene( nBlockXOff * nBlockXSize,
+                            nBlockYOff * nBlockYSize,
+                            nBlockXSize, nBlockYSize, 1.0);
+    LTISceneBuffer  oSceneBuf( *poPixel, poGDS->nBlockXSize,
+                               poGDS->nBlockYSize, &pImage );
+
+    if( !LT_SUCCESS(poGDS->poImageWriter->writeBegin(oScene)) )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "MrSIDRasterBand::IWriteBlock(): writeBegin failed." );
+        return CE_Failure;
+    }
+
+    if( !LT_SUCCESS(poGDS->poImageWriter->writeStrip(oSceneBuf, oScene)) )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "MrSIDRasterBand::IWriteBlock(): writeStrip failed." );
+        return CE_Failure;
+    }
+
+    if( !LT_SUCCESS(poGDS->poImageWriter->writeEnd()) )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "MrSIDRasterBand::IWriteBlock(): writeEnd failed." );
+        return CE_Failure;
+    }
+
+    return CE_None;
+}
+
+#endif /* MRSID_ESDK */
+
+/************************************************************************/
+/*                             IRasterIO()                              */
+/************************************************************************/
+
+CPLErr MrSIDRasterBand::IRasterIO( GDALRWFlag eRWFlag,
+                                   int nXOff, int nYOff, int nXSize, int nYSize,
+                                   void * pData, int nBufXSize, int nBufYSize,
+                                   GDALDataType eBufType,
+                                   int nPixelSpace, int nLineSpace )
+    
+{
+    MrSIDDataset *poGDS = (MrSIDDataset *) poDS;
+
+/* -------------------------------------------------------------------- */
+/*      Fallback to default implementation if the whole scanline        */
+/*      without subsampling requested.                                  */
+/* -------------------------------------------------------------------- */
+    if ( nXSize == poGDS->GetRasterXSize()
+         && nXSize == nBufXSize
+         && nYSize == nBufYSize )
+    {
+        return GDALRasterBand::IRasterIO( eRWFlag, nXOff, nYOff,
+                                          nXSize, nYSize, pData,
+                                          nBufXSize, nBufYSize, eBufType,
+                                          nPixelSpace, nLineSpace );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Handle via the dataset level IRasterIO()                        */
+/* -------------------------------------------------------------------- */
+    return poGDS->IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, 
+                             nBufXSize, nBufYSize, eBufType, 
+                             1, &nBand, nPixelSpace, nLineSpace, 0 );
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp MrSIDRasterBand::GetColorInterpretation()
+{
+    MrSIDDataset      *poGDS = (MrSIDDataset *) poDS;
+
+    switch( poGDS->eColorSpace )
+    {
+        case LTI_COLORSPACE_RGB:
+            if( nBand == 1 )
+                return GCI_RedBand;
+            else if( nBand == 2 )
+                return GCI_GreenBand;
+            else if( nBand == 3 )
+                return GCI_BlueBand;
+            else
+                return GCI_Undefined;
+        case LTI_COLORSPACE_RGBK:
+            if( nBand == 1 )
+                return GCI_RedBand;
+            else if( nBand == 2 )
+                return GCI_GreenBand;
+            else if( nBand == 3 )
+                return GCI_BlueBand;
+            else if( nBand == 4 )
+                return GCI_AlphaBand;
+            else
+                return GCI_Undefined;
+        case LTI_COLORSPACE_CMYK:
+            if( nBand == 1 )
+                return GCI_CyanBand;
+            else if( nBand == 2 )
+                return GCI_MagentaBand;
+            else if( nBand == 3 )
+                return GCI_YellowBand;
+            else if( nBand == 4 )
+                return GCI_BlackBand;
+            else
+                return GCI_Undefined;
+        case LTI_COLORSPACE_GRAYSCALE:
+            return GCI_GrayIndex;
+        default:
+            return GCI_Undefined;
+    }
+
+}
+
+/************************************************************************/
+/*                           GetNoDataValue()                           */
+/************************************************************************/
+
+double MrSIDRasterBand::GetNoDataValue( int * pbSuccess )
+
+{
+    if( pbSuccess )
+        *pbSuccess = bNoDataSet;
+
+    return dfNoDataValue;
+}
+
+/************************************************************************/
+/*                          GetOverviewCount()                          */
+/************************************************************************/
+
+int MrSIDRasterBand::GetOverviewCount()
+
+{
+    MrSIDDataset        *poGDS = (MrSIDDataset *) poDS;
+
+    return poGDS->nOverviewCount;
+}
+
+/************************************************************************/
+/*                            GetOverview()                             */
+/************************************************************************/
+
+GDALRasterBand *MrSIDRasterBand::GetOverview( int i )
+
+{
+    MrSIDDataset        *poGDS = (MrSIDDataset *) poDS;
+
+    if( i < 0 || i >= poGDS->nOverviewCount )
+        return NULL;
+    else
+        return poGDS->papoOverviewDS[i]->GetRasterBand( nBand );
+}
+
+/************************************************************************/
+/*                           MrSIDDataset()                             */
+/************************************************************************/
+
+MrSIDDataset::MrSIDDataset()
+{
+    poImageReader = NULL;
+#ifdef MRSID_ESDK
+    poImageWriter = NULL;
+#endif
+    poLTINav = NULL;
+    poMetadata = NULL;
+    poNDPixel = NULL;
+    eSampleType = LTI_DATATYPE_UINT8;
+    nBands = 0;
+    eDataType = GDT_Byte;
+    
+    poBuffer = NULL;
+    bPrevBlockRead = FALSE;
+    nPrevBlockXOff = 0;
+    nPrevBlockYOff = 0;
+    
+    pszProjection = CPLStrdup( "" );
+    bHasGeoTransform = FALSE;
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+    psDefn = NULL;
+    
+    dfCurrentMag = 1.0;
+    bIsOverview = FALSE;
+    poParentDS = this;
+    nOverviewCount = 0;
+    papoOverviewDS = NULL;
+}
+
+/************************************************************************/
+/*                            ~MrSIDDataset()                           */
+/************************************************************************/
+
+MrSIDDataset::~MrSIDDataset()
+{
+    FlushCache();
+
+#ifdef MRSID_ESDK
+    if ( poImageWriter )
+        delete poImageWriter;
+#endif
+
+    if ( poImageReader && !bIsOverview )
+        delete poImageReader;
+    if ( poLTINav )
+        delete poLTINav;
+    if ( poBuffer )
+        delete poBuffer;
+    if ( poMetadata )
+        delete poMetadata;
+    if ( pszProjection )
+        CPLFree( pszProjection );
+    if ( psDefn )
+        delete psDefn;
+    if ( papoOverviewDS )
+    {
+        for( int i = 0; i < nOverviewCount; i++ )
+            delete papoOverviewDS[i];
+        CPLFree( papoOverviewDS );
+    }
+}
+
+/************************************************************************/
+/*                             IRasterIO()                              */
+/************************************************************************/
+
+CPLErr MrSIDDataset::IRasterIO( GDALRWFlag eRWFlag,
+                                int nXOff, int nYOff, int nXSize, int nYSize,
+                                void * pData, int nBufXSize, int nBufYSize,
+                                GDALDataType eBufType, 
+                                int nBandCount, int *panBandMap,
+                                int nPixelSpace, int nLineSpace, int nBandSpace )
+
+{
+/* -------------------------------------------------------------------- */
+/*      We need various criteria to skip out to block based methods.    */
+/* -------------------------------------------------------------------- */
+    int bUseBlockedIO = bForceCachedIO;
+
+    if( nYSize == 1 || nXSize * ((double) nYSize) < 100.0 )
+        bUseBlockedIO = TRUE;
+
+    if( nBufYSize == 1 || nBufXSize * ((double) nBufYSize) < 100.0 )
+        bUseBlockedIO = TRUE;
+
+    if( CSLTestBoolean( CPLGetConfigOption( "GDAL_ONE_BIG_READ", "NO") ) )
+        bUseBlockedIO = FALSE;
+
+    if( bUseBlockedIO )
+        return GDALDataset::BlockBasedRasterIO( 
+            eRWFlag, nXOff, nYOff, nXSize, nYSize,
+            pData, nBufXSize, nBufYSize, eBufType, 
+            nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace );
+    CPLDebug( "MrSID", "RasterIO() - using optimized dataset level IO." );
+    
+/* -------------------------------------------------------------------- */
+/*      What is our requested window relative to the base dataset.      */
+/*      We want to operate from here on as if we were operating on      */
+/*      the full res band.                                              */
+/* -------------------------------------------------------------------- */
+    int nZoomMag = (int) ((1/dfCurrentMag) * 1.0000001);
+
+    nXOff *= nZoomMag;
+    nYOff *= nZoomMag;
+    nXSize *= nZoomMag;
+    nYSize *= nZoomMag;
+
+/* -------------------------------------------------------------------- */
+/*      We need to figure out the best zoom level to use for this       */
+/*      request.  We apply a small fudge factor to make sure that       */
+/*      request just very, very slightly larger than a zoom level do    */
+/*      not force us to the next level.                                 */
+/* -------------------------------------------------------------------- */
+    int iOverview = 0;
+    double dfZoomMag = MIN((nXSize / (double)nBufXSize), 
+                           (nYSize / (double)nBufYSize));
+
+    for( nZoomMag = 1; 
+         nZoomMag * 2 < (dfZoomMag + 0.1) 
+             && iOverview < poParentDS->nOverviewCount; 
+         nZoomMag *= 2, iOverview++ ) {}
+
+/* -------------------------------------------------------------------- */
+/*      Work out the size of the temporary buffer and allocate it.      */
+/*      The temporary buffer will generally be at a moderately          */
+/*      higher resolution than the buffer of data requested.            */
+/* -------------------------------------------------------------------- */
+    int  nTmpPixelSize;
+    LTIPixel       oPixel( eColorSpace, nBands, eSampleType );
+    
+    LT_STATUS eLTStatus;
+    unsigned int maxWidth;
+    unsigned int maxHeight;
+
+    eLTStatus = poImageReader->getDimsAtMag(1.0/nZoomMag,maxWidth,maxHeight);
+
+    if( !LT_SUCCESS(eLTStatus)) {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "MrSIDDataset::IRasterIO(): Failed to get zoomed image dimensions.\n%s",
+                  getLastStatusString( eLTStatus ) );
+        return CE_Failure;
+    }
+
+    int maxWidthAtL0 = bIsOverview?poParentDS->GetRasterXSize():this->GetRasterXSize();
+    int maxHeightAtL0 = bIsOverview?poParentDS->GetRasterYSize():this->GetRasterYSize();
+
+    int sceneUlXOff = nXOff / nZoomMag;
+    int sceneUlYOff = nYOff / nZoomMag;
+    int sceneWidth  = (int)(nXSize * maxWidth / (double)maxWidthAtL0 + 0.99);
+    int sceneHeight = (int)(nYSize * maxHeight / (double)maxHeightAtL0 + 0.99);
+
+    if( (sceneUlXOff + sceneWidth) > (int) maxWidth )
+        sceneWidth = maxWidth - sceneUlXOff;
+
+    if( (sceneUlYOff + sceneHeight) > (int) maxHeight )
+        sceneHeight = maxHeight - sceneUlYOff;
+
+    LTISceneBuffer oLTIBuffer( oPixel, sceneWidth, sceneHeight, NULL );
+
+    nTmpPixelSize = GDALGetDataTypeSize( eDataType ) / 8;
+
+/* -------------------------------------------------------------------- */
+/*      Create navigator, and move to the requested scene area.         */
+/* -------------------------------------------------------------------- */
+    LTINavigator oNav( *poImageReader );
+    
+    if( !LT_SUCCESS(oNav.setSceneAsULWH( sceneUlXOff, sceneUlYOff, 
+                                         sceneWidth, sceneHeight, 
+                                         1.0 / nZoomMag )) )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "MrSIDDataset::IRasterIO(): Failed to set scene position." );
+
+        return CE_Failure;
+    }
+
+    CPLDebug( "MrSID", 
+              "Dataset:IRasterIO(%d,%d %dx%d -> %dx%d -> %dx%d, zoom=%d)",
+              nXOff, nYOff, nXSize, nYSize, 
+              sceneWidth, sceneHeight,
+              nBufXSize, nBufYSize, 
+              nZoomMag );
+
+    if( !oNav.isSceneValid() )
+        CPLDebug( "MrSID", "LTINavigator in invalid state." );
+
+#ifdef notdef
+    {
+        lt_uint32 iWidth, iHeight;
+        
+        poImageReader->getDimsAtMag( dfCurrentMag, iWidth, iHeight );
+    }
+#endif
+
+/* -------------------------------------------------------------------- */
+/*      Read into the buffer.                                           */
+/* -------------------------------------------------------------------- */
+
+    eLTStatus = poImageReader->read(oNav.getScene(),oLTIBuffer);
+    if(!LT_SUCCESS(eLTStatus) )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "MrSIDRasterBand::IRasterIO(): Failed to load image.\n%s",
+                  getLastStatusString( eLTStatus ) );
+        return CE_Failure;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Manually resample to our target buffer.                         */
+/* -------------------------------------------------------------------- */
+    int         iBufLine, iBufPixel;
+
+    for( iBufLine = 0; iBufLine < nBufYSize; iBufLine++ )
+    {
+        int iTmpLine = (int) floor(((iBufLine+0.5) / nBufYSize) * sceneHeight);
+
+        for( iBufPixel = 0; iBufPixel < nBufXSize; iBufPixel++ )
+        {
+            int iTmpPixel = (int) 
+                floor(((iBufPixel+0.5) / nBufXSize) * sceneWidth);
+
+            for( int iBand = 0; iBand < nBandCount; iBand++ )
+            {
+                GByte *pabySrc, *pabyDst;
+
+                pabyDst = ((GByte *) pData) 
+                    + nPixelSpace * iBufPixel
+                    + nLineSpace * iBufLine
+                    + nBandSpace * iBand;
+
+                pabySrc = (GByte *) oLTIBuffer.getTotalBandData( 
+                    panBandMap[iBand] - 1 );
+                pabySrc += (iTmpLine * sceneWidth + iTmpPixel) * nTmpPixelSize;
+
+                if( eDataType == eBufType )
+                    memcpy( pabyDst, pabySrc, nTmpPixelSize );
+                else
+                    GDALCopyWords( pabySrc, eDataType, 0, 
+                                   pabyDst, eBufType, 0, 1 );
+            }
+        }
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr MrSIDDataset::GetGeoTransform( double * padfTransform )
+{
+    if( bHasGeoTransform )
+    {
+        memcpy( padfTransform, adfGeoTransform, sizeof(adfGeoTransform[0]) * 6 );
+        return CE_None;
+    }
+    else
+        return GDALPamDataset::GetGeoTransform( padfTransform );
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *MrSIDDataset::GetProjectionRef()
+{
+    if( pszProjection && strlen(pszProjection) > 0 )
+        return pszProjection;
+    else
+        return GDALPamDataset::GetProjectionRef();
+}
+
+/************************************************************************/
+/*                        SerializeMetadataRec()                        */
+/************************************************************************/
+
+char *MrSIDDataset::SerializeMetadataRec( const LTIMetadataRecord *poMetadataRec )
+{
+    GUInt32  iNumDims = 0;
+    const GUInt32  *paiDims = NULL;
+    const void     *pData = poMetadataRec->getArrayData( iNumDims, paiDims );
+    GUInt32        i, j, k = 0, iLength;
+    const char     *pszTemp = NULL;
+    char           *pszMetadata = CPLStrdup( "" );
+
+    for ( i = 0; i < iNumDims; i++ )
+        for ( j = 0; j < paiDims[i]; j++ )
+        {
+            switch( poMetadataRec->getDataType() )
+            {
+                case LTI_METADATA_DATATYPE_UINT8:
+                case LTI_METADATA_DATATYPE_SINT8:
+                    pszTemp = CPLSPrintf( "%d", ((GByte *)pData)[k++] );
+                    break;
+                case LTI_METADATA_DATATYPE_UINT16:
+                    pszTemp = CPLSPrintf( "%u", ((GUInt16 *)pData)[k++] );
+                    break;
+                case LTI_METADATA_DATATYPE_SINT16:
+                    pszTemp = CPLSPrintf( "%d", ((GInt16 *)pData)[k++] );
+                    break;
+                case LTI_METADATA_DATATYPE_UINT32:
+                    pszTemp = CPLSPrintf( "%lu", ((unsigned long *)pData)[k++] );
+                    break;
+                case LTI_METADATA_DATATYPE_SINT32:
+                    pszTemp = CPLSPrintf( "%ld", ((long *)pData)[k++] );
+                    break;
+                case LTI_METADATA_DATATYPE_FLOAT32:
+                    pszTemp = CPLSPrintf( "%f", ((float *)pData)[k++] );
+                    break;
+                case LTI_METADATA_DATATYPE_FLOAT64:
+                    pszTemp = CPLSPrintf( "%lf", ((double *)pData)[k++] );
+                    break;
+                case LTI_METADATA_DATATYPE_ASCII:
+                    pszTemp = ((const char **)pData)[k++];
+                    break;
+                default:
+                    pszTemp = "";
+                    break;
+            }
+
+            iLength = strlen(pszMetadata) + strlen(pszTemp) + 2;
+
+            pszMetadata = (char *)CPLRealloc( pszMetadata, iLength );
+            if ( !EQUAL( pszMetadata, "" ) )
+                strncat( pszMetadata, ",", 1 );
+            strncat( pszMetadata, pszTemp, iLength );
+        }
+
+    return pszMetadata;
+}
+
+/************************************************************************/
+/*                          GetMetadataElement()                        */
+/************************************************************************/
+
+int MrSIDDataset::GetMetadataElement( const char *pszKey, void *pValue,
+                                      int iLength )
+{
+    if ( !poMetadata->has( pszKey ) )
+        return FALSE;
+
+    const LTIMetadataRecord *poMetadataRec = NULL;
+    poMetadata->get( pszKey, poMetadataRec );
+
+    if ( !poMetadataRec->isScalar() )
+	return FALSE;
+
+    // XXX: return FALSE if we have more than one element in metadata record
+    int iSize;
+    switch( poMetadataRec->getDataType() )
+    {
+         case LTI_METADATA_DATATYPE_UINT8:
+         case LTI_METADATA_DATATYPE_SINT8:
+	     iSize = 1;
+	     break;
+         case LTI_METADATA_DATATYPE_UINT16:
+         case LTI_METADATA_DATATYPE_SINT16:
+             iSize = 2;
+             break;
+         case LTI_METADATA_DATATYPE_UINT32:
+         case LTI_METADATA_DATATYPE_SINT32:
+         case LTI_METADATA_DATATYPE_FLOAT32:
+             iSize = 4;
+             break;
+         case LTI_METADATA_DATATYPE_FLOAT64:
+             iSize = 8;
+             break;
+	 case LTI_METADATA_DATATYPE_ASCII:
+	     iSize = iLength;
+	     break;
+         default:
+             iSize = 0;
+             break;
+    }
+
+    if ( poMetadataRec->getDataType() == LTI_METADATA_DATATYPE_ASCII )
+    {
+	strncpy( (char *)pValue,
+		 ((const char**)poMetadataRec->getScalarData())[0], iSize );
+        ((char *)pValue)[iSize - 1] = '\0';
+    }
+    else
+        memcpy( pValue, poMetadataRec->getScalarData(), iSize );
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                             OpenZoomLevel()                          */
+/************************************************************************/
+
+CPLErr MrSIDDataset::OpenZoomLevel( lt_int32 iZoom )
+{
+/* -------------------------------------------------------------------- */
+/*      Get image geometry.                                            */
+/* -------------------------------------------------------------------- */
+    if ( iZoom != 0 )
+    {
+        lt_uint32 iWidth, iHeight;
+        dfCurrentMag = LTIUtils::levelToMag( iZoom );
+        poImageReader->getDimsAtMag( dfCurrentMag, iWidth, iHeight );
+	nRasterXSize = iWidth;
+	nRasterYSize = iHeight;
+    }
+    else
+    {
+        dfCurrentMag = 1.0;
+        nRasterXSize = poImageReader->getWidth();
+        nRasterYSize = poImageReader->getHeight();
+    }
+
+    nBands = poImageReader->getNumBands();
+    nBlockXSize = nRasterXSize;
+    nBlockYSize = poImageReader->getStripHeight();
+
+    CPLDebug( "MrSID", "Opened zoom level %d with size %dx%d.\n",
+              iZoom, nRasterXSize, nRasterYSize );
+
+    try
+    {
+        poLTINav = new LTINavigator( *poImageReader );
+    }
+    catch ( ... )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "MrSIDDataset::OpenZoomLevel(): "
+                  "Failed to create LTINavigator object." );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*  Handle sample type and color space.                                 */
+/* -------------------------------------------------------------------- */
+    eColorSpace = poImageReader->getColorSpace();
+    eSampleType = poImageReader->getDataType();
+    switch ( eSampleType )
+    {
+      case LTI_DATATYPE_UINT16:
+        eDataType = GDT_UInt16;
+        break;
+      case LTI_DATATYPE_SINT16:
+        eDataType = GDT_Int16;
+        break;
+      case LTI_DATATYPE_UINT32:
+        eDataType = GDT_UInt32;
+        break;
+      case LTI_DATATYPE_SINT32:
+        eDataType = GDT_Int32;
+        break;
+      case LTI_DATATYPE_FLOAT32:
+        eDataType = GDT_Float32;
+        break;
+      case LTI_DATATYPE_FLOAT64:
+        eDataType = GDT_Float64;
+        break;
+      case LTI_DATATYPE_UINT8:
+      case LTI_DATATYPE_SINT8:
+      default:
+        eDataType = GDT_Byte;
+        break;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read georeferencing.                                            */
+/* -------------------------------------------------------------------- */
+    if ( !poImageReader->isGeoCoordImplicit() )
+    {
+        const LTIGeoCoord& oGeo = poImageReader->getGeoCoord();
+        oGeo.get( adfGeoTransform[0], adfGeoTransform[3],
+	          adfGeoTransform[1], adfGeoTransform[5],
+	          adfGeoTransform[2], adfGeoTransform[4] );
+        
+        adfGeoTransform[0] = adfGeoTransform[0] - adfGeoTransform[1] / 2;
+        adfGeoTransform[3] = adfGeoTransform[3] - adfGeoTransform[5] / 2;
+	bHasGeoTransform = TRUE;
+    }
+    else if( iZoom == 0 )
+    {
+        bHasGeoTransform = 
+            GDALReadWorldFile( GetDescription(), ".sdw",  
+                               adfGeoTransform )
+            || GDALReadWorldFile( GetDescription(), ".sidw", 
+                                  adfGeoTransform )
+            || GDALReadWorldFile( GetDescription(), ".wld", 
+                                  adfGeoTransform );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Read wkt.                                                       */
+/* -------------------------------------------------------------------- */
+#ifdef MRSID_HAVE_GETWKT
+    if( !poImageReader->isGeoCoordImplicit() )
+    {
+	const LTIGeoCoord& oGeo = poImageReader->getGeoCoord();
+	
+	if( oGeo.getWKT() )
+            pszProjection =  CPLStrdup( oGeo.getWKT() );
+    }
+#endif // HAVE_MRSID_GETWKT
+
+/* -------------------------------------------------------------------- */
+/*      Read NoData value.                                              */
+/* -------------------------------------------------------------------- */
+    poNDPixel = poImageReader->getNoDataPixel();
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    int             iBand;
+
+    for( iBand = 1; iBand <= nBands; iBand++ )
+        SetBand( iBand, new MrSIDRasterBand( this, iBand ) );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                             MrSIDOpen()                              */
+/*                                                                      */
+/*      This is just a jacket to verify that the file is MrSID.         */
+/************************************************************************/
+
+static GDALDataset *MrSIDOpen( GDALOpenInfo * poOpenInfo )
+{
+    if( poOpenInfo->nHeaderBytes < 32 )
+        return NULL;
+
+    if ( !EQUALN((const char *) poOpenInfo->pabyHeader, "msid", 4) )
+        return NULL;
+
+    return MrSIDDataset::Open( poOpenInfo );
+}
+
+/************************************************************************/
+/*                              JP2Open()                               */
+/*                                                                      */
+/*      This is just a jacket to verify that the file is JPEG2000.      */
+/************************************************************************/
+
+static unsigned char jpc_header[] = 
+{0xff,0x4f};
+
+#ifdef MRSID_J2K
+
+static GDALDataset *JP2Open( GDALOpenInfo *poOpenInfo )
+
+{
+    if( poOpenInfo->nHeaderBytes < 32 )
+        return NULL;
+
+    if( memcmp( poOpenInfo->pabyHeader, jpc_header, sizeof(jpc_header) ) == 0 )
+    {
+        const char *pszExtension;
+
+        pszExtension = CPLGetExtension( poOpenInfo->pszFilename );
+        
+        if( !EQUAL(pszExtension,"jpc") && !EQUAL(pszExtension,"j2k") 
+            && !EQUAL(pszExtension,"jp2") && !EQUAL(pszExtension,"jpx") 
+            && !EQUAL(pszExtension,"j2c") )
+            return NULL;
+    }
+    else if( !EQUALN((const char *) poOpenInfo->pabyHeader + 4, "jP  ", 4) )
+        return NULL;
+
+    return MrSIDDataset::Open( poOpenInfo );
+}
+#endif /* def MRSID_J2K */
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *MrSIDDataset::Open( GDALOpenInfo * poOpenInfo )
+{
+    int     bIsJP2 = FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Is this a mrsid or jpeg 2000 file?                              */
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->nHeaderBytes < 32 )
+        return NULL;
+
+    if( EQUALN((const char *) poOpenInfo->pabyHeader + 4, "jP  ", 4) )
+        bIsJP2 = TRUE;
+
+    else if( memcmp( poOpenInfo->pabyHeader, jpc_header, 
+                     sizeof(jpc_header) ) == 0 )
+        bIsJP2 = TRUE;
+
+    else if ( !EQUALN((const char *) poOpenInfo->pabyHeader, "msid", 4) )
+        return NULL;
+
+    if(poOpenInfo->fp)
+    {
+        VSIFClose( poOpenInfo->fp );
+        poOpenInfo->fp = NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    MrSIDDataset        *poDS;
+    const LTFileSpec    oFileSpec( poOpenInfo->pszFilename );
+
+    poDS = new MrSIDDataset();
+#ifdef MRSID_J2K
+    if ( bIsJP2 )
+        poDS->poImageReader = new J2KImageReader( oFileSpec, true );
+    else
+#endif
+        poDS->poImageReader = new MrSIDImageReader( oFileSpec );
+
+    if ( !LT_SUCCESS( poDS->poImageReader->initialize() ) )
+    {
+        delete poDS;
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "MrSIDDataset::Open(): Failed to open file %s",
+                  poOpenInfo->pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read metadata.                                                  */
+/* -------------------------------------------------------------------- */
+    poDS->poMetadata =
+        new LTIMetadataDatabase( poDS->poImageReader->getMetadata() );
+    const GUInt32       iNumRecs = poDS->poMetadata->getIndexCount();
+    GUInt32             i;
+
+    for ( i = 0; i < iNumRecs; i++ )
+    {
+        const LTIMetadataRecord *poMetadataRec = NULL;
+        if ( LT_SUCCESS(poDS->poMetadata->getDataByIndex(i, poMetadataRec)) )
+	{
+            char    *pszElement = poDS->SerializeMetadataRec( poMetadataRec );
+            char    *pszKey = CPLStrdup( poMetadataRec->getTagName() );
+            char    *pszTemp = pszKey;
+
+            // GDAL metadata keys should not contain ':' and '=' characters.
+            // We will replace them with '_'.
+            do
+            {
+                if ( *pszTemp == ':' || *pszTemp == '=' )
+                    *pszTemp = '_';
+            }
+            while ( *++pszTemp );
+
+            poDS->SetMetadataItem( pszKey, pszElement );
+
+            CPLFree( pszElement );
+            CPLFree( pszKey );
+	}
+    }
+
+    poDS->GetGTIFDefn();
+    
+/* -------------------------------------------------------------------- */
+/*      Get number of resolution levels (we will use them as overviews).*/
+/* -------------------------------------------------------------------- */
+#ifdef MRSID_J2K
+    if( bIsJP2 )
+        poDS->nOverviewCount
+            = ((J2KImageReader *) (poDS->poImageReader))->getNumLevels();
+    else
+#endif
+        poDS->nOverviewCount
+            = ((MrSIDImageReader *) (poDS->poImageReader))->getNumLevels();
+
+    if ( poDS->nOverviewCount > 0 )
+    {
+        lt_int32        i;
+
+        poDS->papoOverviewDS = (MrSIDDataset **)
+            CPLMalloc( poDS->nOverviewCount * (sizeof(void*)) );
+
+        for ( i = 0; i < poDS->nOverviewCount; i++ )
+        {
+            poDS->papoOverviewDS[i] = new MrSIDDataset();
+            poDS->papoOverviewDS[i]->poImageReader = poDS->poImageReader;
+            poDS->papoOverviewDS[i]->OpenZoomLevel( i + 1 );
+            poDS->papoOverviewDS[i]->bIsOverview = TRUE;
+            poDS->papoOverviewDS[i]->poParentDS = poDS;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create object for the whole image.                              */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->OpenZoomLevel( 0 );
+
+    CPLDebug( "MrSID",
+              "Opened image: width %d, height %d, bands %d",
+              poDS->nRasterXSize, poDS->nRasterYSize, poDS->nBands );
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+#ifdef MRSID_ESDK
+
+/************************************************************************/
+/* ==================================================================== */
+/*                        MrSIDDummyImageReader                         */
+/*                                                                      */
+/*  This is a helper class to wrap GDAL calls in MrSID interface.       */
+/* ==================================================================== */
+/************************************************************************/
+
+class MrSIDDummyImageReader : public LTIImageReader
+{
+  public:
+
+    MrSIDDummyImageReader( GDALDataset *poSrcDS );
+    ~MrSIDDummyImageReader();
+    LT_STATUS           initialize();
+    lt_int64            getPhysicalFileSize(void) const { return 0; };
+
+  private:
+    GDALDataset         *poDS;
+    GDALDataType        eDataType;
+    LTIDataType         eSampleType;
+    const LTIPixel      *poPixel;
+    
+    double              adfGeoTransform[6];
+
+    virtual LT_STATUS   decodeStrip( LTISceneBuffer& stripBuffer,
+                                     const LTIScene& stripScene );
+    virtual LT_STATUS   decodeBegin( const LTIScene& scene )
+                            { return LT_STS_Success; };
+    virtual LT_STATUS   decodeEnd() { return LT_STS_Success; };
+};
+
+/************************************************************************/
+/*                        MrSIDDummyImageReader()                       */
+/************************************************************************/
+
+MrSIDDummyImageReader::MrSIDDummyImageReader( GDALDataset *poSrcDS ) :
+                                            LTIImageReader(), poDS(poSrcDS)
+{
+    poPixel = NULL;
+}
+
+/************************************************************************/
+/*                        ~MrSIDDummyImageReader()                      */
+/************************************************************************/
+
+MrSIDDummyImageReader::~MrSIDDummyImageReader()
+{
+    if ( poPixel )
+        delete poPixel;
+}
+
+/************************************************************************/
+/*                             initialize()                             */
+/************************************************************************/
+
+LT_STATUS MrSIDDummyImageReader::initialize()
+{
+    if ( !LT_SUCCESS(LTIImageReader::initialize()) )
+        return LT_STS_Failure;
+    
+    lt_uint16 nBands = (lt_uint16)poDS->GetRasterCount();
+    LTIColorSpace eColorSpace = LTI_COLORSPACE_RGB;
+    switch ( nBands )
+    {
+        case 1:
+            eColorSpace = LTI_COLORSPACE_GRAYSCALE;
+            break;
+        case 3:
+            eColorSpace = LTI_COLORSPACE_RGB;
+            break;
+        default:
+            eColorSpace = LTI_COLORSPACE_MULTISPECTRAL;
+            break;
+    }
+    
+    eDataType = poDS->GetRasterBand(1)->GetRasterDataType();
+    switch ( eDataType )
+    {
+        case GDT_UInt16:
+            eSampleType = LTI_DATATYPE_UINT16;
+            break;
+        case GDT_Int16:
+            eSampleType = LTI_DATATYPE_SINT16;
+            break;
+        case GDT_UInt32:
+            eSampleType = LTI_DATATYPE_UINT32;
+            break;
+        case GDT_Int32:
+            eSampleType = LTI_DATATYPE_SINT32;
+            break;
+        case GDT_Float32:
+            eSampleType = LTI_DATATYPE_FLOAT32;
+            break;
+        case GDT_Float64:
+            eSampleType = LTI_DATATYPE_FLOAT64;
+            break;
+        case GDT_Byte:
+        default:
+            eSampleType = LTI_DATATYPE_UINT8;
+            break;
+    }
+
+    poPixel = new LTIPixel( eColorSpace, nBands, eSampleType );
+    if ( !LT_SUCCESS(setPixelProps(*poPixel)) )
+        return LT_STS_Failure;
+
+    if ( !LT_SUCCESS(setDimensions(poDS->GetRasterXSize(),
+                                   poDS->GetRasterYSize())) )
+        return LT_STS_Failure;
+
+    if ( poDS->GetGeoTransform( adfGeoTransform ) == CE_None )
+    {
+#ifdef MRSID_SDK_40
+        LTIGeoCoord oGeo( adfGeoTransform[0] + adfGeoTransform[1] / 2,
+                          adfGeoTransform[3] + adfGeoTransform[5] / 2,
+                          adfGeoTransform[1], adfGeoTransform[5],
+                          adfGeoTransform[2], adfGeoTransform[4], NULL,
+                          poDS->GetProjectionRef() );
+#else
+        LTIGeoCoord oGeo( adfGeoTransform[0] + adfGeoTransform[1] / 2,
+                          adfGeoTransform[3] + adfGeoTransform[5] / 2,
+                          adfGeoTransform[1], adfGeoTransform[5],
+                          adfGeoTransform[2], adfGeoTransform[4], 
+                          poDS->GetProjectionRef() );
+#endif
+        if ( !LT_SUCCESS(setGeoCoord( oGeo )) )
+            return LT_STS_Failure;
+    }
+
+    /*int     bSuccess;
+    double  dfNoDataValue = poDS->GetNoDataValue( &bSuccess );
+    if ( bSuccess )
+    {
+        LTIPixel    oNoDataPixel( *poPixel );
+        lt_uint16   iBand;
+
+        for (iBand = 0; iBand < (lt_uint16)poDS->GetRasterCount(); iBand++)
+            oNoDataPixel.setSampleValueFloat32( iBand, dfNoDataValue );
+        if ( !LT_SUCCESS(setNoDataPixel( &oNoDataPixel )) )
+            return LT_STS_Failure;
+    }*/
+
+    setDefaultDynamicRange();
+    setClassicalMetadata();
+
+    return LT_STS_Success;
+}
+
+/************************************************************************/
+/*                             decodeStrip()                            */
+/************************************************************************/
+
+LT_STATUS MrSIDDummyImageReader::decodeStrip(LTISceneBuffer& stripData,
+                                             const LTIScene& stripScene)
+{
+    const lt_int32  nXOff = stripScene.getUpperLeftCol();
+    const lt_int32  nYOff = stripScene.getUpperLeftRow();
+    const lt_int32  nBufXSize = stripScene.getNumCols();
+    const lt_int32  nBufYSize = stripScene.getNumRows();
+    const lt_uint16 nBands = poPixel->getNumBands();
+
+    void    *pData = CPLMalloc(nBufXSize * nBufYSize * poPixel->getNumBytes());
+    if ( !pData )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "MrSIDDummyImageReader::decodeStrip(): "
+                  "Cannot allocate enough space for scene buffer" );
+        return LT_STS_Failure;
+    }
+
+    poDS->RasterIO( GF_Read, nXOff, nYOff, nBufXSize, nBufYSize, 
+                    pData, nBufXSize, nBufYSize, eDataType, nBands, NULL, 
+                    0, 0, 0 );
+    stripData.importDataBSQ( pData );
+
+    CPLFree( pData );
+
+    return LT_STS_Success;
+}
+
+
+/************************************************************************/
+/*                             FlushCache()                             */
+/************************************************************************/
+
+void MrSIDDataset::FlushCache()
+
+{
+    GDALDataset::FlushCache();
+}
+
+/************************************************************************/
+/*                          MrSIDCreateCopy()                           */
+/************************************************************************/
+
+static GDALDataset *
+MrSIDCreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
+                 int bStrict, char ** papszOptions, 
+                 GDALProgressFunc pfnProgress, void * pProgressData )
+
+{ 
+    const char* pszVersion = CSLFetchNameValue(papszOptions, "VERSION");
+    LT_STATUS eStat;
+
+#ifdef DEBUG
+    bool bMeter = false;
+#else
+    bool bMeter = true;
+#endif    
+
+    // Output Mrsid Version 2 file.
+    if( pszVersion && atoi(pszVersion) == 2 )
+    {
+        int nXSize = poSrcDS->GetRasterXSize();
+        int nYSize = poSrcDS->GetRasterYSize();
+      
+        if( !pfnProgress( 0.0, NULL, pProgressData ) )
+            return NULL;
+      
+        // Create the file.                                               
+        MrSIDDummyImageReader oImageReader( poSrcDS );
+        eStat = oImageReader.initialize();
+        if( eStat != LT_STS_Success )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "MrSIDDummyImageReader.Initialize failed.\n%s",
+                      getLastStatusString( eStat ) );
+            return NULL;
+        }
+      
+        MG2ImageWriter oImageWriter(&oImageReader);
+        eStat = oImageWriter.initialize();
+        if( eStat != LT_STS_Success )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "MG3ImageWriter.initialize() failed.\n%s",
+                      getLastStatusString( eStat ) );
+            return NULL;
+        }
+
+        oImageWriter.setUsageMeterEnabled(bMeter);
+   
+        // set output filename
+        oImageWriter.setOutputFileSpec( pszFilename );
+
+        // Set defaults
+        oImageWriter.params().setBlockSize(oImageWriter.params().getBlockSize());
+        oImageWriter.setStripHeight(oImageWriter.getStripHeight());
+
+        // check for compression option
+        const char* pszValue = CSLFetchNameValue(papszOptions, "COMPRESSION");
+        if( pszValue != NULL )
+            oImageWriter.params().setCompressionRatio( atof(pszValue) );
+
+        // set MrSID world file
+        if( CSLFetchNameValue(papszOptions, "WORLDFILE") != NULL )
+            oImageWriter.setWorldFileSupport( true );
+      
+        // write the scene
+        const LTIScene oScene( 0, 0, nXSize, nYSize, 1.0 );
+        eStat = oImageWriter.write( oScene );
+        if( eStat != LT_STS_Success )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "MG2ImageWriter.write() failed.\n%s",
+                      getLastStatusString( eStat ) );
+            return NULL;
+        }
+    }
+    // Output Mrsid Version 3 file.
+    else
+    {
+        int nXSize = poSrcDS->GetRasterXSize();
+        int nYSize = poSrcDS->GetRasterYSize();
+      
+        if( !pfnProgress( 0.0, NULL, pProgressData ) )
+            return NULL;
+      
+        // Create the file.   
+        MrSIDDummyImageReader oImageReader( poSrcDS );
+        eStat = oImageReader.initialize();
+        if( eStat != LT_STS_Success )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "MrSIDDummyImageReader.Initialize failed.\n%s",
+                      getLastStatusString( eStat ) );
+            return NULL;
+        }
+      
+        MG3ImageWriter oImageWriter(&oImageReader);
+        eStat = oImageWriter.initialize();
+        if( eStat != LT_STS_Success )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "MG3ImageWriter.initialize() failed.\n%s",
+                      getLastStatusString( eStat ) );
+            return NULL;
+        }
+      
+        // Set 64-bit Interface for large files.
+        oImageWriter.setFileStream64(true);
+
+        oImageWriter.setUsageMeterEnabled(bMeter);
+      
+        // set output filename
+        oImageWriter.setOutputFileSpec( pszFilename );
+
+        // Set defaults
+        oImageWriter.setStripHeight(oImageWriter.getStripHeight());
+
+        // set 2 pass optimizer option
+        if( CSLFetchNameValue(papszOptions, "TWOPASS") != NULL )
+            oImageWriter.params().setTwoPassOptimizer( true );
+
+        // set MrSID world file
+        if( CSLFetchNameValue(papszOptions, "WORLDFILE") != NULL )
+            oImageWriter.setWorldFileSupport( true );
+      
+        const char* pszValue;
+      
+        // set filesize in KB
+        pszValue = CSLFetchNameValue(papszOptions, "FILESIZE");
+        if( pszValue != NULL )
+            oImageWriter.params().setTargetFilesize( atoi(pszValue) );
+	
+        // write the scene
+        const LTIScene oScene( 0, 0, nXSize, nYSize, 1.0 );
+        eStat = oImageWriter.write( oScene );
+        if( eStat != LT_STS_Success )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "MG2ImageWriter.write() failed.\n%s",
+                      getLastStatusString( eStat ) );
+            return NULL;
+        }
+    }
+  
+/* -------------------------------------------------------------------- */
+/*      Re-open dataset, and copy any auxilary pam information.         */
+/* -------------------------------------------------------------------- */
+    GDALPamDataset *poDS = (GDALPamDataset *) 
+        GDALOpen( pszFilename, GA_ReadOnly );
+
+    if( poDS )
+        poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
+
+    return poDS;
+}
+
+#ifdef MRSID_J2K
+/************************************************************************/
+/*                           JP2CreateCopy()                            */
+/************************************************************************/
+
+static GDALDataset *
+JP2CreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
+                 int bStrict, char ** papszOptions, 
+                 GDALProgressFunc pfnProgress, void * pProgressData )
+
+{ 
+#ifdef DEBUG
+    bool bMeter = false;
+#else
+    bool bMeter = true;
+#endif    
+
+    int nXSize = poSrcDS->GetRasterXSize();
+    int nYSize = poSrcDS->GetRasterYSize();
+    LT_STATUS  eStat;
+      
+    if( !pfnProgress( 0.0, NULL, pProgressData ) )
+        return NULL;
+      
+    // Create the file.   
+    MrSIDDummyImageReader oImageReader( poSrcDS );
+    eStat = oImageReader.initialize();
+    if( eStat != LT_STS_Success )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "MrSIDDummyImageReader.Initialize failed.\n%s",
+                  getLastStatusString( eStat ) );
+        return NULL;
+    }
+      
+#ifdef MRSID_POST5
+    JP2WriterManager oImageWriter(&oImageReader);
+#else
+    J2KImageWriter oImageWriter(&oImageReader);
+#endif
+    eStat = oImageWriter.initialize();
+    if( eStat != LT_STS_Success )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "J2KImageWriter.Initialize failed.\n%s",
+                  getLastStatusString( eStat ) );
+        return NULL;
+    }
+      
+    // Set 64-bit Interface for large files.
+    oImageWriter.setFileStream64(true);
+
+    oImageWriter.setUsageMeterEnabled(bMeter);
+      
+    // set output filename
+    oImageWriter.setOutputFileSpec( pszFilename );
+
+    // Set defaults
+    //oImageWriter.setStripHeight(oImageWriter.getStripHeight());
+
+    // set MrSID world file
+    if( CSLFetchNameValue(papszOptions, "WORLDFILE") != NULL )
+        oImageWriter.setWorldFileSupport( true );
+      
+    // check for compression option
+    const char* pszValue = CSLFetchNameValue(papszOptions, "COMPRESSION");
+    if( pszValue != NULL )
+        oImageWriter.params().setCompressionRatio( atof(pszValue) );
+	
+    pszValue = CSLFetchNameValue(papszOptions, "XMLPROFILE");
+    if( pszValue != NULL )
+    {
+        LTFileSpec xmlprofile(pszValue);
+        eStat = oImageWriter.params().readProfile(xmlprofile);
+        if( eStat != LT_STS_Success )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "JPCWriterParams.readProfile failed.\n%s",
+                      getLastStatusString( eStat ) );
+            return NULL;
+        }
+    }
+
+    // write the scene
+    const LTIScene oScene( 0, 0, nXSize, nYSize, 1.0 );
+    eStat = oImageWriter.write( oScene );
+    if( eStat != LT_STS_Success )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "J2KImageWriter.write() failed.\n%s",
+                  getLastStatusString( eStat ) );
+        return NULL;
+    }
+  
+/* -------------------------------------------------------------------- */
+/*      Re-open dataset, and copy any auxilary pam information.         */
+/* -------------------------------------------------------------------- */
+    GDALPamDataset *poDS = (GDALPamDataset *) 
+        GDALOpen( pszFilename, GA_ReadOnly );
+
+    if( poDS )
+        poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
+
+    return poDS;
+}
+#endif /* MRSID_J2K */
+#endif /* MRSID_ESDK */
+
+/************************************************************************/
+/*                        GDALRegister_MrSID()                          */
+/************************************************************************/
+
+void GDALRegister_MrSID()
+
+{
+    GDALDriver  *poDriver;
+
+/* -------------------------------------------------------------------- */
+/*      MrSID driver.                                                   */
+/* -------------------------------------------------------------------- */
+    if( GDALGetDriverByName( "MrSID" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+
+        poDriver->SetDescription( "MrSID" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
+                        "Multi-resolution Seamless Image Database (MrSID)" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_mrsid.html" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "sid" );
+
+#ifdef MRSID_ESDK
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte Int16 UInt16 Int32 UInt32 Float32 Float64" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
+"<CreationOptionList>"
+// Version 2 Options
+"   <Option name='COMPRESSION' type='double' description='Set compression ratio (0.0 default is meant to be lossless)'/>"
+// Version 3 Options
+"   <Option name='TWOPASS' type='int' description='Use twopass optimizer algorithm'/>"
+"   <Option name='FILESIZE' type='int' description='Set target file size (0 implies lossless compression)'/>"
+// Version 2 and 3 Option
+"   <Option name='WORLDFILE' type='boolean' description='Write out world file'/>"
+// Version Type
+"   <Option name='VERSION' type='int' description='Valid versions are 2 and 3, default = 3'/>"
+"</CreationOptionList>" );
+
+        poDriver->pfnCreateCopy = MrSIDCreateCopy;
+#endif
+
+        poDriver->pfnOpen = MrSIDOpen;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      JP2MRSID driver.                                                */
+/* -------------------------------------------------------------------- */
+#ifdef MRSID_J2K
+    if( GDALGetDriverByName( "JP2MrSID" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+
+        poDriver->SetDescription( "JP2MrSID" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
+                        "MrSID JPEG2000" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_jp2mrsid.html" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "jp2" );
+
+#ifdef MRSID_ESDK
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte Int16 UInt16" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
+"<CreationOptionList>"
+"   <Option name='COMPRESSION' type='double' description='Set compression ratio (0.0 default is meant to be lossless)'/>"
+"   <Option name='WORLDFILE' type='boolean' description='Write out world file'/>"
+"   <Option name='XMLPROFILE' type='string' description='Use named xml profile file'/>"
+"</CreationOptionList>" );
+
+        poDriver->pfnCreateCopy = JP2CreateCopy;
+#endif
+        poDriver->pfnOpen = JP2Open;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+#endif /* def MRSID_J2K */
+}
diff --git a/Utilities/GDAL/frmts/msg/GNUmakefile b/Utilities/GDAL/frmts/msg/GNUmakefile
new file mode 100644
index 0000000000..8256482592
--- /dev/null
+++ b/Utilities/GDAL/frmts/msg/GNUmakefile
@@ -0,0 +1,20 @@
+
+VPATH = PublicDecompWT/DISE/:PublicDecompWT/COMP/Src/:PublicDecompWT/COMP/WT/Src/
+
+include ../../GDALmake.opt
+
+OBJ1=$(patsubst %.cpp,%.o,$(wildcard PublicDecompWT/COMP/WT/Src/*.cpp))
+OBJ2=$(patsubst %.cpp,%.o,$(wildcard PublicDecompWT/COMP/Src/*.cpp))
+OBJ3=$(patsubst %.cpp,%.o,$(wildcard PublicDecompWT/DISE/*.cpp))
+WTOBJ=$(subst PublicDecompWT/COMP/WT/Src/, ,$(OBJ1)) $(subst PublicDecompWT/COMP/Src/, , $(OBJ2)) $(subst PublicDecompWT/DISE/, , $(OBJ3))
+
+OBJ	=	msgdataset.o xritheaderparser.o prologue.o msgcommand.o reflectancecalculator.o $(WTOBJ)
+
+CPPFLAGS	=	$(GDAL_INCLUDE) -I PublicDecompWT/DISE -I PublicDecompWT/COMP/WT/Inc -I PublicDecompWT/COMP/Inc -I.
+
+default:	$(OBJ) 
+
+clean:
+	rm -f *.o
+
+install-obj:	$(O_OBJ) 
diff --git a/Utilities/GDAL/frmts/msg/PublicDecompWTMakefiles.zip b/Utilities/GDAL/frmts/msg/PublicDecompWTMakefiles.zip
new file mode 100644
index 0000000000000000000000000000000000000000..63d40cd4316fbbcbb4523bbd8f656093cf505317
GIT binary patch
literal 1025
zcmWIWW@Zs#U}E54Na{~AVwY0u+seSe@Q#6jL79PpA)quVCo|b4H90@GAUs6h+21!n
zKe#AaKQ}QuH7zqIRj(}h@zYP`pB`;_T2iLv>+P+3>9qf;pn`8_e0}^|4Vb-!i;cXs
zYJ-Kly}N_D9h>&V?1@<?sOZ?2XV>@cAlEjoIlJanRT<8!Jjs>7Ca|-DZMW8oT^g;`
zza*tK8%=iXw)k~~{pas?hPO>C8Wkt3S+soqX8#r4tDCymViu<P#N{QKU65o|RZ!Jj
znc2D2QZ;OjmXw2d_NSskwddj?S{aRVJ9CokJW7lFvXbVB229NJ%Hneo^pIdc_`143
z$>`fP{pY6{7#Kns85q=X_&PiU-Q%$nY`G5^2(&#H`6R!{V#Ni%U7}V;mU%6ST5-{e
zBPdzMB)3bx`TxD{t3j>O_nue0^Wd{SnbOB3a>>BNz4YsuhdvB5CgsL3ypS?0yv_0}
zaC4{Gr%xvrsir+TZtPkbx;<J=CyCwqV%U-rsmnLJSXg%6`CPVV$`jdtMk0?_ckEa*
z`{UD!D_cJsyFIR*rNU%)Iph8&r*{3r6=#($y*7WB5H9x9wQ||6=%*|EemdW;z4H2S
zDBBIkOCkP7HTFOAS8ikuTD|AX!QzKDThnjrBZ9-MKgnoe(Vx`q3=9lk85kJkF@wX!
zGuRb50l4=0ayL0hw4B%eA#e6_L8II&wTc^m_yP?U^Eu_b6T7xed$!}>-!aO~-;Ip<
z-}kH&ea-u!;lI{ptt`pXBQLqsrv~_}(8*L*sr;jSWao>*`&@Qa3#T}#%@Dr!sk^Ae
z_xd5{&_x=~AH}K!ZZvN6|9tgQK%mTmHxXj4rXtpUf2~E&Ub=I>%#q{yGnYuy{f}>N
zSo2!c>4l_y<ol+bmO0x?cuf{nA3kc4TBR;>%b;tgwSd{eH{L7$@dtP_GKnzYPWlWC
z3=E763JlvCK`bIN0<wYFQWeM;h=JcQ0~MUJFw+>anb@KRVJ0gRMs7uqr~q$PHjoBp
P24;p&3=9lEnLs=MEntCm

literal 0
HcmV?d00001

diff --git a/Utilities/GDAL/frmts/msg/frmt_msg.html b/Utilities/GDAL/frmts/msg/frmt_msg.html
new file mode 100644
index 0000000000..2f70a52112
--- /dev/null
+++ b/Utilities/GDAL/frmts/msg/frmt_msg.html
@@ -0,0 +1,141 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><title>MSG -- Meteosat Second Generation</title></head><body style="background-color: rgb(255, 255, 255);">
+
+<h1>MSG -- Meteosat Second Generation<br>
+</h1>
+This driver implemets reading support for Meteosat Second
+Generation files. These are files with names like
+H-000-MSG1__-MSG1________-HRV______-000007___-200405311115-C_, commonly
+distributed into a folder structure with dates (e.g. 2004\05\31 for the
+file mentioned).<br>
+
+<br>
+
+The MSG files are wavelet-compressed. A decompression library licensed from Eumetsat is needed ("<font face="Arial,Helvetica"><font size="-1"><font color="#000000">Wavelet
+Transform Software</font></font></font>" -
+<a class="moz-txt-link-freetext" href="http://www.eumetsat.int/en/dps/helpdesk/tools.html#wavelet">http://www.eumetsat.int/en/dps/helpdesk/tools.html#wavelet</a>). Under some restrictions, this library is free to download.<br>
+<br>
+Part of the source of the file xrithdr_extr.c from XRIT2PIC is used to
+parse MSG headers. This source is licensed under the terms of the GNU
+General Public License as published by the Free Software Foundation.<br>
+<br>
+This driver is not "enabled" by default. See "Build Instructions" on how to include this driver in your GDAL library.<br>
+
+<h2>Build Instructions<br>
+</h2>Download the Eumetsat library for wavelet decompression. This is a
+file
+named PublicDecompWT.zip. Extract the content in a subdirectory with
+the same name (under frmts/msg). If you are building with Visual Studio
+6.0, extract the
+.vc makefiles for the PublicDecompWT from the file
+PublicDecompWTMakefiles.zip. If you build using the GNUMakefile and you
+find that some adjustments are needed in the makefile and/or the msg
+source files, please "commit" them. The Eumetsat library promises to be
+"platform indepentent", but as we are working with Microsoft Windows
+and Visual Studio 6.0, we did not have the facilities to check if the
+rest of the msg driver is. Furthermore, apply steps 4 to 7 from the <a href="http://www.gdal.org/gdal_drivertut.html">GDAL Driver Implementation Tutorial</a>, section "Adding Driver to GDAL Tree".<br>
+
+<h2>Specification of Source Dataset<br>
+</h2>
+
+It is possible to select individual files for opening. In this case,
+the driver will gather the files that correspond to the other strips of
+the same image, and correctly compose the image.<br>
+Example with gdal_translate.exe:<br>
+gdal_translate
+C:\hrit_a\2004\05\31\H-000-MSG1__-MSG1________-HRV______-000008___-200405311115-C_
+c:\output\myimage.tif<br>
+
+<br>
+
+It is also possible to use the following syntax for opening the MSG files:<br>
+
+<ul>
+
+  <li>MSG(source_folder,timestamp,(channel,channel,...,channel),use_root_folder,data_conversion,nr_cycles,step)</li>
+  <ul>
+    <li>source_folder: a path to a folder structure that contains the files</li>
+    <li>timestamp: 12 digits representing a date/time that identifies the 114 files of the 12 images of that time, e.g. 200501181200</li>
+    <li>channel: a number between 1 and 12, representing each of the 12
+available channels. When only specifying one channel, the brackets are
+optional.</li>
+    <li>use_root_folder: Y to indicate that the files reside directly
+into the source_folder specified. N to indicate that the files reside
+in date structured folders: source_folder/YYYY/MM/DD</li>
+    <li>data_conversion:</li>
+    <ul>
+      <li>N to keep the original 10 bits DN values. The result is UInt16.</li>
+      <li>B to convert to 8 bits (handy for GIF and JPEG images). The result is Byte.</li>
+      <li>R to perform radiometric calibration and get the result in mW/m2/sr/(cm-1)-1. The result is Float32.</li>
+      <li>L to perform radiometric calibration and get the result in W/m2/sr/um. The result is Float32.</li>
+      <li>T
+to get the reflectance for the visible bands (1, 2, 3 and 12) and the
+temprature in degrees Kelvin for the infrared bands (all other bands).
+The result is Float32.</li>
+    </ul>
+    <li>nr_cycles: a number that indicates the number of consecutive
+cycles to be included in the same file (time series). These are
+appended as additional bands.</li>
+    <li>step: a number that indicates what is the stepsize when
+multiple cycles are chosen. E.g. every 15 minutes: step = 1, every 30
+minutes: step = 2 etc. Note that the cycles are exactly 15 minutes
+apart, so you can not get images from times in-between (the step is an
+integer).</li>
+  </ul>
+</ul>
+Examples with gdal_translate.exe:<br>
+<br>
+Example call to fetch an MSG image of 200501181200 with bands 1, 2 and 3 in IMG format:<br>
+gdal_translate -of HFA MSG(\\pc2133-24002\RawData\,200501181200,(1,2,3),N,N,1,1) d:\output\outfile.img<br>
+<br>
+In JPG format, and converting the 10 bits image to 8 bits by dividing all values by 4:<br>
+gdal_translate -of JPEG MSG(\\pc2133-24002\RawData\,200501181200,(1,2,3),N,B,1,1) d:\output\outfile.jpg<br>
+<br>
+The same, but reordering the bands in the JPEG image to resemble RGB:<br>
+gdal_translate -of JPEG MSG(\\pc2133-24002\RawData\,200501181200,(3,2,1),N,B,1,1) d:\output\outfile.jpg<br>
+<br>
+Geotiff output, only band 2, original 10 bits values:<br>
+gdal_translate -of GTiff MSG(\\pc2133-24002\RawData\,200501181200,2,N,N,1,1) d:\output\outfile.tif<br>
+<br>
+Band 12:<br>
+gdal_translate -of GTiff MSG(\\pc2133-24002\RawData\,200501181200,12,N,N,1,1) d:\output\outfile.tif<br>
+<br>
+The same band 12 with radiometric calibration in mW/m2/sr/(cm-1)-1:<br>
+gdal_translate -of GTiff MSG(\\pc2133-24002\RawData\,200501181200,12,N,R,1,1) d:\output\outfile.tif<br>
+<br>
+Retrieve data from c:\hrit-data\2005\01\18 instead of \\pc2133-24002\RawData\... :<br>
+gdal_translate -of GTiff MSG(c:\hrit-data\2005\01\18,200501181200,12,Y,R,1,1) d:\output\outfile.tif<br>
+<br>
+Another option to do the same (note the difference in the Y and the N for the &#8220;use_root_folder&#8221; parameter:<br>
+gdal_translate -of GTiff MSG(c:\hrit-data\,200501181200,12,N,R,1,1) d:\output\outfile.tif<br>
+<br>
+Without radiometric calibration, but for 10 consecutive cycles (thus from 1200 to 1415):<br>
+gdal_translate -of GTiff MSG(c:\hrit-data\,200501181200,12,N,N,10,1) d:\output\outfile.tif<br>
+<br>
+10 cycles, but every hour (thus from 1200 to 2100):<br>
+gdal_translate -of GTiff MSG(c:\hrit-data\,200501181200,12,N,N,10,4) d:\output\outfile.tif<br>
+<br>
+10 cycles, every hour, and bands 3, 2 and 1:<br>
+gdal_translate -of GTiff MSG(c:\hrit-data\,200501181200,(3,2,1),N,N,10,4) d:\output\outfile.tif<br>
+<h2>Georeference and Projection<br>
+</h2>
+
+The images are using the Geostationary Satellite View projection. Most
+GIS packages don't recognize this projection (we only know of ILWIS
+that does have this projection), but gdalwarp.exe can be used to
+re-project the images.<br>
+
+<p>See Also:</p>
+<p>
+
+</p>
+<ul>
+
+<li> Implemented as <tt>gdal/frmts/msg/msgdataset.cpp</tt>.<p>
+
+</p></li><li><a href="http://www.eumetsat.int">http://www.eumetsat.int</a><p>
+
+</p></li>
+</ul>
+
+
+</body></html>
\ No newline at end of file
diff --git a/Utilities/GDAL/frmts/msg/makefile.vc b/Utilities/GDAL/frmts/msg/makefile.vc
new file mode 100644
index 0000000000..906fd23f22
--- /dev/null
+++ b/Utilities/GDAL/frmts/msg/makefile.vc
@@ -0,0 +1,26 @@
+
+OBJ	=	msgdataset.obj xritheaderparser.obj prologue.obj msgcommand.obj reflectancecalculator.obj
+
+EXTRAFLAGS = 	-I PublicDecompWT\DISE -I PublicDecompWT\COMP\WT\Inc -I PublicDecompWT\COMP\Inc
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ) waveletlib
+	copy *.obj ..\o
+
+waveletlib:
+	cd PublicDecompWT\COMP\WT\Src \
+	&& nmake /NOLOGO /f makefile.vc \
+	&& cd ..\..\..\..
+	cd PublicDecompWT\COMP\Src \
+	&& nmake /NOLOGO /f makefile.vc \
+	&& cd ..\..\..
+	cd PublicDecompWT\DISE \
+	&& nmake /NOLOGO /f makefile.vc \
+	&& cd ..\..
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/msg/msgcommand.cpp b/Utilities/GDAL/frmts/msg/msgcommand.cpp
new file mode 100644
index 0000000000..fa11b6b97b
--- /dev/null
+++ b/Utilities/GDAL/frmts/msg/msgcommand.cpp
@@ -0,0 +1,463 @@
+// msgcommand.cpp: implementation of the MSGCommand class.
+//
+//////////////////////////////////////////////////////////////////////
+
+/******************************************************************************
+ *
+ * Purpose:  Parse the src_dataset string that is meant for the MSG driver.
+ * Author:   Bas Retsios, retsios@itc.nl
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, ITC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+
+#include "msgcommand.h"
+
+#ifdef _WIN32
+#define PATH_SEP '\\'
+#else
+#define PATH_SEP '/'
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+#define min(a,b) (((a)<(b))?(a):(b))
+
+MSGCommand::MSGCommand()
+: sRootFolder("")
+, sTimeStamp("")
+, cDataConversion('N')
+, iNrCycles(1)
+, iStep(1)
+, fUseTimestampFolder(true)
+{
+  for (int i = 0; i < 12; ++i)
+    channel[i] = 0;
+}
+
+MSGCommand::~MSGCommand()
+{
+
+}
+
+std::string MSGCommand::sTrimSpaces(std::string str)
+{
+  int iStart = 0;
+
+  while ((iStart < str.length()) && (str[iStart] == ' '))
+    ++iStart;
+
+  int iLength = str.length() - iStart;
+  while ((iLength > 0) && (str[iStart + iLength - 1] == ' '))
+    --iLength;
+
+  return str.substr(iStart, iLength);
+}
+
+std::string MSGCommand::sNextTerm(std::string str, int & iPos)
+{
+  int iOldPos = iPos;
+  iPos = str.find(',', iOldPos);
+  iPos = min(iPos, str.find(')', iOldPos));
+  if (iPos > iOldPos)
+  {
+    std::string sRet = str.substr(iOldPos, iPos - iOldPos);
+    if (str[iPos] != ')')
+      ++iPos;
+    return sTrimSpaces(sRet);
+  }
+  else
+    return "";
+}
+
+bool fTimeStampCorrect(std::string sTimeStamp)
+{
+  if (sTimeStamp.length() != 12)
+    return false;
+
+  for (int i = 0; i < 12; ++i)
+  {
+    if (sTimeStamp[i] < '0' || sTimeStamp[i] > '9')
+      return false;
+  }
+
+  return true;
+}
+
+std::string MSGCommand::parse(std::string command_line)
+{
+  // expected:
+  // MSG(folder,timestamp,channel,in_same_folder,data_conversion,nr_cycles,step)
+  // or
+  // MSG(folder,timestamp,(channel,channel,...,channel),in_same_folder,data_conversion,nr_cycles,step)
+  // or
+  // <path>\H-000-MSG1__-MSG1________.....
+
+  std::string sErr ("");
+
+  std::string sID = command_line.substr(0, 4);
+  if (sID.compare("MSG(") == 0)
+  {
+    int iPos = 4; // after bracket open
+    sRootFolder = sNextTerm(command_line, iPos);
+    if (sRootFolder.length() > 0)
+    {
+      if (sRootFolder[sRootFolder.length() - 1] != PATH_SEP)
+      sRootFolder += PATH_SEP;
+      sTimeStamp = sNextTerm(command_line, iPos);
+      if (fTimeStampCorrect(sTimeStamp))
+      {
+        try // for eventual exceptions
+        {
+          while ((iPos < command_line.length()) && (command_line[iPos] == ' '))
+            ++iPos;
+          if (command_line[iPos] == '(')
+          {
+            ++iPos; // skip the ( bracket
+            int i = 1;
+            std::string sChannel = sNextTerm(command_line, iPos);
+            while (command_line[iPos] != ')')
+            {
+              int iChan = atoi(sChannel.c_str());
+              if (iChan >= 1 && iChan <= 12)
+                channel[iChan - 1] = i;
+              else
+                sErr = "Channel numbers must be between 1 and 12";
+              sChannel = sNextTerm(command_line, iPos);
+              ++i;
+            }
+            int iChan = atoi(sChannel.c_str());
+            if (iChan >= 1 && iChan <= 12)
+              channel[iChan - 1] = i;
+            else
+              sErr = "Channel numbers must be between 1 and 12";
+            ++iPos; // skip the ) bracket
+            while ((iPos < command_line.length()) && (command_line[iPos] == ' '))
+              ++iPos;
+            if (command_line[iPos] == ',')
+              ++iPos;
+          }
+          else
+          {
+            std::string sChannel = sNextTerm(command_line, iPos);
+            int iChan = atoi(sChannel.c_str());
+            if (iChan >= 1 && iChan <= 12)
+              channel[iChan - 1] = 1;
+            else
+              sErr = "Channel numbers must be between 1 and 12";
+          }
+          std::string sInRootFolder = sNextTerm(command_line, iPos);
+          if ((sInRootFolder.compare("N") != 0) && (sInRootFolder.compare("Y") != 0))
+            sErr = "Please specify N for data that is in a date dependent folder or Y for data that is in specified folder.";
+          else
+            fUseTimestampFolder = (sInRootFolder.compare("N") == 0);
+          std::string sDataConversion = sNextTerm(command_line, iPos);
+          cDataConversion = (sDataConversion.length()>0)?sDataConversion[0]:'N';
+          std::string sNrCycles = sNextTerm(command_line, iPos);
+          iNrCycles = atoi(sNrCycles.c_str());
+          if (iNrCycles < 1)
+            iNrCycles = 1;
+          std::string sStep = sNextTerm(command_line, iPos);
+          iStep = atoi(sStep.c_str());
+          if (iStep < 1)
+            iStep = 1;
+          while ((iPos < command_line.length()) && (command_line[iPos] == ' '))
+            ++iPos;
+          // additional correctness checks
+          if (command_line[iPos] != ')')
+            sErr = "Invalid syntax. Please review the MSG(...) statement.";
+          else if ((iNrChannels() > 1) && (channel[11] != 0))
+            sErr = "It is not possible to combine channel 12 (HRV) with the other channels.";
+          else if (iNrChannels() == 0 && sErr.length() == 0)
+            sErr = "At least one channel should be specified.";
+          else if ((cDataConversion != 'N') && (cDataConversion != 'B') && (cDataConversion != 'R') && (cDataConversion != 'L') && (cDataConversion != 'T'))
+            sErr = "Please specify N(o change), B(yte conversion), R(adiometric calibration), L(radiometric using central wavelength) or T(reflectance or temperature) for data conversion.";
+        }
+        catch(...)
+        {
+          sErr = "Invalid syntax. Please review the MSG(...) statement.";
+        }
+      }
+      else
+        sErr = "Timestamp should be exactly 12 digits.";
+    }
+    else
+      sErr = "A folder must be filled in indicating the root of the image data folders.";
+  }
+  else if (command_line.find("H-000-MSG1__-MSG1________") >= 0)
+  {
+    int iPos = command_line.find("H-000-MSG1__-MSG1________");
+    if ((command_line.length() - iPos) == 61)
+    {
+      fUseTimestampFolder = false;
+      sRootFolder = command_line.substr(0, iPos);
+      sTimeStamp = command_line.substr(iPos + 46, 12);
+      if (fTimeStampCorrect(sTimeStamp))
+      {
+        int iChan = iChannel(command_line.substr(iPos + 26, 9));
+        if (iChan >= 1 && iChan <= 12)
+        {
+          channel[iChan - 1] = 1;
+          cDataConversion = 'N';
+          iNrCycles = 1;
+          iStep = 1;
+        }
+        else
+          sErr = "Channel numbers must be between 1 and 12";
+      }
+      else
+        sErr = "Timestamp should be exactly 12 digits.";
+    }
+    else
+      sErr = "-"; // the source data set it is not for the MSG driver
+  }
+  else
+    sErr = "-"; // the source data set it is not for the MSG driver
+  return sErr;
+}
+
+int MSGCommand::iNrChannels()
+{
+  int iRet = 0;
+  for (int i=0; i<12; ++i)
+    if (channel[i] != 0)
+      ++iRet;
+
+  return iRet;
+}
+
+int MSGCommand::iChannel(int iChannelNumber)
+{
+  // return the iChannelNumber-th channel
+  // iChannelNumber is a value between 1 and 12
+  // note that channels are ordered. their order number is the value in the array
+  // As we can't combine channel 12 with channels 1 to 11, it does not make sense to inquire for iNr == 12
+  int iRet = 0;
+  if (iChannelNumber <= iNrChannels())
+  {
+    while ((iRet < 12) && (channel[iRet] != iChannelNumber))
+      ++iRet;
+  }
+
+  // will return a number between 1 and 12
+  return (iRet + 1);
+}
+
+int MSGCommand::iNrStrips(int iChannel)
+{
+  if (iChannel == 12)
+    return 24;
+  else if (iChannel >= 1 && iChannel <= 11)
+    return 8;
+  else
+    return 0;
+}
+
+int MSGCommand::iChannel(std::string sChannel)
+{
+  if (sChannel.compare("VIS006___") == 0)
+    return 1;
+  else if (sChannel.compare("VIS008___") == 0)
+    return 2;
+  else if (sChannel.compare("IR_016___") == 0)
+    return 3;
+  else if (sChannel.compare("IR_039___") == 0)
+    return 4;
+  else if (sChannel.compare("WV_062___") == 0)
+    return 5;
+  else if (sChannel.compare("WV_073___") == 0)
+    return 6;
+  else if (sChannel.compare("IR_087___") == 0)
+    return 7;
+  else if (sChannel.compare("IR_097___") == 0)
+    return 8;
+  else if (sChannel.compare("IR_108___") == 0)
+    return 9;
+  else if (sChannel.compare("IR_120___") == 0)
+    return 10;
+  else if (sChannel.compare("IR_134___") == 0)
+    return 11;
+  else if (sChannel.compare("HRV______") == 0)
+    return 12;
+  else
+    return 0;
+}
+
+std::string MSGCommand::sChannel(int iChannel)
+{
+  switch (iChannel)
+  {
+    case 1:
+      return "VIS006___";
+      break;
+    case 2:
+      return "VIS008___";
+      break;
+    case 3:
+      return "IR_016___";
+      break;
+    case 4:
+      return "IR_039___";
+      break;
+    case 5:
+      return "WV_062___";
+      break;
+    case 6:
+      return "WV_073___";
+      break;
+    case 7:
+      return "IR_087___";
+      break;
+    case 8:
+      return "IR_097___";
+      break;
+    case 9:
+      return "IR_108___";
+      break;
+    case 10:
+      return "IR_120___";
+      break;
+    case 11:
+      return "IR_134___";
+      break;
+    case 12:
+      return "HRV______";
+      break;
+    default:
+      return "_________";
+      break;
+  }
+}
+
+std::string MSGCommand::sTimeStampToFolder(std::string & sTimeStamp)
+{
+  std::string sYear (sTimeStamp.substr(0,4));
+  std::string sMonth (sTimeStamp.substr(4, 2));
+  std::string sDay (sTimeStamp.substr(6, 2));
+  return (sYear + PATH_SEP + sMonth + PATH_SEP + sDay + PATH_SEP);
+}
+
+int MSGCommand::iDaysInMonth(int iMonth, int iYear)
+{
+  int iDays;
+
+  if ((iMonth == 4) || (iMonth == 6) || (iMonth == 9) || (iMonth == 11))
+    iDays = 30;
+  else if (iMonth == 2)
+  {
+    iDays = 28;
+    if (iYear % 100 == 0) // century year
+    {
+      if (iYear % 400 == 0) // century leap year
+        ++iDays;
+    }
+    else
+    {
+      if (iYear % 4 == 0) // normal leap year
+        ++iDays;
+    }
+  }
+  else
+    iDays = 31;
+
+  return iDays;
+}
+
+std::string MSGCommand::sCycle(int iCycle)
+{
+  // find nth full quarter
+  // e.g. for n = 1, 200405311114 should result in 200405311115
+  // 200405311115 should result in 200405311130
+  // 200405311101 should result in 200405311115
+  // 200412312345 should result in 200501010000
+  
+  std::string sYear (sTimeStamp.substr(0, 4));
+  std::string sMonth (sTimeStamp.substr(4, 2));
+  std::string sDay (sTimeStamp.substr(6, 2));
+  std::string sHours (sTimeStamp.substr(8, 2));
+  std::string sMins (sTimeStamp.substr(10, 2));
+
+  int iYear = atoi(sYear.c_str());
+  int iMonth = atoi(sMonth.c_str());
+  int iDay = atoi(sDay.c_str());
+  int iHours = atoi(sHours.c_str());
+  int iMins = atoi(sMins.c_str());
+
+  iMins += (iCycle - 1)*15*iStep;
+
+  // round off the mins found down to a multiple of 15 mins
+  iMins = ((int)(iMins / 15)) * 15;
+  // now handle the whole chain back to the year ...
+  while (iMins >= 60)
+  {
+    iMins -= 60;
+    ++iHours;
+  }
+  while (iHours >= 24)
+  {
+    iHours -= 24;
+    ++iDay;
+  }
+  while (iDay > iDaysInMonth(iMonth, iYear))
+  {
+    iDay -= iDaysInMonth(iMonth, iYear);
+    ++iMonth;
+  }
+  while (iMonth > 12)
+  {
+    iMonth -= 12;
+    ++iYear;
+  }
+
+  char sRet [100];
+  sprintf(sRet, "%.4d%.2d%.2d%.2d%.2d", iYear, iMonth, iDay, iHours, iMins);
+
+  return sRet;  
+}
+
+std::string MSGCommand::sFileName(int iSequence, int iStrip)
+{
+  int iNr = iNrChannels();
+  int iChannelNumber = 1 + (iSequence - 1) % iNr;;
+  int iCycle = 1 + (iSequence - 1) / iNr;
+  char sRet [4096];
+  std::string siThCycle (sCycle(iCycle));
+  if (fUseTimestampFolder)
+    sprintf(sRet, "%s%sH-000-MSG1__-MSG1________-%s-%.6d___-%s-C_", sRootFolder.c_str(), sTimeStampToFolder(siThCycle).c_str(), sChannel(iChannel(iChannelNumber)).c_str(), iStrip, siThCycle.c_str());
+  else
+    sprintf(sRet, "%sH-000-MSG1__-MSG1________-%s-%.6d___-%s-C_", sRootFolder.c_str(), sChannel(iChannel(iChannelNumber)).c_str(), iStrip, siThCycle.c_str());
+  return sRet;
+}
+
+std::string MSGCommand::sPrologueFileName(int iSequence)
+{
+  int iCycle = 1 + (iSequence - 1) / iNrChannels();
+  char sRet [4096];
+  std::string siThCycle (sCycle(iCycle));
+  if (fUseTimestampFolder)
+    sprintf(sRet, "%s%sH-000-MSG1__-MSG1________-_________-PRO______-%s-__", sRootFolder.c_str(), sTimeStampToFolder(siThCycle).c_str(), siThCycle.c_str());
+  else
+    sprintf(sRet, "%sH-000-MSG1__-MSG1________-_________-PRO______-%s-__", sRootFolder.c_str(), siThCycle.c_str());
+  return sRet;
+}
\ No newline at end of file
diff --git a/Utilities/GDAL/frmts/msg/msgcommand.h b/Utilities/GDAL/frmts/msg/msgcommand.h
new file mode 100644
index 0000000000..23fe0b23a3
--- /dev/null
+++ b/Utilities/GDAL/frmts/msg/msgcommand.h
@@ -0,0 +1,73 @@
+// msgcommand.h: interface for the msgcommand class.
+//
+//////////////////////////////////////////////////////////////////////
+
+/******************************************************************************
+ *
+ * Purpose:  Parse the src_dataset string that is meant for the MSG driver.
+ * Author:   Bas Retsios, retsios@itc.nl
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, ITC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#if !defined(AFX_MSGCOMMAND_H__64E1CE65_4F49_4198_9C4C_13625CF54EC5__INCLUDED_)
+#define AFX_MSGCOMMAND_H__64E1CE65_4F49_4198_9C4C_13625CF54EC5__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include <string>
+
+class MSGCommand  
+{
+public:
+  MSGCommand();
+  virtual ~MSGCommand();
+
+  std::string parse(std::string command_line);
+  std::string sFileName(int iSequence, int iStrip);
+  std::string sPrologueFileName(int iCycle);
+  std::string sCycle(int iCycle);
+  int iNrChannels();
+  int iChannel(int iNr);
+
+  static int iNrStrips(int iChannel);
+
+  char cDataConversion;
+  int iNrCycles;
+  int channel[12];
+
+private:
+  std::string sTrimSpaces(std::string str);
+  std::string sNextTerm(std::string str, int & iPos);
+  int iDaysInMonth(int iMonth, int iYear);
+  static std::string sChannel(int iChannel);
+  static int iChannel(std::string sChannel);
+  static std::string sTimeStampToFolder(std::string & sTimeStamp);
+  std::string sRootFolder;
+  std::string sTimeStamp;
+  int iStep;
+  bool fUseTimestampFolder;
+};
+
+#endif // !defined(AFX_MSGCOMMAND_H__64E1CE65_4F49_4198_9C4C_13625CF54EC5__INCLUDED_)
diff --git a/Utilities/GDAL/frmts/msg/msgdataset.cpp b/Utilities/GDAL/frmts/msg/msgdataset.cpp
new file mode 100644
index 0000000000..b562696852
--- /dev/null
+++ b/Utilities/GDAL/frmts/msg/msgdataset.cpp
@@ -0,0 +1,717 @@
+/******************************************************************************
+ *
+ * Project:  MSG Driver
+ * Purpose:  GDALDataset driver for MSG translator for read support.
+ * Author:   Bas Retsios, retsios@itc.nl
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, ITC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#include "msgdataset.h"
+#include "prologue.h"
+#include "xritheaderparser.h"
+#include "reflectancecalculator.h"
+
+#include "PublicDecompWT/COMP/WT/Inc/CWTDecoder.h"
+#include "PublicDecompWT/DISE/CDataField.h" // Util namespace
+
+#include <vector>
+
+#if _MSC_VER > 1000
+#include  <io.h>
+#else
+#include <stdio.h>
+#endif
+
+const double MSGDataset::rCentralWvl[12] = {0.635, 0.810, 1.640, 3.900, 6.250, 7.350, 8.701, 9.660, 10.800, 12.000, 13.400, 0.750};
+const double MSGDataset::rVc[12] = {-1, -1, -1, 2569.094, 1598.566, 1362.142, 1149.083, 1034.345, 930.659, 839.661, 752.381, -1};
+const double MSGDataset::rA[12] = {-1, -1, -1, 0.9959, 0.9963, 0.9991, 0.9996, 0.9999, 0.9983, 0.9988, 0.9981, -1};
+const double MSGDataset::rB[12] = {-1, -1, -1, 3.471, 2.219, 0.485, 0.181, 0.060, 0.627, 0.397, 0.576, -1};
+
+/************************************************************************/
+/*                    MSGDataset()                                     */
+/************************************************************************/
+
+MSGDataset::MSGDataset()
+
+{
+  poTransform = NULL;
+  pszProjection = CPLStrdup("");
+  adfGeoTransform[0] = 0.0;
+  adfGeoTransform[1] = 1.0;
+  adfGeoTransform[2] = 0.0;
+  adfGeoTransform[3] = 0.0;
+  adfGeoTransform[4] = 0.0;
+  adfGeoTransform[5] = 1.0;
+}
+
+/************************************************************************/
+/*                    ~MSGDataset()                                     */
+/************************************************************************/
+
+MSGDataset::~MSGDataset()
+
+{
+  if( poTransform != NULL )
+    delete poTransform;
+
+  CPLFree( pszProjection );
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *MSGDataset::GetProjectionRef()
+
+{
+  return ( pszProjection );
+}
+
+/************************************************************************/
+/*                          SetProjection()                             */
+/************************************************************************/
+
+CPLErr MSGDataset::SetProjection( const char * pszNewProjection )
+{
+    CPLFree( pszProjection );
+    pszProjection = CPLStrdup( pszNewProjection );
+
+    return CE_None;
+
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr MSGDataset::GetGeoTransform( double * padfTransform )
+
+{
+    memcpy( padfTransform,  adfGeoTransform, sizeof(double) * 6 );
+    return( CE_None );
+}
+
+/************************************************************************/
+/*                       Open()                                         */
+/************************************************************************/
+
+GDALDataset *MSGDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+/* -------------------------------------------------------------------- */
+/*     Does this look like a MSG file                                   */
+/* -------------------------------------------------------------------- */
+    //if( poOpenInfo->fp == NULL)
+    //  return NULL;
+    // Do not touch the fp .. it will close by itself if not null after we return (whether it is recognized as HRIT or not)
+
+    std::string command_line (poOpenInfo->pszFilename);
+
+    MSGCommand command;
+    std::string sErr = command.parse(command_line);
+    if (sErr.length() > 0)
+    {
+        if (sErr.compare("-") != 0) // this driver does not recognize this format .. be silent and return false so that another driver can try
+          CPLError( CE_Failure, CPLE_AppDefined, (sErr+"\n").c_str() );
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read the prologue.                                              */
+/* -------------------------------------------------------------------- */
+
+    XRITHeaderParser xhp;
+    Prologue pp;
+
+    std::string sPrologueFileName = command.sPrologueFileName(1);
+    if (access(sPrologueFileName.c_str(), 0) == 0)
+    {
+      std::ifstream p_file(sPrologueFileName.c_str(), std::ios::in|std::ios::binary);
+      xhp.read_xrithdr(p_file);
+      if (xhp.xrit_hdr().file_type == 128)
+        pp.read(p_file);
+      p_file.close();
+    }
+    else
+    {
+        std::string sErr = "The prologue of the data set could not be found at the location specified:\n" + sPrologueFileName + "\n";
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  sErr.c_str() );
+        return FALSE;
+    }
+      
+
+// We're confident the string is formatted as an MSG command_line
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    
+    MSGDataset   *poDS;
+    poDS = new MSGDataset();
+    poDS->command = command; // copy it
+
+/* -------------------------------------------------------------------- */
+/*      Capture raster size from MSG prologue and submit it to GDAL     */
+/* -------------------------------------------------------------------- */
+
+    bool fLowerHRVShiftedRight = pp.idr()->PlannedCoverageHRV->UpperWestColumnPlanned >= pp.idr()->PlannedCoverageHRV->LowerWestColumnPlanned;
+    if (command.channel[11] != 0) // the HRV band
+    {
+      int iRasterXSize;
+      if (fLowerHRVShiftedRight)
+        iRasterXSize = abs(pp.idr()->PlannedCoverageHRV->UpperWestColumnPlanned - pp.idr()->PlannedCoverageHRV->LowerEastColumnPlanned) + 1;
+      else
+        iRasterXSize = abs(pp.idr()->PlannedCoverageHRV->LowerWestColumnPlanned - pp.idr()->PlannedCoverageHRV->UpperEastColumnPlanned) + 1;
+      iRasterXSize += 20; // reserve space for different x-sizes in time series
+      iRasterXSize = 100 * (1 + iRasterXSize / 100); // next multiple of 100 .. to have a more or less constant x-size over multiple runs
+      poDS->nRasterXSize = iRasterXSize;
+      poDS->nRasterYSize = abs(pp.idr()->PlannedCoverageHRV->UpperNorthLinePlanned - pp.idr()->PlannedCoverageHRV->LowerSouthLinePlanned) + 1;
+    }
+    else
+    {
+      poDS->nRasterXSize = abs(pp.idr()->PlannedCoverageVIS_IR->WesternColumnPlanned - pp.idr()->PlannedCoverageVIS_IR->EasternColumnPlanned) + 1;
+      poDS->nRasterYSize = abs(pp.idr()->PlannedCoverageVIS_IR->NorthernLinePlanned - pp.idr()->PlannedCoverageVIS_IR->SouthernLinePlanned) + 1;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Set Georeference Information                                    */
+/* -------------------------------------------------------------------- */
+
+    double rPixelSizeX;
+    double rPixelSizeY;
+    double rMinX;
+    double rMaxY;
+
+    if (command.channel[11] != 0)
+    {
+      int iXOffset = 0;
+      if (fLowerHRVShiftedRight)
+        iXOffset = poDS->nRasterXSize - pp.idr()->PlannedCoverageHRV->UpperWestColumnPlanned + pp.idr()->PlannedCoverageHRV->LowerEastColumnPlanned - 1;
+      rPixelSizeX = 1000 * pp.idr()->ReferenceGridHRV->ColumnDirGridStep;
+      rPixelSizeY = 1000 * pp.idr()->ReferenceGridHRV->LineDirGridStep;
+      rMinX = -rPixelSizeX * (pp.idr()->ReferenceGridHRV->NumberOfColumns / 2.0); // assumption: (0,0) falls in centre
+      if (fLowerHRVShiftedRight)
+        rMinX += rPixelSizeX * (pp.idr()->ReferenceGridHRV->NumberOfColumns - pp.idr()->PlannedCoverageHRV->UpperWestColumnPlanned);
+      else
+        rMinX += rPixelSizeX * (pp.idr()->ReferenceGridHRV->NumberOfColumns - pp.idr()->PlannedCoverageHRV->LowerWestColumnPlanned);
+      rMinX -= rPixelSizeX * iXOffset;
+      rMaxY = rPixelSizeY * (pp.idr()->ReferenceGridHRV->NumberOfLines / 2.0);
+    }
+    else
+    {
+      rPixelSizeX = 1000 * pp.idr()->ReferenceGridVIS_IR->ColumnDirGridStep;
+      rPixelSizeY = 1000 * pp.idr()->ReferenceGridVIS_IR->LineDirGridStep;
+      rMinX = -rPixelSizeX * (pp.idr()->ReferenceGridVIS_IR->NumberOfColumns / 2.0); // assumption: (0,0) falls in centre
+      rMaxY = rPixelSizeY * (pp.idr()->ReferenceGridVIS_IR->NumberOfLines / 2.0);
+    }
+    poDS->adfGeoTransform[0] = rMinX;
+    poDS->adfGeoTransform[3] = rMaxY;
+    poDS->adfGeoTransform[1] = rPixelSizeX;
+    poDS->adfGeoTransform[5] = -rPixelSizeY;
+    poDS->adfGeoTransform[2] = 0.0;
+    poDS->adfGeoTransform[4] = 0.0;
+
+/* -------------------------------------------------------------------- */
+/*      Set Projection Information                                      */
+/* -------------------------------------------------------------------- */
+
+    poDS->oSRS.SetGEOS(  0, 35785831, 0, 0 );
+    poDS->oSRS.SetWellKnownGeogCS( "WGS84" ); // Temporary line to satisfy ERDAS (otherwise the ellips is "unnamed"). Eventually this should become the custom a and b ellips (CGMS).
+    poDS->oSRS.exportToWkt( &(poDS->pszProjection) );
+
+    // The following are 3 different try-outs for also setting the ellips a and b parameters.
+    // We leave them out for now however because this does not work. In gdalwarp, when choosing some
+    // specific target SRS, the result is an error message:
+    // 
+    // ERROR 1: geocentric transformation missing z or ellps
+    // ERROR 1: GDALWarperOperation::ComputeSourceWindow() failed because
+    // the pfnTransformer failed.
+    // 
+    // I can't explain the reason for the message at this time (could be a problem in the way the SRS is set here,
+    // but also a bug in Proj.4 or GDAL.
+    /*
+    oSRS.SetGeogCS( NULL, NULL, NULL, 6378169, 295.488065897, NULL, 0, NULL, 0 );
+
+    oSRS.SetGeogCS( "unnamed ellipse", "unknown", "unnamed", 6378169, 295.488065897, "Greenwich", 0.0);
+
+    if( oSRS.importFromProj4("+proj=geos +h=35785831 +a=6378169 +b=6356583.8") == OGRERR_NONE )
+    {
+        oSRS.exportToWkt( &(poDS->pszProjection) );
+    }
+    */
+
+/* -------------------------------------------------------------------- */
+/*   Create a transformer to LatLon (only for Reflectance calculation)  */
+/* -------------------------------------------------------------------- */
+
+    char *pszLLTemp;
+    (poDS->oSRS.GetAttrNode("GEOGCS"))->exportToWkt(&pszLLTemp);
+    poDS->oLL.importFromWkt(&pszLLTemp);
+    poDS->poTransform = OGRCreateCoordinateTransformation( &(poDS->oSRS), &(poDS->oLL) );
+
+/* -------------------------------------------------------------------- */
+/*      Set the radiometric calibration parameters.                     */
+/* -------------------------------------------------------------------- */
+
+    memcpy( poDS->rCalibrationOffset,  pp.rpr()->Cal_Offset, sizeof(double) * 12 );
+    memcpy( poDS->rCalibrationSlope,  pp.rpr()->Cal_Slope, sizeof(double) * 12 );
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    poDS->nBands = command.iNrChannels()*command.iNrCycles;
+    for( int iBand = 0; iBand < poDS->nBands; iBand++ )
+    {
+        poDS->SetBand( iBand+1, new MSGRasterBand( poDS, iBand+1 ) );
+    }
+    
+    return( poDS );
+}
+
+/************************************************************************/
+/*                         MSGRasterBand()                              */
+/************************************************************************/
+
+const double MSGRasterBand::rRTOA[12] = {20.76, 23.24, 19.85, -1, -1, -1, -1, -1, -1, -1, -1, 25.11};
+
+MSGRasterBand::MSGRasterBand( MSGDataset *poDS, int nBand )
+: cScanDir('s')
+, iLowerShift(0)
+, iSplitLine(0)
+
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+
+    nBlockXSize = poDS->GetRasterXSize();
+    nBlockYSize = poDS->GetRasterYSize();
+
+/* -------------------------------------------------------------------- */
+/*      Open an input file and capture the header for the nr. of bits.  */
+/* -------------------------------------------------------------------- */
+    int iStrip = 1;
+    int iChannel = poDS->command.iChannel(1 + ((nBand - 1) % poDS->command.iNrChannels()));
+    std::string input_file = poDS->command.sFileName(nBand, iStrip);
+    while ((access(input_file.c_str(), 0) != 0) && (iStrip <= poDS->command.iNrStrips(iChannel))) // compensate for missing strips
+      input_file = poDS->command.sFileName(nBand, ++iStrip);
+
+    if (iStrip <= poDS->command.iNrStrips(iChannel))
+    {
+      std::ifstream i_file (input_file.c_str(), std::ios::in|std::ios::binary);
+
+      if (i_file.good())
+      {
+        XRITHeaderParser xhp;
+
+        if (xhp.read_xrithdr(i_file))
+        {
+          // Data type is either 8 or 16 bits .. we tell this to GDAL here
+          eDataType = GDT_Byte; // default .. always works
+          if (xhp.xrit_hdr().nb > 8)
+          {
+            if (poDS->command.cDataConversion == 'N')
+              eDataType = GDT_UInt16; // normal case: MSG 10 bits data
+            else if (poDS->command.cDataConversion == 'B')
+              eDataType = GDT_Byte; // output data type Byte
+            else
+              eDataType = GDT_Float32; // Radiometric calibration
+          }
+
+          // make IReadBlock be called once per file
+          nBlockYSize = xhp.xrit_hdr().nl;
+
+          // remember the scan direction
+
+          cScanDir = xhp.xrit_hdr().scan_dir;
+        }
+      }
+
+      i_file.close();
+    }
+    else if (nBand > 1)
+    {
+      // missing entire band .. take data from first band
+      MSGRasterBand* pFirstRasterBand = (MSGRasterBand*)poDS->GetRasterBand(1);
+      eDataType = pFirstRasterBand->eDataType;
+      nBlockYSize = pFirstRasterBand->nBlockYSize;
+      cScanDir = pFirstRasterBand->cScanDir;
+    }
+    else // also first band is missing .. do something for fail-safety
+    {
+      eDataType = GDT_Byte; // default .. always works
+      if (poDS->command.cDataConversion == 'N')
+        eDataType = GDT_UInt16; // normal case: MSG 10 bits data
+      else if (poDS->command.cDataConversion == 'B')
+        eDataType = GDT_Byte; // output data type Byte
+      else
+        eDataType = GDT_Float32; // Radiometric calibration
+
+      // nBlockYSize : default
+      // cScanDir : default
+
+    }
+/* -------------------------------------------------------------------- */
+/*      For the HRV band, read the prologue for shift and splitline.    */
+/* -------------------------------------------------------------------- */
+
+    if (iChannel == 12)
+    {
+      XRITHeaderParser xhp;
+      Prologue pp;
+
+      std::string sPrologueFileName = poDS->command.sPrologueFileName(nBand);
+      if (access(sPrologueFileName.c_str(), 0) == 0)
+      {
+        std::ifstream p_file(sPrologueFileName.c_str(), std::ios::in|std::ios::binary);
+        xhp.read_xrithdr(p_file);
+        if (xhp.xrit_hdr().file_type == 128)
+          pp.read(p_file);
+        p_file.close();
+
+        iLowerShift = pp.idr()->PlannedCoverageHRV->UpperWestColumnPlanned - pp.idr()->PlannedCoverageHRV->LowerWestColumnPlanned;
+        iSplitLine = abs(pp.idr()->PlannedCoverageHRV->UpperNorthLinePlanned - pp.idr()->PlannedCoverageHRV->LowerNorthLinePlanned) + 1; // without the "+ 1" the image of 1-Jan-2005 splits incorrectly
+      }
+    }
+
+/* -------------------------------------------------------------------- */
+/*  Initialize the ReflectanceCalculator with the band-dependent info.  */
+/* -------------------------------------------------------------------- */
+
+    int iCycle = 1 + (nBand - 1) / poDS->command.iNrChannels();
+    std::string sTimeStamp = poDS->command.sCycle(iCycle);
+
+    m_rc = new ReflectanceCalculator(sTimeStamp, rRTOA[nBand-1]);
+}
+
+/************************************************************************/
+/*                           ~MSGRasterBand()                           */
+/************************************************************************/
+MSGRasterBand::~MSGRasterBand()
+{
+    delete m_rc;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+CPLErr MSGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+
+{
+        
+    MSGDataset  *poGDS = (MSGDataset *) poDS;
+
+
+    int iBytesPerPixel = 1;
+    if (eDataType == GDT_UInt16)
+      iBytesPerPixel = 2;
+    else if (eDataType == GDT_Float32)
+      iBytesPerPixel = 4;
+/* -------------------------------------------------------------------- */
+/*      Calculate the correct input file name based on nBlockYOff       */
+/* -------------------------------------------------------------------- */
+
+    int strip_number;
+    int iChannel = poGDS->command.iChannel(1 + ((nBand - 1) % poGDS->command.iNrChannels()));
+
+    if (cScanDir == 'n')
+      strip_number = nBlockYOff + 1;
+    else
+      strip_number = poGDS->command.iNrStrips(iChannel) - nBlockYOff;
+
+    std::string strip_input_file = poGDS->command.sFileName(nBand, strip_number);
+    
+/* -------------------------------------------------------------------- */
+/*      Open the input file                                             */
+/* -------------------------------------------------------------------- */
+    if (access(strip_input_file.c_str(), 0) == 0) // does it exist?
+    {
+      std::ifstream i_file (strip_input_file.c_str(), std::ios::in|std::ios::binary);
+
+      if (i_file.good())
+      {
+        XRITHeaderParser xhp;
+
+        if (xhp.read_xrithdr(i_file))
+        {
+          std::vector <short> QualityInfo;
+          unsigned short chunck_height = xhp.xrit_hdr().nl;
+          unsigned short chunck_bpp = xhp.xrit_hdr().nb;
+          unsigned short chunck_width = xhp.xrit_hdr().nc;
+          unsigned __int8 NR = (unsigned __int8)chunck_bpp;
+          unsigned int nb_ibytes = xhp.xrit_hdr().data_len;
+          int iShift = 0;
+          bool fSplitStrip = false; // in the split strip the "shift" only happens before the split "row"
+          int iSplitRow = 0;
+          if (iChannel == 12)
+          {
+            iSplitRow = iSplitLine % xhp.xrit_hdr().nl;
+            int iSplitBlock = iSplitLine / xhp.xrit_hdr().nl;
+            fSplitStrip = (nBlockYOff == iSplitBlock); // in the split strip the "shift" only happens before the split "row"
+
+            // When iLowerShift > 0, the lower HRV image is shifted to the right and must be right-aligned in the raster.
+            // When iLowerShift < 0, the lower HRV image is shifted to the left and must be left-aligned in the raster.
+            // The available raster may be wider than needed, so that time series don't fall outside the raster.
+            
+            if (nBlockYOff <= iSplitBlock)
+              iShift = -iLowerShift;
+            // iShift < 0 means upper image moves to the left, and the lower image aligns to the right
+            // iShift > 0 means upper image moves to the right, and the lower image aligns to the left
+          }
+          
+          std::auto_ptr< unsigned char > ibuf( new unsigned char[nb_ibytes]);
+          
+          if (ibuf.get() == 0)
+          {
+             CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Not enough memory to perform wavelet decompression\n");
+            return CE_Failure;
+          }
+
+          i_file.read( (char *)(ibuf.get()), nb_ibytes);
+          
+          Util::CDataFieldCompressedImage  img_compressed(ibuf.release(),
+                                  nb_ibytes*8,
+                                  (unsigned char)chunck_bpp,
+                                  chunck_width,
+                                  chunck_height      );
+          
+          Util::CDataFieldUncompressedImage img_uncompressed;
+
+          //****************************************************
+          //*** Here comes the wavelets decompression routine
+          COMP::DecompressWT(img_compressed, NR, img_uncompressed, QualityInfo);
+          //****************************************************
+
+          // convert:
+          // Depth:
+          // 8 bits -> 8 bits
+          // 10 bits -> 16 bits (img_uncompressed contains the 10 bits data in packed form)
+          // Geometry:
+          // chunck_width x chunck_height to nBlockXSize x nBlockYSize
+
+          // cases:
+          // combination of the following:
+          // - scandir == 'n' or 's'
+          // - eDataType can be GDT_Byte, GDT_UInt16 or GDT_Float32
+          // - nBlockXSize == chunck_width or nBlockXSize > chunck_width
+          // - when nBlockXSize > chunck_width, fSplitStrip can be true or false
+          // we won't distinguish the following cases:
+          // - NR can be == 8 or != 8
+          // - when nBlockXSize > chunck_width, iShift iMinCOff-iMaxCOff <= iShift <= 0
+
+          int nBlockSize = nBlockXSize * nBlockYSize;
+          int y = chunck_width * chunck_height;
+          int iStep = -1;
+          if (xhp.xrit_hdr().scan_dir=='n') // image is the other way around
+          {
+            y = -1; // See how y is used below: += happens first, the result is used in the []
+            iStep = 1;
+          }
+
+          COMP::CImage cimg (img_uncompressed); // unpack
+          if (eDataType == GDT_Byte)
+          {
+            if (nBlockXSize == chunck_width) // optimized version
+            {
+              if (poGDS->command.cDataConversion == 'B')
+              {
+                for( int i = 0; i < nBlockSize; ++i )
+                    ((GByte *)pImage)[i] = cimg.Get()[y+=iStep] / 4;
+              }
+              else
+              {
+                for( int i = 0; i < nBlockSize; ++i )
+                    ((GByte *)pImage)[i] = cimg.Get()[y+=iStep];
+              }
+            }
+            else
+            {
+              // initialize to 0's (so that it does not have to be done in an 'else' statement <performance>)
+              memset(pImage, 0, nBlockXSize * nBlockYSize * iBytesPerPixel);
+              if (poGDS->command.cDataConversion == 'B')
+              {
+                for( int j = 0; j < chunck_height; ++j ) // assumption: nBlockYSize == chunck_height
+                { 
+                  int iXOffset = j * nBlockXSize + iShift;
+                  if (fSplitStrip && (j >= iSplitRow)) // In splitstrip, below splitline, thus do not shift!!
+                    iXOffset -= iShift;
+                  if (iLowerShift > 0) // In this case right-align the lower HRV part
+                    iXOffset += nBlockXSize - chunck_width - 1;
+                  for (int i = 0; i < chunck_width; ++i)
+                    ((GByte *)pImage)[++iXOffset] = cimg.Get()[y+=iStep] / 4;
+                }
+              }
+              else
+              {
+                for( int j = 0; j < chunck_height; ++j ) // assumption: nBlockYSize == chunck_height
+                { 
+                  int iXOffset = j * nBlockXSize + iShift;
+                  if (fSplitStrip && (j >= iSplitRow)) // In splitstrip, below splitline, thus do not shift!!
+                    iXOffset -= iShift;
+                  if (iLowerShift > 0) // In this case right-align the lower HRV part
+                    iXOffset += nBlockXSize - chunck_width - 1;
+                  for (int i = 0; i < chunck_width; ++i)
+                    ((GByte *)pImage)[++iXOffset] = cimg.Get()[y+=iStep];
+                }
+              }
+            }
+          }
+          else if (eDataType == GDT_UInt16) // this is our "normal case" if scan_dir is 's' 10 bit MSG data became 2 bytes per pixel
+          {
+            if (nBlockXSize == chunck_width) // optimized version
+            {
+              for( int i = 0; i < nBlockSize; ++i )
+                  ((GUInt16 *)pImage)[i] = cimg.Get()[y+=iStep];
+            }
+            else
+            {
+              // initialize to 0's (so that it does not have to be done in an 'else' statement <performance>)
+              memset(pImage, 0, nBlockXSize * nBlockYSize * iBytesPerPixel);
+              for( int j = 0; j < chunck_height; ++j ) // assumption: nBlockYSize == chunck_height
+              {
+                int iXOffset = j * nBlockXSize + iShift;
+                if (fSplitStrip && (j >= iSplitRow)) // In splitstrip, below splitline, thus do not shift!!
+                  iXOffset -= iShift;
+                if (iLowerShift > 0) // In this case right-align the lower HRV part
+                  iXOffset += nBlockXSize - chunck_width - 1;
+                for (int i = 0; i < chunck_width; ++i)
+                  ((GUInt16 *)pImage)[++iXOffset] = cimg.Get()[y+=iStep];
+              }
+            }
+          }
+          else if (eDataType == GDT_Float32) // radiometric calibration is requested
+          {
+            if (nBlockXSize == chunck_width) // optimized version
+            {
+              for( int i = 0; i < nBlockSize; ++i )
+                ((float *)pImage)[i] = (float)rRadiometricCorrection(cimg.Get()[y+=iStep], iChannel, nBlockYOff * nBlockYSize + i / nBlockXSize, i % nBlockXSize, poGDS);
+            }
+            else
+            {
+              // initialize to 0's (so that it does not have to be done in an 'else' statement <performance>)
+              memset(pImage, 0, nBlockXSize * nBlockYSize * iBytesPerPixel);
+              for( int j = 0; j < chunck_height; ++j ) // assumption: nBlockYSize == chunck_height
+              {
+                int iXOffset = j * nBlockXSize + iShift;
+                if (fSplitStrip && (j >= iSplitRow)) // In splitstrip, below splitline, thus do not shift!!
+                  iXOffset -= iShift;
+                int iXFrom;
+                int iXTo;
+                if (iLowerShift > 0) // In this case right-align the lower HRV part
+                {
+                  iXOffset += nBlockXSize - chunck_width - 1;
+                  iXFrom = nBlockXSize - chunck_width + iShift;
+                  iXTo = nBlockXSize + iShift;
+                }
+                else // left-align the lower HRV part
+                {
+                  iXFrom = 1 + iShift;
+                  iXTo = 1 + iShift + chunck_width;
+                }
+                for (int i = iXFrom; i < iXTo; ++i) // range always equal to chunck_width .. this is only to utilize i to get iCol
+                  ((float *)pImage)[++iXOffset] = (float)rRadiometricCorrection(cimg.Get()[y+=iStep], iChannel, nBlockYOff * nBlockYSize + j, (fSplitStrip && (j >= iSplitRow))?(i - iShift):i, poGDS);
+              }
+            }
+          }
+        }
+        else // header could not be opened .. make sure block contains 0's
+          memset(pImage, 0, nBlockXSize * nBlockYSize * iBytesPerPixel);
+      }
+      else // file could not be opened .. make sure block contains 0's
+        memset(pImage, 0, nBlockXSize * nBlockYSize * iBytesPerPixel);
+
+      i_file.close();
+    }
+    else // file does not exist .. make sure block contains 0's
+      memset(pImage, 0, nBlockXSize * nBlockYSize * iBytesPerPixel);
+
+    return CE_None;
+}
+
+double MSGRasterBand::rRadiometricCorrection(unsigned int iDN, int iChannel, int iRow, int iCol, MSGDataset* poGDS)
+{
+	int iIndex = iChannel - 1; // just for speed optimization
+
+  double rSlope = poGDS->rCalibrationSlope[iIndex];
+  double rOffset = poGDS->rCalibrationOffset[iIndex];
+  
+  if (poGDS->command.cDataConversion == 'T') // reflectance for visual bands, temperatore for IR bands
+  {
+    double rRadiance = rOffset + (iDN * rSlope);
+
+		if (iChannel >= 4 && iChannel <= 11) // Channels 4 to 11 (infrared): Temperature
+		{
+			const double rC1 = 1.19104e-5;
+			const double rC2 = 1.43877e+0;
+
+			double cc2 = rC2 * poGDS->rVc[iIndex];
+			double cc1 = rC1 * pow(poGDS->rVc[iIndex], 3) / rRadiance;
+			double rTemperature = ((cc2 / log(cc1 + 1)) - poGDS->rB[iIndex]) / poGDS->rA[iIndex];
+			return rTemperature;
+		}
+		else // Channels 1,2,3 and 12 (visual): Reflectance
+		{
+      double rLon = poGDS->adfGeoTransform[0] + iCol * poGDS->adfGeoTransform[1]; // X, in "geos" meters
+      double rLat = poGDS->adfGeoTransform[3] + iRow * poGDS->adfGeoTransform[5]; // Y, in "geos" meters
+      if ((poGDS->poTransform != NULL) && poGDS->poTransform->Transform( 1, &rLon, &rLat )) // transform it to latlon
+	      return m_rc->rGetReflectance(rRadiance, rLat, rLon);
+			else
+				return 0;
+		}
+  }
+  else // radiometric
+  {
+    if (poGDS->command.cDataConversion == 'R')
+      return rOffset + (iDN * rSlope);
+    else
+    {
+      double rFactor = 10 / pow(poGDS->rCentralWvl[iIndex], 2);
+      return rFactor * (rOffset + (iDN * rSlope));
+    }
+  }
+}
+
+/************************************************************************/
+/*                      GDALRegister_MSG()                              */
+/************************************************************************/
+
+void GDALRegister_MSG()
+
+{
+    GDALDriver  *poDriver;
+
+    if( GDALGetDriverByName( "MSG" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "MSG" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "MSG HRIT Data" );
+
+        poDriver->pfnOpen = MSGDataset::Open;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
\ No newline at end of file
diff --git a/Utilities/GDAL/frmts/msg/msgdataset.h b/Utilities/GDAL/frmts/msg/msgdataset.h
new file mode 100644
index 0000000000..7a71ec8a28
--- /dev/null
+++ b/Utilities/GDAL/frmts/msg/msgdataset.h
@@ -0,0 +1,94 @@
+/******************************************************************************
+ *
+ * Project:  MSG Driver
+ * Purpose:  GDALDataset driver for MSG translator for read support.
+ * Author:   Bas Retsios, retsios@itc.nl
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, ITC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#include "gdal_priv.h"
+#include "cpl_csv.h"
+#include "ogr_spatialref.h"
+#include "msgcommand.h"
+
+#include <string>
+#include <fstream>
+
+CPL_C_START
+void  GDALRegister_MSG(void);
+CPL_C_END
+
+/************************************************************************/
+/*                            MSGRasterBand                             */
+/************************************************************************/
+
+class ReflectanceCalculator;
+class MSGRasterBand : public GDALRasterBand
+{
+  friend class MSGDataset;
+
+  public:
+    MSGRasterBand( MSGDataset *, int );
+    virtual ~MSGRasterBand();
+    virtual CPLErr IReadBlock( int, int, void * );
+
+  private:
+    double rRadiometricCorrection(unsigned int iDN, int iChannel, int iRow, int iCol, MSGDataset* poGDS);
+    char cScanDir;
+    int iLowerShift; // nr of pixels that lower HRV image is shifted compared to upper
+    int iSplitLine; // line from top where the HRV image splits
+    ReflectanceCalculator* m_rc;
+    static const double rRTOA[12];
+};
+
+/************************************************************************/
+/*                      MSGDataset                                       */
+/************************************************************************/
+class MSGDataset : public GDALDataset
+{
+  friend class MSGRasterBand;
+  
+  public:
+    MSGDataset();
+    ~MSGDataset();
+
+    static GDALDataset *Open( GDALOpenInfo * );
+    virtual const char *GetProjectionRef(void);
+    virtual CPLErr SetProjection( const char * );
+    virtual CPLErr GetGeoTransform( double * padfTransform );
+
+  private:
+    MSGCommand command;
+    double adfGeoTransform[6]; // Calculate and store once as GetGeoTransform may be called multiple times
+    char   *pszProjection;
+    OGRSpatialReference oSRS;
+    OGRSpatialReference oLL;
+    OGRCoordinateTransformation *poTransform;
+    double rCalibrationOffset[12];
+    double rCalibrationSlope[12];
+    static const double rCentralWvl[12];
+    static const double rVc[12];
+    static const double rA[12];
+    static const double rB[12];
+};
+
diff --git a/Utilities/GDAL/frmts/msg/prologue.cpp b/Utilities/GDAL/frmts/msg/prologue.cpp
new file mode 100644
index 0000000000..38835d37b0
--- /dev/null
+++ b/Utilities/GDAL/frmts/msg/prologue.cpp
@@ -0,0 +1,237 @@
+// prologue.cpp: implementation of the Prologue class.
+//
+//////////////////////////////////////////////////////////////////////
+
+/******************************************************************************
+ *
+ * Purpose:  Parse the prologue of one repeat cycle and keep the interesting info.
+ * Author:   Bas Retsios, retsios@itc.nl
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, ITC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#include "prologue.h"
+
+int size_SatelliteStatus()
+{
+  int iSizePrimary = 1+4+1+1+4+4+1+1+4+4+1;;
+
+  int iSizeOrbitCoef = 4 + 4 + 8*8 + 8*8 + 8*8 + 8*8 + 8*8 + 8*8;
+  int iSizeOrbit = 4 + 4 + 100*iSizeOrbitCoef;
+  int iSizeAttitudeCoef = 4 + 4 + 8*8 + 8*8 + 8*8;
+  int iSizeAttitude = 4 + 4 + 8 + 100*iSizeAttitudeCoef;
+  int iSizeSpinRateatRCStart = 8;
+  int iSizeUTCCorrelation = 4 + 4 + 4*4*3 + 8 + 8 + 8 + 8 + 8;
+  
+  int iTotalSize = iSizePrimary + iSizeOrbit + iSizeAttitude + iSizeSpinRateatRCStart + iSizeUTCCorrelation;
+
+  return iTotalSize;
+}
+
+int size_ImageAcquisition()
+{
+  // up to  DHSSSynchSelection
+  int iSize1 = 8 + 8 + 8 + 12 + 42 + 42*2 + 2 + 2 + 2 + 2 + 1;
+  // up to RefocusingDirection
+  int iSize2 = 42*2 + 42 + 42*2 + 42*2 + 42*2 + 27*2 + 15*2 + 6*2 + 1 + 2 + 1;
+  // to end
+  int iSize3 = 2 + 1 + 2 + 4 + 2 + 2 + 2 + 1 + 4 + 1 + 4 + 4 + 1 + 1 + 2 + 2 + 2 + 2;
+    
+  int iTotalSize = iSize1 + iSize2 + iSize3;
+
+  return iTotalSize;
+}
+
+int size_CelestialEvents()
+{
+  int iSizeCelestialBodies = 2 + 2 + 4 + 4 + 3*100*(2 + 2 + 8*8 + 8*8) + 100*(20*(2 + 2 + 2 + 8*8 + 8*8));
+
+  int iSizeRelationToImage = 1 + 2 + 2 + 1 + 1 + 1;
+    
+  int iTotalSize = iSizeCelestialBodies + iSizeRelationToImage;
+
+  return iTotalSize;
+}
+
+int size_Correction()
+{
+  return 19229;
+}
+
+double iReadDouble(std::ifstream & ifile)
+{
+  // will use 8 bytes from the file to read a DOUBLE (according to the MSG definition of DOUBLE)
+    unsigned char buf [8];
+    
+    ifile.read((char*)buf, 8);
+    double rVal;
+    ((char*)(&rVal))[0] = buf[7];
+    ((char*)(&rVal))[1] = buf[6];
+    ((char*)(&rVal))[2] = buf[5];
+    ((char*)(&rVal))[3] = buf[4];
+    ((char*)(&rVal))[4] = buf[3];
+    ((char*)(&rVal))[5] = buf[2];
+    ((char*)(&rVal))[6] = buf[1];
+    ((char*)(&rVal))[7] = buf[0];
+    
+    return rVal;
+}
+
+double iReadReal(std::ifstream & ifile)
+{
+  // will use 4 bytes from the file to read a REAL (according to the MSG definition of REAL)
+    unsigned char buf [4];
+    
+    ifile.read((char*)buf, 4);
+    float rVal;
+    ((char*)(&rVal))[0] = buf[3];
+    ((char*)(&rVal))[1] = buf[2];
+    ((char*)(&rVal))[2] = buf[1];
+    ((char*)(&rVal))[3] = buf[0];
+    
+    return rVal;
+}
+
+int iReadInt(std::ifstream & ifile)
+{
+  // will use 4 bytes from the file to read an int (according to the MSG definition of int)
+    unsigned char buf [4];
+    
+    ifile.read((char*)buf, 4);
+    int iResult = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3];
+
+    return iResult;
+}
+
+unsigned char iReadByte (std::ifstream & ifile)
+{
+  // will read 1 byte from the file
+    char b;
+    
+    ifile.read(&b, 1);
+
+    return b;
+}
+
+ReferenceGridRecord::ReferenceGridRecord(std::ifstream & ifile)
+{
+    NumberOfLines = iReadInt(ifile);
+    NumberOfColumns = iReadInt(ifile);
+    LineDirGridStep = iReadReal(ifile);
+    ColumnDirGridStep = iReadReal(ifile);
+    GridOrigin = iReadByte(ifile);
+}
+
+PlannedCoverageVIS_IRRecord::PlannedCoverageVIS_IRRecord(std::ifstream & ifile)
+{
+    SouthernLinePlanned = iReadInt(ifile);
+    NorthernLinePlanned = iReadInt(ifile);
+    EasternColumnPlanned = iReadInt(ifile);
+    WesternColumnPlanned = iReadInt(ifile);
+}
+
+PlannedCoverageHRVRecord::PlannedCoverageHRVRecord(std::ifstream & ifile)
+{
+    LowerSouthLinePlanned = iReadInt(ifile);
+    LowerNorthLinePlanned = iReadInt(ifile);
+    LowerEastColumnPlanned = iReadInt(ifile);
+    LowerWestColumnPlanned = iReadInt(ifile);
+    UpperSouthLinePlanned = iReadInt(ifile);
+    UpperNorthLinePlanned = iReadInt(ifile);
+    UpperEastColumnPlanned = iReadInt(ifile);
+    UpperWestColumnPlanned = iReadInt(ifile);
+}
+
+
+ImageDescriptionRecord::ImageDescriptionRecord(std::ifstream & ifile)
+{
+    TypeOfProjection = iReadByte(ifile);
+    LongitudeOfSSP = iReadReal(ifile);
+    ReferenceGridVIS_IR = new ReferenceGridRecord(ifile);
+    ReferenceGridHRV = new ReferenceGridRecord(ifile);
+    PlannedCoverageVIS_IR = new PlannedCoverageVIS_IRRecord(ifile);
+    PlannedCoverageHRV = new PlannedCoverageHRVRecord(ifile);
+    ImageProcDirection = iReadByte(ifile);
+    PixelGenDirection = iReadByte(ifile);
+    for (int i=0; i < 12; ++i)
+      PlannedChannelProcessing[i] = iReadByte(ifile);
+}
+
+ImageDescriptionRecord::~ImageDescriptionRecord()
+{
+    delete ReferenceGridVIS_IR;
+    delete ReferenceGridHRV;
+    delete PlannedCoverageVIS_IR;
+    delete PlannedCoverageHRV;
+}
+
+RadiometricProcessingRecord::RadiometricProcessingRecord(std::ifstream & ifile)
+{
+  // skip a part that doesn't interest us
+    unsigned char dummy [12];
+    int i;
+    for (i = 0; i < 6; ++i)
+      ifile.read((char*)dummy, 12);
+
+    for (i = 0; i < 12; ++i)
+    {
+      Cal_Slope[i] = iReadDouble(ifile);
+      Cal_Offset[i] = iReadDouble(ifile);
+    }
+}
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+Prologue::Prologue()
+: m_idr(0)
+, m_rpr(0)
+{
+
+}
+
+Prologue::~Prologue()
+{
+  if (m_idr)
+    delete m_idr;
+  if (m_rpr)
+    delete m_rpr;
+}
+
+void Prologue::read(std::ifstream & ifile)
+{
+  unsigned char version = iReadByte(ifile);
+
+  int iSkipHeadersSize = size_SatelliteStatus() + size_ImageAcquisition() + size_CelestialEvents() + size_Correction();
+
+#if _MSC_VER > 1000 && _MSC_VER < 1300
+  ifile.seekg(iSkipHeadersSize, std::ios_base::seekdir::cur);
+#else
+  ifile.seekg(iSkipHeadersSize, std::ios_base::cur);
+#endif
+
+  m_idr = new ImageDescriptionRecord(ifile);
+
+  m_rpr = new RadiometricProcessingRecord(ifile);
+  // TODO: file is not left at the end of the Radiometric Processing Record
+}
diff --git a/Utilities/GDAL/frmts/msg/prologue.h b/Utilities/GDAL/frmts/msg/prologue.h
new file mode 100644
index 0000000000..9d5ee00324
--- /dev/null
+++ b/Utilities/GDAL/frmts/msg/prologue.h
@@ -0,0 +1,128 @@
+// prologue.h: interface for the prologue class.
+//
+//////////////////////////////////////////////////////////////////////
+
+/******************************************************************************
+ *
+ * Purpose:  Parse the prologue of one repeat cycle and keep the interesting info.
+ * Author:   Bas Retsios, retsios@itc.nl
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, ITC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#if !defined(AFX_PROLOGUE_H__777B5B86_04F4_4A01_86F6_24615DCD8446__INCLUDED_)
+#define AFX_PROLOGUE_H__777B5B86_04F4_4A01_86F6_24615DCD8446__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include <fstream>
+
+class ReferenceGridRecord
+{
+public:
+  ReferenceGridRecord(std::ifstream & ifile);
+
+  int NumberOfLines;
+  int NumberOfColumns;
+  double LineDirGridStep;
+  double ColumnDirGridStep;
+  unsigned char GridOrigin; // 0 == north-west corner; 1 == sw; 2 == se; 3 == ne;
+};
+
+class PlannedCoverageVIS_IRRecord
+{
+public:
+  PlannedCoverageVIS_IRRecord(std::ifstream & ifile);
+
+  int SouthernLinePlanned;
+  int NorthernLinePlanned;
+  int EasternColumnPlanned;
+  int WesternColumnPlanned;
+};
+
+class PlannedCoverageHRVRecord
+{
+public:
+  PlannedCoverageHRVRecord(std::ifstream & ifile);
+  int LowerSouthLinePlanned;
+  int LowerNorthLinePlanned;
+  int LowerEastColumnPlanned;
+  int LowerWestColumnPlanned;
+  int UpperSouthLinePlanned;
+  int UpperNorthLinePlanned;
+  int UpperEastColumnPlanned;
+  int UpperWestColumnPlanned;
+};
+
+class ImageDescriptionRecord
+{
+public:
+  ImageDescriptionRecord(std::ifstream & ifile);
+  virtual ~ImageDescriptionRecord();
+
+  unsigned char TypeOfProjection; // 1 == Geostationary, Earth centered in grid
+  double LongitudeOfSSP;
+  ReferenceGridRecord * ReferenceGridVIS_IR;
+  ReferenceGridRecord * ReferenceGridHRV;
+  PlannedCoverageVIS_IRRecord * PlannedCoverageVIS_IR;
+  PlannedCoverageHRVRecord * PlannedCoverageHRV;
+  unsigned char ImageProcDirection; // 0 == north-south; 1 == south-north
+  unsigned char PixelGenDirection; // 0 == east-west; 1 == west-east;
+  unsigned char PlannedChannelProcessing [12];
+};
+
+class RadiometricProcessingRecord
+{
+public:
+  RadiometricProcessingRecord(std::ifstream & ifile);
+
+  double Cal_Slope [12];
+  double Cal_Offset [12];
+};
+
+class Prologue  
+{
+public:
+  Prologue();
+  virtual ~Prologue();
+
+  void read(std::ifstream & ifile);
+
+  const ImageDescriptionRecord * idr()
+  {
+    return m_idr;
+  };
+
+  const RadiometricProcessingRecord * rpr()
+  {
+    return m_rpr;
+  };
+
+private:
+  ImageDescriptionRecord * m_idr;
+  RadiometricProcessingRecord * m_rpr;
+
+};
+
+#endif // !defined(AFX_PROLOGUE_H__777B5B86_04F4_4A01_86F6_24615DCD8446__INCLUDED_)
diff --git a/Utilities/GDAL/frmts/msg/reflectancecalculator.cpp b/Utilities/GDAL/frmts/msg/reflectancecalculator.cpp
new file mode 100644
index 0000000000..fdb8763622
--- /dev/null
+++ b/Utilities/GDAL/frmts/msg/reflectancecalculator.cpp
@@ -0,0 +1,163 @@
+// reflectancecalculator.cpp: implementation of the ReflectanceCalculator class.
+//
+//////////////////////////////////////////////////////////////////////
+
+/******************************************************************************
+ *
+ * Purpose:  Calculate reflectance values from radiance, for visual bands.
+ * Author:   Bas Retsios, retsios@itc.nl
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, ITC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#include "reflectancecalculator.h"
+
+#include <math.h>
+
+#define M_PI        3.14159265358979323846
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+ReflectanceCalculator::ReflectanceCalculator(std::string sTimeStamp, double rRTOA)
+: m_rRTOA(rRTOA)
+{
+  std::string sYear (sTimeStamp.substr(0, 4));
+  std::string sMonth (sTimeStamp.substr(4, 2));
+  std::string sDay (sTimeStamp.substr(6, 2));
+  std::string sHours (sTimeStamp.substr(8, 2));
+  std::string sMins (sTimeStamp.substr(10, 2));
+
+  m_iYear = atoi(sYear.c_str());
+  int iMonth = atoi(sMonth.c_str());
+  m_iDay = atoi(sDay.c_str());
+	for (int i = 1; i < iMonth; ++i)
+		m_iDay += iDaysInMonth(i, m_iYear);
+  int iHours = atoi(sHours.c_str());
+  int iMins = atoi(sMins.c_str());
+
+	m_rHours = iHours + iMins / 60.0;
+}
+
+ReflectanceCalculator::~ReflectanceCalculator()
+{
+
+}
+
+double ReflectanceCalculator::rGetReflectance(double rRadiance, double rLat, double rLon) const
+{
+  double phi = rLat * M_PI / 180;
+  double lam = rLon * M_PI / 180;
+  double rSunDist = rSunDistance();
+  double ReflectanceNumerator = rRadiance*rSunDist*rSunDist;
+  double zenithAngle = rZenithAngle(phi, rDeclination(), rHourAngle(rLon));
+  double ReflectanceDenominator = m_rRTOA*cos(zenithAngle*M_PI/180);
+  double Reflectance = ReflectanceNumerator / ReflectanceDenominator;
+  return Reflectance;
+}
+
+double ReflectanceCalculator::rZenithAngle(double phi, double rDeclin, double rHourAngle) const
+{
+  double rCosZen = (sin(phi) * sin(rDeclin) + cos(phi)
+          * cos(rDeclin) * cos(rHourAngle));
+  double zenithAngle = acos(rCosZen) * 180 / M_PI;
+  return zenithAngle;
+}
+
+const double ReflectanceCalculator::rDeclination() const
+{
+  double rJulianDay = m_iDay - 1;
+  double yearFraction = (rJulianDay + m_rHours / 24) / iDaysInYear(m_iYear);
+  double T = 2 * M_PI * yearFraction;
+  
+  double declin = 0.006918 - 0.399912 * cos(T) + 0.070257 * sin(T)
+          - 0.006758 * cos(2 * T) + 0.000907 * sin(2 * T)
+          - 0.002697 * cos(3 * T) + 0.00148 * sin(3 * T);
+  return declin;
+}
+
+double ReflectanceCalculator::rHourAngle(double rLon) const
+{
+	// In: rLon (in degrees)
+	// Out: hourAngle (in radians)
+  double rJulianDay = m_iDay - 1;
+  double yearFraction = (rJulianDay + m_rHours / 24) / iDaysInYear(m_iYear);
+  double T = 2 * M_PI * yearFraction;
+
+  double EOT2 = 229.18 * (0.000075 + 0.001868 * cos(T)- 0.032077 * sin(T));
+  double EOT3 = 229.18 * (- 0.014615 * cos(2 * T) - 0.040849 * sin(2 * T));
+  double EOT = EOT2 + EOT3;
+  double TimeOffset = EOT + (4. * rLon);
+  // True solar time in minutes:
+  double TrueSolarTime = m_rHours * 60 + TimeOffset;
+  // Solar hour angle in degrees and in radians:
+  double HaDegr = (TrueSolarTime / 4. - 180.);
+  double hourAngle = HaDegr * M_PI / 180;
+  return hourAngle;
+}
+
+const double ReflectanceCalculator::rSunDistance() const
+{
+  int iJulianDay = m_iDay - 1;
+  double theta = 2*M_PI *(iJulianDay - 3) / 365.25;
+	// rE0 is the inverse of the square of the sun-distance ratio
+	double rE0 = 1.000110 + 0.034221*cos(theta)+0.00128*sin(theta) + 0.000719*cos(2*theta)+0.000077*sin(2*theta);
+  // The calculated distance is expressed as a factor of the "average sun-distance" (on 1 Jan approx. 0.98, on 1 Jul approx. 1.01)
+  return 1 / sqrt(rE0);
+}
+
+int ReflectanceCalculator::iDaysInYear(int iYear) const
+{
+  bool fLeapYear = iDaysInMonth(2, iYear) == 29;
+  
+  if (fLeapYear)
+      return 366;
+  else
+      return 365;
+}
+
+int ReflectanceCalculator::iDaysInMonth(int iMonth, int iYear) const
+{
+  int iDays;
+
+  if ((iMonth == 4) || (iMonth == 6) || (iMonth == 9) || (iMonth == 11))
+    iDays = 30;
+  else if (iMonth == 2)
+  {
+    iDays = 28;
+    if (iYear % 100 == 0) // century year
+    {
+      if (iYear % 400 == 0) // century leap year
+        ++iDays;
+    }
+    else
+    {
+      if (iYear % 4 == 0) // normal leap year
+        ++iDays;
+    }
+  }
+  else
+    iDays = 31;
+
+  return iDays;
+}
diff --git a/Utilities/GDAL/frmts/msg/reflectancecalculator.h b/Utilities/GDAL/frmts/msg/reflectancecalculator.h
new file mode 100644
index 0000000000..c18e8cb4a0
--- /dev/null
+++ b/Utilities/GDAL/frmts/msg/reflectancecalculator.h
@@ -0,0 +1,61 @@
+// ReflectanceCalculator.h: interface for the ReflectanceCalculator class.
+//
+//////////////////////////////////////////////////////////////////////
+
+/******************************************************************************
+ *
+ * Purpose:  Calculate reflectance values from radiance, for visual bands.
+ * Author:   Bas Retsios, retsios@itc.nl
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, ITC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#if !defined(AFX_REFLECTANCECALCULATOR_H__C9960E01_2A1B_41F0_B903_7957F11618D2__INCLUDED_)
+#define AFX_REFLECTANCECALCULATOR_H__C9960E01_2A1B_41F0_B903_7957F11618D2__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include <string>
+
+class ReflectanceCalculator  
+{
+public:
+	ReflectanceCalculator(std::string sTimeStamp, double rRTOA);
+	virtual ~ReflectanceCalculator();
+	double rGetReflectance(double rRadiance, double rLat, double rLon) const;
+private:
+  double rZenithAngle(double phi, double rDeclin, double rHourAngle) const;
+	const double rDeclination() const;
+  double rHourAngle(double lam) const;
+  const double rSunDistance() const;
+  int iDaysInYear(int iYear) const;
+	int iDaysInMonth(int iMonth, int iYear) const;
+
+	const double m_rRTOA; // solar irradiance on Top of Atmosphere
+	int m_iYear; // e.g. 2005
+	int m_iDay; // 1-365/366
+	double m_rHours; // 0-24
+};
+
+#endif // !defined(AFX_REFLECTANCECALCULATOR_H__C9960E01_2A1B_41F0_B903_7957F11618D2__INCLUDED_)
diff --git a/Utilities/GDAL/frmts/msg/xritheaderparser.cpp b/Utilities/GDAL/frmts/msg/xritheaderparser.cpp
new file mode 100644
index 0000000000..1b837cbbfb
--- /dev/null
+++ b/Utilities/GDAL/frmts/msg/xritheaderparser.cpp
@@ -0,0 +1,415 @@
+// xritheaderparser.cpp: implementation of the XRITHeaderParser class.
+//
+//////////////////////////////////////////////////////////////////////
+
+/******************************************************************************
+ *
+ * Purpose:  Parse the header of the combined XRIT header/data files.
+ * Author:   Bas Retsios, retsios@itc.nl
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, ITC
+ * Parts of code Copyright (c) 2003 R. Alblas 
+ * 
+ * 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 "xritheaderparser.h"
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+XRITHeaderParser::XRITHeaderParser()
+: m_iHeaderLength(0)
+{
+
+}
+
+XRITHeaderParser::~XRITHeaderParser()
+{
+
+}
+
+/*************************************
+ * translate channel name into number 
+ * 
+ *************************************/
+/* Note: Only info in file-name known here! */
+void channame2nr(XRIT_HDR *xrit_hdr)
+{
+  if      (xrit_hdr->special=='p')           xrit_hdr->chan_nr=0;
+  else if (xrit_hdr->special=='P')           xrit_hdr->chan_nr=0;
+  else if (xrit_hdr->special=='e')           xrit_hdr->chan_nr=0;
+  else if (xrit_hdr->special=='E')           xrit_hdr->chan_nr=0;
+  else if (!strcmp(xrit_hdr->chan,"VIS006")) xrit_hdr->chan_nr=1;
+  else if (!strcmp(xrit_hdr->chan,"VIS008")) xrit_hdr->chan_nr=2;
+  else if (!strcmp(xrit_hdr->chan,"IR_016")) xrit_hdr->chan_nr=3;
+  else if (!strcmp(xrit_hdr->chan,"IR_039")) xrit_hdr->chan_nr=4;
+  else if (!strcmp(xrit_hdr->chan,"WV_062")) xrit_hdr->chan_nr=5;
+  else if (!strcmp(xrit_hdr->chan,"WV_073")) xrit_hdr->chan_nr=6;
+  else if (!strcmp(xrit_hdr->chan,"IR_087")) xrit_hdr->chan_nr=7;
+  else if (!strcmp(xrit_hdr->chan,"IR_097")) xrit_hdr->chan_nr=8;
+  else if (!strcmp(xrit_hdr->chan,"IR_108")) xrit_hdr->chan_nr=9;
+  else if (!strcmp(xrit_hdr->chan,"IR_120")) xrit_hdr->chan_nr=10;
+  else if (!strcmp(xrit_hdr->chan,"IR_134")) xrit_hdr->chan_nr=11;
+  else if (!strcmp(xrit_hdr->chan,"HRV"))    xrit_hdr->chan_nr=12;
+  else                                       xrit_hdr->chan_nr=-1; /* not defined here */
+}
+
+/* Remove trailing underscores */
+void remove_tr_usc(char *s)
+{
+  char *p;
+  p=s+strlen(s)-1;
+  while ((p>=s) && (*p=='_'))
+  {
+    *p=0;
+    p--;
+  } 
+}
+
+/*************************************
+ * Extract annotation info MSG
+ * Examples:
+ * 
+ * L-000-MSG1__-GOES7_______-IR_107___-00004____-200202020202-CE
+ * L-000-MSG1__-MSG1________-IR_016___-00001____-200202020202-CE
+ * H-000-MSG1__-MSG1________-_________-EPI______-200305040944-__
+ * H-000-MSG1__-MSG1________-_________-PRO______-200305040914-__
+ *************************************/
+int extract_anno_msg(XRIT_HDR *xh)
+{
+  char *p;
+  char tmp[20];
+  char anno[1000];
+  strcpy(anno,xh->anno);
+
+  if (!strchr(anno,'-')) return 0;
+  if (!(p=strtok(anno,"-"))) return 0;      /* L or H */
+
+  xh->hl=*p; 
+
+  if (!(p=strtok(NULL,"-"))) return 0;      /* version */
+  strncpy(xh->vers,p,4);
+
+
+  if (!(p=strtok(NULL,"-"))) return 0;      /* satellite name */
+  strncpy(xh->sat,p,7);
+  remove_tr_usc(xh->sat);
+
+  if (!(p=strtok(NULL,"-"))) return 0;      /* ID#1: data source (12 chars) */
+  strncpy(xh->src,p,13);    
+  remove_tr_usc(xh->src);
+
+  if (!strncmp(xh->src,"MSG",3))
+    strcpy(xh->satsrc,xh->src);
+  else if (!strncmp(xh->src,"SERVICE",7))
+    strcpy(xh->satsrc,"Srvc");
+  else if (!strncmp(xh->src,"MPEF",7))
+    strcpy(xh->satsrc,"Mpef");
+  else if (!strncmp(xh->src,"MET",3))
+    strcpy(xh->satsrc,"MET");
+  else
+    strcpy(xh->satsrc,"Frgn");
+
+  if (!(p=strtok(NULL,"-"))) return 0;      /* ID#2: channel (9 chars) */
+  strncpy(xh->chan,p,10);
+  remove_tr_usc(xh->chan);
+
+  if ((p=strtok(NULL,"-")))                 /* ID#3: nr. or PRO/EPI (9 chars) */
+  {
+    xh->special=0;
+    if (!strncmp(p,"PRO_",4))
+    {
+      if (!*xh->chan)
+        xh->special='P';                    /* group PRO */
+      else
+        xh->special='p';                    /* channel PRO */
+    }
+    else if (!strncmp(p,"EPI_",4))
+    {
+      if (!*xh->chan)
+        xh->special='E';                    /* group EPI */
+      else
+        xh->special='e';                    /* channel EPI */
+    }
+    else
+    {
+      xh->segment=atoi(p); 
+    }
+  }
+/*
+  if (!*xh->chan)
+  {
+    if (xh->special=='p') strcpy(xh->chan,"PRO");
+    if (xh->special=='e') strcpy(xh->chan,"EPI");
+  }
+*/
+  if ((p=strtok(NULL,"-")))                  /* prod. ID#4: time (12 chars)  */
+  {
+    strcpy(xh->itime,p);
+    memset(&xh->time,0,sizeof(xh->time));
+    strncpy(tmp,p,4); tmp[4]=0; p+=4;
+    xh->time.tm_year=atoi(tmp)-1900;
+
+    strncpy(tmp,p,2); tmp[2]=0; p+=2;
+    xh->time.tm_mon=atoi(tmp)-1;
+
+    strncpy(tmp,p,2); tmp[2]=0; p+=2;
+    xh->time.tm_mday=atoi(tmp);
+
+    strncpy(tmp,p,2); tmp[2]=0; p+=2;
+    xh->time.tm_hour=atoi(tmp);
+
+    strncpy(tmp,p,2); tmp[2]=0; p+=2;
+    xh->time.tm_min=atoi(tmp);
+/*
+Don't use this; time gets confused because of daylight saving!
+    mktime(&xh->time);
+*/
+
+  }
+
+  if ((p=strtok(NULL,"-")))                  /* flags */
+  {
+    xh->compr=xh->encry='_';
+    if (strchr(p,'C')) xh->compr='C';
+    if (strchr(p,'E')) xh->encry='E';
+  }
+
+/* Determine sort-order number: [yyyymmddhhmm][t][c] MUST be always equal length! */
+  channame2nr(xh);
+  if (xh->chan_nr>=0)
+    sprintf(xh->sortn,"%s%c%x%02x",xh->itime,xh->hl,xh->chan_nr,xh->segment);
+  else
+    sprintf(xh->sortn,"%s%c%s%02x",xh->itime,xh->hl,xh->chan,xh->segment);
+  sprintf(xh->id,"%-10s %c   ",xh->chan,xh->hl);
+  strftime(xh->id+14,20,"%d-%m-%y %H:%M  ",&xh->time);             /* time */
+  return 1;
+}
+
+/*************************************
+ * Extract annotation info 
+ *************************************/
+int extract_anno(XRIT_HDR *xh)
+{
+  if (!strncmp(xh->anno+6,"MSG",3))
+    return extract_anno_msg(xh);
+  else 
+    return 0;
+}
+
+void catch_primhdr(unsigned char *l, XRIT_HDR *xrit_hdr)
+{
+  xrit_hdr->file_type=l[3]; /* 0=image,1=GTS mess.,2=text,3=encr. mess.*/
+/* Total header length of this file */
+  xrit_hdr->hdr_len=(l[4]<<24)+(l[5]<<16)+(l[6]<<8)+l[7];
+
+
+/* Total content length of this file */
+  xrit_hdr->datalen_msb=(l[8]<<24)+(l[9]<<16)+(l[10]<<8)+l[11];
+  xrit_hdr->datalen_lsb=(l[12]<<24)+(l[13]<<16)+(l[14]<<8)+l[15];
+  xrit_hdr->data_len=(xrit_hdr->datalen_lsb >> 3) +
+                     (xrit_hdr->datalen_msb << 5);
+}
+
+
+/*************************************
+ * Extract xrit header
+ * Return: Position after headers
+ *************************************/
+unsigned char *catch_xrit_hdr(unsigned char *l,int ln,XRIT_HDR *xrit_hdr)
+{
+  unsigned char *p;
+  int hdr_len=0;
+  int pos=0;
+  memset(xrit_hdr,0,sizeof(*xrit_hdr));
+
+  do
+  {
+/* Get header type and length */
+    xrit_hdr->hdr_type=l[0];
+    xrit_hdr->hdr_rec_len=(l[1]<<8)+l[2];
+
+/* Test: header length < length l */
+    pos+=xrit_hdr->hdr_rec_len;
+    if (pos>ln) break;
+/* Extract headertype dependent info */
+    switch(xrit_hdr->hdr_type)
+    {
+/* ------------------ Primary header ------------------ */
+      case 0:
+        catch_primhdr(l,xrit_hdr);
+      break;
+/* ------------------ Image header ------------------ */
+      case 1:
+        xrit_hdr->nb=l[3];           /* # bitplanes */
+        xrit_hdr->nc=(l[4]<<8)+l[5]; /* # columns (=width) */
+        xrit_hdr->nl=(l[6]<<8)+l[7]; /* # lines */
+        xrit_hdr->cf=l[8];           /* compr. flag: 0, 1=lossless, 2=lossy */
+      break;
+/* ------------------ Image navigation ------------------ */
+      case 2:
+/* Projection name */
+        strncpy(xrit_hdr->proj_name,(const char*)(l+3),32); xrit_hdr->proj_name[32]=0;
+/* Remove leading spaces */
+        for (p=(unsigned char*)(xrit_hdr->proj_name+31); 
+             ((*p==' ') && ((char *)p > xrit_hdr->proj_name));
+             p--);
+        p++; *p=0;
+        xrit_hdr->cfac=(l[35]<<24)+(l[36]<<16)+(l[37]<<8)+l[38];
+        xrit_hdr->lfac=(l[39]<<24)+(l[40]<<16)+(l[41]<<8)+l[42];
+        xrit_hdr->coff=(l[43]<<24)+(l[44]<<16)+(l[45]<<8)+l[46];
+        xrit_hdr->loff=(l[47]<<24)+(l[48]<<16)+(l[49]<<8)+l[50];
+        if (xrit_hdr->lfac > 0) xrit_hdr->scan_dir='n';
+        if (xrit_hdr->lfac < 0) xrit_hdr->scan_dir='s';
+      break;
+/* ------------------ Image data functions ------------------ */
+      case 3:
+      break;
+/* ------------------ Annotation ------------------ */
+      case 4:
+/* Extract annotation */
+        strncpy(xrit_hdr->anno,(const char*)(l+3),64);
+        xrit_hdr->anno[61]=0;
+        extract_anno(xrit_hdr);
+      break;
+/* ------------------ Time stamp ------------------ */
+      case 5:
+      {
+        int i;
+        for (i=0; i<7; i++) xrit_hdr->ccdds[i]=l[3+i];
+      }
+      break;
+/* ------------------ ancillary text ------------------ */
+      case 6:
+      break;
+/* ------------------ key header ------------------ */
+      case 7:
+      break;
+/* ------------------ segment identification ------------------ */
+      case 128:
+        if (!strncmp(xrit_hdr->sat,"MSG",3))
+        {
+/* Eumetsat */
+          xrit_hdr->gp_sc_id   =(l[3]<<8)+l[4];   /* unique for each sat. source? (MSG, GOES...) */
+          xrit_hdr->spec_ch_id =l[5];
+          xrit_hdr->seq_no     =(l[6]<<8)+l[7];   /* segment no. */
+          xrit_hdr->seq_start  =(l[8]<<8)+l[9];   /* "planned" start segment */
+          xrit_hdr->seq_end    =(l[10]<<8)+l[11]; /* "planned" end segment */
+          xrit_hdr->dt_f_rep   =l[12];
+        }
+        else
+        {
+/* NOAA */
+          xrit_hdr->pic_id     =(l[3]<<8)+l[4];   /* unique for each picture?? */
+          xrit_hdr->seq_no     =(l[5]<<8)+l[6];   /* segment seq. no. */
+/* start column=(l[7]<<8)+l[8] */
+/* start row   =(l[9]<<8)+l[10] */
+          xrit_hdr->seq_start  =1;                /* start segment */
+          xrit_hdr->seq_end    =(l[11]<<8)+l[12]; /* max segment */
+/* max column=(l[13]<<8)+l[14] */
+/* max row   =(l[15]<<8)+l[16] */
+        }
+      break;
+/* ------------------ image segment line quality ------------------ */
+      case 129:
+      break;
+/* ------------------ ??? In GOES LRIT ??? ------------------ */
+      case 130:
+
+      break;
+/* ------------------ ??? In GOES LRIT ??? ------------------ */
+      case 131:
+      break;
+      default:
+        printf("Unexpected hdrtype=%d\n",xrit_hdr->hdr_type);
+      break;
+    }
+
+    if (xrit_hdr->hdr_type==0)
+    {
+      hdr_len=xrit_hdr->hdr_len;
+    }
+    l+=xrit_hdr->hdr_rec_len;
+
+    hdr_len-=xrit_hdr->hdr_rec_len;
+
+
+  } while (hdr_len>0);
+  return l;
+}
+
+
+
+/*************************************************************************
+ * Read from a extracted file (channel with certain order number)
+ * the XRIT header.
+ * Remaining file is JPEG.
+ *************************************************************************/
+int XRITHeaderParser::read_xrithdr(std::ifstream & ifile)
+{
+  unsigned char l1[20],*l;
+  int hdr_len;
+
+/* Read in primary header, just to determine length of all headers  */
+  ifile.read((char*)l1, 16);
+  
+#if _MSG_VER > 1000 && _MSC_VER < 1300  
+  ifile.seekg(-16, std::ios_base::seekdir::cur);
+#else
+  ifile.seekg(-16, std::ios_base::cur);
+#endif  
+
+/* Test header; expected primary */
+  if (l1[0]!=0) return 0;
+
+  if (((l1[1]<<8) + l1[2]) !=16) return 0;
+
+/* Determine total header length */
+  hdr_len=(l1[4]<<24)+(l1[5]<<16)+(l1[6]<<8)+l1[7];
+  if ((hdr_len>10000) || (hdr_len<10)) return 0;
+
+/* Allocate and read all headers */
+  l=(unsigned char*)malloc(hdr_len);
+  ifile.read((char*)l, hdr_len);
+  
+/* Extract header info */
+  catch_xrit_hdr(l,hdr_len,&m_xrit_hdr);
+
+/* Determine image type */
+  if (m_xrit_hdr.file_type==0)
+  {
+    unsigned char c[2];
+    ifile.read((char*)c, 2);
+    if ((c[0]==0xff) && (c[1]==0x01))      m_xrit_hdr.image_iformat='w';
+    else if ((c[0]==0xff) && (c[1]==0xd8)) m_xrit_hdr.image_iformat='j';
+    else                                   m_xrit_hdr.image_iformat='?';
+#if _MSC_VER > 1000 && _MSC_VER < 1300  
+    ifile.seekg(-2, std::ios_base::seekdir::cur);
+#else
+    ifile.seekg(-2, std::ios_base::cur);
+#endif    
+  }
+
+  channame2nr(&m_xrit_hdr);
+
+  free(l);
+
+  m_iHeaderLength = hdr_len;
+
+  return hdr_len;
+}
diff --git a/Utilities/GDAL/frmts/msg/xritheaderparser.h b/Utilities/GDAL/frmts/msg/xritheaderparser.h
new file mode 100644
index 0000000000..9b9b4c3472
--- /dev/null
+++ b/Utilities/GDAL/frmts/msg/xritheaderparser.h
@@ -0,0 +1,113 @@
+// xritheaderparser.h: interface for the XRITHeaderParser class.
+//
+//////////////////////////////////////////////////////////////////////
+
+/******************************************************************************
+ *
+ * Purpose:  Parse the header of the combined XRIT header/data files.
+ * Author:   Bas Retsios, retsios@itc.nl
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, ITC
+ * Parts of code Copyright (c) 2003 R. Alblas 
+ * 
+ * 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.
+ ******************************************************************************/
+
+#if !defined(AFX_XRIT_HEADER_H__6BA5C029_3F7A_43B0_9C69_D002B83A2A63__INCLUDED_)
+#define AFX_XRIT_HEADER_H__6BA5C029_3F7A_43B0_9C69_D002B83A2A63__INCLUDED_
+
+#include "cpl_port.h"
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include <time.h>
+#include <fstream>
+
+
+typedef struct xrit_hdr_tag
+{
+  int hdr_type;
+  int hdr_rec_len;
+  int file_type;
+  long hdr_len;
+  long data_len;
+  long datalen_msb;
+  long datalen_lsb;
+
+  int nb,nc,nl,cf;
+  char image_iformat;  /* 'j' or 'w' */
+  char image_oformat;  /* 'j' or 'w' */
+
+  char proj_name[35];
+  GInt32 cfac,lfac,coff,loff;
+
+/*Anno and extracted contents */
+  char anno[100];
+  char hl;            /* hrit/lrit */
+  char vers[10];      /* 000 */
+  char sat[10];       /* MSG1 */
+  char src[20];       /* MSG*, SERVICE, GOES,  ... */
+  char satsrc[20];    /* MSG*, Srvc, Frgn */
+  int scan_dir;       /* 'n', 's' */
+  char chan[20];      /* VIS006, ADMIN, .... */
+  int chan_nr;        /* coding chan into number (1, 2, ...) */
+  char special;       /* p(ro), e(pi) */
+  int segment;        /* segment number */
+  char itime[20];     /* time: year/date/hourmin */
+  char compr;         /* flag compressed */
+  char encry;         /* flag encrypted */
+
+  char sortn[20];
+  char id[40];
+  struct tm time;
+
+  char ccdds[7];
+  int gp_sc_id;
+  int spec_ch_id;
+  int seq_no,seq_start,seq_end;
+  int dt_f_rep;
+
+  GUInt32 pic_id;
+
+} XRIT_HDR;
+
+class XRITHeaderParser  
+{
+public:
+  XRITHeaderParser();
+  virtual ~XRITHeaderParser();
+  /*************************************************************************
+   * Read from a extracted file (channel with certain order number)
+   * the XRIT header.
+   * Remaining file is JPEG/Wavelet.
+   *************************************************************************/
+  int read_xrithdr(std::ifstream & ifile);
+
+  const XRIT_HDR & xrit_hdr()
+  {
+    return m_xrit_hdr;
+  };
+
+private:
+
+  // private var for holding the header info
+  XRIT_HDR m_xrit_hdr;
+  int m_iHeaderLength;
+};
+
+#endif // !defined(AFX_XRIT_HEADER_H__6BA5C029_3F7A_43B0_9C69_D002B83A2A63__INCLUDED_)
diff --git a/Utilities/GDAL/frmts/msgn/GNUmakefile b/Utilities/GDAL/frmts/msgn/GNUmakefile
new file mode 100644
index 0000000000..7763d4cd85
--- /dev/null
+++ b/Utilities/GDAL/frmts/msgn/GNUmakefile
@@ -0,0 +1,13 @@
+
+include ../../GDALmake.opt
+
+OBJ    =       msgndataset.o msg_basic_types.o msg_reader_core.o
+
+CPPFLAGS :=	$(GDAL_INCLUDE) $(CPPFLAGS) -I. -DGDAL_SUPPORT
+
+default:       $(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+       
+install-obj:   $(O_OBJ)
diff --git a/Utilities/GDAL/frmts/msgn/frmt_msgn.html b/Utilities/GDAL/frmts/msgn/frmt_msgn.html
new file mode 100644
index 0000000000..673a0a2632
--- /dev/null
+++ b/Utilities/GDAL/frmts/msgn/frmt_msgn.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+
+<title>MSGN -- Meteosat Second Generation (MSG) Native Archive Format (.nat)</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>MSGN -- Meteosat Second Generation (MSG) Native Archive Format (.nat)</h1>
+
+GDAL supports reading only of MSG native files. These files may have
+nything from 1 to 12 bands, all at 10-bit resolution.<p>
+
+Currently the driver ignores the 12th band (HRV) because it does not have
+the same resolution as the others, so it can not be accommodated within the
+same dataset. Adding support for this band would be straightforward, though.<p>
+
+Georeferencing is currently supported, but the results may not be
+acceptable, depending on your requirements. The current workaround is to
+implement the CGMS Geostationary projection directly.<p>
+
+</body>
+</html>
+
diff --git a/Utilities/GDAL/frmts/msgn/makefile.vc b/Utilities/GDAL/frmts/msgn/makefile.vc
new file mode 100644
index 0000000000..a2431eff0a
--- /dev/null
+++ b/Utilities/GDAL/frmts/msgn/makefile.vc
@@ -0,0 +1,15 @@
+
+OBJ    =        msgndataset.obj msg_basic_types.obj msg_reader_core.obj
+
+EXTRAFLAGS =   -I..\iso8211 -I. -DGDAL_SUPPORT
+
+GDAL_ROOT      =       ..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:       $(OBJ)
+       copy *.obj ..\o
+
+clean:
+       -del *.obj
+
diff --git a/Utilities/GDAL/frmts/msgn/msg_basic_types.cpp b/Utilities/GDAL/frmts/msgn/msg_basic_types.cpp
new file mode 100644
index 0000000000..d061ed1189
--- /dev/null
+++ b/Utilities/GDAL/frmts/msgn/msg_basic_types.cpp
@@ -0,0 +1,231 @@
+/******************************************************************************
+ * $Id: msg_basic_types.cpp,v 1.3 2006/04/04 12:59:28 fwarmerdam Exp $
+ *
+ * Project:  MSG Native Reader
+ * Purpose:  Basic types implementation.
+ * Author:   Frans van den Bergh, fvdbergh@csir.co.za
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frans van den Bergh <fvdbergh@csir.co.za>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: msg_basic_types.cpp,v $
+ * Revision 1.3  2006/04/04 12:59:28  fwarmerdam
+ * added headers with explicit permission from Frans
+ *
+ */
+
+#include "msg_basic_types.h"
+#include "cpl_port.h"
+
+CPL_CVSID("$Id: msg_basic_types.cpp,v 1.3 2006/04/04 12:59:28 fwarmerdam Exp $");
+
+#include <stdio.h>
+
+namespace msg_native_format {
+
+#ifndef SQR 
+#define SQR(x) ((x)*(x))
+#endif
+
+// endian conversion routines
+void to_native(GP_PK_HEADER& h) {
+    h.sourceSUId    = CPL_MSBWORD32(h.sourceSUId);
+    h.sequenceCount = CPL_MSBWORD16(h.sequenceCount);
+    h.packetLength  = CPL_MSBWORD32(h.packetLength);
+}
+
+void to_native(GP_PK_SH1& h) {
+    h.spacecraftId  = CPL_MSBWORD16(h.spacecraftId);
+}
+
+void to_native(SUB_VISIRLINE& v) {
+    v.satelliteId   = CPL_MSBWORD16(v.satelliteId);
+    v.lineNumberInVisirGrid = CPL_MSBWORD32(v.lineNumberInVisirGrid);
+}
+
+static void swap_64_bits(unsigned char* b) {
+    for (int i=0; i < 4; i++) {
+        unsigned char t = b[i];
+        b[i] = b[7-i];
+        b[7-i] = t;
+    }
+}
+
+void to_native(RADIOMETRIC_PROCCESSING_RECORD& r) {
+    for (int i=0; i < 12; i++) {
+        swap_64_bits((unsigned char*)&r.level1_5ImageCalibration[i].cal_slope);
+        swap_64_bits((unsigned char*)&r.level1_5ImageCalibration[i].cal_offset);
+    }
+}
+
+void to_native(IMAGE_DESCRIPTION_RECORD& r) {
+    r.referencegrid_visir.numberOfLines = CPL_MSBWORD32(r.referencegrid_visir.numberOfLines);
+    r.referencegrid_visir.numberOfColumns = CPL_MSBWORD32(r.referencegrid_visir.numberOfColumns);
+    // should floats be swapped too?
+    unsigned int t;
+    
+    // convert float using CPL_MSBWORD32
+    t = *(unsigned int *)&r.referencegrid_visir.lineDirGridStep;
+    t = CPL_MSBWORD32(t);
+    r.referencegrid_visir.lineDirGridStep = *(float *)&t;
+    
+    // convert float using CPL_MSBWORD32
+    t = *(unsigned int *)&r.referencegrid_visir.columnDirGridStep;
+    t = CPL_MSBWORD32(t);
+    r.referencegrid_visir.columnDirGridStep = *(float *)&t;
+}
+
+void to_string(PH_DATA& d) {
+    d.name[29] = 0;
+    d.value[49] = 0;
+}
+
+// unit tests on structures
+bool perform_type_size_check(void) {
+    bool success = true;
+    if (sizeof(MAIN_PROD_HEADER) != 3674) {
+        fprintf(stderr, "MAIN_PROD_HEADER size not 3674 (%d)\n",sizeof(MAIN_PROD_HEADER));
+        success = false;
+    }
+    if (sizeof(SECONDARY_PROD_HEADER) != 1120) {
+        fprintf(stderr, "SECONDARY_PROD_HEADER size not 1120 (%d)\n",sizeof(SECONDARY_PROD_HEADER));
+        success = false;
+    }
+    if (sizeof(SUB_VISIRLINE) != 27) {
+        fprintf(stderr, "SUB_VISIRLINE size not 17 (%d)\n", sizeof(SUB_VISIRLINE));
+        success = false;
+    }
+    if (sizeof(GP_PK_HEADER) != 22) {
+        fprintf(stderr, "GP_PK_HEADER size not 22 (%d)\n", sizeof(GP_PK_HEADER));
+        success = false;
+    }
+    if (sizeof(GP_PK_SH1) != 16) {
+        fprintf(stderr, "GP_PK_SH1 size not 16 (%d)\n", sizeof(GP_PK_SH1));
+        success = false;
+    }
+    return success;
+}
+
+const double Conversions::altitude      =   42164;          // from origin
+const double Conversions::req           =   6378.1690;       // earthequatorial radius
+const double Conversions::rpol          =   6356.5838;       // earth polar radius
+const double Conversions::oblate        =   1.0/298.257;    // oblateness of earth
+const double Conversions::deg_to_rad    =   M_PI/180.0; 
+const double Conversions::rad_to_deg    =   180.0/M_PI; 
+const double Conversions::nlines        =   3712;           // number of lines in an image
+const double Conversions::step          =   17.83/nlines;    // pixel / line step in degrees
+
+const int Conversions::CFAC    = -781648343;
+const int Conversions::LFAC    = -781648343;
+const int Conversions::COFF    = 1856;
+const int Conversions::LOFF    = 1856;
+
+#define SQR(x) ((x)*(x))
+
+void Conversions::convert_pixel_to_geo(double line, double column, double&longitude, double& latitude) {
+    double x = (column - COFF - 0.0) / double(CFAC >> 16);
+    double y = (line - LOFF - 0.0) / double(LFAC >> 16);
+    
+    double sd = sqrt(SQR(altitude*cos(x)*cos(y)) - (SQR(cos(y)) + 1.006803*SQR(sin(y)))*1737121856); 
+    double sn = (altitude*cos(x)*cos(y) - sd)/(SQR(cos(y)) + 1.006803*SQR(sin(y)));
+    double s1 = altitude - sn*cos(x)*cos(y);
+    double s2 = sn*sin(x)*cos(y);
+    double s3 = -sn*sin(y);
+    double sxy = sqrt(s1*s1 + s2*s2);
+    
+    longitude = atan(s2/s1);
+    latitude  = atan(1.006803*s3/sxy);
+    
+    longitude = longitude / M_PI * 180.0;
+    latitude  = latitude  / M_PI * 180.0;
+}
+
+void Conversions::compute_pixel_xyz(double line, double column, double& x,double& y, double& z) {
+    double asamp = -(column - (nlines/2.0 + 0.5)) * step;
+    double aline = (line - (nlines/2.0 + 0.5)) * step;
+    
+    asamp *= deg_to_rad;
+    aline *= deg_to_rad;
+    
+    double tanal = tan(aline);
+    double tanas = tan(asamp);
+    
+    double p = -1;
+    double q = tanas;
+    double r = tanal * sqrt(1 + q*q);
+    
+   double a = q*q + (r*req/rpol)*(r*req/rpol) + p*p;
+    double b = 2 * altitude * p;
+    double c = altitude * altitude  - req*req;
+    
+    double det = b*b - 4*a*c;
+     
+    if (det > 0) {
+        double k = (-b - sqrt(det))/(2*a);
+        x = altitude + k*p;
+        y = k * q;
+        z = k * r;
+        
+    } else {
+        fprintf(stderr, "Warning: pixel not visible\n");
+    }
+}
+
+double Conversions::compute_pixel_area_sqkm(double line, double column) {
+    double x1, x2;
+    double y1, y2;
+    double z1, z2;
+
+    compute_pixel_xyz(line-0.5, column-0.5, x1, y1, z1);
+    compute_pixel_xyz(line+0.5, column-0.5, x2, y2, z2);
+    
+    double xlen = sqrt(SQR(x1 - x2) + SQR(y1 - y2) + SQR(z1 - z2));
+    
+    compute_pixel_xyz(line-0.5, column+0.5, x2, y2, z2);
+    
+    double ylen = sqrt(SQR(x1 - x2) + SQR(y1 - y2) + SQR(z1 - z2));
+    
+    return xlen*ylen;
+}
+
+void Conversions::convert_geo_to_pixel(double longitude, double latitude,unsigned int& line, unsigned int& column) {
+
+    latitude = latitude / 180.0 * M_PI;
+    longitude = longitude / 180.8 * M_PI;
+
+    double c_lat = atan(0.993243 * tan(latitude));
+    double r_l = rpol / sqrt(1 - 0.00675701*cos(c_lat)*cos(c_lat));
+    double r1 = altitude - r_l*cos(c_lat)*cos(longitude);
+    double r2 = -r_l*cos(c_lat)*sin(longitude);
+    double r3 = r_l*sin(c_lat);
+    double rn = sqrt(r1*r1 + r2*r2 + r3*r3);
+    
+    double x = atan(-r2/r1) * (CFAC >> 16) + COFF;
+    double y = asin(-r3/rn) * (LFAC >> 16) + LOFF;
+    
+    line = (unsigned int)floor(x + 0.5);
+    column = (unsigned int)floor(y + 0.5);
+}
+
+} // namespace msg_native_format
+
+
diff --git a/Utilities/GDAL/frmts/msgn/msg_basic_types.h b/Utilities/GDAL/frmts/msgn/msg_basic_types.h
new file mode 100644
index 0000000000..cbbbe6d055
--- /dev/null
+++ b/Utilities/GDAL/frmts/msgn/msg_basic_types.h
@@ -0,0 +1,254 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  MSG Native Reader
+ * Purpose:  Basic types implementation.
+ * Author:   Frans van den Bergh, fvdbergh@csir.co.za
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frans van den Bergh <fvdbergh@csir.co.za>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: msg_basic_types.h,v $
+ * Revision 1.3  2006/04/04 12:59:28  fwarmerdam
+ * added headers with explicit permission from Frans
+ *
+ */
+
+#ifndef MSG_BASIC_TYPES
+#define MSG_BASIC_TYPES
+
+#include <math.h>
+
+#ifndef M_PI
+#define M_PI  3.1415926535897932384626433832795
+#endif
+
+namespace msg_native_format {
+
+const unsigned int      SATELLITESTATUS_RECORD_LENGTH   = 60134;
+const unsigned int      IMAGEACQUISITION_RECORD_LENGTH  = 700;
+const unsigned int      CELESTIALEVENTS_RECORD_LENGTH   = 326058; // should be 56258 according to ICD105 ??
+const unsigned int      IMAGEDESCRIPTION_RECORD_LENGTH  = 101;
+
+const unsigned int      RADIOMETRICPROCESSING_RECORD_OFFSET = 
+    SATELLITESTATUS_RECORD_LENGTH + 
+    IMAGEACQUISITION_RECORD_LENGTH + 
+    CELESTIALEVENTS_RECORD_LENGTH +
+    IMAGEDESCRIPTION_RECORD_LENGTH;
+
+typedef int             INTEGER;                    // 32 bits
+typedef unsigned int    UNSIGNED;                   // 32 bits
+typedef unsigned short  USHORT;                     // 16 bits
+typedef unsigned char   TIME_CDS_SHORT[6];
+typedef unsigned char   TIME_CDS_EXPANDED[10];
+typedef unsigned char   EBYTE;                      // enumerated byte
+typedef unsigned char   UBYTE;                      // enumerated byte
+typedef float           REAL;
+
+typedef unsigned short int  GP_SC_ID;         // 16 bits, enumerated
+typedef unsigned char       GP_SC_CHAN_ID;    // 8 bits,  enumerated
+typedef unsigned char       GP_FAC_ID;        // 8 bits,  enumerated
+typedef unsigned char       GP_FAC_ENV;       // 8 bits,  enumerated
+typedef unsigned int        GP_SU_ID;         // 32 bits, interval partition
+typedef unsigned char       GP_SVCE_TYPE;     // 8 bits, enumerated
+
+// all structures must be packed on byte boundaries
+#pragma pack(1)
+
+typedef struct {
+    unsigned char qualifier1;
+    unsigned char qualifier2;
+    unsigned char qualifier3;
+    unsigned char qualifier4;
+} GP_CPU_ID;
+
+typedef struct {
+    char name[30];
+    char value[50];
+} PH_DATA;
+
+typedef struct {
+    char name[30];
+    char size[16];
+    char address[16];
+} PH_DATA_ID;
+
+typedef struct {
+    PH_DATA     formatName;
+    PH_DATA     formatDocumentName;
+    PH_DATA     formatDocumentMajorVersion;
+    PH_DATA     formatDocumentMinorVersion;
+    PH_DATA     creationDateTime;
+    PH_DATA     creatingCentre;
+    PH_DATA_ID  dataSetIdentification[5];
+    UBYTE       slack[1364];                // what is this? This is not in the documentation?
+    PH_DATA     totalFileSize;
+    PH_DATA     gort;
+    PH_DATA     asti;
+    PH_DATA     llos;
+    PH_DATA     snit;
+    PH_DATA     aiid;
+    PH_DATA     ssbt;
+    PH_DATA     ssst;
+    PH_DATA     rrcc;
+    PH_DATA     rrbt;
+    PH_DATA     rrst;
+    PH_DATA     pprc;
+    PH_DATA     ppdt;
+    PH_DATA     gplv;
+    PH_DATA     apnm;
+    PH_DATA     aarf;
+    PH_DATA     uudt;
+    PH_DATA     qqov;
+    PH_DATA     udsp;
+} MAIN_PROD_HEADER;
+
+typedef struct {
+    PH_DATA     abid;
+    PH_DATA     smod;
+    PH_DATA     apxs;
+    PH_DATA     avpa;
+    PH_DATA     lscd;
+    PH_DATA     lmap;
+    PH_DATA     qdlc;
+    PH_DATA     qdlp;
+    PH_DATA     qqai;
+    PH_DATA     selectedBandIds;
+    PH_DATA     southLineSelectedRectangle;
+    PH_DATA     northLineSelectedRectangle;
+    PH_DATA     eastColumnSelectedRectangle;
+    PH_DATA     westColumnSelectedRectangle;
+} SECONDARY_PROD_HEADER;
+
+typedef struct {
+    UBYTE       visirlineVersion;
+    GP_SC_ID    satelliteId;
+    TIME_CDS_EXPANDED   trueRepeatCycleStart;
+    INTEGER     lineNumberInVisirGrid;
+    GP_SC_CHAN_ID   channelId;
+    TIME_CDS_SHORT  l10LineMeanAcquisitionTime;
+    EBYTE       lineValidity;
+    EBYTE       lineRadiometricQuality;
+    EBYTE       lineGeometricQuality;
+    // actual line data not represented here
+} SUB_VISIRLINE;
+
+typedef struct {
+    UBYTE       headerVersionNo;
+    EBYTE       packetType;         // 2 = mission data 
+    EBYTE       subHeaderType;      // 0 = no subheader, 1 = GP_PK_SH1, 2 = GP_PK_SH2
+    GP_FAC_ID   sourceFacilityId;
+    GP_FAC_ENV  sourceEnvId;
+    UBYTE       sourceInstanceId;
+    GP_SU_ID    sourceSUId;
+    GP_CPU_ID   sourceCPUId;
+    GP_FAC_ID   destFacilityId;
+    GP_FAC_ENV  destEnvId;
+    USHORT      sequenceCount;
+    UNSIGNED    packetLength;      
+} GP_PK_HEADER;
+
+typedef struct {
+    UBYTE       subHeaderVersionNo;
+    EBYTE       checksumFlag;
+    UBYTE       acknowledgement[4];
+    GP_SVCE_TYPE    serviceType;
+    UBYTE       serviceSubType;
+    TIME_CDS_SHORT  packetTime;
+    GP_SC_ID    spacecraftId;
+} GP_PK_SH1;
+
+typedef struct {
+    double  cal_slope;
+    double  cal_offset;
+} CALIBRATION; 
+
+typedef struct {
+    EBYTE       radianceLinearisation[12];
+    EBYTE       detectorEqualisation[12];
+    EBYTE       onboardCalibrationResult[12];
+    EBYTE       MPEFCalFeedback[12];
+    EBYTE       MTFAdaption[12];
+    EBYTE       straylightCorrectionFlag[12];
+    CALIBRATION level1_5ImageCalibration[12];
+    // rest of structure omitted for now
+} RADIOMETRIC_PROCCESSING_RECORD;
+
+typedef struct {
+    INTEGER     numberOfLines;
+    INTEGER     numberOfColumns;
+    REAL        lineDirGridStep;
+    REAL        columnDirGridStep;
+    EBYTE       gridOrigin;
+} REFERENCEGRID_VISIR;
+
+typedef struct {
+    EBYTE       typeOfProjection;
+    REAL        longitudeOfSSP;
+    REFERENCEGRID_VISIR referencegrid_visir;
+    // rest of record omitted, for now
+} IMAGE_DESCRIPTION_RECORD;
+
+// disable byte-packing 
+#pragma pack()
+
+// endian conversion routines
+void to_native(GP_PK_HEADER& h);
+void to_native(GP_PK_SH1& h);
+void to_native(SUB_VISIRLINE& v);
+void to_native(RADIOMETRIC_PROCCESSING_RECORD& r);
+void to_native(IMAGE_DESCRIPTION_RECORD& r);
+
+// utility function, alters string fields permanently
+void to_string(PH_DATA& d);
+
+// unit tests on structures, returns true on success
+bool perform_type_size_check(void);
+
+class Conversions {
+public:
+    static void convert_pixel_to_geo(double line, double column, double& longitude, double& latitude);
+    static void convert_geo_to_pixel(double longitude, double latitude, unsigned int& line, unsigned int& column);
+    
+    static void compute_pixel_xyz(double line, double column, double& x, double& y, double& z);
+    static double compute_pixel_area_sqkm(double line, double column);
+
+    static const double altitude;   // from origin
+    static const double req;        // earth equatorial radius
+    static const double rpol;       // earth polar radius
+    static const double oblate;     // oblateness of earth
+    static const double deg_to_rad; 
+    static const double rad_to_deg; 
+    static const double step;       // pixel / line step in degrees
+    static const double nlines;     // number of lines in an image
+    
+    static const int CFAC;     // Column scale factor
+    static const int LFAC;     // Line scale factor
+    static const int COFF;     // Column offset
+    static const int LOFF;     // Line offset
+    
+};
+
+} // msg_native_format
+
+#endif
+
diff --git a/Utilities/GDAL/frmts/msgn/msg_reader_core.cpp b/Utilities/GDAL/frmts/msgn/msg_reader_core.cpp
new file mode 100644
index 0000000000..75d3e659eb
--- /dev/null
+++ b/Utilities/GDAL/frmts/msgn/msg_reader_core.cpp
@@ -0,0 +1,279 @@
+/******************************************************************************
+ * $Id: msg_reader_core.cpp,v 1.4 2006/04/04 12:59:28 fwarmerdam Exp $
+ *
+ * Project:  MSG Native Reader
+ * Purpose:  Base class for reading in the headers of MSG native images
+ * Author:   Frans van den Bergh, fvdbergh@csir.co.za
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frans van den Bergh <fvdbergh@csir.co.za>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: msg_reader_core.cpp,v $
+ * Revision 1.4  2006/04/04 12:59:28  fwarmerdam
+ * added headers with explicit permission from Frans
+ *
+ */
+
+#include "msg_reader_core.h"
+#include "msg_basic_types.h"
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#ifdef DEBUG
+#ifdef GDAL_SUPPORT
+#undef DEBUG
+#endif
+#endif
+
+#ifdef GDAL_SUPPORT
+#include "cpl_vsi.h"
+
+CPL_CVSID("$Id: msg_reader_core.cpp,v 1.4 2006/04/04 12:59:28 fwarmerdam Exp $");
+
+#else
+#define VSIFSeek(fp, pos, ref)    fseek(fp, pos, ref)
+#define VSIFRead(p, bs, nb, fp)   fread(p, bs, nb, ref)
+#endif
+
+namespace msg_native_format {
+
+const Blackbody_lut_type Msg_reader_core::Blackbody_LUT[MSG_NUM_CHANNELS+1] = {
+    {0,0,0},  // dummy channel
+    {0,0,0},  // N/A
+    {0,0,0},  // N/A
+    {0,0,0},  // N/A
+    {2569.094, 0.9959, 3.471},
+    {1598.566, 0.9963, 2.219},
+    {1362.142, 0.9991, 0.485},
+    {1149.083, 0.9996, 0.181},
+    {1034.345, 0.9999, 0.060},
+    { 930.659, 0.9983, 0.627},
+    { 839.661, 0.9988, 0.397},
+    { 752.381, 0.9981, 0.576},
+    {0,0,0}   // N/A
+};
+
+
+Msg_reader_core::Msg_reader_core(const char* fname) {
+
+    FILE* fin = fopen(fname, "rb");
+    if (!fin) {
+        fprintf(stderr, "Could not open file %s\n", fname);
+        return;
+    }
+    read_metadata_block(fin);
+}
+
+Msg_reader_core::Msg_reader_core(FILE* fp) {
+    read_metadata_block(fp);
+}
+
+
+void Msg_reader_core::read_metadata_block(FILE* fin) {
+
+    unsigned int i;
+
+    VSIFRead(&_main_header, sizeof(_main_header), 1, fin);
+    VSIFRead(&_sec_header, sizeof(_sec_header), 1, fin);
+
+#ifdef DEBUG
+    // print out all the fields in the header
+    PH_DATA* hd = (PH_DATA*)&_main_header;
+    for (int i=0; i < 6; i++) {
+        to_string(*hd);
+        printf("[%02d] %s %s", i, hd->name, hd->value);
+        hd++;
+    }   
+    PH_DATA_ID* hdi = (PH_DATA_ID*)&_main_header.dataSetIdentification;
+
+    for (i=0; i < 5; i++) {
+        printf("%s %s %s", hdi->name, hdi->size, hdi->address);
+        hdi++;
+    }
+    hd = (PH_DATA*)(&_main_header.totalFileSize);
+    for (int i=0; i < 19; i++) {
+        to_string(*hd);
+        printf("[%02d] %s %s", i, hd->name, hd->value);
+        hd++;
+    }   
+#endif // DEBUG    
+
+    // extract data & header positions
+
+    for (i=0; i < 5; i++) {
+        PH_DATA_ID* hdi = (PH_DATA_ID*)&_main_header.dataSetIdentification[i];
+        if (strncmp(hdi->name, "15Header", strlen("15Header")) == 0) {
+            sscanf(hdi->size, "%d", &_f_header_size);
+            sscanf(hdi->address, "%d", &_f_header_offset);    
+        } else
+            if (strncmp(hdi->name, "15Data", strlen("15Data")) == 0) {
+            sscanf(hdi->size, "%d", &_f_data_size);
+            sscanf(hdi->address, "%d", &_f_data_offset);
+        }
+    }
+#ifdef DEBUG
+    printf("Data: %d %d\n", _f_data_offset, _f_data_size);
+    printf("Header: %d %d\n", _f_header_offset, _f_header_size);
+#endif // DEBUG
+
+    unsigned int lines;
+    sscanf(_sec_header.northLineSelectedRectangle.value, "%d", &_lines);
+    sscanf(_sec_header.southLineSelectedRectangle.value, "%d", &lines);
+    _line_start = lines;
+    _lines -= lines - 1;
+
+    unsigned int cols;
+    sscanf(_sec_header.westColumnSelectedRectangle.value, "%d", &_columns);
+    sscanf(_sec_header.eastColumnSelectedRectangle.value, "%d", &cols);
+    _col_start = cols;
+    _columns -= cols - 1;
+
+#ifdef DEBUG
+    printf("lines = %d, cols = %d\n", _lines, _columns);
+#endif // DEBUG
+
+    int records_per_line = 0;
+    for (i=0; i < MSG_NUM_CHANNELS; i++) {
+        if (_sec_header.selectedBandIds.value[i] == 'X') {
+            _bands[i] = 1;
+            records_per_line += (i == (MSG_NUM_CHANNELS-1)) ? 3 : 1;
+        } else {
+            _bands[i] = 0;
+        }
+    }
+
+#ifdef DEBUG
+    printf("reading a total of %d records per line\n", records_per_line);
+#endif // DEBUG
+
+    // extract time fields, assume that SNIT is the correct field:
+    sscanf(_main_header.snit.value +  0, "%04d", &_year);
+    sscanf(_main_header.snit.value +  4, "%02d", &_month);    
+    sscanf(_main_header.snit.value +  6, "%02d", &_day);
+    sscanf(_main_header.snit.value +  8, "%02d", &_hour);
+    sscanf(_main_header.snit.value + 10, "%02d", &_minute);
+    
+    // read radiometric block
+    RADIOMETRIC_PROCCESSING_RECORD rad;
+    off_t offset = RADIOMETRICPROCESSING_RECORD_OFFSET + _f_header_offset + sizeof(GP_PK_HEADER) + sizeof(GP_PK_SH1) + 1;
+    VSIFSeek(fin, offset, SEEK_SET);
+    VSIFRead(&rad, sizeof(RADIOMETRIC_PROCCESSING_RECORD), 1, fin);
+    to_native(rad);
+    memcpy((void*)_calibration, (void*)&rad.level1_5ImageCalibration,sizeof(_calibration));
+
+#ifdef DEBUG
+    for (unsigned int i=0; i < MSG_NUM_CHANNELS; i++) {
+        if (_calibration[i].cal_slope < 0 || _calibration[i].cal_slope > 0.4) {
+            printf("Warning: calibration slope (%lf) out of nominal range. MSG reader probably broken\n", _calibration[i].cal_slope);
+            
+        }
+        if (_calibration[i].cal_offset > 0 || _calibration[i].cal_offset < -20) {
+            printf("Warning: calibration offset (%lf) out of nominal range. MSG reader probably broken\n", _calibration[i].cal_offset);
+        }
+    }
+#endif    
+    
+    // read image description block
+    IMAGE_DESCRIPTION_RECORD idr;
+    offset = RADIOMETRICPROCESSING_RECORD_OFFSET  - IMAGEDESCRIPTION_RECORD_LENGTH + _f_header_offset + sizeof(GP_PK_HEADER) + sizeof(GP_PK_SH1) + 1;
+    VSIFSeek(fin, offset, SEEK_SET);
+    VSIFRead(&idr, sizeof(IMAGE_DESCRIPTION_RECORD), 1, fin);
+    to_native(idr);
+    _line_dir_step = idr.referencegrid_visir.lineDirGridStep;
+    _col_dir_step = idr.referencegrid_visir.columnDirGridStep;
+    
+    
+    // Rather convoluted, but this code is required to compute the real data block sizes
+    // It does this by reading in the first line of every band, to get to the packet size field
+    GP_PK_HEADER gp_header;
+    GP_PK_SH1    sub_header;
+    SUB_VISIRLINE visir_line;
+
+    VSIFSeek(fin, _f_data_offset, SEEK_SET);
+    
+    _hrv_packet_size = 0;
+    _interline_spacing = 0;
+    visir_line.channelId = 0;
+
+    int scanned_bands[MSG_NUM_CHANNELS];
+    int band_count = 0;
+    for (i=0; i < MSG_NUM_CHANNELS; i++) {
+        scanned_bands[i] = _bands[i];
+        band_count += _bands[i];
+    }
+    
+    do {
+        VSIFRead(&gp_header, sizeof(GP_PK_HEADER), 1, fin);
+        VSIFRead(&sub_header, sizeof(GP_PK_SH1), 1, fin);
+        VSIFRead(&visir_line, sizeof(SUB_VISIRLINE), 1, fin);
+        to_native(visir_line);
+        to_native(gp_header);
+        // skip over the actual line data
+        VSIFSeek(fin, 
+            gp_header.packetLength - (sizeof(GP_PK_SH1) + sizeof(SUB_VISIRLINE) - 1), SEEK_CUR );
+        
+        if (scanned_bands[visir_line.channelId - 1]) {
+            scanned_bands[visir_line.channelId - 1] = 0;
+            band_count--;
+            
+            if (visir_line.channelId != 12) { // not the HRV channel
+                _visir_bytes_per_line = gp_header.packetLength - (sizeof(GP_PK_SH1) + sizeof(SUB_VISIRLINE) - 1);
+                _visir_packet_size = gp_header.packetLength + sizeof(GP_PK_HEADER) + 1; 
+                _interline_spacing += _visir_packet_size;
+            } else {
+                _hrv_packet_size = gp_header.packetLength + sizeof(GP_PK_HEADER) + 1;        
+                _interline_spacing +=  3*_hrv_packet_size;
+            }   
+        }
+    } while (band_count > 0);
+}
+
+#ifndef GDAL_SUPPORT
+
+int Msg_reader_core::_chan_to_idx(Msg_channel_names channel) {
+    unsigned int idx = 0;
+    while (idx < MSG_NUM_CHANNELS) {
+        if ( (1 << (idx+1)) == (int)channel ) {
+            return idx;
+        }
+        idx++;
+    }
+    return 0;
+}
+
+void Msg_reader_core::get_pixel_geo_coordinates(unsigned int line, unsigned int column, double& longitude, double& latitude) {
+    Conversions::convert_pixel_to_geo((unsigned int)(line + _line_start), (unsigned int)(column + _col_start), longitude, latitude);
+}
+
+void Msg_reader_core::get_pixel_geo_coordinates(double line, double column, double& longitude, double& latitude) {
+    Conversions::convert_pixel_to_geo(line + _line_start, column + _col_start, longitude, latitude);
+}
+
+double Msg_reader_core::compute_pixel_area_sqkm(double line, double column) {
+    return Conversions::compute_pixel_area_sqkm(line + _line_start, column + _col_start);
+}
+
+#endif // GDAL_SUPPORT
+
+} // namespace msg_native_format
+
diff --git a/Utilities/GDAL/frmts/msgn/msg_reader_core.h b/Utilities/GDAL/frmts/msgn/msg_reader_core.h
new file mode 100644
index 0000000000..230a69ba47
--- /dev/null
+++ b/Utilities/GDAL/frmts/msgn/msg_reader_core.h
@@ -0,0 +1,150 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  MSG Native Reader
+ * Purpose:  Base class for reading in the headers of MSG native images
+ * Author:   Frans van den Bergh, fvdbergh@csir.co.za
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frans van den Bergh <fvdbergh@csir.co.za>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: msg_reader_core.h,v $
+ * Revision 1.2  2006/04/04 12:59:28  fwarmerdam
+ * added headers with explicit permission from Frans
+ *
+ */
+
+#ifndef MSG_READER_CORE_H
+#define MSG_READER_CORE_H
+
+#include "msg_basic_types.h"
+#include <stdio.h>
+
+namespace msg_native_format {
+
+const unsigned int MSG_NUM_CHANNELS = 12;
+
+typedef struct {
+    double vc;
+    double A;
+    double B;
+} Blackbody_lut_type;
+
+typedef enum {
+    VIS0_6  = 2,
+    VIS0_8  = 4,
+    NIR1_6  = 8,
+    IR3_9   = 16,
+    IR6_2   = 32,
+    IR7_3   = 64,
+    IR8_7   = 128,
+    IR9_7   = 256,
+    IR10_8  = 512,
+    IR12_0  = 1024,
+    IR13_4  = 2048,
+    HRV     = 4096
+} Msg_channel_names;
+
+class Msg_reader_core {
+public:
+    Msg_reader_core(const char* fname);
+    Msg_reader_core(FILE* fp);
+    virtual ~Msg_reader_core(void) {};
+    
+    #ifndef GDAL_SUPPORT
+    virtual void radiance_to_blackbody(int using_chan_no = 0) = 0;   // can override which channel's parameters to use
+    virtual double* get_data(int chan_no=0) = 0;
+    #endif
+    
+    unsigned int get_lines(void) { return _lines; }
+    unsigned int get_columns(void) { return _columns; }
+    
+    void get_pixel_geo_coordinates(unsigned int line, unsigned int column, double& longitude, double& latitude); // x and y relative to this image, not full disc image
+    void get_pixel_geo_coordinates(double line, double column, double& longitude, double& latitude); // x and y relative to this image, not full disc image
+    double compute_pixel_area_sqkm(double line, double column);
+    
+    static const Blackbody_lut_type Blackbody_LUT[MSG_NUM_CHANNELS+1];
+    
+    unsigned int get_year(void) { return _year; }
+    unsigned int get_month(void) { return _month; }
+    unsigned int get_day(void) { return _day; }
+    unsigned int get_hour(void) { return _hour; }
+    unsigned int get_minute(void) { return _minute; }
+    
+    unsigned int get_line_start(void) { return _line_start; }
+    unsigned int get_col_start(void) { return _col_start; }
+    
+    float get_col_dir_step(void) { return _col_dir_step; }
+    float get_line_dir_step(void) { return _line_dir_step; }
+    
+    unsigned int get_f_data_offset(void) { return _f_data_offset; }
+    unsigned int get_visir_bytes_per_line(void) { return _visir_bytes_per_line; }
+    unsigned int get_visir_packet_size(void) { return _visir_packet_size; }
+    unsigned int get_interline_spacing(void) { return _interline_spacing; }
+    
+    unsigned char* get_band_map(void) { return _bands; }
+    
+    CALIBRATION*  get_calibration_parameters(void) { return _calibration; }
+    
+private:
+    void read_metadata_block(FILE* fp);
+    
+protected:
+    
+    int _chan_to_idx(Msg_channel_names channel);
+
+    unsigned int    _lines;
+    unsigned int    _columns;
+    
+    unsigned int    _line_start;
+    unsigned int    _col_start;
+    
+    float           _col_dir_step;
+    float           _line_dir_step;
+    
+    MAIN_PROD_HEADER        _main_header;
+    SECONDARY_PROD_HEADER   _sec_header;
+    CALIBRATION             _calibration[MSG_NUM_CHANNELS];
+    
+    unsigned int _f_data_offset;
+    unsigned int _f_data_size;    
+    unsigned int _f_header_offset;
+    unsigned int _f_header_size;
+    
+    unsigned int _visir_bytes_per_line;   // packed length of a VISIR line,wihtout headers
+    unsigned int _visir_packet_size;      // effectively, the spacing between lines of consecutive bands in bytes
+    unsigned int _hrv_packet_size;
+    unsigned int _interline_spacing;
+    
+    unsigned char _bands[MSG_NUM_CHANNELS];
+    
+    unsigned int _year;
+    unsigned int _month;
+    unsigned int _day;
+    unsigned int _hour;
+    unsigned int _minute;
+};
+
+}// namespace msg_native_format
+
+#endif
+
diff --git a/Utilities/GDAL/frmts/msgn/msgndataset.cpp b/Utilities/GDAL/frmts/msgn/msgndataset.cpp
new file mode 100644
index 0000000000..0db8973ef4
--- /dev/null
+++ b/Utilities/GDAL/frmts/msgn/msgndataset.cpp
@@ -0,0 +1,407 @@
+/******************************************************************************
+ * $Id: msgndataset.cpp,v 1.4 2005/07/08 18:13:08 fwarmerdam Exp $
+ *
+ * Project:  MSG Native Reader
+ * Purpose:  All code for EUMETSAT Archive format reader
+ * Author:   Frans van den Bergh, fvdbergh@csir.co.za
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frans van den Bergh <fvdbergh@csir.co.za>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: msgndataset.cpp,v $
+ * Revision 1.4  2005/07/08 18:13:08  fwarmerdam
+ * Added log keyword in header.
+ *
+ */
+ 
+#include "gdal_priv.h"
+#include "ogr_spatialref.h"
+
+#include "msg_reader_core.h"
+using namespace msg_native_format;
+
+CPL_CVSID("$Id: msgndataset.cpp,v 1.4 2005/07/08 18:13:08 fwarmerdam Exp $");
+
+CPL_C_START
+void   GDALRegister_MSGN(void);
+CPL_C_END
+
+class MSGNRasterBand;
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            MSGNDataset                               */
+/* ==================================================================== */
+/************************************************************************/
+
+class MSGNDataset : public GDALDataset
+{
+    friend class MSGNRasterBand;
+
+    FILE       *fp;
+    GByte      abyHeader[1012];
+    
+    Msg_reader_core*    msg_reader_core;
+    double adfGeoTransform[6];
+    char   *pszProjection;
+
+  public:
+        MSGNDataset();
+               ~MSGNDataset();
+    
+    static GDALDataset *Open( GDALOpenInfo * );
+
+    CPLErr     GetGeoTransform( double * padfTransform );
+    const char *GetProjectionRef();
+    
+    static void double2hex(double a, char* s);
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            MSGNRasterBand                            */
+/* ==================================================================== */
+/************************************************************************/
+
+class MSGNRasterBand : public GDALRasterBand
+{
+    friend class MSGNDataset;
+    
+    unsigned int visir_packet_size;
+    unsigned int visir_bytes_per_line;
+    unsigned int interline_spacing;
+    double  GetNoDataValue (int *pbSuccess=NULL) {
+        if (pbSuccess) {
+            *pbSuccess = 1;
+        }
+        return MSGN_NODATA_VALUE;  
+    }
+    
+    static const unsigned short MSGN_NODATA_VALUE;
+    
+  public:
+
+               MSGNRasterBand( MSGNDataset *, int );
+    
+    virtual CPLErr IReadBlock( int, int, void * );
+};
+
+const unsigned short MSGNRasterBand::MSGN_NODATA_VALUE = 0; 
+
+/************************************************************************/
+/*                           MSGNRasterBand()                            */
+/************************************************************************/
+
+MSGNRasterBand::MSGNRasterBand( MSGNDataset *poDS, int nBand )
+
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+    
+    eDataType = GDT_UInt16;
+
+    nBlockXSize = poDS->GetRasterXSize();
+    nBlockYSize = 1;
+    
+    visir_packet_size = poDS->msg_reader_core->get_visir_packet_size();
+    visir_bytes_per_line = poDS->msg_reader_core->get_visir_bytes_per_line();
+    interline_spacing = poDS->msg_reader_core->get_interline_spacing();
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr MSGNRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+
+{
+    MSGNDataset *poGDS = (MSGNDataset *) poDS;
+    char       *pszRecord;
+    
+    unsigned int packet_length =  visir_bytes_per_line + sizeof(SUB_VISIRLINE);
+    unsigned int packet_offset = poGDS->msg_reader_core->get_f_data_offset() +
+        interline_spacing*nBlockYOff  + (nBand-1)*visir_packet_size + 
+        (visir_packet_size - packet_length);
+        
+    VSIFSeek( poGDS->fp, packet_offset, SEEK_SET );
+    
+    pszRecord = (char *) CPLMalloc(packet_length);
+    size_t nread = VSIFRead( pszRecord, 1, packet_length, poGDS->fp );
+    
+    SUB_VISIRLINE* p = (SUB_VISIRLINE*) pszRecord;
+    to_native(*p);
+    
+    if (p->lineValidity != 1) {
+        for (int c=0; c < nBlockXSize; c++) {
+            ((short int *)pImage)[c] = MSGN_NODATA_VALUE;
+        }
+    }
+    
+    if ( nread != packet_length || 
+        (p->lineNumberInVisirGrid - poGDS->msg_reader_core->get_line_start()) != (unsigned int)nBlockYOff ) {
+        CPLFree( pszRecord );
+
+        CPLError( CE_Failure, CPLE_AppDefined, "MSGN Scanline corrupt." );
+        
+        return CE_Failure;
+    }
+    
+    // unpack the 10-bit values into 16-bit unsigned short ints
+    unsigned char *cptr = (unsigned char*)pszRecord + 
+        (packet_length - visir_bytes_per_line);
+    int bitsLeft = 8;
+    unsigned short value = 0;
+    for (int c=0; c < nBlockXSize; c++) {
+        value = 0;
+        for (int bit=0; bit < 10; bit++) {
+            value <<= 1;
+            if (*cptr & 128) {
+                value |= 1;   
+            }
+            *cptr <<= 1;
+            bitsLeft--; 
+            if (bitsLeft == 0) {
+                cptr++;
+            bitsLeft = 8;
+            }
+        }
+        ((short int *)pImage)[c] = value;
+    }
+    
+    return CE_None;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             MSGNDataset                             */
+/* ==================================================================== */
+/************************************************************************/
+
+MSGNDataset::MSGNDataset() {
+    pszProjection = CPLStrdup("");
+}
+
+/************************************************************************/
+/*                            ~MSGNDataset()                             */
+/************************************************************************/
+
+MSGNDataset::~MSGNDataset()
+
+{
+    if( fp != NULL )
+        VSIFClose( fp );
+        
+    CPLFree(pszProjection);    
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+void MSGNDataset::double2hex(double a, char* out_str) {
+    const char hexlut[] = "0123456789abcdef";
+    char* p = (char*)&a;
+    char* s = out_str;
+    for(unsigned int i=0; i < sizeof(double); i++) {
+        *s = hexlut[(*p >> 4) & 0x0f];
+        s++;
+        *s = hexlut[*p & 0x0f];
+        s++;
+        p++;
+    }
+    *s = 0;
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr MSGNDataset::GetGeoTransform( double * padfTransform )
+
+{
+
+    for (int i=0; i < 6; i++) {
+        padfTransform[i] = adfGeoTransform[i];
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *MSGNDataset::GetProjectionRef()
+
+{
+    return ( pszProjection );
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *MSGNDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Before trying MSGNOpen() we first verify that there is at        */
+/*      least one "\n#keyword" type signature in the first chunk of     */
+/*      the file.                                                       */
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->fp == NULL || poOpenInfo->nHeaderBytes < 50 )
+        return NULL;
+
+    /* check if this is a "NATIVE" MSG format image */
+    if( !EQUALN((char *)poOpenInfo->pabyHeader,
+                "FormatName                  : NATIVE", 36) )
+    {
+        return NULL;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    MSGNDataset        *poDS;
+
+    poDS = new MSGNDataset();
+
+    poDS->fp = poOpenInfo->fp;
+    poOpenInfo->fp = NULL;
+    
+/* -------------------------------------------------------------------- */
+/*      Read the header.                                                */
+/* -------------------------------------------------------------------- */
+    // first reset the file pointer, then hand over to the msg_reader_core
+    VSIFSeek( poDS->fp, 0, SEEK_SET );
+    
+    poDS->msg_reader_core = new Msg_reader_core(poDS->fp);    
+
+    poDS->nRasterXSize = poDS->msg_reader_core->get_columns();
+    poDS->nRasterYSize = poDS->msg_reader_core->get_lines();
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    unsigned int band_count = 1, i;
+    unsigned char* bands = poDS->msg_reader_core->get_band_map();
+    for ( i=0; i < MSG_NUM_CHANNELS-1; i++) {
+        if (bands[i]) {
+            poDS->SetBand( band_count, new MSGNRasterBand( poDS, band_count ));
+            band_count++;
+        }
+    }
+    
+    double pixel_gsd_x = 1000 * poDS->msg_reader_core->get_col_dir_step();  // convert from km to m
+    double pixel_gsd_y = 1000 * poDS->msg_reader_core->get_line_dir_step(); // convert from km to m
+    double origin_x = -pixel_gsd_x * (-(Conversions::nlines / 2.0) + poDS->msg_reader_core->get_col_start()); 
+    double origin_y = -pixel_gsd_y * ((Conversions::nlines / 2.0) - poDS->msg_reader_core->get_line_start()); 
+    
+    poDS->adfGeoTransform[0] = origin_x;
+    poDS->adfGeoTransform[1] = -pixel_gsd_x;
+    poDS->adfGeoTransform[2] = 0.0;
+    
+    poDS->adfGeoTransform[3] = origin_y;
+    poDS->adfGeoTransform[4] = 0.0;
+    poDS->adfGeoTransform[5] = pixel_gsd_y;
+    
+    OGRSpatialReference oSRS;
+
+    oSRS.SetProjCS("Geostationary projection (MSG)");
+    oSRS.SetGEOS(  0, 35785831, 0, 0 );
+    oSRS.SetGeogCS(
+        "MSG Ellipsoid",
+        "MSG_DATUM",
+        "MSG_SPHEROID",
+        Conversions::rpol * 1000.0,
+        1 / ( 1 - Conversions::rpol/Conversions::req)
+    );
+        
+    oSRS.exportToWkt( &(poDS->pszProjection) );
+    
+    CALIBRATION* cal = poDS->msg_reader_core->get_calibration_parameters();
+    char tagname[30];
+    char field[300];
+    char hexvalue1[30];
+    char hexvalue2[30];
+    
+    poDS->SetMetadataItem("Radiometric parameters format", "offset slopehex_offset hex_slope");
+    for (i=0; i < MSG_NUM_CHANNELS; i++) {
+        sprintf(tagname, "ch%02d_cal", i+1);
+        
+        MSGNDataset::double2hex(cal[i].cal_offset, hexvalue1);
+        MSGNDataset::double2hex(cal[i].cal_slope, hexvalue2);
+        
+        sprintf(field, "%.12e %.12e %s %s", cal[i].cal_offset,cal[i].cal_slope, hexvalue1, hexvalue2);
+        poDS->SetMetadataItem(tagname, field);
+    }
+    
+    poDS->SetMetadataItem("Blackbody parameters format", "vc A B");
+    for (i=4; i < MSG_NUM_CHANNELS-1; i++) {
+        sprintf(tagname, "ch%02d_blackbody", i);
+        sprintf(field, "%.4f %.4f %.4f",
+            Msg_reader_core::Blackbody_LUT[i].vc,
+            Msg_reader_core::Blackbody_LUT[i].A,
+            Msg_reader_core::Blackbody_LUT[i].B
+        );
+        poDS->SetMetadataItem(tagname, field);
+    }
+    
+    sprintf(field, "%04d%02d%02d/%02d:%02d",
+        poDS->msg_reader_core->get_year(),
+        poDS->msg_reader_core->get_month(),
+        poDS->msg_reader_core->get_day(),
+        poDS->msg_reader_core->get_hour(),
+        poDS->msg_reader_core->get_minute()
+    );
+    poDS->SetMetadataItem("Date/Time", field);
+    
+    
+    return( poDS );
+}
+
+/************************************************************************/
+/*                          GDALRegister_MSGN()                          */
+/************************************************************************/
+
+void GDALRegister_MSGN()
+
+{
+    GDALDriver *poDriver;
+
+    if( GDALGetDriverByName( "MSGN" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "MSGN" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "EUMETSAT Archive native (.nat)" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_various.html#MSGN" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "nat" );
+
+        poDriver->pfnOpen = MSGNDataset::Open;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
diff --git a/Utilities/GDAL/frmts/o/.cvsignore b/Utilities/GDAL/frmts/o/.cvsignore
new file mode 100644
index 0000000000..c8b522d6e7
--- /dev/null
+++ b/Utilities/GDAL/frmts/o/.cvsignore
@@ -0,0 +1 @@
+*.lo
diff --git a/Utilities/GDAL/frmts/o/README.TXT b/Utilities/GDAL/frmts/o/README.TXT
new file mode 100644
index 0000000000..2058412330
--- /dev/null
+++ b/Utilities/GDAL/frmts/o/README.TXT
@@ -0,0 +1,7 @@
+gdal/frmts/o
+------------
+
+This directory is where object files are put from the various format
+drivers so they can be easily linked into GDAL.  This file is here to
+ensure that the "o" directory isn't lost when people do a cvs checkout
+with pruning. 
diff --git a/Utilities/GDAL/frmts/ogdi/GNUmakefile b/Utilities/GDAL/frmts/ogdi/GNUmakefile
new file mode 100644
index 0000000000..caf0e895e5
--- /dev/null
+++ b/Utilities/GDAL/frmts/ogdi/GNUmakefile
@@ -0,0 +1,13 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	ogdidataset.o
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(OGDI_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/ogdi/frmt_ogdi.html b/Utilities/GDAL/frmts/ogdi/frmt_ogdi.html
new file mode 100644
index 0000000000..fd02984980
--- /dev/null
+++ b/Utilities/GDAL/frmts/ogdi/frmt_ogdi.html
@@ -0,0 +1,83 @@
+<html>
+<head>
+<title>OGDI -- OGDI Bridge</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>OGDI -- OGDI Bridge</h1>
+
+OGDI raster data sources are supported by GDAL for reading.  Both Matrix
+and Image families should be fully supported, as well as reading of colormap
+and projection metadata.  The GDAL reader is intended
+to be used with OGDI 3.1 drivers, but OGDI 3.0 drivers should also work.<p>
+
+OGDI datasets are opened within GDAL by selecting the GLTP url.  For
+instance, gltp://gdal.velocet.ca/adrg/usr4/mpp1/adrg/TPSUS0101 would open
+the ADRG dataset stored at /usr4/mpp1/adrg/TPSUS0101 on the machine 
+gdal.velocet.ca (assuming it has an OGDI server running) using the 'adrg'
+driver.  This default access to a whole datastore will attempt to represent
+all layers (and all supported family types) as bands all at the resolution
+and region reported by the datastore when initially accessed.<p>
+
+It is also possible to select a particular layer and access family
+from an OGDI datastore by indicating the layer name family in the name.
+The GDAL dataset name 
+gltp:/adrg/usr4/mpp1/adrg/TPUS0101:"TPUS0102.IMG":Matrix would select
+the layer named TPUS0102.IMG from the dataset /usr4/mpp1/adrg/TPUS0101 
+on the local system using the ADRG driver, and access the Matrix family.
+When a specific layer is accessed in this manner GDAL will attempt to determine
+the region and resolution from the OGDI 3.1 capabilities document.  Note that
+OGDI 3.0 datastores must have the layer and family specified in the 
+dataset name since they cannot be determined automatically.<p>
+
+<pre>
+eg.
+  gltp://gdal.velocet.ca/adrg/usr4/mpp1/adrg/TPUS0101
+  gltp:/adrg/usr4/mpp1/adrg/TPUS0101
+  gltp:/adrg/usr4/mpp1/adrg/TPUS0101:"TPUS0102.IMG":Matrix
+</pre>
+
+OGDI Matrix family layers (pseudocolored integer layers) are represented
+as a single band of raster data with a color table.  Though the Matrix layers
+contain 32bit integer values, they are represented through GDAL as eight
+layers.  All values over 255 are truncated to 255, and only 256 colormap 
+entries are captured.  While this works well for most Matrix layers, it is
+anticipated that at some point in the future Matrix layers with a larger
+dynamic range will be represented as other data types. <p>
+
+OGDI Image family layers may internally have a type of RGB (1) which is
+represented as three 8bit bands in GDAL, or Byte (2), UInt16 (3), Int16 (4) 
+or Int32 (5).   There is no support for floating points bands in OGDI 3.1.<p>
+
+The GDAL OGDI driver will represent OGDI datasources as having <i>arbitrary</i>
+overviews.  Any GDAL raster read requests at a reduced resolution will be
+passed on to the OGDI driver at that reduced resolution; potentially allowing
+efficient reading of overview information from OGDI datastores.<p>
+
+If an OGDI datastore is opened without selecting a layer name in the dataset
+name, and if the datastore has OGDI 3.1 style capabilities, the list of layers
+will be made available as SUBDATASETS metadata.  For instance, the 
+<i>gdalinfo</i> command might report the following.  This information can be 
+used to establish available layers for direct access.<p>
+
+<pre>
+Subdatasets:
+  SUBDATASET_1_NAME=gltp:/adrg/usr4/mpp1/adrg/TPUS0101:"TPUS0101.IMG":Matrix
+  SUBDATASET_1_DESC=TPUS0101.IMG as Matrix
+  SUBDATASET_2_NAME=gltp:/adrg/usr4/mpp1/adrg/TPUS0101:"TPUS0102.IMG":Matrix
+  SUBDATASET_2_DESC=TPUS0102.IMG as Matrix
+  SUBDATASET_3_NAME=gltp:/adrg/usr4/mpp1/adrg/TPUS0101:"TPUS0101.IMG":Image
+  SUBDATASET_3_DESC=TPUS0101.IMG as Image
+  SUBDATASET_4_NAME=gltp:/adrg/usr4/mpp1/adrg/TPUS0101:"TPUS0102.IMG":Image
+  SUBDATASET_4_DESC=TPUS0102.IMG as Image
+</pre>
+
+See Also:<p>
+
+<ul>
+<li> <a href="http://ogdi.sourceforge.net/">ogdi.sourceforge.net</a>
+</ul>
+
+</body>
+</html>
diff --git a/Utilities/GDAL/frmts/ogdi/makefile.vc b/Utilities/GDAL/frmts/ogdi/makefile.vc
new file mode 100644
index 0000000000..b208147a2f
--- /dev/null
+++ b/Utilities/GDAL/frmts/ogdi/makefile.vc
@@ -0,0 +1,16 @@
+
+OBJ	=	ogdidataset.obj 
+
+GDAL_ROOT	=	..\..
+
+EXTRAFLAGS	=	-I$(OGDIDIR)/ogdi/include -I$(OGDIDIR)/include/win32 \
+			-I$(OGDIDIR)/proj -DWIN32 -D_WINDOWS
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/ogdi/ogdidataset.cpp b/Utilities/GDAL/frmts/ogdi/ogdidataset.cpp
new file mode 100644
index 0000000000..6d5ff2d4e0
--- /dev/null
+++ b/Utilities/GDAL/frmts/ogdi/ogdidataset.cpp
@@ -0,0 +1,1029 @@
+/******************************************************************************
+ * $Id: ogdidataset.cpp,v 1.22 2006/04/04 05:03:38 fwarmerdam Exp $
+ *
+ * Name:     ogdidataset.cpp
+ * Project:  OGDI Bridge
+ * Purpose:  Main driver for OGDI.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1998, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************
+ *
+ * $Log: ogdidataset.cpp,v $
+ * Revision 1.22  2006/04/04 05:03:38  fwarmerdam
+ * Update contact info.
+ *
+ * Revision 1.21  2006/03/28 15:03:47  fwarmerdam
+ * make sure we reset the region if currentindex is not zero
+ *
+ * Revision 1.20  2004/12/02 20:31:03  fwarmerdam
+ * upgraded with support for advise api
+ *
+ * Revision 1.19  2004/10/30 15:51:48  fwarmerdam
+ * undid the last change, breaks things if buf/win sizes differ
+ *
+ * Revision 1.18  2004/03/25 18:00:11  aubin
+ * request is satisfied based on nYSize, not destination buffer size
+ *
+ * Revision 1.17  2002/09/04 06:50:37  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.16  2002/06/12 21:12:25  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.15  2002/01/13 01:44:44  warmerda
+ * cleanup debugs a bit
+ *
+ * Revision 1.14  2001/11/02 22:21:25  warmerda
+ * fixed memory leaks
+ *
+ * Revision 1.13  2001/07/18 04:51:57  warmerda
+ * added CPL_CVSID
+ *
+ * Revision 1.12  2001/06/29 18:29:30  warmerda
+ * don't refree pszURL when failing with non-raster datastores.
+ *
+ * Revision 1.11  2001/06/28 19:18:35  warmerda
+ * allow double quotes to be used in filename toprotect colons
+ *
+ * Revision 1.10  2001/06/23 22:40:53  warmerda
+ * added SUBDATASETS support
+ *
+ * Revision 1.9  2001/06/20 16:09:40  warmerda
+ * utilize capabilities data
+ *
+ * Revision 1.8  2001/04/18 17:58:41  warmerda
+ * fixed raster size calculation
+ *
+ * Revision 1.7  2001/04/17 21:46:04  warmerda
+ * complete Image support, utilize cln_GetLayerCapabilities()
+ *
+ * Revision 1.6  2000/08/28 21:30:17  warmerda
+ * restructure to use cln_GetNextObject
+ *
+ * Revision 1.5  2000/08/28 20:15:07  warmerda
+ * added projection translation
+ *
+ * Revision 1.4  2000/08/25 21:31:04  warmerda
+ * added colortable support
+ *
+ * Revision 1.3  2000/08/25 14:28:04  warmerda
+ * preliminary support with IRasterIO
+ *
+ * Revision 1.2  2000/02/28 16:32:20  warmerda
+ * use SetBand method
+ *
+ * Revision 1.1  1999/01/11 15:29:16  warmerda
+ * New
+ *
+ */
+
+#include <math.h>
+#include "ecs.h"
+#include "gdal_priv.h"
+#include "cpl_string.h"
+#include "ogr_spatialref.h"
+
+CPL_CVSID("$Id: ogdidataset.cpp,v 1.22 2006/04/04 05:03:38 fwarmerdam Exp $");
+
+CPL_C_START
+void	GDALRegister_OGDI(void);
+CPL_C_END
+
+/************************************************************************/
+/* ==================================================================== */
+/*				OGDIDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class OGDIRasterBand;
+
+class CPL_DLL OGDIDataset : public GDALDataset
+{
+    friend class OGDIRasterBand;
+    
+    int		nClientID;
+
+    ecs_Region	sGlobalBounds;
+    ecs_Region  sCurrentBounds;
+    int         nCurrentBand;
+    int         nCurrentIndex;
+
+    char	*pszProjection;
+
+    static CPLErr CollectLayers(int, char***,char***);
+    static CPLErr OverrideGlobalInfo(OGDIDataset*,const char *);
+
+    void        AddSubDataset( const char *pszType, const char *pszLayer );
+    char	**papszSubDatasets;
+
+  public:
+    		OGDIDataset();
+    		~OGDIDataset();
+                
+    static GDALDataset *Open( GDALOpenInfo * );
+
+    int		GetClientID() { return nClientID; }
+
+    virtual const char *GetProjectionRef(void);
+    virtual CPLErr GetGeoTransform( double * );
+
+    virtual void *GetInternalHandle( const char * );
+
+    virtual char **GetMetadata( const char * pszDomain = "" );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            OGDIRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class OGDIRasterBand : public GDALRasterBand
+{
+    friend class OGDIDataset;
+
+    int		nOGDIImageType; /* ie. 1 for RGB */
+
+    char	*pszLayerName;
+    ecs_Family  eFamily;
+
+    int		nComponent; /* varies only for RGB layers */
+
+    GDALColorTable *poCT;
+
+    virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
+                              void *, int, int, GDALDataType,
+                              int, int );
+
+    CPLErr         EstablishAccess( int nXOff, int nYOff, 
+                                    int nXSize, int nYSize,
+                                    int nBufXSize, int nBufYSize );
+
+  public:
+
+                   OGDIRasterBand( OGDIDataset *, int, const char *,
+                                   ecs_Family, int );
+                   ~OGDIRasterBand();
+
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual int    HasArbitraryOverviews();
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual GDALColorTable *GetColorTable();
+
+    virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize,
+                               int nBufXSize, int nBufYSize, 
+                               GDALDataType eDT, char **papszOptions );
+
+};
+
+/************************************************************************/
+/*                           OGDIRasterBand()                            */
+/************************************************************************/
+
+OGDIRasterBand::OGDIRasterBand( OGDIDataset *poDS, int nBand, 
+                                const char * pszName, ecs_Family eFamily,
+                                int nComponent )
+
+{
+    ecs_Result	*psResult;
+    
+    this->poDS = poDS;
+    this->nBand = nBand;
+    this->eFamily = eFamily;
+    this->pszLayerName = CPLStrdup(pszName);
+    this->nComponent = nComponent;
+    poCT = NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Make this layer current.                                        */
+/* -------------------------------------------------------------------- */
+    EstablishAccess( 0, 0, 
+                     poDS->GetRasterXSize(), poDS->GetRasterYSize(), 
+                     poDS->GetRasterXSize(), poDS->GetRasterYSize() );
+
+/* -------------------------------------------------------------------- */
+/*      Get the raster info.                                            */
+/* -------------------------------------------------------------------- */
+    psResult = cln_GetRasterInfo( poDS->nClientID );
+    if( ECSERROR(psResult) )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "%s", psResult->message );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Establish if we have meaningful colortable information.         */
+/* -------------------------------------------------------------------- */
+    if( eFamily == Matrix )
+    {
+        int     i;
+
+        poCT = new GDALColorTable();
+        
+        for( i = 0; i < (int) ECSRASTERINFO(psResult).cat.cat_len; i++ ) {
+            GDALColorEntry   sEntry;
+
+            sEntry.c1 = ECSRASTERINFO(psResult).cat.cat_val[i].r;
+            sEntry.c2 = ECSRASTERINFO(psResult).cat.cat_val[i].g;
+            sEntry.c3 = ECSRASTERINFO(psResult).cat.cat_val[i].b;
+            sEntry.c4 = 255;
+
+            poCT->SetColorEntry( ECSRASTERINFO(psResult).cat.cat_val[i].no_cat, 
+                                 &sEntry );
+        }
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Get the GDAL data type.  Eventually we might use the            */
+/*      category info to establish what to do here.                     */
+/* -------------------------------------------------------------------- */
+    if( eFamily == Matrix )
+        eDataType = GDT_Byte;
+    else if( ECSRASTERINFO(psResult).width == 1 )
+        eDataType = GDT_Byte;
+    else if( ECSRASTERINFO(psResult).width == 2 )
+        eDataType = GDT_Byte;
+    else if( ECSRASTERINFO(psResult).width == 3 )
+        eDataType = GDT_UInt16;
+    else if( ECSRASTERINFO(psResult).width == 4 )
+        eDataType = GDT_Int16;
+    else if( ECSRASTERINFO(psResult).width == 5 )
+        eDataType = GDT_Int32;
+    else
+        eDataType = GDT_UInt32;
+
+    nOGDIImageType = ECSRASTERINFO(psResult).width;
+    
+/* -------------------------------------------------------------------- */
+/*	Currently only works for strips 				*/
+/* -------------------------------------------------------------------- */
+    nBlockXSize = poDS->GetRasterXSize();
+    nBlockYSize = 1;
+}
+
+/************************************************************************/
+/*                          ~OGDIRasterBand()                           */
+/************************************************************************/
+
+OGDIRasterBand::~OGDIRasterBand()
+
+{
+    FlushCache();
+    CPLFree( pszLayerName );
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr OGDIRasterBand::IReadBlock( int, int nBlockYOff, void * pImage )
+
+{
+    return IRasterIO( GF_Read, 0, nBlockYOff, nBlockXSize, 1, 
+                      pImage, nBlockXSize, 1, eDataType, 
+                      GDALGetDataTypeSize(eDataType)/8, 0 );
+}
+
+/************************************************************************/
+/*                             IRasterIO()                              */
+/************************************************************************/
+
+CPLErr OGDIRasterBand::IRasterIO( GDALRWFlag eRWFlag,
+                                  int nXOff, int nYOff, int nXSize, int nYSize,
+                                  void * pData, int nBufXSize, int nBufYSize,
+                                  GDALDataType eBufType,
+                                  int nPixelSpace, int nLineSpace )
+
+{
+    OGDIDataset	*poODS = (OGDIDataset *) poDS;
+    CPLErr    eErr;
+#ifdef notdef
+    CPLDebug( "OGDIRasterBand", 
+              "RasterIO(%d,%d,%d,%d -> %dx%d)", 
+              nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize );
+#endif
+
+/* -------------------------------------------------------------------- */
+/*      Establish access at the desired resolution.                     */
+/* -------------------------------------------------------------------- */
+    eErr = EstablishAccess( nXOff, nYOff, nXSize, nYSize, 
+                            nBufXSize, nBufYSize );
+    if( eErr != CE_None )
+        return eErr;
+
+/* -------------------------------------------------------------------- */
+/*      Read back one scanline at a time, till request is satisfied.    */
+/* -------------------------------------------------------------------- */
+    int      iScanline;
+
+    for( iScanline = 0; iScanline < nBufYSize; iScanline++ )
+    {
+        ecs_Result	*psResult;
+        void		*pLineData;
+        pLineData = ((unsigned char *) pData) + iScanline * nLineSpace;
+
+        poODS->nCurrentIndex++;
+        psResult = cln_GetNextObject( poODS->nClientID );
+
+        if( ECSERROR(psResult) )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "%s", psResult->message );
+            return( CE_Failure );
+        }
+
+        if( eFamily == Matrix )
+        {
+            GDALCopyWords( ECSRASTER(psResult), GDT_UInt32, 4, 
+                           pLineData, eBufType, nPixelSpace,
+                           nBufXSize );
+        }
+        else if( nOGDIImageType == 1 )
+        {
+            GDALCopyWords( ((GByte *) ECSRASTER(psResult)) + nComponent, 
+                           GDT_Byte, 4,
+                           pLineData, eBufType, nPixelSpace, nBufXSize );
+
+            if( nComponent == 3 )
+            {
+                int	i;
+
+                for( i = 0; i < nBufXSize; i++ )
+                {
+                    if( ((GByte *) pLineData)[i] != 0 )
+                        ((GByte *) pLineData)[i] = 255;
+                    else
+                        ((GByte *) pLineData)[i] = 0;
+                    
+                }
+            }
+        }
+        else if( nOGDIImageType == 2 )
+        {
+            GDALCopyWords( ECSRASTER(psResult), GDT_Byte, 1,
+                           pLineData, eBufType, nPixelSpace,
+                           nBufXSize );
+        }
+        else if( nOGDIImageType == 3 )
+        {
+            GDALCopyWords( ECSRASTER(psResult), GDT_UInt16, 2,
+                           pLineData, eBufType, nPixelSpace,
+                           nBufXSize );
+        }
+        else if( nOGDIImageType == 4 )
+        {
+            GDALCopyWords( ECSRASTER(psResult), GDT_Int16, 2,
+                           pLineData, eBufType, nPixelSpace,
+                           nBufXSize );
+        }
+        else if( nOGDIImageType == 5 )
+        {
+            GDALCopyWords( ECSRASTER(psResult), GDT_Int32, 4,
+                           pLineData, eBufType, nPixelSpace,
+                           nBufXSize );
+        }
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       HasArbitraryOverviews()                        */
+/************************************************************************/
+
+int OGDIRasterBand::HasArbitraryOverviews()
+
+{
+    return TRUE;
+}
+
+/************************************************************************/
+/*                          EstablishAccess()                           */
+/************************************************************************/
+
+CPLErr OGDIRasterBand::EstablishAccess( int nXOff, int nYOff, 
+                                        int nWinXSize, int nWinYSize, 
+                                        int nBufXSize, int nBufYSize )
+
+{
+    ecs_Result	 *psResult;
+    OGDIDataset  *poODS = (OGDIDataset *) poDS;
+
+/* -------------------------------------------------------------------- */
+/*      Is this already the current band?  If not, make it so now.      */
+/* -------------------------------------------------------------------- */
+    if( poODS->nCurrentBand != nBand )
+    {
+        ecs_LayerSelection sSelection;
+        
+        sSelection.Select = pszLayerName;
+        sSelection.F = eFamily;
+        
+        CPLDebug( "OGDIRasterBand", "<EstablishAccess: SelectLayer(%s)>",
+                  pszLayerName );
+        psResult = cln_SelectLayer( poODS->nClientID, &sSelection );
+        if( ECSERROR(psResult) )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "%s", psResult->message );
+            return CE_Failure;
+        }
+
+        poODS->nCurrentBand = nBand;
+        poODS->nCurrentIndex = -1;
+    }
+        
+/* -------------------------------------------------------------------- */
+/*      What region would represent this resolution and window?         */
+/* -------------------------------------------------------------------- */
+    ecs_Region   sWin;
+    double       dfNSTolerance = 0.0000001;
+
+    sWin.west = nXOff * poODS->sGlobalBounds.ew_res
+        + poODS->sGlobalBounds.west;
+    sWin.east = (nXOff+nWinXSize) * poODS->sGlobalBounds.ew_res
+        + poODS->sGlobalBounds.west;
+    sWin.ew_res = poODS->sGlobalBounds.ew_res*(nWinXSize/(double)nBufXSize);
+
+    sWin.north = poODS->sGlobalBounds.north 
+        - nYOff*poODS->sGlobalBounds.ns_res;
+    if( nBufYSize == 1 && nWinYSize == 1 )
+    {
+        sWin.ns_res = sWin.ew_res 
+            * (poODS->sGlobalBounds.ns_res / poODS->sGlobalBounds.ew_res);
+        nWinYSize = (int) ((sWin.north - poODS->sGlobalBounds.south + sWin.ns_res*0.9)
+                                / sWin.ns_res);
+
+        sWin.south = sWin.north - nWinYSize * sWin.ns_res;
+        dfNSTolerance = MAX(poODS->sCurrentBounds.ns_res,sWin.ns_res);
+    }
+    else if( nBufYSize == 1 )
+    {
+        sWin.ns_res = poODS->sGlobalBounds.ns_res
+            *(nWinYSize/(double)nBufYSize);
+        nWinYSize = (int) ((sWin.north - poODS->sGlobalBounds.south + sWin.ns_res*0.9)
+                                / sWin.ns_res);
+
+        sWin.south = sWin.north - nWinYSize * sWin.ns_res;
+        dfNSTolerance = MAX(poODS->sCurrentBounds.ns_res,sWin.ns_res);
+    }
+    else
+    {
+        sWin.ns_res = poODS->sGlobalBounds.ns_res
+            *(nWinYSize/(double)nBufYSize);
+        sWin.south = sWin.north - nWinYSize * sWin.ns_res;
+        dfNSTolerance = sWin.ns_res * 0.001;
+    }
+
+    if( poODS->nCurrentIndex != 0
+        || ABS(sWin.west - poODS->sCurrentBounds.west) > 0.0001 
+        || ABS(sWin.east - poODS->sCurrentBounds.east) > 0.0001 
+        || ABS(sWin.north - (poODS->sCurrentBounds.north - poODS->nCurrentIndex * poODS->sCurrentBounds.ns_res)) > dfNSTolerance 
+        || ABS(sWin.ew_res/poODS->sCurrentBounds.ew_res - 1.0) > 0.0001
+        || ABS(sWin.ns_res - poODS->sCurrentBounds.ns_res) > dfNSTolerance )
+    {
+        CPLDebug( "OGDIRasterBand", 
+                  "<EstablishAccess: Set Region(%d,%d,%d,%d,%d,%d>",
+                  nXOff, nYOff, nWinXSize, nWinYSize, nBufXSize, nBufYSize );
+
+        psResult = cln_SelectRegion( poODS->nClientID, &sWin );
+        if( ECSERROR(psResult) )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "%s", psResult->message );
+            return CE_Failure;
+        }
+        
+        poODS->sCurrentBounds = sWin;
+        poODS->nCurrentIndex = 0;
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp OGDIRasterBand::GetColorInterpretation()
+
+{
+    if( poCT != NULL )
+        return GCI_PaletteIndex;
+    else if( nOGDIImageType == 1 && eFamily == Image && nComponent == 0 )
+        return GCI_RedBand;
+    else if( nOGDIImageType == 1 && eFamily == Image && nComponent == 1 )
+        return GCI_GreenBand;
+    else if( nOGDIImageType == 1 && eFamily == Image && nComponent == 2 )
+        return GCI_BlueBand;
+    else if( nOGDIImageType == 1 && eFamily == Image && nComponent == 3 )
+        return GCI_AlphaBand;
+    else
+        return GCI_Undefined;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *OGDIRasterBand::GetColorTable()
+
+{
+    return poCT;
+}
+
+/************************************************************************/
+/*                             AdviseRead()                             */
+/*                                                                      */
+/*      Allow the application to give us a hint in advance how they     */
+/*      want the data.                                                  */
+/************************************************************************/
+
+CPLErr OGDIRasterBand::AdviseRead( int nXOff, int nYOff, 
+                                   int nXSize, int nYSize,
+                                   int nBufXSize, int nBufYSize, 
+                                   GDALDataType eDT, char **papszOptions )
+
+{
+    return EstablishAccess( nXOff, nYOff, nXSize, nYSize, 
+                            nBufXSize, nBufYSize );
+}
+    
+/************************************************************************/
+/* ==================================================================== */
+/*                            OGDIDataset                               */
+/* ==================================================================== */
+/************************************************************************/
+
+
+/************************************************************************/
+/*                            OGDIDataset()                            */
+/************************************************************************/
+
+OGDIDataset::OGDIDataset()
+
+{
+    nClientID = -1;
+    nCurrentBand = -1;
+    nCurrentIndex = -1;
+    papszSubDatasets = NULL;
+}
+
+/************************************************************************/
+/*                           ~OGDIDataset()                            */
+/************************************************************************/
+
+OGDIDataset::~OGDIDataset()
+
+{
+    cln_DestroyClient( nClientID );
+    CSLDestroy( papszSubDatasets );
+    CPLFree( pszProjection );
+}
+
+/************************************************************************/
+/*                            GetMetadata()                             */
+/************************************************************************/
+
+char **OGDIDataset::GetMetadata( const char *pszDomain )
+
+{
+    if( pszDomain != NULL && EQUAL(pszDomain,"SUBDATASETS") )
+        return papszSubDatasets;
+    else
+        return GDALDataset::GetMetadata( pszDomain );
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *OGDIDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    ecs_Result	*psResult;
+    int		nClientID;
+    char        **papszImages=NULL, **papszMatrices=NULL;
+    
+    if( !EQUALN(poOpenInfo->pszFilename,"gltp:",5) )
+        return( NULL );
+
+/* -------------------------------------------------------------------- */
+/*      Has the user hardcoded a layer and family in the URL?           */
+/*      Honour quoted strings for the layer name, since some layers     */
+/*      (ie. RPF/CADRG) have embedded colons.                           */
+/* -------------------------------------------------------------------- */
+    int       nC1=-1, nC2=-1, i, bInQuotes = FALSE;
+    char      *pszURL = CPLStrdup(poOpenInfo->pszFilename);
+
+    for( i = strlen(pszURL)-1; i > 0; i-- )
+    {
+        if( pszURL[i] == '/' )
+            break;
+
+        if( pszURL[i] == '"' && pszURL[i-1] != '\\' )
+            bInQuotes = !bInQuotes;
+            
+        else if( pszURL[i] == ':' && !bInQuotes )
+        {
+            if( nC1 == -1 )
+            {
+                nC1 = i;
+                pszURL[nC1] = '\0';
+            }
+            else if( nC2 == -1 )
+            {
+                nC2 = i;
+                pszURL[nC2] = '\0';
+            }
+        }
+    }	
+
+/* -------------------------------------------------------------------- */
+/*      If we got a "family", and it is a vector family then return     */
+/*      quietly.                                                        */
+/* -------------------------------------------------------------------- */
+    if( nC2 != -1 
+        && !EQUAL(pszURL+nC1+1,"Matrix") 
+        && !EQUAL(pszURL+nC1+1,"Image") )
+    {
+        CPLFree( pszURL );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Open the client interface.                                      */
+/* -------------------------------------------------------------------- */
+    psResult = cln_CreateClient( &nClientID, pszURL );
+
+    if( ECSERROR(psResult) )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "%s", psResult->message );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      collect the list of images and matrices available.              */
+/* -------------------------------------------------------------------- */
+    if( nC2 == -1 )
+    {
+        CollectLayers( nClientID, &papszImages, &papszMatrices );
+    }
+    else
+    {
+        char	*pszLayerName = CPLStrdup( pszURL+nC2+1 );
+        
+        if( pszLayerName[0] == '"' )
+        {
+            int		nOut = 0;
+
+            for( i = 1; pszLayerName[i] != '\0'; i++ )
+            {
+                if( pszLayerName[i+1] == '"' && pszLayerName[i] == '\\' )
+                    pszLayerName[nOut++] = pszLayerName[++i];
+                else if( pszLayerName[i] != '"' )
+                    pszLayerName[nOut++] = pszLayerName[i];
+                else 
+                    break;
+            }
+            pszLayerName[nOut] = '\0';
+        }
+
+        if( EQUAL(pszURL+nC1+1,"Image") )
+            papszImages = CSLAddString( papszImages, pszLayerName );
+        else
+            papszMatrices = CSLAddString( papszMatrices, pszLayerName );
+
+        CPLFree( pszLayerName );
+    }
+
+    CPLFree( pszURL );
+
+/* -------------------------------------------------------------------- */
+/*      If this is a 3.1 server (ie, it support                         */
+/*      cln_GetLayerCapabilities()) and it has no raster layers then    */
+/*      we can assume it must be a vector datastore.  End without an    */
+/*      error in case the application wants to try this through         */
+/*      OGR.                                                            */
+/* -------------------------------------------------------------------- */
+    psResult = cln_GetVersion(nClientID);
+    
+    if( (ECSERROR(psResult) || atof(ECSTEXT(psResult)) >= 3.1)
+        && CSLCount(papszMatrices) == 0 
+        && CSLCount(papszImages) == 0 )
+    {
+        CPLDebug( "OGDIDataset",
+                  "While this is an OGDI datastore, it does not appear to\n"
+                  "have any identifiable raster layers.  Perhaps it is a\n"
+                  "vector datastore?" );
+        cln_DestroyClient( nClientID );
+        return NULL;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    OGDIDataset 	*poDS;
+
+    poDS = new OGDIDataset();
+
+    poDS->nClientID = nClientID;
+    poDS->SetDescription( poOpenInfo->pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*      Capture some information from the file that is of interest.     */
+/* -------------------------------------------------------------------- */
+    psResult = cln_GetGlobalBound( nClientID );
+    if( ECSERROR(psResult) )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "%s", psResult->message );
+        return NULL;
+    }
+
+    poDS->sGlobalBounds = ECSREGION(psResult);
+
+    psResult = cln_GetServerProjection(nClientID);
+    if( ECSERROR(psResult) )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "%s", psResult->message );
+        return NULL;
+    }
+
+    OGRSpatialReference  oSRS;
+
+    if( oSRS.importFromProj4( ECSTEXT(psResult) ) == OGRERR_NONE )
+    {
+        poDS->pszProjection = NULL;
+        oSRS.exportToWkt( &(poDS->pszProjection) );
+    }
+    else
+    {
+        CPLError( CE_Warning, CPLE_NotSupported,
+                  "untranslatable PROJ.4 projection: %s\n", 
+                  ECSTEXT(psResult) );
+        poDS->pszProjection = CPLStrdup("");
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Select the global region.                                       */
+/* -------------------------------------------------------------------- */
+    psResult = cln_SelectRegion( nClientID, &(poDS->sGlobalBounds) );
+    if( ECSERROR(psResult) )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "%s", psResult->message );
+        return NULL;
+    }
+
+    poDS->sCurrentBounds = poDS->sGlobalBounds;
+
+/* -------------------------------------------------------------------- */
+/*      If we have only one layer try to find the corresponding         */
+/*      capabilities, and override the global bounds and resolution     */
+/*      based on it.                                                    */
+/* -------------------------------------------------------------------- */
+    if( CSLCount(papszMatrices) + CSLCount(papszImages) == 1 )
+    {
+        if( CSLCount(papszMatrices) == 1 )
+            OverrideGlobalInfo( poDS, papszMatrices[0] );
+        else
+            OverrideGlobalInfo( poDS, papszImages[0] );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Otherwise setup a subdataset list.                              */
+/* -------------------------------------------------------------------- */
+    else
+    {
+        int	i;
+
+        for( i = 0; papszMatrices != NULL && papszMatrices[i] != NULL; i++ )
+            poDS->AddSubDataset( "Matrix", papszMatrices[i] );
+
+        for( i = 0; papszImages != NULL && papszImages[i] != NULL; i++ )
+            poDS->AddSubDataset( "Image", papszImages[i] );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Establish raster info.                                          */
+/* -------------------------------------------------------------------- */
+    poDS->nRasterXSize = (int) 
+        (((poDS->sGlobalBounds.east - poDS->sGlobalBounds.west)
+          / poDS->sGlobalBounds.ew_res) + 0.5);
+    
+    poDS->nRasterYSize = (int) 
+        (((poDS->sGlobalBounds.north - poDS->sGlobalBounds.south)
+          / poDS->sGlobalBounds.ns_res) + 0.5);
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    for( i=0; papszMatrices != NULL && papszMatrices[i] != NULL; i++)
+    {
+        if( CSLFindString( papszImages, papszMatrices[i] ) == -1 )
+            poDS->SetBand( poDS->GetRasterCount()+1, 
+                           new OGDIRasterBand( poDS, poDS->GetRasterCount()+1, 
+                                               papszMatrices[i], Matrix, 0 ) );
+    }
+
+    for( i=0; papszImages != NULL && papszImages[i] != NULL; i++)
+    {
+        OGDIRasterBand	*poBand;
+
+        poBand = new OGDIRasterBand( poDS, poDS->GetRasterCount()+1, 
+                                     papszImages[i], Image, 0 );
+
+        poDS->SetBand( poDS->GetRasterCount()+1, poBand );
+
+        /* special case for RGBt Layers */
+        if( poBand->nOGDIImageType == 1 )
+        {
+            poDS->SetBand( poDS->GetRasterCount()+1, 
+                           new OGDIRasterBand( poDS, poDS->GetRasterCount()+1, 
+                                               papszImages[i], Image, 1 ));
+            poDS->SetBand( poDS->GetRasterCount()+1, 
+                           new OGDIRasterBand( poDS, poDS->GetRasterCount()+1, 
+                                               papszImages[i], Image, 2 ));
+            poDS->SetBand( poDS->GetRasterCount()+1, 
+                           new OGDIRasterBand( poDS, poDS->GetRasterCount()+1, 
+                                               papszImages[i], Image, 3 ));
+        }
+    }
+
+    CSLDestroy( papszMatrices );
+    CSLDestroy( papszImages );
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                           AddSubDataset()                            */
+/************************************************************************/
+
+void OGDIDataset::AddSubDataset( const char *pszType, const char *pszLayer )
+
+{
+    char	szName[80];
+    int		nCount = CSLCount( papszSubDatasets ) / 2;
+
+    sprintf( szName, "SUBDATASET_%d_NAME", nCount+1 );
+    papszSubDatasets = 
+        CSLSetNameValue( papszSubDatasets, szName, 
+              CPLSPrintf( "%s:\"%s\":%s", GetDescription(), pszLayer, pszType ) );
+
+    sprintf( szName, "SUBDATASET_%d_DESC", nCount+1 );
+    papszSubDatasets = 
+        CSLSetNameValue( papszSubDatasets, szName, 
+              CPLSPrintf( "%s as %s", pszLayer, pszType ) );
+}
+
+/************************************************************************/
+/*                           CollectLayers()                            */
+/************************************************************************/
+
+CPLErr OGDIDataset::CollectLayers( int nClientID, 
+                                   char ***ppapszImages, 
+                                   char ***ppapszMatrices )
+
+{
+    const ecs_LayerCapabilities	*psLayer;
+    int		iLayer;
+
+    for( iLayer = 0; 
+         (psLayer = cln_GetLayerCapabilities(nClientID,iLayer)) != NULL;
+         iLayer++ )
+    {
+        if( psLayer->families[Image] )
+        {
+            *ppapszImages = CSLAddString( *ppapszImages, psLayer->name );
+        }
+        if( psLayer->families[Matrix] )
+        {
+            *ppapszMatrices = CSLAddString( *ppapszMatrices, psLayer->name );
+        }
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                         OverrideGlobalInfo()                         */
+/*                                                                      */
+/*      Override the global bounds and resolution based on a layers     */
+/*      capabilities, if possible.                                      */
+/************************************************************************/
+
+CPLErr OGDIDataset::OverrideGlobalInfo( OGDIDataset *poDS,
+                                        const char *pszLayer )
+
+{
+    const ecs_LayerCapabilities	*psLayer;
+    int		iLayer;
+
+    for( iLayer = 0; 
+         (psLayer = cln_GetLayerCapabilities(poDS->nClientID,iLayer)) != NULL;
+         iLayer++ )
+    {
+        if( EQUAL(psLayer->name, pszLayer) )
+        {
+            poDS->sGlobalBounds.north = psLayer->srs_north;
+            poDS->sGlobalBounds.south = psLayer->srs_south;
+            poDS->sGlobalBounds.east = psLayer->srs_east;
+            poDS->sGlobalBounds.west = psLayer->srs_west;
+            poDS->sGlobalBounds.ew_res = psLayer->srs_ewres;
+            poDS->sGlobalBounds.ns_res = psLayer->srs_nsres;
+        }
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *OGDIDataset::GetProjectionRef()
+
+{
+    return( pszProjection );
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr OGDIDataset::GetGeoTransform( double * padfTransform )
+
+{
+    padfTransform[0] = sGlobalBounds.west;
+    padfTransform[1] = sGlobalBounds.ew_res;
+    padfTransform[2] = 0.0;
+
+    padfTransform[3] = sGlobalBounds.north;
+    padfTransform[4] = 0.0;
+    padfTransform[5] = -sGlobalBounds.ns_res;
+
+    return( CE_None );
+}
+
+/************************************************************************/
+/*                         GetInternalHandle()                          */
+/************************************************************************/
+
+void *OGDIDataset::GetInternalHandle( const char * pszRequest )
+
+{
+    if( EQUAL(pszRequest,"ClientID") )
+        return (void *) nClientID;
+    else
+        return NULL;
+}
+
+/************************************************************************/
+/*                          GDALRegister_OGDI()                        */
+/************************************************************************/
+
+void GDALRegister_OGDI()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "OGDI" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "OGDI" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "OGDI Bridge" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_ogdi.html" );
+
+        poDriver->pfnOpen = OGDIDataset::Open;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/pcidsk/GNUmakefile b/Utilities/GDAL/frmts/pcidsk/GNUmakefile
new file mode 100644
index 0000000000..3a78640b5c
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcidsk/GNUmakefile
@@ -0,0 +1,13 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	pcidskdataset.o pcidsktiledrasterband.o
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) -I../raw $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/pcidsk/frmt_pcidsk.html b/Utilities/GDAL/frmts/pcidsk/frmt_pcidsk.html
new file mode 100644
index 0000000000..c524684e96
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcidsk/frmt_pcidsk.html
@@ -0,0 +1,89 @@
+<html>
+<head>
+	<title>PCIDSK --- PCI Geomatics Database File</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>PCIDSK --- PCI Geomatics Database File</h1>
+
+PCIDSK database file used by PCI EASI/PACE software for image analysis.
+It is supported for reading, and writing by GDAL.  All pixel data types, and
+data organizations (pixel interleaved, band interleaved, file interleaved
+and tiled) should be supported, but compressed PCIDSK files are not 
+supported.  Currently LUT and PCT segments are ignored.  Overall file, 
+and band specific metadata should be correctly associated with the image
+or bands.<P>
+
+Georeferencing is supported though there may be some limitations
+in support of datums and ellipsoids.   If GCP segments are present, the
+first will be used, and the rest ignored.<p>
+
+Internal overview (pyramid) images will also be correctly read though newly
+requested overviews will be built externally as an .ovr file.<p>
+
+Vector segments are not supported by GDAL or OGR at this time. <p>
+
+<h2>Metadata</h2>
+
+Dataset:<p>
+
+<ul>
+	<li> <b>SOFTWARE</b>: Identifies the software system, and its
+	current version, that generated the PCIDSK database.<p>
+
+	<li> <b>FILE_ID</b>: Descriptive field identifying the file. Usually
+	supplied by the user at file creation time.<p>
+
+	<li> <b>GENERATING_FACILITY</b>: Descriptive field used to identify
+	which facility or institution produced this file.<p>
+
+	<li> <b>DESCRIPTION1</b>, <b>DESCRIPTION2</b>: Descriptive fields,
+	usually supplied by the user at file creation time, identifying the
+	contents of the file.<p>
+
+	<li> <b>DATE_OF_CREATION</b>, <b>DATE_OF_UPDATE</b>: Dates and times
+	of the file creation and last update. Formatted as 'hh:mmdd-mmm-yy'.<p>
+
+	<li> <b>GENERATING_FACILITY</b>: Descriptive field used to identify
+	which facility or institution produced this file.<p>
+</ul>
+
+Band:<p>
+<ul>
+	<li> <b>DATE_OF_CREATION</b>, <b>DATE_OF_UPDATE</b>: Dates and times
+	of the file creation and last update. Formatted as 'hh:mmdd-mmm-yy'.<p>
+
+	<li> <b>UNITS</b>: Data measurement units.<p>
+
+	<li> <b>HISTORYn</b> (<b>n</b>=1--8): Image channel history records.<p>
+</ul>
+
+<h2>Creation Options</h2>
+
+Note that PCIDSK files are always produced pixel interleaved, even though 
+other organizations are supported for read.<p>
+
+<ul>
+	<li> <b>FILEDESC1="description"</b>, <b>FILEDESC2="description"</b>:
+	The first and second lines of descriptive text identifying the file,
+	each one should be maximum 64 character in size.<p>
+
+	<li> <b>BANDDESCn="description"</b>: Text (maximum 64 character in
+	size) describing contents of the specified band <B>n</B>. If no
+	description for band supplied by user, "Image band n" string will
+	be used by default. Band numbers start from 1.<p>
+</ul>
+
+<h2>See Also:</h2>
+
+<ul>
+<li> Implemented as <tt>gdal/frmts/pcidsk/pcidskdataset.cpp</tt>.<p>
+
+<li> <a href="">PCIDSK SDK</a>, including PCIDSK Database Reference Manual.<p>
+
+</ul>
+
+</body>
+</html>
+
diff --git a/Utilities/GDAL/frmts/pcidsk/gdal_pcidsk.h b/Utilities/GDAL/frmts/pcidsk/gdal_pcidsk.h
new file mode 100644
index 0000000000..fb67223110
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcidsk/gdal_pcidsk.h
@@ -0,0 +1,215 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  PCIDSK Database File
+ * Purpose:  PCIDSK driver declarations.
+ * Author:   Andrey Kiselev, dron@remotesensing.org
+ *
+ ******************************************************************************
+ * Copyright (c) 2003, Andrey Kiselev <dron@remotesensing.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: gdal_pcidsk.h,v $
+ * Revision 1.3  2005/06/09 19:01:40  fwarmerdam
+ * added support for tiled primary bands
+ *
+ * Revision 1.2  2005/06/08 01:14:35  fwarmerdam
+ * dos2unix
+ *
+ * Revision 1.1  2005/06/08 01:08:33  fwarmerdam
+ * New
+ *
+ */
+
+#include "cpl_conv.h"
+#include "cpl_string.h"
+#include "ogr_spatialref.h"
+#include "rawdataset.h"
+
+typedef enum
+{
+    PDI_PIXEL,
+    PDI_BAND,
+    PDI_FILE
+} PCIDSKInterleaving;
+
+/************************************************************************/
+/*                              PCIDSKDataset                           */
+/************************************************************************/
+
+class PCIDSKDataset : public RawDataset
+{
+    friend class PCIDSKRawRasterBand;
+    friend class PCIDSKTiledRasterBand;
+
+    const char          *pszFilename;
+    FILE                *fp;
+
+    char                *pszCreatTime;  // Date/time of the database creation
+
+    vsi_l_offset        nGeoPtrOffset;  // Offset in bytes to the pointer
+                                        // to GEO segment
+    vsi_l_offset        nGeoOffset;     // Offset in bytes to the GEO segment
+    vsi_l_offset        nGcpPtrOffset;  // Offset in bytes to the pointer
+                                        // to GCP segment
+    vsi_l_offset        nGcpOffset;     // Offset in bytes to the GCP segment
+
+    int                 bGeoSegmentDirty;
+
+    int                 nBlockMapSeg;
+
+    GDAL_GCP            *pasGCPList;
+    long                 nGCPCount;
+
+    double              adfGeoTransform[6];
+    char                *pszProjection;
+    char                *pszGCPProjection;
+
+    GDALDataType  PCIDSKTypeToGDAL( const char *);
+    void          WriteGeoSegment();
+
+    void          CollectPCIDSKMetadata( int nSegment );
+
+    // Segment map
+    int           nSegCount;
+    int          *panSegType;
+    char        **papszSegName;
+    vsi_l_offset *panSegOffset;
+    vsi_l_offset *panSegSize;
+
+  public:
+                PCIDSKDataset();
+                ~PCIDSKDataset();
+
+    static GDALDataset  *Open( GDALOpenInfo * );
+    static GDALDataset  *Create( const char * pszFilename,
+                                 int nXSize, int nYSize, int nBands,
+                                 GDALDataType eType, char **papszParmList );
+    static GDALDataset *CreateCopy( const char * pszFilename, 
+                                    GDALDataset *poSrcDS, 
+                                    int bStrict, char ** papszOptions, 
+                                    GDALProgressFunc pfnProgress, 
+                                    void * pProgressData );
+
+    virtual void        FlushCache( void );
+
+    CPLErr              GetGeoTransform( double * padfTransform );
+    virtual CPLErr      SetGeoTransform( double * );
+    const char          *GetProjectionRef();
+    virtual CPLErr      SetProjection( const char * );
+    virtual int         GetGCPCount();
+    virtual const       char *GetGCPProjection();
+    virtual const       GDAL_GCP *GetGCPs();
+
+    // pcidsk specific
+    int                 SegRead( int nSegment,
+                                 vsi_l_offset nOffset,
+                                 int nSize,
+                                 void *pBuffer );
+};
+
+/************************************************************************/
+/*                         PCIDSKTiledRasterBand                        */
+/************************************************************************/
+
+class PCIDSKTiledRasterBand : public GDALPamRasterBand
+{
+    friend class PCIDSKDataset;
+
+    PCIDSKDataset *poPDS;
+    
+    int          nImage;
+
+    int           nBlocks;
+    vsi_l_offset *panBlockOffset;// offset in physical file.
+
+    int           nTileCount;
+    vsi_l_offset *panTileOffset; // offset in "image" virtual file.
+    int          *panTileSize;
+
+    int         nOverviewCount;
+    GDALRasterBand **papoOverviews;
+    
+    void        AttachOverview( GDALRasterBand *poOvBand ) {
+
+        nOverviewCount++;
+        papoOverviews = (GDALRasterBand **)
+            CPLRealloc(papoOverviews,sizeof(void*) * nOverviewCount);
+        papoOverviews[nOverviewCount-1] = poOvBand;
+    }
+
+    int         BuildBlockMap();
+    int         BuildTileMap();
+    
+  public:
+                PCIDSKTiledRasterBand( PCIDSKDataset *, int, int );
+                ~PCIDSKTiledRasterBand();
+
+    virtual CPLErr IReadBlock( int, int, void * );
+
+    int         SysRead( vsi_l_offset nOffset, int nSize, void * );
+
+    virtual int GetOverviewCount() { return nOverviewCount; }
+    virtual GDALRasterBand *GetOverview(int iOverview)
+        { return papoOverviews[iOverview]; }
+};
+
+/************************************************************************/
+/*                         PCIDSKRawRasterBand                          */
+/************************************************************************/
+
+class PCIDSKRawRasterBand : public RawRasterBand
+{
+    friend class PCIDSKDataset;
+
+    int         nOverviewCount;
+    GDALRasterBand **papoOverviews;
+    
+    void        AttachOverview( GDALRasterBand *poOvBand ) {
+        nOverviewCount++;
+        papoOverviews = (GDALRasterBand **)
+            CPLRealloc(papoOverviews,sizeof(void*) * nOverviewCount);
+        papoOverviews[nOverviewCount-1] = poOvBand;
+    }
+    
+  public:
+    PCIDSKRawRasterBand( GDALDataset *poDS, int nBand, FILE * fpRaw, 
+                         vsi_l_offset nImgOffset, int nPixelOffset,
+                         int nLineOffset,
+                         GDALDataType eDataType, int bNativeOrder )
+        : RawRasterBand( poDS, nBand, fpRaw, nImgOffset, nPixelOffset,
+                         nLineOffset, eDataType, bNativeOrder, TRUE ) {
+        nOverviewCount = 0;
+        papoOverviews = NULL;
+    }
+    ~PCIDSKRawRasterBand() {
+        FlushCache();
+        for( int i = 0; i < nOverviewCount; i++ )
+            delete papoOverviews[i];
+        CPLFree( papoOverviews );
+    }
+
+    virtual int GetOverviewCount() { return nOverviewCount; }
+    virtual GDALRasterBand *GetOverview(int iOverview)
+        { return papoOverviews[iOverview]; }
+};
+
+
diff --git a/Utilities/GDAL/frmts/pcidsk/makefile.vc b/Utilities/GDAL/frmts/pcidsk/makefile.vc
new file mode 100644
index 0000000000..9f921d2583
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcidsk/makefile.vc
@@ -0,0 +1,15 @@
+
+OBJ	=	pcidskdataset.obj pcidsktiledrasterband.obj
+
+GDAL_ROOT	=	..\..
+
+EXTRAFLAGS	=	-I..\raw
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/pcidsk/notes.txt b/Utilities/GDAL/frmts/pcidsk/notes.txt
new file mode 100644
index 0000000000..9ec80834f7
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcidsk/notes.txt
@@ -0,0 +1,46 @@
+SysBMap:
+
+512 byte header block:
+   - "VERSION  1"
+   - 8 bytes: # of images.
+   - 8 bytes: # of absolute blocks.
+   - 8 bytes: # of blocks actually used.
+
+block map entries:
+  - each is 28 bytes
+  - 4 bytes for SysBData Segment - often 1023.
+  - 8 bytes for absolute block index (zero based, within SysBData). 
+  - 8 bytes for "image" number (-1 means unallocated)
+  - 8 bytes for the next absolute block in this images block chain
+    (-1 means end of chain)
+
+each block in block map is 8K on disk (16 512byte blocks).
+
+
+
+
+Virtual tiled images:
+
+128 byte header:
+ - 8 bytes: image width
+ - 8 bytes: image height
+ - 8 bytes: tile width
+ - 8 bytes: tile height 
+ - 3 bytes: tile pixel data type. 
+ - some spaces
+ - compression type (normally NONE)
+ - some spaces.
+
+Tilemap:
+ - 12 bytes per tile containing offset within virtual file. 
+
+Tile Size map:
+ - 8 bytes per tile containing size of tile data. 
+
+Tile Data:
+ - Absolute data. 
+
+
+
+
+
diff --git a/Utilities/GDAL/frmts/pcidsk/pcidskdataset.cpp b/Utilities/GDAL/frmts/pcidsk/pcidskdataset.cpp
new file mode 100644
index 0000000000..b160bcc7fa
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcidsk/pcidskdataset.cpp
@@ -0,0 +1,1599 @@
+/******************************************************************************
+ * $Id: pcidskdataset.cpp,v 1.18 2005/10/18 18:15:55 fwarmerdam Exp $
+ *
+ * Project:  PCIDSK Database File
+ * Purpose:  Read/write PCIDSK Database File used by the PCI software
+ * Author:   Andrey Kiselev, dron@remotesensing.org
+ *
+ ******************************************************************************
+ * Copyright (c) 2003, Andrey Kiselev <dron@remotesensing.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: pcidskdataset.cpp,v $
+ * Revision 1.18  2005/10/18 18:15:55  fwarmerdam
+ * avoid very long string constant
+ *
+ * Revision 1.17  2005/10/17 02:26:49  fwarmerdam
+ * ensure Create produces a defaulted GEO segment
+ *
+ * Revision 1.16  2005/09/11 16:32:47  fwarmerdam
+ * Ensure things work even if the openinfo->fp is NULL.
+ *
+ * Revision 1.15  2005/06/09 19:01:40  fwarmerdam
+ * added support for tiled primary bands
+ *
+ * Revision 1.14  2005/06/08 01:16:57  fwarmerdam
+ * fix warnings
+ *
+ * Revision 1.13  2005/06/08 01:08:47  fwarmerdam
+ * added tiled bands
+ *
+ * Revision 1.12  2005/05/05 15:54:49  fwarmerdam
+ * PAM Enabled
+ *
+ * Revision 1.11  2004/11/12 15:37:46  fwarmerdam
+ * Added logic to compute the channel type if it is missing from
+ * the channel header.
+ *
+ * Revision 1.10  2004/01/31 09:27:19  dron
+ * Fixed number of projection parameters mismatch.
+ *
+ * Revision 1.9  2003/11/28 20:45:36  dron
+ * Warn user if dataset contain no raster bands.
+ *
+ * Revision 1.8  2003/10/29 16:38:56  warmerda
+ * set default extension
+ *
+ * Revision 1.7  2003/10/17 07:07:33  dron
+ * Use locale selection option in CPLScanDouble()/CPLPrintDouble().
+ *
+ * Revision 1.6  2003/10/08 07:58:53  dron
+ * Few fixes in WriteGeoSegment().
+ *
+ * Revision 1.5  2003/10/05 15:31:38  dron
+ * Added support for external raw files.
+ *
+ * Revision 1.4  2003/09/28 14:15:27  dron
+ * Implemented GCP segment reading, header parsing improved.
+ *
+ * Revision 1.3  2003/09/17 22:20:48  gwalter
+ * Added CreateCopy() function.
+ *
+ * Revision 1.2  2003/09/11 20:07:36  warmerda
+ * avoid casting warning
+ *
+ * Revision 1.1  2003/09/09 08:31:28  dron
+ * New.
+ *
+ */
+
+#include "gdal_pcidsk.h"
+
+CPL_CVSID("$Id: pcidskdataset.cpp,v 1.18 2005/10/18 18:15:55 fwarmerdam Exp $");
+
+CPL_C_START
+void    GDALRegister_PCIDSK(void);
+CPL_C_END
+
+const int       nSegBlocks = 64;    // Number of blocks of Segment Pointers
+const int       nGeoSegBlocks = 8;  // Number of blocks in GEO Segment
+
+/************************************************************************/
+/*                           PCIDSKDataset()                            */
+/************************************************************************/
+
+PCIDSKDataset::PCIDSKDataset()
+{
+    pszFilename = NULL;
+    fp = NULL;
+    nBands = 0;
+    pszCreatTime = NULL;
+    nGeoOffset = 0;
+    bGeoSegmentDirty = FALSE;
+
+    nBlockMapSeg = 0;
+
+    nSegCount = 0;
+    panSegType = NULL;
+    papszSegName = NULL;
+    panSegOffset = NULL;
+    panSegSize = NULL;
+
+    pszProjection = CPLStrdup( "" );
+    pszGCPProjection = CPLStrdup( "" );
+    nGCPCount = 0;
+    pasGCPList = NULL;
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+}
+
+/************************************************************************/
+/*                            ~PCIDSKDataset()                          */
+/************************************************************************/
+
+PCIDSKDataset::~PCIDSKDataset()
+{
+    int  i;
+
+    FlushCache();
+
+    if ( pszProjection )
+        CPLFree( pszProjection );
+    if ( pszGCPProjection )
+        CPLFree( pszGCPProjection );
+    if( fp != NULL )
+        VSIFCloseL( fp );
+    if( pszCreatTime )
+        CPLFree( pszCreatTime );
+    if( nGCPCount > 0 )
+    {
+        for( i = 0; i < nGCPCount; i++ )
+        {
+            if ( pasGCPList[i].pszId )
+                CPLFree( pasGCPList[i].pszId );
+            if ( pasGCPList[i].pszInfo )
+                CPLFree( pasGCPList[i].pszInfo );
+        }
+
+        CPLFree( pasGCPList );
+    }
+
+    CPLFree( panSegOffset );
+    CPLFree( panSegSize );
+    CPLFree( panSegType );
+
+    for( i = 0; i < nSegCount; i++ )
+        if( papszSegName[i] != NULL )
+            CPLFree( papszSegName[i] );
+    CPLFree( papszSegName );
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr PCIDSKDataset::GetGeoTransform( double * padfTransform )
+{
+    memcpy( padfTransform, adfGeoTransform, sizeof(adfGeoTransform[0]) * 6 );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          SetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr PCIDSKDataset::SetGeoTransform( double * padfTransform )
+{
+    memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 );
+    bGeoSegmentDirty = TRUE;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *PCIDSKDataset::GetProjectionRef()
+{
+    if( pszProjection )
+        return pszProjection;
+    else
+        return GDALPamDataset::GetProjectionRef();
+}
+
+/************************************************************************/
+/*                          SetProjection()                             */
+/************************************************************************/
+
+CPLErr PCIDSKDataset::SetProjection( const char *pszNewProjection )
+
+{
+    if( pszProjection )
+	CPLFree( pszProjection );
+    pszProjection = CPLStrdup( pszNewProjection );
+    bGeoSegmentDirty = TRUE;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                            GetGCPCount()                             */
+/************************************************************************/
+
+int PCIDSKDataset::GetGCPCount()
+
+{
+    return nGCPCount;
+}
+
+/************************************************************************/
+/*                          GetGCPProjection()                          */
+/************************************************************************/
+
+const char *PCIDSKDataset::GetGCPProjection()
+
+{
+    if( nGCPCount > 0 )
+        return pszGCPProjection;
+    else
+        return GDALPamDataset::GetGCPProjection();
+}
+
+/************************************************************************/
+/*                               GetGCPs()                              */
+/************************************************************************/
+
+const GDAL_GCP *PCIDSKDataset::GetGCPs()
+{
+    return pasGCPList;
+}
+
+/************************************************************************/
+/*                             FlushCache()                             */
+/************************************************************************/
+
+void PCIDSKDataset::FlushCache()
+{
+    GDALPamDataset::FlushCache();
+
+    if( GetAccess() == GA_Update )
+    {
+        char        szTemp[64];
+
+/* -------------------------------------------------------------------- */
+/*      Write out pixel size.                                           */
+/* -------------------------------------------------------------------- */
+        CPLPrintDouble( szTemp, "%16.9E", ABS(adfGeoTransform[1]), "C" );
+        CPLPrintDouble( szTemp + 16, "%16.9E", ABS(adfGeoTransform[5]), "C" );
+
+        VSIFSeekL( fp, 408, SEEK_SET );
+        VSIFWriteL( (void *)szTemp, 1, 32, fp );
+
+/* -------------------------------------------------------------------- */
+/*      Write out Georeferencing segment.                               */
+/* -------------------------------------------------------------------- */
+        if ( nGeoOffset && bGeoSegmentDirty )
+        {
+            WriteGeoSegment();
+            bGeoSegmentDirty = FALSE;
+        }
+    }
+}
+
+/************************************************************************/
+/*                         WriteGeoSegment()                            */
+/************************************************************************/
+
+void PCIDSKDataset::WriteGeoSegment( )
+{
+    char            szTemp[3072];
+    struct tm       oUpdateTime;
+    time_t          nTime = VSITime(NULL);
+    char            *pszP = pszProjection;
+    OGRSpatialReference oSRS;
+    int             i;
+
+#ifdef DEBUG
+    CPLDebug( "PCIDSK", "Writing out georeferencing segment." );
+#endif
+    
+    VSILocalTime( &nTime, &oUpdateTime );
+
+    CPLPrintStringFill( szTemp, "Master Georeferencing Segment for File", 64 );
+    CPLPrintStringFill( szTemp + 64, "", 64 );
+    if ( pszCreatTime )
+        CPLPrintStringFill( szTemp + 128, pszCreatTime, 16 );
+    else
+        CPLPrintTime( szTemp + 128, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" );
+    CPLPrintTime( szTemp + 144, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" );
+    CPLPrintStringFill( szTemp + 160, "", 224 );
+    // Write the history line
+    CPLPrintStringFill( szTemp + 384,
+                        "GDAL: Master Georeferencing Segment for File", 64 );
+    CPLPrintTime( szTemp + 448, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" );
+    // Fill other lines with spaces
+    CPLPrintStringFill( szTemp + 464, "", 80 * 7 );
+
+    // More history lines may be used, if needed.
+    // CPLPrintStringFill( szTemp + 464, "History2", 80 );
+    // CPLPrintStringFill( szTemp + 544, "History3", 80 );
+    // CPLPrintStringFill( szTemp + 624, "History4", 80 );
+    // CPLPrintStringFill( szTemp + 704, "History5", 80 );
+    // CPLPrintStringFill( szTemp + 784, "History6", 80 );
+    // CPLPrintStringFill( szTemp + 864, "History7", 80 );
+    // CPLPrintStringFill( szTemp + 944, "History8", 80 );
+
+    VSIFSeekL( fp, nGeoOffset, SEEK_SET );
+    VSIFWriteL( (void *)szTemp, 1, 1024, fp );
+
+    CPLPrintStringFill( szTemp, "PROJECTION", 16 );
+    CPLPrintStringFill( szTemp + 16, "PIXEL", 16 );
+
+    if( pszProjection != NULL && !EQUAL( pszProjection, "" )
+        && oSRS.importFromWkt( &pszP ) == OGRERR_NONE )
+    {
+        char      *pszProj = NULL;
+        char      *pszUnits = NULL;
+        double    *padfPrjParms = NULL;
+
+        oSRS.exportToPCI( &pszProj, &pszUnits, &padfPrjParms );
+        CPLPrintStringFill( szTemp + 32, pszProj, 16 );
+
+        CPLPrintInt32( szTemp + 48, 3, 8 );
+        CPLPrintInt32( szTemp + 56, 3, 8 );
+
+        CPLPrintStringFill( szTemp + 64, pszUnits, 16 );
+
+        for ( i = 0; i < 17; i++ )
+        {
+            CPLPrintDouble( szTemp + 80 + 26 * i,
+                            "%26.18E", padfPrjParms[i], "C" );
+        }
+
+        CPLPrintStringFill( szTemp + 522, "", 936 );
+
+        if ( pszProj )
+            CPLFree( pszProj );
+        if ( pszUnits )
+            CPLFree(pszUnits );
+        if ( padfPrjParms )
+            CPLFree( padfPrjParms );
+    }
+    else
+    {
+        CPLPrintStringFill( szTemp + 32, "PIXEL", 16 );
+        CPLPrintInt32( szTemp + 48, 3, 8 );
+        CPLPrintInt32( szTemp + 56, 3, 8 );
+        CPLPrintStringFill( szTemp + 64, "METER", 16 );
+        CPLPrintStringFill( szTemp + 80, "", 1378 );
+    }
+
+    /* TODO: USGS format */
+    CPLPrintStringFill( szTemp + 1458, "", 1614 );
+
+    for ( i = 0; i < 3; i++ )
+    {
+        CPLPrintDouble( szTemp + 1980 + 26 * i,
+                        "%26.18E", adfGeoTransform[i], "C" );
+    }
+    for ( i = 0; i < 3; i++ )
+    {
+        CPLPrintDouble( szTemp + 2526 + 26 * i,
+                        "%26.18E", adfGeoTransform[i + 3], "C" );
+    }
+
+    VSIFWriteL( (void *)szTemp, 1, 3072, fp );
+
+    // Now make the segment active
+    szTemp[0] = 'A';
+    VSIFSeekL( fp, nGeoPtrOffset, SEEK_SET );
+    VSIFWriteL( (void *)szTemp, 1, 1, fp );
+}
+
+/************************************************************************/
+/*                         PCIDSKTypeToGDAL()                           */
+/************************************************************************/
+
+GDALDataType PCIDSKDataset::PCIDSKTypeToGDAL( const char *pszType )
+{
+    if ( EQUALN( pszType, "8U", 2 ) )
+        return GDT_Byte;
+    if ( EQUALN( pszType, "16S", 3 ) )
+        return GDT_Int16;
+    if ( EQUALN( pszType, "16U", 3 ) )
+        return GDT_UInt16;
+    if ( EQUALN( pszType, "32R", 3 ) )
+        return GDT_Float32;
+
+    return GDT_Unknown;
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *PCIDSKDataset::Open( GDALOpenInfo * poOpenInfo )
+{
+    if( poOpenInfo->nHeaderBytes < 512 
+        || !EQUALN((const char *) poOpenInfo->pabyHeader, "PCIDSK  ", 8) )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    PCIDSKDataset   *poDS;
+
+    poDS = new PCIDSKDataset();
+
+    if( poOpenInfo->eAccess == GA_ReadOnly )
+        poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
+    else
+        poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" );
+    if ( !poDS->fp )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Failed to re-open %s within PCIDSK driver.\n",
+                  poOpenInfo->pszFilename );
+        delete poDS;
+        return NULL;
+    }
+    poDS->eAccess = poOpenInfo->eAccess;
+
+/* ==================================================================== */
+/*   Read PCIDSK File Header.                                           */
+/* ==================================================================== */
+    char            szTemp[1024];
+    char            *pszString;
+
+/* -------------------------------------------------------------------- */
+/*      Read File Identification.                                       */
+/* -------------------------------------------------------------------- */
+    VSIFSeekL( poDS->fp, 0, SEEK_SET );
+    VSIFReadL( szTemp, 1, 512, poDS->fp );
+
+    pszString = CPLScanString( szTemp + 8, 8, TRUE, TRUE );
+    poDS->SetMetadataItem( "SOFTWARE", pszString );
+    CPLFree( pszString );
+
+    pszString = CPLScanString( szTemp + 48, 64, TRUE, TRUE );
+    poDS->SetMetadataItem( "FILE_ID", pszString );
+    CPLFree( pszString );
+
+    pszString = CPLScanString( szTemp + 112, 32, TRUE, TRUE );
+    poDS->SetMetadataItem( "GENERATING_FACILITY", pszString );
+    CPLFree( pszString );
+
+    pszString = CPLScanString( szTemp + 144, 64, TRUE, TRUE );
+    poDS->SetMetadataItem( "DESCRIPTION1", pszString );
+    CPLFree( pszString );
+
+    pszString = CPLScanString( szTemp + 208, 64, TRUE, TRUE );
+    poDS->SetMetadataItem( "DESCRIPTION2", pszString );
+    CPLFree( pszString );
+
+    pszString = CPLScanString( szTemp + 272, 16, TRUE, TRUE );
+    poDS->SetMetadataItem( "DATE_OF_CREATION", pszString );
+    CPLFree( pszString );
+    // Store original creation time string for later use
+    poDS->pszCreatTime = CPLScanString( szTemp + 272, 16, TRUE, FALSE );
+
+    pszString = CPLScanString( szTemp + 288, 16, TRUE, TRUE );
+    poDS->SetMetadataItem( "DATE_OF_UPDATE", pszString );
+    CPLFree( pszString );
+
+/* ==================================================================== */
+/*   Read Segment Pointers.                                             */
+/* ==================================================================== */
+    vsi_l_offset    nSegPointersStart;  // Start block of Segment Pointers
+    vsi_l_offset    nSegPointersOffset; // Offset in bytes to Pointers
+    int             nSegBlocks;         // Number of blocks of Segment Pointers
+
+    {
+        VSIFSeekL( poDS->fp, 440, SEEK_SET );
+        VSIFReadL( szTemp, 1, 16, poDS->fp );
+        szTemp[16] = '\0';
+        nSegPointersStart = atol( szTemp ); // XXX: should be atoll()
+        nSegPointersOffset = ( nSegPointersStart - 1 ) * 512;
+        
+        VSIFSeekL( poDS->fp, 456, SEEK_SET );
+        VSIFReadL( szTemp, 1, 8, poDS->fp );
+        nSegBlocks = CPLScanLong( szTemp, 8 );
+        poDS->nSegCount = ( nSegBlocks * 512 ) / 32;
+
+/* -------------------------------------------------------------------- */
+/*      Allocate segment info structures.                               */
+/* -------------------------------------------------------------------- */
+        poDS->panSegType = (int *) CPLCalloc(sizeof(int),poDS->nSegCount );
+        poDS->papszSegName = (char **) CPLCalloc(sizeof(char*),poDS->nSegCount );
+        poDS->panSegOffset = (vsi_l_offset *) 
+            CPLCalloc(sizeof(vsi_l_offset),poDS->nSegCount );
+        poDS->panSegSize = (vsi_l_offset *) 
+            CPLCalloc(sizeof(vsi_l_offset),poDS->nSegCount );
+
+/* -------------------------------------------------------------------- */
+/*      Parse each segment pointer.                                     */
+/* -------------------------------------------------------------------- */
+        int             iSeg;
+    
+        for( iSeg = 0; iSeg < poDS->nSegCount; iSeg++ )
+        {
+            int bActive, nSegStartBlock, nSegSize;
+            char szSegName[9];
+            
+            VSIFSeekL( poDS->fp, nSegPointersOffset + iSeg * 32, SEEK_SET );
+            VSIFReadL( szTemp, 1, 32, poDS->fp );
+            szTemp[32] = '\0';
+            
+            strncpy( szSegName, szTemp+4, 8 );
+            szSegName[8] = '\0';
+            
+            if ( szTemp[0] == 'A' || szTemp[0] == 'L' )
+                bActive = TRUE;
+            else
+                bActive = FALSE;
+            
+            if( !bActive )
+                continue;
+            
+            poDS->panSegType[iSeg] = CPLScanLong( szTemp + 1, 3 );
+            nSegStartBlock = CPLScanLong( szTemp+12, 11 );
+            nSegSize = CPLScanLong( szTemp+23, 9 );
+            
+            poDS->papszSegName[iSeg] = CPLStrdup( szSegName );
+            poDS->panSegOffset[iSeg] = 512 * ((vsi_l_offset) (nSegStartBlock-1));
+            poDS->panSegSize[iSeg] = 512 * ((vsi_l_offset) nSegSize);
+            
+            CPLDebug( "PCIDSK", 
+                      "Seg=%d, Type=%d, Start=%9d, Size=%7d, Name=%s",
+                      iSeg+1, poDS->panSegType[iSeg], 
+                      nSegStartBlock, nSegSize, szSegName );
+            
+            // Some segments will be needed sooner, rather than later. 
+            
+            if( poDS->panSegType[iSeg] == 182 && EQUAL(szSegName,"SysBMDir"))
+                poDS->nBlockMapSeg = iSeg+1;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read Image Data.                                                */
+/* -------------------------------------------------------------------- */
+    vsi_l_offset    nImageStart;        // Start block of image data
+    vsi_l_offset    nImgHdrsStart;      // Start block of image headers
+    vsi_l_offset    nImageOffset;       // Offset to the first byte of the image
+    int             nByteBands, nInt16Bands, nUInt16Bands, nFloat32Bands;
+
+    pszString = CPLScanString( szTemp + 304, 16, TRUE, FALSE );
+    if ( !EQUAL( pszString, "" ) )
+        nImageStart = atol( pszString );// XXX: should be atoll()
+    else
+        nImageStart = 1;
+    CPLFree( pszString );
+    nImageOffset = (nImageStart - 1) * 512;
+
+    pszString = CPLScanString( szTemp + 336, 16, TRUE, FALSE );
+    nImgHdrsStart = atol( pszString );  // XXX: should be atoll()
+    CPLFree( pszString );
+
+    poDS->nBands = CPLScanLong( szTemp + 376, 8 );
+    poDS->nRasterXSize = CPLScanLong( szTemp + 384, 8 );
+    poDS->nRasterYSize = CPLScanLong( szTemp + 392, 8 );
+
+    nByteBands = CPLScanLong( szTemp + 464, 4 );
+    nInt16Bands = CPLScanLong( szTemp + 468, 4 );
+    nUInt16Bands = CPLScanLong( szTemp + 472, 4 );
+    nFloat32Bands = CPLScanLong( szTemp + 476, 4 );
+
+    // If these fields are blank, then it is assumed that all channels are 8-bit
+    if ( nByteBands == 0 && nInt16Bands == 0
+         && nUInt16Bands == 0 && nFloat32Bands == 0 )
+        nByteBands = poDS->nBands;
+
+/* ==================================================================== */
+/*   Read Image Headers and create band information objects.            */
+/* ==================================================================== */
+    int             iBand;
+    PCIDSKInterleaving eInterleaving;
+
+/* -------------------------------------------------------------------- */
+/*      Read type of interleaving and set up image parameters.          */
+/* -------------------------------------------------------------------- */
+    pszString = CPLScanString( szTemp + 360, 8, TRUE, FALSE );
+    if ( EQUALN( pszString, "PIXEL", 5 ) )
+        eInterleaving = PDI_PIXEL;
+    else if ( EQUALN( pszString, "BAND", 4 ) )
+        eInterleaving = PDI_BAND;
+    else if ( EQUALN( pszString, "FILE", 4 ) )
+        eInterleaving = PDI_FILE;
+    else
+    {
+        CPLDebug( "PCIDSK",
+                  "PCIDSK interleaving type %s is not supported by GDAL",
+                  pszString );
+        delete poDS;
+        return NULL;
+    }
+    CPLFree( pszString );
+
+    for( iBand = 0; iBand < poDS->nBands; iBand++ )
+    {
+        GDALDataType    eType;
+        GDALRasterBand  *poBand = NULL;
+        vsi_l_offset    nImgHdrOffset = (nImgHdrsStart - 1 + iBand * 2) * 512;
+        vsi_l_offset    nPixelOffset = 0, nLineOffset = 0, nLineSize = 0;
+        int             bNativeOrder;
+        int             i;
+        FILE            *fp = poDS->fp;
+
+        VSIFSeekL( poDS->fp, nImgHdrOffset, SEEK_SET );
+        VSIFReadL( szTemp, 1, 1024, poDS->fp );
+
+        pszString = CPLScanString( szTemp + 160, 8, TRUE, FALSE );
+        eType = poDS->PCIDSKTypeToGDAL( pszString );
+
+        // Old files computed type based on list.
+        if( eType == GDT_Unknown && pszString[0] == '\0' )
+        {
+            if( iBand < nByteBands )
+                eType = GDT_Byte;
+            else if( iBand < nByteBands + nInt16Bands )
+                eType = GDT_Int16;
+            else if( iBand < nByteBands + nInt16Bands + nUInt16Bands )
+                eType = GDT_UInt16;
+            else if( iBand < nByteBands + nInt16Bands  + nUInt16Bands
+                     + nFloat32Bands )
+                eType = GDT_Float32;
+        }
+
+        if ( eType == GDT_Unknown )
+        {
+            CPLDebug( "PCIDSK",
+                      "PCIDSK data type %s is not supported by GDAL",
+                      pszString );
+            delete poDS;
+            return NULL;
+        }
+        CPLFree( pszString );
+
+        switch ( eInterleaving )
+        {
+          case PDI_PIXEL:
+            nPixelOffset = nByteBands + 2 * (nInt16Bands + nUInt16Bands)
+                + 4 * nFloat32Bands;
+            nLineSize = nPixelOffset * poDS->nRasterXSize;
+            nLineOffset = ((int)((nLineSize + 511)/512)) * 512;
+            break;
+          case PDI_BAND:
+            nPixelOffset = GDALGetDataTypeSize( eType ) / 8;
+            nLineOffset = nPixelOffset * poDS->nRasterXSize;
+            break;
+          case PDI_FILE:
+          {
+              char    *pszFilename;
+
+              // Read the filename
+              pszFilename = CPLScanString( szTemp + 64, 64, TRUE, FALSE );
+
+              // /SIS=n is special case for internal tiled file.
+              if( EQUALN(pszFilename,"/SIS=",5) )
+              {
+                  int nImage = atoi(pszFilename+5);
+                  poBand = new PCIDSKTiledRasterBand( poDS, iBand+1, nImage );
+              }
+
+              // Non-empty filename means we have data stored in
+              // external raw file
+              else if ( !EQUAL(pszFilename, "") )
+              {
+                  CPLDebug( "PCIDSK", "pszFilename=%s", pszFilename );
+
+                  if( poOpenInfo->eAccess == GA_ReadOnly )
+                      fp = VSIFOpenL( pszFilename, "rb" );
+                  else
+                      fp = VSIFOpenL( pszFilename, "r+b" );
+
+                  if ( !fp )
+                  {
+                      CPLDebug( "PCIDSK",
+                                "Cannot open external raw file %s",
+                                pszFilename );
+                      iBand--;
+                      poDS->nBands--;
+                      CPLFree( pszFilename );
+                      continue;
+                  }
+              }
+
+              pszString = CPLScanString( szTemp + 168, 16, TRUE, FALSE );
+              nImageOffset = atol( pszString ); // XXX: should be atoll()
+              CPLFree( pszString );
+
+              nPixelOffset = CPLScanLong( szTemp + 184, 8 );
+              nLineOffset = CPLScanLong( szTemp +  + 192, 8 );
+
+              CPLFree( pszFilename );
+          }
+          break;
+          default: /* NOTREACHED */
+            break;
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Create raw band, only if we didn't already get a tiled band.    */
+/* -------------------------------------------------------------------- */
+        if( poBand == NULL )
+        {
+#ifdef CPL_MSB
+            bNativeOrder = ( szTemp[201] == 'S')?FALSE:TRUE;
+#else
+            bNativeOrder = ( szTemp[201] == 'S')?TRUE:FALSE;
+#endif
+
+#ifdef DEBUG
+#if defined(WIN32) && defined(_MSC_VER)
+            CPLDebug( "PCIDSK",
+                      "Band %d: nImageOffset=%I64d, nPixelOffset=%I64d, "
+                      "nLineOffset=%I64d, nLineSize=%I64d",
+                      iBand + 1, nImageOffset, nPixelOffset,
+                      nLineOffset, nLineSize );
+#elif HAVE_LONG_LONG
+            CPLDebug( "PCIDSK",
+                      "Band %d: nImageOffset=%Ld, nPixelOffset=%Ld, "
+                      "nLineOffset=%Ld, nLineSize=%Ld",
+                      iBand + 1, nImageOffset, nPixelOffset,
+                      nLineOffset, nLineSize );
+#else
+            CPLDebug( "PCIDSK",
+                      "Band %d: nImageOffset=%ld, nPixelOffset=%ld, "
+                      "nLineOffset=%ld, nLineSize=%ld",
+                      iBand + 1, nImageOffset, nPixelOffset,
+                      nLineOffset, nLineSize );
+#endif
+#endif
+            
+            poBand = new PCIDSKRawRasterBand( poDS, iBand + 1, fp,
+                                              nImageOffset, 
+                                              (int) nPixelOffset, 
+                                              (int) nLineOffset, 
+                                              eType, bNativeOrder);
+
+            switch ( eInterleaving )
+            {
+              case PDI_PIXEL:
+                nImageOffset += GDALGetDataTypeSize( eType ) / 8;
+                break;
+              case  PDI_BAND:
+                nImageOffset += nLineOffset * poDS->nRasterYSize;
+                break;
+              default:
+                break;
+            }
+        }
+
+        poDS->SetBand( iBand + 1, poBand );
+
+/* -------------------------------------------------------------------- */
+/*      Read and assign few metadata parameters to each image band.     */
+/* -------------------------------------------------------------------- */
+        pszString = CPLScanString( szTemp, 64, TRUE, TRUE );
+        poBand->SetDescription( pszString );
+        CPLFree( pszString );
+
+        pszString = CPLScanString( szTemp + 128, 16, TRUE, TRUE );
+        poBand->SetMetadataItem( "DATE_OF_CREATION", pszString );
+        CPLFree( pszString );
+
+        pszString = CPLScanString( szTemp + 144, 16, TRUE, TRUE );
+        poBand->SetMetadataItem( "DATE_OF_UPDATE",  pszString );
+        CPLFree( pszString );
+
+        pszString = CPLScanString( szTemp + 202, 16, TRUE, TRUE );
+        if ( !EQUAL( szTemp, "" ) )
+            poBand->SetMetadataItem( "UNITS",  pszString );
+        CPLFree( pszString );
+
+        for ( i = 0; i < 8; i++ )
+        {
+            pszString = CPLScanString( szTemp + 384 + i * 80, 80, TRUE, TRUE );
+            if ( !EQUAL( pszString, "" ) )
+                poBand->SetMetadataItem( CPLSPrintf("HISTORY%d", i + 1),
+                                         pszString );
+            CPLFree( pszString );
+        }
+
+    }
+
+    if (!poDS->GetRasterCount())
+        CPLError(CE_Warning, CPLE_None, "Dataset contain no raster bands.");
+   
+/* ==================================================================== */
+/*      Process some segments of interest.                              */
+/* ==================================================================== */
+    int iSeg;
+
+    for( iSeg = 0; iSeg < poDS->nSegCount; iSeg++ )
+    {
+        switch( poDS->panSegType[iSeg] )
+        {
+/* -------------------------------------------------------------------- */
+/*      Georeferencing segment.                                         */
+/* -------------------------------------------------------------------- */
+            case 150:                   // GEO segment
+            {
+                vsi_l_offset    nGeoDataOffset;
+                int             j, nXCoeffs, nYCoeffs;
+                OGRSpatialReference oSRS;
+
+                poDS->nGeoPtrOffset = nSegPointersOffset + iSeg * 32;
+                poDS->nGeoOffset = poDS->panSegOffset[iSeg];
+                nGeoDataOffset = poDS->nGeoOffset + 1024;
+
+                VSIFSeekL( poDS->fp, nGeoDataOffset, SEEK_SET );
+                VSIFReadL( szTemp, 1, 16, poDS->fp );
+                szTemp[16] = '\0';
+                if ( EQUALN( szTemp, "POLYNOMIAL", 10 ) )
+                {
+                    char        szProj[17];
+
+                    // Read projection definition
+                    VSIFSeekL( poDS->fp, nGeoDataOffset + 32, SEEK_SET );
+                    VSIFReadL( szProj, 1, 16, poDS->fp );
+                    szProj[16] = '\0';
+                    if ( EQUALN( szProj, "PIXEL", 5 )
+                         || EQUALN( szProj, "METRE", 5 ) )
+                        break;
+
+                    // Read number of transform coefficients
+                    VSIFSeekL( poDS->fp, nGeoDataOffset + 48, SEEK_SET );
+                    VSIFReadL( szTemp, 1, 16, poDS->fp );
+                    nXCoeffs = CPLScanLong( szTemp, 8 );
+                    if ( nXCoeffs > 3 )
+                        nXCoeffs = 3;
+                    nYCoeffs = CPLScanLong( szTemp + 8, 8 );
+                    if ( nYCoeffs > 3 )
+                        nYCoeffs = 3;
+
+                    // Read geotransform coefficients
+                    VSIFSeekL( poDS->fp, nGeoDataOffset + 212, SEEK_SET );
+                    VSIFReadL( szTemp, 1, nXCoeffs * 26, poDS->fp );
+                    for ( j = 0; j < nXCoeffs; j++ )
+                    {
+                        poDS->adfGeoTransform[j] =
+                            CPLScanDouble( szTemp + 26 * j, 26, "C" );
+                    }
+                    VSIFSeekL( poDS->fp, nGeoDataOffset + 1642, SEEK_SET );
+                    VSIFReadL( szTemp, 1, nYCoeffs * 26, poDS->fp );
+                    for ( j = 0; j < nYCoeffs; j++ )
+                    {
+                        poDS->adfGeoTransform[j + 3] =
+                            CPLScanDouble( szTemp + 26 * j, 26, "C" );
+                    }
+
+                    oSRS.importFromPCI( szProj, NULL, NULL );
+                    if ( poDS->pszProjection )
+                        CPLFree( poDS->pszProjection );
+                    oSRS.exportToWkt( &poDS->pszProjection );
+                }
+                else if ( EQUALN( szTemp, "PROJECTION", 10 ) )
+                {
+                    char        szProj[17], szUnits[17];
+                    double      adfProjParms[17];
+
+                    // Read projection definition
+                    VSIFSeekL( poDS->fp, nGeoDataOffset + 32, SEEK_SET );
+                    VSIFReadL( szProj, 1, 16, poDS->fp );
+                    szProj[16] = '\0';
+                    if ( EQUALN( szProj, "PIXEL", 5 )
+                         || EQUALN( szProj, "METRE", 5 ) )
+                        break;
+
+                    // Read number of transform coefficients
+                    VSIFSeekL( poDS->fp, nGeoDataOffset + 48, SEEK_SET );
+                    VSIFReadL( szTemp, 1, 16, poDS->fp );
+                    nXCoeffs = CPLScanLong( szTemp, 8 );
+                    if ( nXCoeffs > 3 )
+                        nXCoeffs = 3;
+                    nYCoeffs = CPLScanLong( szTemp + 8, 8 );
+                    if ( nYCoeffs > 3 )
+                        nYCoeffs = 3;
+
+                    // Read grid units definition
+                    VSIFSeekL( poDS->fp, nGeoDataOffset + 64, SEEK_SET );
+                    VSIFReadL( szUnits, 1, 16, poDS->fp );
+                    szUnits[16] = '\0';
+
+                    // Read 16 projection parameters
+                    VSIFSeekL( poDS->fp, nGeoDataOffset + 80, SEEK_SET );
+                    VSIFReadL( szTemp, 1, 26 * 16, poDS->fp );
+                    for ( j = 0; j < 17; j++ )
+                    {
+                        adfProjParms[j] =
+                            CPLScanDouble( szTemp + 26 * j, 26, "C" );
+                    }
+
+                    // Read geotransform coefficients
+                    VSIFSeekL( poDS->fp, nGeoDataOffset + 1980, SEEK_SET );
+                    VSIFReadL( szTemp, 1, nXCoeffs * 26, poDS->fp );
+                    for ( j = 0; j < nXCoeffs; j++ )
+                    {
+                        poDS->adfGeoTransform[j] =
+                            CPLScanDouble( szTemp + 26 * j, 26, "C" );;
+                    }
+                    VSIFSeekL( poDS->fp, nGeoDataOffset + 2526, SEEK_SET );
+                    VSIFReadL( szTemp, 1, nYCoeffs * 26, poDS->fp );
+                    for ( j = 0; j < nYCoeffs; j++ )
+                    {
+                        poDS->adfGeoTransform[j + 3] =
+                            CPLScanDouble( szTemp + 26 * j, 26, "C" );
+                    }
+
+                    oSRS.importFromPCI( szProj, szUnits, adfProjParms );
+                    if ( poDS->pszProjection )
+                        CPLFree( poDS->pszProjection );
+                    oSRS.exportToWkt( &poDS->pszProjection );
+                }
+            }
+            break;
+
+/* -------------------------------------------------------------------- */
+/*      GCP Segment                                                     */
+/* -------------------------------------------------------------------- */
+            case 214:                       // GCP segment
+            {
+                vsi_l_offset    nGcpDataOffset;
+                int             j;
+                OGRSpatialReference oSRS;
+
+                poDS->nGcpPtrOffset = nSegPointersOffset + iSeg * 32;
+                poDS->nGcpOffset = poDS->panSegOffset[iSeg];
+                nGcpDataOffset = poDS->nGcpOffset + 1024;
+
+                if( !poDS->nGCPCount )  // XXX: We will read the
+                    // first GCP segment only
+                {
+                    VSIFSeekL( poDS->fp, nGcpDataOffset, SEEK_SET );
+                    VSIFReadL( szTemp, 1, 80, poDS->fp );
+                    poDS->nGCPCount = CPLScanLong( szTemp, 16 );
+                    if ( poDS->nGCPCount > 0 )
+                    {
+                        double      dfUnitConv = 1.0;
+                        char        szProj[17];
+
+                        memcpy( szProj, szTemp + 32, 16 );
+                        szProj[16] = '\0';
+                        oSRS.importFromPCI( szProj, NULL, NULL );
+                        if ( poDS->pszGCPProjection )
+                            CPLFree( poDS->pszGCPProjection );
+                        oSRS.exportToWkt( &poDS->pszGCPProjection );
+                        poDS->pasGCPList = (GDAL_GCP *)
+                            CPLCalloc( poDS->nGCPCount, sizeof(GDAL_GCP) );
+                        GDALInitGCPs( poDS->nGCPCount, poDS->pasGCPList );
+                        if ( EQUALN( szTemp + 64, "FEET     ", 9 ) )
+                            dfUnitConv = atof(SRS_UL_FOOT_CONV);
+                        for ( j = 0; j < poDS->nGCPCount; j++ )
+                        {
+                            VSIFSeekL( poDS->fp, nGcpDataOffset + j * 128 + 512,
+                                       SEEK_SET );
+                            VSIFReadL( szTemp, 1, 128, poDS->fp );
+                            poDS->pasGCPList[j].dfGCPPixel =
+                                CPLScanDouble( szTemp + 6, 18, "C" );
+                            poDS->pasGCPList[j].dfGCPLine = 
+                                CPLScanDouble( szTemp + 24, 18, "C" );
+                            poDS->pasGCPList[j].dfGCPX = 
+                                CPLScanDouble( szTemp + 60, 18, "C" );
+                            poDS->pasGCPList[j].dfGCPY = 
+                                CPLScanDouble( szTemp + 78, 18, "C" );
+                            poDS->pasGCPList[j].dfGCPZ =
+                                CPLScanDouble(szTemp + 96, 18, "C")/dfUnitConv;
+                        }
+                    }
+                }
+            }
+            break;
+
+/* -------------------------------------------------------------------- */
+/*      SYS Segments.  Process metadata immediately.                    */
+/* -------------------------------------------------------------------- */
+            case 182: // SYS segment.
+            {
+                if( EQUAL(poDS->papszSegName[iSeg],"METADATA") )
+                    poDS->CollectPCIDSKMetadata( iSeg+1 );
+            }
+            break;
+
+            default:
+                break;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for band overviews.                                       */
+/* -------------------------------------------------------------------- */
+    for( iBand = 0; iBand < poDS->GetRasterCount(); iBand++ )
+    {
+        GDALRasterBand *poBand = poDS->GetRasterBand( iBand+1 );
+        char **papszMD = poBand->GetMetadata( "PCISYS" );
+        int iMD;
+
+        for( iMD = 0; papszMD != NULL && papszMD[iMD] != NULL; iMD++ )
+        {
+            if( EQUALN(papszMD[iMD],"Overview_",9) )
+            {
+                int nBlockXSize, nBlockYSize;
+                int nImage = atoi(CPLParseNameValue( papszMD[iMD], NULL ));
+                PCIDSKTiledRasterBand *poOvBand;
+
+                poOvBand = new PCIDSKTiledRasterBand( poDS, 0, nImage );
+
+                poBand->GetBlockSize( &nBlockXSize, &nBlockYSize );
+               
+                if( nBlockYSize == 1 )
+                    ((PCIDSKRawRasterBand *) poBand)->
+                        AttachOverview( poOvBand );
+                else
+                    ((PCIDSKTiledRasterBand *) poBand)->
+                        AttachOverview( poOvBand );
+            }
+        }
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Open overviews.                                                 */
+/* -------------------------------------------------------------------- */
+    poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                              SegRead()                               */
+/************************************************************************/
+
+int PCIDSKDataset::SegRead( int nSegment, vsi_l_offset nOffset, 
+                            int nSize, void *pBuffer )
+
+{
+    if( nSegment < 1 || nSegment > nSegCount || panSegType[nSegment-1] == 0 )
+        return 0;
+
+    if( nOffset + nSize > panSegSize[nSegment-1] )
+    {
+        return 0;
+    }
+    else
+    {
+        if( VSIFSeekL( fp, panSegOffset[nSegment-1]+nOffset+1024, 
+                       SEEK_SET ) != 0 ) 
+            return 0;
+
+        return VSIFReadL( pBuffer, 1, nSize, fp );
+    }
+}
+
+
+/************************************************************************/
+/*                       CollectPCIDSKMetadata()                        */
+/************************************************************************/
+
+void PCIDSKDataset::CollectPCIDSKMetadata( int nSegment )
+
+{
+    int nSegSize = panSegSize[nSegment-1];
+
+/* -------------------------------------------------------------------- */
+/*      Read all metadata in one gulp.                                  */
+/* -------------------------------------------------------------------- */
+    char *pszMetadataBuf = (char *) CPLCalloc(1,nSegSize + 1);
+
+    if( !SegRead( nSegment, 0, nSegSize, pszMetadataBuf ) )
+    {
+        CPLFree( pszMetadataBuf );
+        CPLError( CE_Warning, CPLE_FileIO,
+                  "IO error reading metadata, ignoring." );
+        return;
+    }
+
+/* ==================================================================== */
+/*      Parse out domain/name/value sets.                               */
+/* ==================================================================== */
+    char *pszNext = pszMetadataBuf;
+
+    while( *pszNext != '\0' )
+    {
+        char *pszName, *pszValue;
+
+        pszName = pszNext;
+        
+/* -------------------------------------------------------------------- */
+/*      Identify the end of this line, and zero terminate it.           */
+/* -------------------------------------------------------------------- */
+        while( *pszNext != 10 && *pszNext != 12 && *pszNext != 0 )
+            pszNext++;
+
+        if( *pszNext != 0 )
+        {
+            *(pszNext++) = '\0';
+            while( *pszNext == 10 || *pszNext == 12 )
+                pszNext++;
+        }
+            
+/* -------------------------------------------------------------------- */
+/*      Split off the value from the name.                              */
+/* -------------------------------------------------------------------- */
+        pszValue = pszName;
+        while( *pszValue != 0 && *pszValue != ':' ) 
+            pszValue++;
+
+        if( *pszValue != 0 )
+            *(pszValue++) = '\0';
+
+        while( *pszValue == ' ' )
+            pszValue++;
+
+/* -------------------------------------------------------------------- */
+/*      Handle METADATA_IMG values by assigning to the appropriate      */
+/*      band object.                                                    */
+/* -------------------------------------------------------------------- */
+        if( EQUALN(pszName,"METADATA_IMG_",13) )
+        {
+            int nBand = atoi(pszName+13);
+            pszName += 13;
+            while( *pszName && *pszName != '_' )
+                pszName++;
+
+            if( *pszName == '_' )
+                pszName++;
+
+            if( nBand > 0 && nBand <= GetRasterCount() )
+            {
+                GDALRasterBand *poBand = GetRasterBand( nBand );
+
+                if( *pszName == '_' )
+                    poBand->SetMetadataItem( pszName+1, pszValue, "PCISYS" );
+                else
+                    poBand->SetMetadataItem( pszName, pszValue );
+            }
+        }
+        
+        else if( EQUALN(pszName,"METADATA_FIL",13) )
+        {
+            pszName += 13;
+            if( *pszName == '_' )
+                pszName++;
+
+            if( *pszName == '_' )
+                SetMetadataItem( pszName+1, pszValue, "PCISYS" );
+            else
+                SetMetadataItem( pszName, pszValue );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    CPLFree( pszMetadataBuf );
+}
+
+/************************************************************************/
+/*                               Create()                               */
+/************************************************************************/
+
+GDALDataset *PCIDSKDataset::Create( const char * pszFilename,
+                                    int nXSize, int nYSize, int nBands,
+                                    GDALDataType eType, char **papszOptions )
+
+{
+    if ( eType != GDT_Byte
+         && eType != GDT_Int16
+         && eType != GDT_UInt16
+         && eType != GDT_Float32 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+              "Attempt to create PCIDSK dataset with an illegal data type (%s),\n"
+              "only Byte, Int16, UInt16 and Float32 supported by the format.\n",
+              GDALGetDataTypeName(eType) );
+
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try to create the file.                                         */
+/* -------------------------------------------------------------------- */
+    FILE        *fp = VSIFOpenL( pszFilename, "wb" );
+
+    if( fp == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Unable to create file %s.\n", pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Get current time to fill appropriate fields.                    */
+/* -------------------------------------------------------------------- */
+    struct tm       oUpdateTime;
+    time_t          nTime = VSITime(NULL);
+
+    VSILocalTime( &nTime, &oUpdateTime );
+
+/* ==================================================================== */
+/*      Fill the PCIDSK File Header.                                    */
+/* ==================================================================== */
+    const char      *pszDesc;
+    char            szTemp[1024];
+    vsi_l_offset    nImageStart;        // Start block of image data
+    vsi_l_offset    nSegPointersStart;  // Start block of Segment Pointers
+    vsi_l_offset    nImageBlocks;       // Number of blocks of image data
+    int             nImgHdrBlocks;      // Number of blocks of image header data
+
+/* -------------------------------------------------------------------- */
+/*      Calculate offsets.                                              */
+/* -------------------------------------------------------------------- */
+    nImgHdrBlocks = nBands * 2;
+    nSegPointersStart = 2 + nImgHdrBlocks;
+    nImageStart = nSegPointersStart + nSegBlocks;
+    nImageBlocks = (vsi_l_offset)
+        (nXSize * nYSize * nBands * GDALGetDataTypeSize(eType) / 8 + 512) / 512;
+ 
+/* -------------------------------------------------------------------- */
+/*      Fill the File Identification.                                   */
+/* -------------------------------------------------------------------- */
+    CPLPrintStringFill( szTemp, "PCIDSK  ", 8 );
+    CPLPrintStringFill( szTemp + 8, "GDAL", 4 );
+    CPLPrintStringFill( szTemp + 12, GDALVersionInfo( "VERSION_NUM" ), 4 );
+    CPLPrintUIntBig( szTemp + 16,
+                  nImageStart + nImageBlocks + nGeoSegBlocks - 1, 16 );
+    CPLPrintStringFill( szTemp + 32, "", 16 );
+    CPLPrintStringFill( szTemp + 48, CPLGetFilename(pszFilename), 64 );
+    CPLPrintStringFill( szTemp + 112, "Created with GDAL", 32 );
+
+    pszDesc = CSLFetchNameValue( papszOptions, "FILEDESC1" );
+    if ( !pszDesc )
+        pszDesc = "";
+    CPLPrintStringFill( szTemp + 144, pszDesc, 64 );
+
+    pszDesc = CSLFetchNameValue( papszOptions, "FILEDESC2" );
+    if ( !pszDesc )
+        pszDesc = "";
+    CPLPrintStringFill( szTemp + 208, pszDesc, 64 );
+
+    CPLPrintTime( szTemp + 272, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" );
+    CPLPrintTime( szTemp + 288, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" );
+
+/* -------------------------------------------------------------------- */
+/*      Fill the Image Data and Segment Pointers.                       */
+/* -------------------------------------------------------------------- */
+    CPLPrintUIntBig( szTemp + 304, nImageStart, 16 );
+    CPLPrintUIntBig( szTemp + 320, nImageBlocks, 16 );
+    sprintf( szTemp + 336, "%16d", 2 );
+    sprintf( szTemp + 352, "%8d", nImgHdrBlocks );
+    CPLPrintStringFill( szTemp + 360, "BAND", 8 );
+    CPLPrintStringFill( szTemp + 368, "", 8 );
+    sprintf( szTemp + 376, "%8d", nBands );
+    sprintf( szTemp + 384, "%8d", nXSize );
+    sprintf( szTemp + 392, "%8d", nYSize );
+    CPLPrintStringFill( szTemp + 400, "METRE", 8 );
+    // Two following parameters will be filled with real values in FlushCache()
+    CPLPrintStringFill( szTemp + 408, "", 16 );    // X size of pixel
+    CPLPrintStringFill( szTemp + 424, "", 16 );    // Y size of pixel
+
+    CPLPrintUIntBig( szTemp + 440, nSegPointersStart, 16 );
+    sprintf( szTemp + 456, "%8d", nSegBlocks );
+    if ( eType == GDT_Byte )
+        sprintf( szTemp + 464, "%4d", nBands );
+    else
+        CPLPrintStringFill( szTemp + 464, "", 4 );
+    if ( eType == GDT_Int16 )
+        sprintf( szTemp + 468, "%4d", nBands );
+    else
+        CPLPrintStringFill( szTemp + 468, "", 4 );
+    if ( eType == GDT_UInt16 )
+        sprintf( szTemp + 472, "%4d", nBands );
+    else
+        CPLPrintStringFill( szTemp + 472, "", 4 );
+    if ( eType == GDT_Float32 )
+        sprintf( szTemp + 476, "%4d", nBands );
+    else
+        CPLPrintStringFill( szTemp + 476, "", 4 );
+    CPLPrintStringFill( szTemp + 480, "", 32 );
+    
+    VSIFSeekL( fp, 0, SEEK_SET );
+    VSIFWriteL( (void *)szTemp, 1, 512, fp );
+
+/* ==================================================================== */
+/*      Fill the Image Headers.                                         */
+/* ==================================================================== */
+    int         i;
+
+    for ( i = 0; i < nBands; i++ )
+    {
+        pszDesc =
+            CSLFetchNameValue( papszOptions, CPLSPrintf("BANDDESC%d", i + 1) );
+
+        if ( !pszDesc )
+            pszDesc = CPLSPrintf( "Image band %d", i + 1 );
+
+        CPLPrintStringFill( szTemp, pszDesc, 64 );
+        CPLPrintStringFill( szTemp + 64, "", 64 );
+        CPLPrintTime( szTemp + 128, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" );
+        CPLPrintTime( szTemp + 144, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" );
+        switch ( eType )
+        {
+            case GDT_Byte:
+                CPLPrintStringFill( szTemp + 160, "8U", 8 );
+                break;
+            case GDT_Int16:
+                CPLPrintStringFill( szTemp + 160, "16S", 8 );
+                break;
+            case GDT_UInt16:
+                CPLPrintStringFill( szTemp + 160, "16U", 8 );
+                break;
+            case GDT_Float32:
+                CPLPrintStringFill( szTemp + 160, "32R", 8 );
+                break;
+            default:
+                break;
+        }
+        CPLPrintStringFill( szTemp + 168, "", 16 );
+        CPLPrintStringFill( szTemp + 184, "", 8 );
+        CPLPrintStringFill( szTemp + 192, "", 8 );
+        CPLPrintStringFill( szTemp + 200, " ", 1 );
+#ifdef CPL_MSB
+        CPLPrintStringFill( szTemp + 201, "N", 1 );
+#else
+        if ( eType == GDT_Byte )
+            CPLPrintStringFill( szTemp + 201, "N", 1 );
+        else
+            CPLPrintStringFill( szTemp + 201, "S", 1 );
+#endif
+        CPLPrintStringFill( szTemp + 202, "", 48 );
+        CPLPrintStringFill( szTemp + 250, "", 32 );
+        CPLPrintStringFill( szTemp + 282, "", 8 );
+        CPLPrintStringFill( szTemp + 290, "", 94 );
+
+        // Write the history line
+        CPLPrintStringFill( szTemp + 384,
+                            "GDAL: Image band created with GDAL", 64 );
+        CPLPrintTime( szTemp + 448, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" );
+        // Fill other lines with spaces
+        CPLPrintStringFill( szTemp + 464, "", 80 * 7 );
+
+        // More history lines may be used if needed.
+        // CPLPrintStringFill( szTemp + 464, "HistoryLine2", 80 );
+        // CPLPrintStringFill( szTemp + 544, "HistoryLine3", 80 );
+        // CPLPrintStringFill( szTemp + 624, "HistoryLine4", 80 );
+        // CPLPrintStringFill( szTemp + 704, "HistoryLine5", 80 );
+        // CPLPrintStringFill( szTemp + 784, "HistoryLine6", 80 );
+        // CPLPrintStringFill( szTemp + 864, "HistoryLine7", 80 );
+        // CPLPrintStringFill( szTemp + 944, "HistoryLine8", 80 );
+
+        VSIFWriteL( (void *)szTemp, 1, 1024, fp );
+    }
+
+/* ==================================================================== */
+/*      Fill the Segment Pointers.                                      */
+/* ==================================================================== */
+    int         nSegments = ( nSegBlocks * 512 ) / 32;
+
+/* -------------------------------------------------------------------- */
+/*      Write out pointer to the Georeferencing segment.                */
+/* -------------------------------------------------------------------- */
+    CPLPrintStringFill( szTemp, "A150GEOref", 12 );
+    CPLPrintUIntBig( szTemp + 12, nImageStart + nImageBlocks, 11 );
+    sprintf( szTemp + 23, "%9d", nGeoSegBlocks );
+    VSIFWriteL( (void *)szTemp, 1, 32, fp );
+
+/* -------------------------------------------------------------------- */
+/*      Blank all other segment pointers                                */
+/* -------------------------------------------------------------------- */
+    CPLPrintStringFill( szTemp, "", 32 );
+    for ( i = 1; i < nSegments; i++ )
+        VSIFWriteL( (void *)szTemp, 1, 32, fp );
+
+/* -------------------------------------------------------------------- */
+/*      Write out default georef segment.                               */
+/* -------------------------------------------------------------------- */
+    static const char *apszGeoref[] = {
+        "Master Georeferencing Segment for File                                                                                          17:27 11Nov2003 17:27 11Nov2003                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     ","                                                                            POLYNOMIAL      PIXEL           PIXEL                  3       3                                                                                                                                                      0.000000000000000000D+00  1.000000000000000000D+00  0.000000000000000000D+00                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      ","                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    0.000000000000000000D+00  0.000000000000000000D+00  1.000000000000000000D+00                                                                                                    ","                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    ","                                                                                                                                                                                                                                                                                                                ", NULL};
+
+    for( i = 0; apszGeoref[i] != NULL; i++ )
+        VSIFWriteL( (void *) apszGeoref[i], 1, strlen(apszGeoref[i]), fp );
+
+    VSIFCloseL( fp );
+    return (GDALDataset *) GDALOpen( pszFilename, GA_Update );
+}
+
+
+
+/************************************************************************/
+/*                             CreateCopy()                             */
+/************************************************************************/
+
+GDALDataset *
+PCIDSKDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, 
+                        int bStrict, char ** papszOptions, 
+                        GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    PCIDSKDataset	*poDS;
+    GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
+    int          iBand;
+
+    if( !pfnProgress( 0.0, NULL, pProgressData ) )
+        return NULL;
+
+    /* check that other bands match type- sets type */
+    /* to unknown if they differ.                  */
+    for( iBand = 1; iBand < poSrcDS->GetRasterCount(); iBand++ )
+     {
+         GDALRasterBand *poBand = poSrcDS->GetRasterBand( iBand+1 );
+         eType = GDALDataTypeUnion( eType, poBand->GetRasterDataType() );
+     }
+
+    poDS = (PCIDSKDataset *) Create( pszFilename, 
+                                  poSrcDS->GetRasterXSize(), 
+                                  poSrcDS->GetRasterYSize(), 
+                                  poSrcDS->GetRasterCount(), 
+                                  eType, papszOptions );
+
+   /* Check that Create worked- return Null if it didn't */
+    if (poDS == NULL)
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Copy the image data.                                            */
+/* -------------------------------------------------------------------- */
+    int         nXSize = poDS->GetRasterXSize();
+    int         nYSize = poDS->GetRasterYSize();
+    int  	nBlockXSize, nBlockYSize, nBlockTotal, nBlocksDone;
+
+    poDS->GetRasterBand(1)->GetBlockSize( &nBlockXSize, &nBlockYSize );
+
+    nBlockTotal = ((nXSize + nBlockXSize - 1) / nBlockXSize)
+        * ((nYSize + nBlockYSize - 1) / nBlockYSize)
+        * poSrcDS->GetRasterCount();
+
+    nBlocksDone = 0;
+    for( iBand = 0; iBand < poSrcDS->GetRasterCount(); iBand++ )
+    {
+        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 );
+        GDALRasterBand *poDstBand = poDS->GetRasterBand( iBand+1 );
+        int	       iYOffset, iXOffset;
+        void           *pData;
+        CPLErr  eErr;
+
+
+        pData = CPLMalloc(nBlockXSize * nBlockYSize
+                          * GDALGetDataTypeSize(eType) / 8);
+
+        for( iYOffset = 0; iYOffset < nYSize; iYOffset += nBlockYSize )
+        {
+            for( iXOffset = 0; iXOffset < nXSize; iXOffset += nBlockXSize )
+            {
+                int	nTBXSize, nTBYSize;
+
+                if( !pfnProgress( (nBlocksDone++) / (float) nBlockTotal,
+                                  NULL, pProgressData ) )
+                {
+                    CPLError( CE_Failure, CPLE_UserInterrupt, 
+                              "User terminated" );
+                    delete poDS;
+
+                    GDALDriver *poPCIDSKDriver = 
+                        (GDALDriver *) GDALGetDriverByName( "PCIDSK" );
+                    poPCIDSKDriver->Delete( pszFilename );
+                    return NULL;
+                }
+
+                nTBXSize = MIN(nBlockXSize,nXSize-iXOffset);
+                nTBYSize = MIN(nBlockYSize,nYSize-iYOffset);
+
+                eErr = poSrcBand->RasterIO( GF_Read, 
+                                            iXOffset, iYOffset, 
+                                            nTBXSize, nTBYSize,
+                                            pData, nTBXSize, nTBYSize,
+                                            eType, 0, 0 );
+                if( eErr != CE_None )
+                {
+                    return NULL;
+                }
+            
+                eErr = poDstBand->RasterIO( GF_Write, 
+                                            iXOffset, iYOffset, 
+                                            nTBXSize, nTBYSize,
+                                            pData, nTBXSize, nTBYSize,
+                                            eType, 0, 0 );
+
+                if( eErr != CE_None )
+                {
+                    return NULL;
+                }
+            }
+        }
+
+        CPLFree( pData );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Copy georeferencing information, if enough is available.        */
+/* -------------------------------------------------------------------- */
+
+    double *tempGeoTransform=NULL; 
+
+    tempGeoTransform = (double *) CPLMalloc(6*sizeof(double));
+
+    if (( poSrcDS->GetGeoTransform( tempGeoTransform ) == CE_None)
+        && (tempGeoTransform[0] != 0.0 || tempGeoTransform[1] != 1.0
+        || tempGeoTransform[2] != 0.0 || tempGeoTransform[3] != 0.0
+        || tempGeoTransform[4] != 0.0 || ABS(tempGeoTransform[5]) != 1.0 ))
+    {
+          poDS->SetProjection(poSrcDS->GetProjectionRef());
+          poDS->SetGeoTransform(tempGeoTransform);
+    }
+    CPLFree(tempGeoTransform);
+        
+   
+    poDS->FlushCache();
+
+    if( !pfnProgress( 1.0, NULL, pProgressData ) )
+    {
+        CPLError( CE_Failure, CPLE_UserInterrupt, 
+                  "User terminated" );
+        delete poDS;
+
+        GDALDriver *poPCIDSKDriver = 
+            (GDALDriver *) GDALGetDriverByName( "PCIDSK" );
+        poPCIDSKDriver->Delete( pszFilename );
+        return NULL;
+    }
+
+    poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
+
+    return poDS;
+}
+/************************************************************************/
+/*                        GDALRegister_PCIDSK()                         */
+/************************************************************************/
+
+void GDALRegister_PCIDSK()
+
+{
+    GDALDriver  *poDriver;
+
+    if( GDALGetDriverByName( "PCIDSK" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+
+        poDriver->SetDescription( "PCIDSK" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
+                                   "PCIDSK Database File" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
+                                   "frmt_pcidsk.html" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "pix" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte UInt16 Int16 Float32" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
+"<CreationOptionList>"
+"   <Option name='FILEDESC1' type='string' description='The first line of descriptive text'/>"
+"   <Option name='FILEDESC2' type='string' description='The second line of descriptive text'/>"
+"   <Option name='BANDDESCn' type='string' description='Text describing contents of the specified band'/>"
+"</CreationOptionList>" ); 
+
+        poDriver->pfnOpen = PCIDSKDataset::Open;
+        poDriver->pfnCreate = PCIDSKDataset::Create;
+        poDriver->pfnCreateCopy = PCIDSKDataset::CreateCopy;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
+
diff --git a/Utilities/GDAL/frmts/pcidsk/pcidsktiledrasterband.cpp b/Utilities/GDAL/frmts/pcidsk/pcidsktiledrasterband.cpp
new file mode 100644
index 0000000000..e630e4b5d9
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcidsk/pcidsktiledrasterband.cpp
@@ -0,0 +1,311 @@
+/******************************************************************************
+ * $Id: pcidsktiledrasterband.cpp,v 1.4 2005/06/09 19:01:40 fwarmerdam Exp $
+ *
+ * Project:  PCIDSK Database File
+ * Purpose:  Implementation of PCIDSKTiledRasterBand
+ * Author:   Frank Warmerdam <warmerdam@pobox.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: pcidsktiledrasterband.cpp,v $
+ * Revision 1.4  2005/06/09 19:01:40  fwarmerdam
+ * added support for tiled primary bands
+ *
+ * Revision 1.3  2005/06/08 01:16:57  fwarmerdam
+ * fix warnings
+ *
+ * Revision 1.2  2005/06/08 01:14:35  fwarmerdam
+ * dos2unix
+ *
+ * Revision 1.1  2005/06/08 01:08:33  fwarmerdam
+ * New
+ *
+ */
+
+#include "gdal_pcidsk.h"
+
+CPL_CVSID("$Id: pcidsktiledrasterband.cpp,v 1.4 2005/06/09 19:01:40 fwarmerdam Exp $");
+
+/************************************************************************/
+/*                           PCIDSKRasterBand()                         */
+/************************************************************************/
+
+PCIDSKTiledRasterBand::PCIDSKTiledRasterBand( PCIDSKDataset *poDS, 
+                                              int nBand, int nImage )
+
+{
+    poPDS = poDS;
+    this->poDS = poDS;
+
+    this->nBand = nBand;
+    this->nImage = nImage;
+
+    nOverviewCount = 0;
+    papoOverviews = NULL;
+
+    nBlocks = 0;
+    panBlockOffset = NULL;
+    
+    if( !BuildBlockMap() )
+        return;
+
+/* -------------------------------------------------------------------- */
+/*      Load and parse image header.  This is the image header          */
+/*      within the tiled image data.                                    */
+/* -------------------------------------------------------------------- */
+    char achBData[128];
+
+    SysRead( 0, 128, achBData );
+ 
+    nRasterXSize = (int) CPLScanLong(achBData + 0, 8);
+    nRasterYSize = (int) CPLScanLong(achBData + 8, 8);
+    nBlockXSize = (int) CPLScanLong(achBData + 16, 8);
+    nBlockYSize = (int) CPLScanLong(achBData + 24, 8);
+    
+    eDataType = poPDS->PCIDSKTypeToGDAL( achBData + 32 );
+}
+
+/************************************************************************/
+/*                       ~PCIDSKTiledRasterBand()                       */
+/************************************************************************/
+
+PCIDSKTiledRasterBand::~PCIDSKTiledRasterBand()
+{
+    FlushCache();
+
+    int i;
+
+    for( i = 0; i < nOverviewCount; i++ )
+        delete papoOverviews[i];
+    CPLFree( papoOverviews );
+
+    CPLFree( panBlockOffset );
+    CPLFree( panTileOffset );
+    CPLFree( panTileSize );
+}
+
+/************************************************************************/
+/*                           BuildBlockMap()                            */
+/************************************************************************/
+
+int PCIDSKTiledRasterBand::BuildBlockMap()
+
+{
+    nBlocks = 0;
+    panBlockOffset = NULL;
+
+    nTileCount = 0;
+    panTileOffset = NULL;
+    panTileSize = NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Read the whole block map segment.                               */
+/* -------------------------------------------------------------------- */
+    int  nBMapSize;
+    char *pachBMap;
+
+    if( poPDS->nBlockMapSeg < 1 )
+        return FALSE;
+
+    nBMapSize = poPDS->panSegSize[poPDS->nBlockMapSeg-1];
+    pachBMap = (char *) CPLCalloc(nBMapSize+1,1);
+    
+    if( !poPDS->SegRead( poPDS->nBlockMapSeg, 0, nBMapSize, pachBMap ) )
+        return FALSE;
+    
+/* -------------------------------------------------------------------- */
+/*      Parse the header.                                               */
+/* -------------------------------------------------------------------- */
+    int nMaxBlocks = (int) CPLScanLong(pachBMap + 18,8);
+
+    if( !EQUALN(pachBMap,"VERSION",7) )
+        return FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Build a "back link" map for this image's blocks.  We need       */
+/*      this to positively identify the first block in the chain.       */
+/* -------------------------------------------------------------------- */
+    int *panBackLink;
+    int i, nLastBlock = -1;
+
+    panBackLink = (int *) CPLCalloc(sizeof(int),nMaxBlocks);
+    for( i = 0; i < nMaxBlocks; i++ )
+        panBackLink[i] = -1;
+
+    for( i = 0; i < nMaxBlocks; i++ )
+    {
+        char *pachEntry = pachBMap + i * 28 + 512;
+        int nThisImage = (int) CPLScanLong(pachEntry + 12,8);
+        int nNextBlock = (int) CPLScanLong(pachEntry + 20,8);
+
+        if( nThisImage != nImage )
+            continue;
+
+        if( nNextBlock == -1 )
+            nLastBlock = i;
+        else
+            panBackLink[nNextBlock] = i;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Track back through chain to identify the first entry (while     */
+/*      counting).                                                      */
+/* -------------------------------------------------------------------- */
+    int iBlock = nLastBlock;
+
+    nBlocks = 1;
+    while( panBackLink[iBlock] != -1 )
+    {
+        nBlocks++;
+        iBlock = panBackLink[iBlock];
+    }
+
+    CPLFree( panBackLink );
+    panBlockOffset = (vsi_l_offset *) CPLMalloc(sizeof(vsi_l_offset)*nBlocks);
+
+/* -------------------------------------------------------------------- */
+/*      Process blocks, transforming to absolute offsets in the         */
+/*      PCIDSK file.                                                    */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < nBlocks; i++ )
+    {
+        char *pachEntry = pachBMap + iBlock * 28 + 512;
+        int nBDataSeg = CPLScanLong( pachEntry + 0, 4 );
+        int nBDataBlock = CPLScanLong( pachEntry + 4, 8 );
+
+        CPLAssert( poPDS->panSegType[nBDataSeg-1] == 182 );
+
+        panBlockOffset[i] = 
+            ((vsi_l_offset) nBDataBlock) * 8192
+            + poPDS->panSegOffset[nBDataSeg-1] + 1024;
+
+        iBlock = (int) CPLScanLong( pachEntry + 20, 8 );
+    }            
+
+    CPLFree( pachBMap );
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                            BuildTileMap()                            */
+/************************************************************************/
+
+int PCIDSKTiledRasterBand::BuildTileMap()
+
+{
+    if( nTileCount )
+        return TRUE;
+
+    int nBPR = (nRasterXSize + nBlockXSize - 1) / nBlockXSize;
+    int nBPC = (nRasterYSize + nBlockYSize - 1) / nBlockYSize;
+
+    nTileCount = nBPR * nBPC;
+    panTileOffset = (vsi_l_offset *) 
+        CPLCalloc(sizeof(vsi_l_offset),nTileCount);
+    panTileSize = (int *) CPLCalloc(sizeof(int),nTileCount);
+
+    char *pachTileInfo = (char *) CPLMalloc(20 * nTileCount);
+    if( !SysRead( 128, 20 * nTileCount, pachTileInfo ) )
+        return FALSE;
+
+    for( int iTile = 0; iTile < nTileCount; iTile++ )
+    {
+        panTileOffset[iTile] = (vsi_l_offset)
+            CPLScanUIntBig( pachTileInfo+12*iTile, 12 );
+        panTileSize[iTile] = (int)
+            CPLScanLong( pachTileInfo+12*nTileCount+8*iTile, 8 );
+    }
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr PCIDSKTiledRasterBand::IReadBlock( int nBlockX, int nBlockY, 
+                                          void *pData )
+
+{
+    int iTile;
+
+    if( !BuildTileMap() )
+        return CE_Failure;
+    
+    int nBPR = (nRasterXSize + nBlockXSize - 1) / nBlockXSize;
+
+    iTile = nBlockX + nBlockY * nBPR;
+
+    if( !SysRead( panTileOffset[iTile], panTileSize[iTile], pData ) )
+        return CE_Failure;
+
+
+/* -------------------------------------------------------------------- */
+/*      PCIDSK multibyte data is always big endian.  Swap if needed.    */
+/* -------------------------------------------------------------------- */
+#ifdef CPL_LSB
+    int   nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
+    GDALSwapWords( pData, nWordSize, nBlockXSize * nBlockYSize, nWordSize );
+#endif
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                              SysRead()                               */
+/************************************************************************/
+
+int PCIDSKTiledRasterBand::SysRead( vsi_l_offset nOffset, 
+                                    int nSize, 
+                                    void *pData )
+
+{
+    int iReadSoFar = 0;
+
+    while( iReadSoFar < nSize )
+    {
+        int iBlock;
+        vsi_l_offset nNextOffset = nOffset + iReadSoFar;
+        vsi_l_offset nRealOffset;
+        int          nOffsetInBlock, nThisReadBytes;
+        
+        iBlock = (int) (nNextOffset >> 13);
+        nOffsetInBlock = (nNextOffset & 0x1fff);
+
+        nRealOffset = panBlockOffset[iBlock] + nOffsetInBlock;
+        
+        nThisReadBytes = MIN(nSize - iReadSoFar,8192 - nOffsetInBlock);
+        
+        if( VSIFSeekL( poPDS->fp, nRealOffset, SEEK_SET ) != 0 )
+            return 0;
+
+        if( VSIFReadL( ((char *) pData) + iReadSoFar, 1, nThisReadBytes,
+                       poPDS->fp ) != (size_t) nThisReadBytes )
+            return 0;
+
+        iReadSoFar += nThisReadBytes;
+    }
+
+    return nSize;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/.cvsignore b/Utilities/GDAL/frmts/pcraster/.cvsignore
new file mode 100644
index 0000000000..455336d2ee
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/.cvsignore
@@ -0,0 +1,2 @@
+.libs
+*.lo
diff --git a/Utilities/GDAL/frmts/pcraster/GNUmakefile b/Utilities/GDAL/frmts/pcraster/GNUmakefile
new file mode 100644
index 0000000000..458077bdac
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/GNUmakefile
@@ -0,0 +1,28 @@
+
+include ../../GDALmake.opt
+
+CPPFLAGS := $(XTRA_OPT) $(GDAL_INCLUDE) $(CPPFLAGS)
+
+ifeq ($(PCRASTER_SETTING),internal)
+CPPFLAGS += -DUSE_IN_GDAL -Ilibcsf
+OBJ = _getcell.o _getrow.o _gsomece.o _putcell.o _rputrow.o angle.o attravai.o attrsize.o cellsize.o create2.o csfglob.o csfsup.o delattr.o dumconv.o endian.o filename.o gattrblk.o gattridx.o gcellrep.o gdattype.o getattr.o getx0.o gety0.o ggisfid.o gmaxval.o gminval.o gnrcols.o gnrrows.o gproj.o gputproj.o gvalscal.o gvartype.o gversion.o ismv.o kernlcsf.o legend.o mclose.o mopen.o moreattr.o mperror.o pgisfid.o pmaxval.o pminval.o putallmv.o putattr.o putsomec.o putx0.o puty0.o pvalscal.o rattrblk.o rcomp.o rcoords.o rdup2.o reseterr.o rextend.o rmalloc.o rrowcol.o ruseas.o setangle.o setmv.o setvtmv.o strconst.o strpad.o swapio.o trackmm.o vs2.o vsdef.o vsis.o vsvers.o wattrblk.o
+endif
+
+OBJ += pcrasterdataset.o pcrastermisc.o pcrasterrasterband.o pcrasterutil.o
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o libcsf/*.o $(O_OBJ)
+	rm -fR html
+
+../o/%.o: libcsf/%.c
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@
+
+install-obj:	$(O_OBJ)
+
+docs:
+	doxygen doxygen.cfg
+
+ctags:
+	ctags *cpp *h
diff --git a/Utilities/GDAL/frmts/pcraster/doxygen.cfg b/Utilities/GDAL/frmts/pcraster/doxygen.cfg
new file mode 100644
index 0000000000..df6c96147b
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/doxygen.cfg
@@ -0,0 +1,1161 @@
+# Doxyfile 1.3.9.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = 
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = 
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of source 
+# files, where putting all generated files in the same directory would otherwise 
+# cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, 
+# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, 
+# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, 
+# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, 
+# Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# This tag can be used to specify the encoding used in the generated output. 
+# The encoding is not always determined by the language that is chosen, 
+# but also whether or not the output is meant for Windows or non-Windows users. 
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 
+# forces the Windows encoding (this is the default for the Windows binary), 
+# whereas setting the tag to NO uses a Unix-style encoding (the default for 
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING   = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is used 
+# as the annotated text. Otherwise, the brief description is used as-is. If left 
+# blank, the following values are used ("$name" is automatically replaced with the 
+# name of the entity): "The $name class" "The $name widget" "The $name file" 
+# "is" "provides" "specifies" "contains" "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 
+# members of a class in the documentation of that class as if those members were 
+# ordinary class members. Constructors, destructors and assignment operators of 
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = 
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like the Qt-style comments (thus requiring an 
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
+# only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance, namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation.
+
+SHOW_DIRECTORIES       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = 
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp 
+# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm
+
+FILE_PATTERNS          = 
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 4
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse the 
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or 
+# super classes. Setting the tag to NO turns the diagrams off. Note that this 
+# option is superseded by the HAVE_DOT option below. This is only a fallback. It is 
+# recommended to install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 
+# generate a call dependency graph for every global function or class method. 
+# Note that enabling this option will significantly increase the time of a run. 
+# So in most cases it will be better to enable call graphs for selected 
+# functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes that 
+# lay further from the root node will be omitted. Note that setting this option to 
+# 1 or 2 may greatly reduce the computation time needed for large code bases. Also 
+# note that a graph may be further truncated if the graph's image dimensions are 
+# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). 
+# If 0 is used for the depth value (the default), the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/AUTHORS b/Utilities/GDAL/frmts/pcraster/libcsf/AUTHORS
new file mode 100644
index 0000000000..764f9661a4
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/AUTHORS
@@ -0,0 +1,10 @@
+DESIGN, IMPLEMENTATION and MAINTENANCE:
+ Cees Wesseling, Willem van Deursen
+ Utrecht University,              1992-1997
+ PCRaster Environmental Software, 1997-present
+PORTING ISSUES:
+ Edzer Pebesma
+ Utrecht University
+PACKAGING:
+ Kor de Jong
+ Utrecht University
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/COPYING b/Utilities/GDAL/frmts/pcraster/libcsf/COPYING
new file mode 100644
index 0000000000..761a7d2b90
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/COPYING
@@ -0,0 +1,30 @@
+Copyright (c) 1997-2003, Utrecht University
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* 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.
+
+* Neither the name of Utrecht University nor the names of its contributors
+  may be used to endorse or promote products derived from this software
+  without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"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 COPYRIGHT
+OWNER OR CONTRIBUTORS 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.
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/README b/Utilities/GDAL/frmts/pcraster/libcsf/README
new file mode 100644
index 0000000000..5a20675199
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/README
@@ -0,0 +1,11 @@
+Hi!
+
+For information about how to use the library see csf.pdf which can found
+at http://pcraster.geog.uu.nl. More information about PCRaster can be
+found at: http://www.pcraster.nl and http://pcraster.geog.uu.nl.
+
+Questions can be directed to the PCRaster mailing list:
+http://pcraster.geog.uu.nl/support.html
+
+Have fun!
+PCRaster research and development team.
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/_getcell.c b/Utilities/GDAL/frmts/pcraster/libcsf/_getcell.c
new file mode 100644
index 0000000000..15089b3919
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/_getcell.c
@@ -0,0 +1,29 @@
+/*
+ * _getcell.c
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* read one cell from a CSF raster file
+ * RgetCell reads one cell value from a
+ * file. 
+ * returns
+ * 1 if cell is successfully read,
+ * 0 if not
+ *
+ * example
+ * .so examples/csfdump1.tr
+ */
+size_t RgetCell(
+	MAP *map,        /* map handle */
+	size_t rowNr,     /* row number of cell */
+	size_t colNr,     /* column number of cell */
+	void *cellValue) /* write-only. buffer, large enough to hold
+	                  * the value of the cell in the file and app
+	                  * cell representation
+	                  */
+{
+	return RgetSomeCells(map, 
+				( (map->raster.nrCols) * rowNr) + colNr,
+	             		(size_t)1, cellValue);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/_getrow.c b/Utilities/GDAL/frmts/pcraster/libcsf/_getrow.c
new file mode 100644
index 0000000000..5c52858415
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/_getrow.c
@@ -0,0 +1,28 @@
+/*
+ * _getrow.c
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* read one row from a CSF raster file
+ * RgetRow reads one row of cells from a
+ * file. 
+ * returns
+ * Number of cells successfully read
+ *
+ * example
+ * .so examples/_row.tr
+ */
+size_t RgetRow(
+	MAP *map,        /* map handle */
+	size_t rowNr,     /* row number to be read */
+	void *buf)       /* write-only. buffer large enough to hold
+	                  * cell values of one row in both the file
+	                  * and in-app cell representation
+	                  */
+{
+	return RgetSomeCells(map,
+				map->raster.nrCols*rowNr,
+				(size_t)map->raster.nrCols, 
+				buf) ;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/_gsomece.c b/Utilities/GDAL/frmts/pcraster/libcsf/_gsomece.c
new file mode 100644
index 0000000000..2cfd99f45f
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/_gsomece.c
@@ -0,0 +1,41 @@
+/*
+ * _gsomece.c
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* read a stream of cells
+ * RgetSomeCells views a raster as one linear stream of
+ * cells, with row i+1 placed after row i. 
+ * In this stream any sequence can be read by specifying an
+ * offset and the number of cells to be read
+ * returns the number of cells read, just as fread
+ *
+ * example
+ * .so examples/somecell.tr
+ */
+size_t RgetSomeCells(
+	MAP *map,	/* map handle */
+	size_t offset,   /* offset from pixel (row,col) = (0,0) */
+	size_t nrCells,  /* number of cells to be read */
+	void *buf)/* write-only. Buffer large enough to
+                   * hold nrCells cells in the in-file cell representation
+                   * or the in-app cell representation.
+                   */
+{
+
+	CSF_FADDR  readAt;
+	size_t cellsRead;
+	UINT2  inFileCR = RgetCellRepr(map);
+
+	offset <<= LOG_CELLSIZE(inFileCR);
+	readAt = ADDR_DATA + (CSF_FADDR)offset;
+	fseek(map->fp, (long)readAt, SEEK_SET);
+	cellsRead = map->read(buf, (size_t)CELLSIZE(inFileCR),
+	(size_t)nrCells, map->fp);
+
+	PRECOND(map->file2app != NULL);
+	map->file2app(nrCells, buf);
+
+	return(cellsRead);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/_putcell.c b/Utilities/GDAL/frmts/pcraster/libcsf/_putcell.c
new file mode 100644
index 0000000000..4f749a1685
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/_putcell.c
@@ -0,0 +1,31 @@
+/*
+ * _putcell.c
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* write one cell to a CSF raster file
+ * RputCell writes one cell value to a
+ * file. 
+ * returns
+ * 1 if cell is successfully written, not 1 if not.
+ *
+ * example
+ * .so examples/rawbin.tr
+ */
+size_t RputCell(
+MAP *map,         /* map handle */
+size_t rowNr,      /* Row number of cell */
+size_t colNr,      /* Column number of cell */
+void *cellValue)  /* read-write. Buffer large enough to
+                   * hold one cell in the in-file cell representation
+                   * or the in-app cell representation.
+                   * If these types are not equal then the buffer is
+                   * converted from the in-app to the in-file 
+                   * cell representation. 
+                   */
+{
+	return RputSomeCells(map, 
+	                      (map->raster.nrCols * rowNr) + colNr,
+	                      (size_t)1, cellValue) ;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/_rputrow.c b/Utilities/GDAL/frmts/pcraster/libcsf/_rputrow.c
new file mode 100644
index 0000000000..05e9fec5c1
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/_rputrow.c
@@ -0,0 +1,31 @@
+/*
+ * _rputrow.c
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+
+/* write one row to a CSF raster file
+ * RputRow writes one row of cell values to a
+ * file.
+ * returns
+ * number of cells successfully written. Should be equal
+ * to the number of columns if everything is OK.
+ *
+ * example
+ * .so examples/_row.tr
+ */
+size_t RputRow(
+MAP *map,         /* map handle */
+size_t rowNr,      /* Row number of row */
+void *buf)        /* read-write. Buffer large enough to
+                   * hold one row in the in-file cell representation
+                   * or the in-app cell representation.
+                   * If these types are not equal then the buffer is
+                   * converted from the in-app to the in-file 
+                   * cell representation. 
+                   */
+{
+	return RputSomeCells(map, (map->raster.nrCols)*rowNr,
+ 	                          (size_t)map->raster.nrCols, buf) ;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/angle.c b/Utilities/GDAL/frmts/pcraster/libcsf/angle.c
new file mode 100644
index 0000000000..fb9775c9b0
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/angle.c
@@ -0,0 +1,60 @@
+
+/*
+ * angle.c 
+ */
+
+/********/
+/* USES */
+/********/
+
+/* libs ext. <>, our ""  */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* M_PI */
+#include <math.h> 
+
+#ifndef M_PI
+# define M_PI        ((double)3.14159265358979323846)
+#endif
+
+/* put new angle 
+ * RputAngle changes the angle 
+ * of the map.
+ * returns new angle or -1 in case of an error.
+ *
+ * Merrno
+ * NOACCESS
+ * BAD_ANGLE
+ */
+REAL8 RputAngle(
+	MAP *map,    /* map handle */
+	REAL8 angle) /* new angle */
+{
+	CHECKHANDLE_GOTO(map, error);
+	if(! WRITE_ENABLE(map))
+	{
+		 M_ERROR(NOACCESS);
+		 goto error;
+	}
+	if ((0.5*-M_PI) >= angle || angle >= (0.5*M_PI))
+	{
+		 M_ERROR(BAD_ANGLE);
+		 goto error;
+	}
+	map->raster.angle = angle;
+
+	return(angle);
+error:	return(-1.0);
+}
+
+/* get new angle 
+ * RgetAngle returns the angle 
+ * of the map.
+ * returns angle of the map
+ */
+REAL8 RgetAngle(
+	const MAP *map) /* map handle */
+{
+	return map->raster.angle;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/attravai.c b/Utilities/GDAL/frmts/pcraster/libcsf/attravai.c
new file mode 100644
index 0000000000..25c2dedd59
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/attravai.c
@@ -0,0 +1,26 @@
+/*
+ * attravai.c
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* check if an attribute is available
+ * MattributeAvail search for the given id in the map.
+ *
+ * returns
+ *  0 if the attribute is not available,
+ *  nonzero if the attribute is available
+ *
+ * Merrno
+ * ILLHANDLE
+ */
+int MattributeAvail(
+	MAP *m,    /* map handle */
+	CSF_ATTR_ID id)  /* identification of attribute */
+{
+	ATTR_CNTRL_BLOCK b;
+
+     	if (! CsfIsValidMap(m))
+	 	return 0;
+	return(CsfGetAttrBlock(m, id, &b) != 0);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/attrsize.c b/Utilities/GDAL/frmts/pcraster/libcsf/attrsize.c
new file mode 100644
index 0000000000..3fb80f4246
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/attrsize.c
@@ -0,0 +1,21 @@
+/*
+ * attrsize.c
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* get the size of an attribute (LIBRARY_INTERNAL)
+ * returns
+ * 0 if the attribute is not available,
+ * or the nonzero size if the attribute is available.
+ */
+size_t CsfAttributeSize(
+	 MAP   *m,    /* map handle */
+	 CSF_ATTR_ID id)    /* identification of attribute */
+{
+	ATTR_CNTRL_BLOCK b;
+
+	if (CsfGetAttrBlock(m, id, &b) != 0)
+		return b.attrs[CsfGetAttrIndex(id, &b)].attrSize;
+        return 0;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/cellsize.c b/Utilities/GDAL/frmts/pcraster/libcsf/cellsize.c
new file mode 100644
index 0000000000..307166907d
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/cellsize.c
@@ -0,0 +1,111 @@
+
+/*
+ * cellsize.c 
+   $Log: cellsize.c,v $
+   Revision 1.3  2006/02/07 10:17:15  kdejong
+   Fixed endian compile problem
+   some rcs issues of Kor, I guess
+   Checked in by cees (cees@pcraster.nl) on account of Kor
+
+   Revision 1.3  2005/10/03 07:23:00  kor
+   Removed rcs id string
+
+   Revision 1.2  2000/02/05 21:25:48  cees
+   added LOCATION_ATTRIBUTER struct
+
+   Revision 1.1.1.1  2000/01/04 21:04:12  cees
+   Initial import Cees
+
+   Revision 2.0  1996/05/23 13:16:26  cees
+   csf2clean
+
+   Revision 1.1  1996/05/23 13:11:49  cees
+   Initial revision
+
+   Revision 1.2  1995/11/01 17:23:03  cees
+   .
+
+ * Revision 1.1  1994/09/08  17:16:23  cees
+ * Initial revision
+ *
+ */
+
+/********/
+/* USES */
+/********/
+
+/* libs ext. <>, our ""  */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* global header (opt.) and cellsize's prototypes "" */
+
+
+/* headers of this app. modules called */ 
+
+/***************/
+/* EXTERNALS   */
+/***************/
+
+/**********************/ 
+/* LOCAL DECLARATIONS */
+/**********************/ 
+
+/*********************/ 
+/* LOCAL DEFINITIONS */
+/*********************/ 
+
+/******************/
+/* IMPLEMENTATION */
+/******************/
+
+
+/* get cell size 
+ * returns the cell size or -1 in case of an error
+ *
+ * Merrno
+ *  ILL_CELLSIZE
+ *  ILLHANDLE
+ */
+REAL8 RgetCellSize(
+	const MAP *map) /* map handle */
+{
+	CHECKHANDLE(map);
+	if ( map->raster.cellSize != map->raster.cellSizeDupl)
+	{
+		M_ERROR(ILL_CELLSIZE);
+		return -1;
+	}
+
+	return(map->raster.cellSize);
+}
+
+/* put cell size
+ * returns the new cell size or -1
+ * in case of an error.
+ *
+ * Merrno
+ * ILLHANDLE
+ * NOACCESS
+ * ILL_CELLSIZE
+ */
+REAL8 RputCellSize(
+	MAP *map, /* map handle */
+	REAL8 cellSize) /* new cell size */
+{
+	CHECKHANDLE_GOTO(map, error);
+	if(! WRITE_ENABLE(map))
+	{
+		M_ERROR(NOACCESS);
+		goto error;
+	}
+	if (cellSize <= 0.0)
+	{
+		M_ERROR(ILL_CELLSIZE);
+		goto error;
+	}
+	map->raster.cellSize     = cellSize;
+	map->raster.cellSizeDupl = cellSize;
+	return(cellSize);
+error:  return(-1.0);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/create2.c b/Utilities/GDAL/frmts/pcraster/libcsf/create2.c
new file mode 100644
index 0000000000..a2017f1808
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/create2.c
@@ -0,0 +1,222 @@
+#include "csf.h" 
+#include "csfimpl.h" 
+
+#include <errno.h>
+#include <string.h>
+
+/* M_PI */
+#include <math.h> 
+#ifndef M_PI
+# define M_PI        ((double)3.14159265358979323846)
+#endif
+
+/* 
+ * Create a new CSF-Raster-file
+ * The Rcreate function
+ * creates a new CSF-Raster-file of nrRows by nrCols where each
+ * cell is of type cellRepr. If the file already exists its
+ * contents is destroyed. The value of
+ * the pixels is undefined. MinMaxStatus is MM_KEEPTRACK. The
+ * access mode is M_READ_WRITE. 
+ * It is not
+ * known if a file is created after a NOSPACE message.
+ * Returns
+ * if the file is created successfully, Rcreate returns
+ * a map handle. In case of an error Rcreate returns NULL.
+ *
+ * Merrno 
+ * NOCORE, BAD_CELLREPR, BAD_PROJECTION, OPENFAILED, NOSPACE.
+ * CONFL_CELLREPR and BAD_VALUESCALE will generate a failed assertion in DEBUG mode.
+ *
+ * Example:  
+ * .so examples/create2.tr
+ *
+ */
+
+MAP *Rcreate(
+	const char *fileName,  /* name of the file to be created */
+	size_t nrRows,          /* the number of rows */
+	size_t nrCols,          /* the number of columns */
+	CSF_CR cellRepr,       /* the cell representation must be complaint with the data type
+	                        */
+	CSF_VS dataType,      /* a.k.a. the value scale.
+	                       */
+	CSF_PT projection,     /* 
+	                        */
+	REAL8 xUL,              /* x co-ordinate of upper left */
+	REAL8 yUL,              /* y co-ordinate of upper left */
+	REAL8 angle,           /* counter clockwise rotation angle
+	                        * of the grid top compared to the
+	                        * x-axis in radians. Legal value are 
+	                        * between -0.5 pi and 0.5 pi
+	                        */
+	REAL8 cellSize)        /* cell size of pixel */
+{
+	MAP    *newMap;
+	size_t  fileSize;
+	char    crap = 0;
+
+	if (! CsfIsBootedCsfKernel())
+		CsfBootCsfKernel();
+
+	newMap = (MAP *)CSF_MALLOC(sizeof(MAP));
+	if (newMap == NULL)
+	{
+		M_ERROR(NOCORE);
+		goto errorMapAlloc;
+	}
+	newMap->fileName = (char *)CSF_MALLOC(strlen(fileName)+1);
+	if (newMap->fileName == NULL)
+	{
+		M_ERROR(NOCORE);
+		goto errorNameAlloc;
+	}
+
+	if (!(
+		cellRepr == CR_INT4 ||
+		cellRepr == CR_UINT1 ||
+		cellRepr == CR_REAL4 ||
+		cellRepr == CR_REAL8 ))
+	{
+		M_ERROR(BAD_CELLREPR);
+		goto error_notOpen;
+	}
+
+	switch(dataType) {
+	 case VS_BOOLEAN: 
+	 case VS_LDD: 
+	 	if (cellRepr != CR_UINT1)
+		{
+			PROG_ERROR(CONFL_CELLREPR);
+			goto error_notOpen;
+		}
+		break;
+	case VS_NOMINAL: 
+	case VS_ORDINAL:
+	 	if (IS_REAL(cellRepr))
+		{
+			PROG_ERROR(CONFL_CELLREPR);
+			goto error_notOpen;
+		}
+		break;
+	case VS_SCALAR:
+	case VS_DIRECTION:
+	 	if (!IS_REAL(cellRepr))
+		{
+			PROG_ERROR(CONFL_CELLREPR);
+			goto error_notOpen;
+		}
+		break;
+	default:
+		PROG_ERROR(BAD_VALUESCALE);
+		goto error_notOpen;
+	}
+
+	if (cellSize <= 0.0)
+	{
+		M_ERROR(ILL_CELLSIZE);
+		goto error_notOpen;
+	}
+
+	if ((0.5*-M_PI) >= angle || angle >= (0.5*M_PI))
+	{
+		M_ERROR(BAD_ANGLE);
+		goto error_notOpen;
+	}
+
+	newMap->fileAccessMode = M_READ_WRITE;
+	(void)strcpy(newMap->fileName, fileName);
+
+	newMap->fp = fopen (fileName, S_CREATE);
+	if(newMap->fp == NULL)
+	{	
+	   /* we should analyse the errno parameter
+	    * here to get the reason
+	    */
+		M_ERROR(OPENFAILED);
+		goto error_notOpen;
+	}
+	/*
+	   fflush(newMap->fp); WHY? 
+	 */
+
+	(void)memset(&(newMap->main),0, sizeof(CSF_MAIN_HEADER));
+	(void)memset(&(newMap->raster),0, sizeof(CSF_RASTER_HEADER));
+	/* put defaults values     */
+
+	/* assure signature is padded with 0x0 */
+	(void)memset(newMap->main.signature, 0x0, (size_t)CSF_SIG_SPACE);
+	(void)strcpy(newMap->main.signature, CSF_SIG);
+	newMap->main.version = CSF_VERSION_2;
+	newMap->main.gisFileId = 0;
+	newMap->main.projection = PROJ_DEC_T2B(projection);
+	newMap->main.attrTable = 0; /* initially no attributes */
+	newMap->main.mapType = T_RASTER;
+
+	/* write endian mode current machine: */
+	newMap->main.byteOrder= ORD_OK;
+
+#ifdef DEBUG
+	newMap->read  = (CSF_READ_FUNC)CsfReadPlain;
+	newMap->write = (CSF_READ_FUNC)CsfWritePlain;
+#else
+	newMap->read  = (CSF_READ_FUNC)fread;
+	newMap->write = (CSF_READ_FUNC)fwrite;
+#endif
+
+	newMap->raster.valueScale = dataType;
+	newMap->raster.cellRepr = cellRepr;
+	CsfSetVarTypeMV( &(newMap->raster.minVal), cellRepr);
+	CsfSetVarTypeMV( &(newMap->raster.maxVal), cellRepr);
+	newMap->raster.xUL = xUL;
+	newMap->raster.yUL = yUL;
+	newMap->raster.nrRows = nrRows;
+	newMap->raster.nrCols = nrCols;
+	newMap->raster.cellSize = cellSize;
+	newMap->raster.cellSizeDupl = cellSize;
+	newMap->raster.angle = angle;
+	CsfFinishMapInit(newMap);
+
+	/*  set types to value cellRepr 
+	newMap->types[STORED_AS]= (UINT1)newMap->raster.cellRepr;
+	newMap->types[READ_AS]  = (UINT1)newMap->raster.cellRepr;
+	*/
+	newMap->appCR  = (UINT1)newMap->raster.cellRepr;
+	newMap->app2file = CsfDummyConversion;
+	newMap->file2app = CsfDummyConversion;
+
+	/*  make file the size of the header and data */
+	fileSize   = nrRows*nrCols;
+	fileSize <<= LOG_CELLSIZE(cellRepr);
+	fileSize  += ADDR_DATA;
+
+	/* enlarge the file to the length needed by seeking and writing
+	one byte of crap */
+
+	if ( fseek(newMap->fp, (long)(fileSize-1),SEEK_SET) || /* fsetpos() is better */
+	    newMap->write(&crap, (size_t)1, (size_t)1, newMap->fp) != 1 )
+	{
+		M_ERROR(NOSPACE);
+		goto error_open;
+	}
+	(void)fflush(newMap->fp);
+	if ( ftell(newMap->fp) != (long)fileSize)
+	{
+		M_ERROR(NOSPACE);
+		goto error_open;
+	}
+
+	newMap->minMaxStatus = MM_KEEPTRACK;
+
+	CsfRegisterMap(newMap);
+
+	return(newMap);
+error_open: 
+	(void)fclose(newMap->fp);
+error_notOpen: 
+	CSF_FREE(newMap->fileName);
+errorNameAlloc:
+        CSF_FREE(newMap);
+errorMapAlloc:
+	return(NULL);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/csf.h b/Utilities/GDAL/frmts/pcraster/libcsf/csf.h
new file mode 100644
index 0000000000..c9cb3fe6a9
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/csf.h
@@ -0,0 +1,361 @@
+#ifndef INCLUDED_CSF
+#define INCLUDED_CSF
+
+#ifdef CSF_V1
+# error new include file used while CSF_V1 is defined
+#endif
+
+#ifndef INCLUDED_CSFTYPES
+#include "csftypes.h"
+#define INCLUDED_CSFTYPES
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif 
+
+
+
+#include <stdio.h>
+#include "csfattr.h"
+
+
+/*****************************************************************/
+/*                                                               */
+/*  RUU CROSS SYSTEM MAP FORMAT                                  */
+/*  VERSION 2                                                    */
+/*****************************************************************/
+
+extern int Merrno; /* declared in csfglob.c */
+
+/* CSF_VAR_TYPE can hold every possible */
+/* data type                        */
+typedef REAL8 CSF_VAR_TYPE;
+
+/* values for CSF_MAIN_HEADER.mapType :  */
+#define T_RASTER 1
+
+/* CSF_FADDR can hold any location in the file 
+ * CSF_FADDR is always an offset from the begin 
+ * (0) of the file                                
+ */
+typedef UINT4 CSF_FADDR32;
+typedef long  CSF_FADDR;
+
+/* value for first 27 bytes of MAIN_HEADER.signature */
+#define CSF_SIG  "RUU CROSS SYSTEM MAP FORMAT"
+#define CSF_SIZE_SIG (sizeof(CSF_SIG)-1)
+
+#define CSF_SIG_SPACE ((size_t)32)
+
+typedef struct CSF_MAIN_HEADER
+{
+ char        signature[CSF_SIG_SPACE];
+ UINT2       version;
+ UINT4       gisFileId;
+ UINT2       projection;
+ CSF_FADDR32 attrTable;
+ UINT2       mapType;
+ UINT4       byteOrder;
+} CSF_MAIN_HEADER;
+
+/******************************************************************/
+/* Definition of the second header                                */
+/******************************************************************/
+/* CSF_MAIN_HEADER.mapType decides which structure is                */
+/* used as second header                                          */
+/******************************************************************/
+/******************************************************************/
+/* Definition of the raster header                                */
+/******************************************************************/
+typedef struct CSF_RASTER_HEADER
+{
+   /* see #def's of VS_*
+    */
+   UINT2    valueScale;
+   /* see #def's of CR_*
+    */
+   UINT2    cellRepr;
+
+  /* minVal holds a value equal or less than the
+   * minimum value in the cell matrix
+   */
+   CSF_VAR_TYPE minVal;
+
+  /* maxVal holds a value equal or greater than the
+   * maximum value in the cell matrix
+   */
+   CSF_VAR_TYPE maxVal;
+
+  /* co-ordinate of upper left corner
+   */
+   REAL8    xUL;
+   REAL8    yUL;
+
+   UINT4    nrRows;
+   UINT4    nrCols;
+
+  /* CSF version 1 problem: X and Y cellsize 
+   * could differ, no longer the case
+   * even though cellSizeX and cellSizeY
+   * are stored separate, they should be equal
+   * all apps. are working with square pixels
+   */
+   REAL8    cellSize;     /* was cellSizeX */
+   REAL8    cellSizeDupl; /* was cellSizeY */
+
+  /* new in version 2
+   * rotation angle of grid
+   */
+   REAL8    angle;
+
+  /* remainder is not part of
+   * file header 
+   */
+  /* cosine and sine of
+   * the angle are computed
+   * when opening or creating
+   * the file
+   */
+  REAL8  angleCos;
+  REAL8  angleSin;
+  CSF_PT projection;   /* copy of main header */
+} CSF_RASTER_HEADER;
+
+/*******************************************************************/
+/*  mode values               */
+/*******************************************************************/
+
+/* bit-mapped values: */
+enum MOPEN_PERM {
+  M_READ=1,      /* open read only */
+  M_WRITE=2,     /* open write only */
+  M_READ_WRITE=3 /* open for both reading and writing */
+};
+
+
+
+/****************************************************************/
+/* Error listing return messages              */
+/****************************************************************/
+
+/* values for errolist  */
+/* happens frequently 
+ * assure 0 value
+ * bogs on mingw
+ * # if NOERROR != 0
+ * #  error EXPECT NOERROR TO BE 0
+ */
+# define NOERROR         0
+
+#define OPENFAILED       1
+#define NOT_CSF          2
+#define BAD_VERSION      3
+#define BAD_BYTEORDER    4
+#define NOCORE           5
+#define BAD_CELLREPR     6
+#define NOACCESS         7
+#define ROWNR2BIG        8
+#define COLNR2BIG        9
+#define NOT_RASTER      10
+#define BAD_CONVERSION  11
+#define NOSPACE         12
+#define WRITE_ERROR     13
+#define ILLHANDLE       14
+#define READ_ERROR      15
+#define BADACCESMODE    16
+#define ATTRNOTFOUND    17
+#define ATTRDUPL        18
+#define ILL_CELLSIZE    19
+#define CONFL_CELLREPR  20
+#define BAD_VALUESCALE  21
+#define XXXXXXXXXXXX    22
+#define BAD_ANGLE       23
+#define CANT_USE_AS_BOOLEAN    24
+#define CANT_USE_WRITE_BOOLEAN 25
+#define CANT_USE_WRITE_LDD     26
+#define CANT_USE_AS_LDD        27
+#define CANT_USE_WRITE_OLDCR   28
+#define ILLEGAL_USE_TYPE       29
+/* number of errors  */
+#define ERRORNO                30
+
+typedef void (*CSF_CONV_FUNC)(size_t, void *);
+/* conversion function for reading
+ * and writing
+ */
+typedef size_t (*CSF_WRITE_FUNC)(void *buf, size_t size, size_t n, FILE  *f);
+typedef size_t (*CSF_READ_FUNC)(void *buf, size_t size, size_t n, FILE *f);
+
+typedef struct MAP
+{
+  CSF_CONV_FUNC file2app;
+  CSF_CONV_FUNC app2file;
+     UINT2 appCR;
+  CSF_MAIN_HEADER main;
+  CSF_RASTER_HEADER raster;
+  char *fileName;
+  FILE *fp;
+  int fileAccessMode;
+  int mapListId;
+  UINT2 minMaxStatus;
+
+  CSF_WRITE_FUNC write;
+  CSF_READ_FUNC read;
+}MAP;
+
+typedef CSF_RASTER_HEADER CSF_RASTER_LOCATION_ATTRIBUTES;
+
+
+/************************************************************/
+/*                  */
+/*  PROTOTYPES OF  RUU CSF            */
+/*                  */
+/************************************************************/
+
+MAP *Rcreate(const char *fileName, 
+         size_t nrRows, size_t nrCols, 
+         CSF_CR cellRepr, CSF_VS dataType, 
+         CSF_PT projection, REAL8 xUL, REAL8 yUL, REAL8 angle, REAL8 cellSize);
+MAP  *Rdup(const char *toFile , const MAP *from, 
+           CSF_CR cellRepr, CSF_VS dataType);
+void *Rmalloc(const MAP *m, size_t nrOfCells);
+int RuseAs(MAP *m, CSF_CR useType);
+
+MAP  *Mopen(const char *fname, enum MOPEN_PERM mode);
+enum MOPEN_PERM MopenPerm(const MAP *m);
+
+int Rcompare(const MAP *m1,const MAP *m2);
+int RgetLocationAttributes(
+  CSF_RASTER_LOCATION_ATTRIBUTES *l, /* fill in this struct */
+  const MAP *m); /* map handle to copy from */
+int RcompareLocationAttributes(
+  const CSF_RASTER_LOCATION_ATTRIBUTES *m1, /* */
+  const CSF_RASTER_LOCATION_ATTRIBUTES *m2); /* */
+
+int   Mclose(MAP *map);
+void  Merror(int nr);
+void  Mperror(const char *userString);
+void  MperrorExit(const char *userString, int exitCode);
+const char *MstrError(void);
+const char *MgetFileName(const MAP *m);
+void  ResetMerrno(void);
+
+int    RputAllMV(MAP *newMap);
+size_t  RputRow(MAP *map, size_t rowNr, void *buf);
+size_t  RputSomeCells (MAP *map, size_t somePlace, size_t nrCells, void *buf);
+size_t  RputCell(MAP *map, size_t rowNr, size_t colNr, void *cellValue);
+size_t  RgetRow(MAP *map, size_t rowNr, void *buf);
+size_t  RgetSomeCells (MAP *map, size_t somePlace, size_t nrCells, void *buf);
+size_t  RgetCell(MAP *map, size_t rowNr, size_t colNr, void *cellValue);
+
+int    RputDoNotChangeValues(const MAP *map);
+int    MnativeEndian(const MAP *map);
+
+
+UINT4  MgetMapDataType(const MAP *map);
+UINT4  MgetVersion(const MAP *map);
+UINT4  MgetGisFileId(const MAP *map);
+UINT4  MputGisFileId(MAP *map,UINT4 gisFileId);
+int    IsMVcellRepr(CSF_CR cellRepr, const void *cellValue);
+int    IsMV(const MAP *map, const void *cellValue);
+CSF_VS RgetValueScale(const MAP *map);
+CSF_VS RputValueScale(MAP *map, CSF_VS valueScale);
+int    RvalueScaleIs(const MAP *m, CSF_VS vs);
+int    RvalueScale2(CSF_VS vs);
+CSF_CR RdefaultCellRepr(CSF_VS vs);
+CSF_CR RgetCellRepr(const MAP *map);
+CSF_CR RgetUseCellRepr(const MAP *map);
+
+int    RgetMinVal(const MAP *map, void *minVal);
+int    RgetMaxVal(const MAP *map, void *maxVal);
+void   RputMinVal(MAP *map, const void *minVal);
+void   RputMaxVal(MAP *map, const void *maxVal);
+void   RdontTrackMinMax(MAP *m);
+
+REAL8  RgetXUL(const MAP *map);
+REAL8  RgetYUL(const MAP *map);
+REAL8  RputXUL(MAP *map, REAL8 xUL);
+REAL8  RputYUL(MAP *map, REAL8 yUL);
+
+/* old names: 
+ */
+#define RgetX0 RgetXUL
+#define RgetY0 RgetYUL
+#define RputX0 RputXUL
+#define RputY0 RputYUL
+ 
+
+REAL8  RgetAngle(const MAP *map);
+REAL8  RputAngle(MAP *map, REAL8 Angle);
+
+size_t  RgetNrCols(const MAP *map);
+size_t  RgetNrRows(const MAP *map);
+
+REAL8  RgetCellSize(const MAP *map);
+REAL8  RputCellSize(MAP *map, REAL8 newCellSize);
+
+int RgetCoords( const MAP *m, int inCelPos, size_t row, size_t col, double *x, double *y);
+int RrowCol2Coords(const MAP *m, double row, double col, double *x, double *y);
+void RasterRowCol2Coords(const CSF_RASTER_LOCATION_ATTRIBUTES *m,
+double row, double col, double *x, double *y);
+
+CSF_PT MgetProjection(const MAP *map);
+CSF_PT MputProjection(MAP *map, CSF_PT p);
+
+void   SetMV(const MAP *m, void *cell);
+void   SetMVcellRepr(CSF_CR cellRepr, void *cell);
+void   SetMemMV(void *dest,size_t nrElements,CSF_CR type);
+/* historical error, implemented twice 
+ *  SetArrayMV => SetMemMV
+ */
+
+int MattributeAvail(MAP *m, CSF_ATTR_ID id);
+CSF_ATTR_ID MdelAttribute(MAP *m, CSF_ATTR_ID id);
+
+
+size_t MgetNrLegendEntries(MAP *m);
+int MgetLegend(MAP *m, CSF_LEGEND *l);
+int MputLegend(MAP *m, CSF_LEGEND *l, size_t nrEntries);
+
+size_t MgetHistorySize(MAP *m);
+size_t MgetDescriptionSize(MAP *m);
+size_t MgetNrColourPaletteEntries(MAP *m);
+size_t MgetNrGreyPaletteEntries(MAP *m);
+int MgetDescription(MAP *m, char *des);
+int MgetHistory(MAP *m, char *history);
+int MgetColourPalette(MAP *m, UINT2 *pal);
+int MgetGreyPalette(MAP *m, UINT2 *pal);
+int MputDescription(MAP *m, char *des);
+int MputHistory(MAP *m, char *history);
+int MputColourPalette(MAP *m, UINT2 *pal, size_t nrTupels);
+int MputGreyPalette(MAP *m, UINT2 *pal, size_t nrTupels);
+
+int Rcoords2RowCol( const MAP *m,
+  double x,   double y,  
+  double *row, double *col);
+void RasterCoords2RowCol( const CSF_RASTER_LOCATION_ATTRIBUTES *m,
+  double x,   double y,  
+  double *row, double *col);
+int RasterCoords2RowColChecked( const CSF_RASTER_LOCATION_ATTRIBUTES *m,
+  double x,   double y,  
+  double *row, double *col);
+
+int RgetRowCol(const MAP *m,
+  double x,   double y,  
+  size_t *row, size_t *col);
+
+const char *RstrCellRepr(CSF_CR cr);
+const char *RstrValueScale(CSF_VS vs);
+const char *MstrProjection(CSF_PT p);
+int   RgetValueScaleVersion(const MAP *m);
+
+void RcomputeExtend(REAL8 *xUL, REAL8 *yUL, size_t *nrRows, size_t *nrCols, 
+ double x_1, double y_1, double x_2, double y_2, CSF_PT projection, REAL8 cellSize, double rounding);
+
+#ifdef __cplusplus
+ }
+#endif 
+
+/* INCLUDED_CSF */
+#endif 
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/csfattr.h b/Utilities/GDAL/frmts/pcraster/libcsf/csfattr.h
new file mode 100644
index 0000000000..115550ab63
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/csfattr.h
@@ -0,0 +1,29 @@
+#ifndef CSF__ATTR_H
+#define CSF__ATTR_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+typedef enum CSF_ATTR_ID {
+        ATTR_ID_LEGEND_V1=1,   /* version 1 legend */
+        ATTR_ID_HISTORY=2,     /* history fields */
+        ATTR_ID_COLOUR_PAL=3,  /* colour palette */
+        ATTR_ID_GREY_PAL=4,    /* grey palette */
+        ATTR_ID_DESCRIPTION=5, /* description */
+        ATTR_ID_LEGEND_V2=6    /* version 2 legend */
+} CSF_ATTR_ID;
+
+#define CSF_LEGEND_ENTRY_SIZE  64
+#define CSF_LEGEND_DESCR_SIZE  60
+
+typedef struct CSF_LEGEND {
+	INT4    nr;
+	char    descr[60];
+} CSF_LEGEND;
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /*  CSF__ATTR_H */
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/csfglob.c b/Utilities/GDAL/frmts/pcraster/libcsf/csfglob.c
new file mode 100644
index 0000000000..521030d04a
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/csfglob.c
@@ -0,0 +1,12 @@
+/*
+ * csfglob.c
+ */
+
+
+#include "csf.h"
+#include "csfimpl.h"
+
+/* global variable set on the last error condition
+ * Most functions sets this variable in case of an error condition.
+ */
+int Merrno = NOERROR;
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/csfimpl.h b/Utilities/GDAL/frmts/pcraster/libcsf/csfimpl.h
new file mode 100644
index 0000000000..e8bd3d74ae
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/csfimpl.h
@@ -0,0 +1,238 @@
+#ifndef CSF__IMPL_H
+#define CSF__IMPL_H
+
+/******************************************************************/
+/******************************************************************/
+/**                                                               */
+/**  RUU CROSS SYSTEM MAP FORMAT                                  */
+/**                                                               */
+/******************************************************************/
+/* number of maps that can be open at one time 
+ * FOPEN_MAX should be there in Ansi-C in <stdio.h>
+ * stdio.h is included in csf.h, check if csf.h is included first
+ */
+#ifndef INCLUDED_CSF
+# error csfimpl.h included before csf.h
+#endif
+
+/******************************************************************/
+/* CSFIMPL.H							  */
+/******************************************************************/
+
+/******************************************************************/
+/* Starting Addresses                                             */
+/******************************************************************/
+/* Constants of type CSF_FADDR */
+
+#define ADDR_MAIN_HEADER       ((CSF_FADDR)0)
+#define ADDR_SECOND_HEADER    ((CSF_FADDR)64)
+#define ADDR_DATA            ((CSF_FADDR)256)
+
+/* Padding of headers
+ */
+#define RASTER_HEADER_FILL_SIZE ((size_t)124)
+#define MAIN_HEADER_FILL_SIZE   ((size_t)14)
+/* Used in mclose.c
+ */
+#define MAX_HEADER_FILL_SIZE    (RASTER_HEADER_FILL_SIZE)
+
+/* values for MAIN_HEADER.byteOrder */
+#define ORD_OK   0x00000001L
+#define ORD_SWAB 0x01000000L
+
+/* INTERFACE with PCRaster software
+ */
+#ifdef USE_IN_PCR
+# include "stddefx.h" 
+# include "misc.h" /* malloc, free */
+# define  CSF_MALLOC ChkMalloc
+# define  CSF_FREE   Free
+#else
+# include <stdlib.h> /* malloc, free,abs */
+# include <assert.h> 
+# define  CSF_MALLOC malloc
+# define  CSF_FREE   free
+# ifdef DEBUG
+#  define  PRECOND(x)	assert(x)
+#  define  POSTCOND(x)	assert(x)
+# else
+#  define  PRECOND(x)
+#  define  POSTCOND(x)
+# endif
+#ifndef USE_IN_GDAL
+# define  ABS(x)        abs(x)
+#endif
+# define  USED_UNINIT_ZERO 0
+#endif
+
+/******************************************************************/
+/* Definition of the main header                                  */
+/******************************************************************/
+
+/* value for MAIN_HEADER.version */
+#define CSF_VERSION_1 1
+#define CSF_VERSION_2 2
+
+
+#define IS_UNSIGNED(type) 	(!((type) & CSF_FLOAT_SIGN_MASK))
+#define IS_SIGNED(type)   	((type) & CSF_SIGN_MASK)
+#define IS_REAL(type)     	((type) & CSF_FLOAT_MASK)
+
+/******************************************************************/
+/* Compiler conditions                                            */
+/******************************************************************/
+/*
+ sizeof(INT1)  == 1
+ sizeof(INT2)  == 2
+ sizeof(INT4)  == 4
+ sizeof(UINT1) == 1
+ sizeof(UINT2) == 2
+ sizeof(UINT4) == 4
+ sizeof(REAL4) == 4
+ sizeof(REAL8) == 8
+*/
+
+/******************************************************************/
+/* Definition of an attribute control block	                  */
+/******************************************************************/
+
+#define NR_ATTR_IN_BLOCK 	10
+#define LAST_ATTR_IN_BLOCK 	(NR_ATTR_IN_BLOCK-1)
+
+
+typedef struct ATTR_REC 
+{
+		UINT2 attrId;	/* attribute identifier */
+		CSF_FADDR attrOffset;   /* file-offset of attribute */
+		UINT4 attrSize;	/* size of attribute in bytes */
+} ATTR_REC;
+
+typedef struct ATTR_CNTRL_BLOCK
+{
+	ATTR_REC attrs[NR_ATTR_IN_BLOCK];
+	CSF_FADDR    next; /* file-offset of next block */
+} ATTR_CNTRL_BLOCK;
+
+#define SIZE_OF_ATTR_CNTRL_BLOCK  \
+ ((NR_ATTR_IN_BLOCK * (sizeof(UINT2) + sizeof(CSF_FADDR) + sizeof(UINT4))) \
+  + sizeof(CSF_FADDR) )
+
+/* Note that two empty holes in the attribute area are never merged */
+
+
+#define ATTR_NOT_USED 0x0
+	/* value of attrId field if an attribute is deleted */
+	/* attrOffset and attrSize must remain valid; so a new
+	 * attribute can be inserted if it's size is equal or
+	 * smaller then attrSize
+	 */
+
+#define END_OF_ATTRS 0xFFFF
+	/* value of attrId field if there are no more attributes */
+	/* INDEED: A BUG we wanted to use the highest value (0xFFFFFFFF)
+	 *  but we made a mistake. Don't change, 1023  is just as
+	 *  good as (2^16)-1
+	 */
+
+/* does y decrements from 
+ * top to bottom in this projection type?
+ * this will also hold for the old types
+ * since only PT_XY was increments from
+ * top to bottom, like PT_YINCT2B
+ * PT_XY and PT_YINCT2B are the only one that are
+ * 0, the others all have a nonzero value
+ */
+#define PROJ_DEC_T2B(x)	(x != 0)
+
+#define MM_KEEPTRACK		0
+#define MM_DONTKEEPTRACK	1
+#define MM_WRONGVALUE		2
+
+#define M_ERROR(errorCode) Merrno = errorCode
+#define PROG_ERROR(errorCode) Merrno = errorCode
+
+#define S_READ		"rb"   /* Open for read only */
+#define S_WRITE	 	"r+b"  /* Open for write only  I don't know an */
+				/* appropriate mode "r+b" seems most app. */
+#define S_READ_WRITE  	"r+b"  /* Open for reading and writing */
+#define S_CREATE  	"w+b"  /* Create new file for reading and writing */
+
+#define WRITE_ENABLE(m)	   (m->fileAccessMode & M_WRITE)
+#define READ_ENABLE(m)	   (m->fileAccessMode & M_READ)
+#define IS_BAD_ACCESS_MODE(mode) \
+		 (mode >> 2) 	/* use only 2 bits for modes */
+
+#define READ_AS	 0 /* note that READ_AS is also used on procedures
+			that implies write access, under the condition of
+			write access both type bytes are equal, and the 
+			READ_AS byte is 0-alligned in the record, so this
+			byte is quicker accessible */
+	/* we will call READ_AS the ONLY_AS if write access is implied */
+#define ONLY_AS		0
+#define STORED_AS	1
+
+/* Typed zero values to keep lint happy
+ * mainly used in conversion macro's
+ */
+#define ZERO_UINT1	((UINT1)0)
+#define ZERO_UINT2	((UINT2)0)
+#define ZERO_UINT4	((UINT4)0)
+#define ZERO_INT1	((INT1) 0)
+#define ZERO_INT2	((INT2) 0)
+#define ZERO_INT4	((INT4) 0)
+#define ZERO_REAL4	((REAL4)0)
+#define	ZERO_REAL8	((REAL8)0)
+
+/* LIBRARY_INTERNAL's: */
+/* OLD STUFF
+void TransForm(const MAP *map, UINT4 nrCells, void *buf);
+ */
+
+void  CsfFinishMapInit(MAP *m);
+void  CsfDummyConversion(size_t n, void *buf);
+int   CsfIsValidMap(const MAP *m);
+void  CsfUnloadMap(MAP *m);
+void  CsfRegisterMap(MAP *m);
+int   CsfIsBootedCsfKernel(void);
+void  CsfBootCsfKernel(void);
+void  CsfSetVarTypeMV( CSF_VAR_TYPE *var, CSF_CR cellRepr);
+void  CsfGetVarType(void *dest, const CSF_VAR_TYPE *src, CSF_CR cellRepr);
+void  CsfReadAttrBlock( MAP *m, CSF_FADDR pos, ATTR_CNTRL_BLOCK *b);
+int   CsfWriteAttrBlock(MAP *m, CSF_FADDR pos, ATTR_CNTRL_BLOCK *b);
+int   CsfGetAttrIndex(CSF_ATTR_ID id, const ATTR_CNTRL_BLOCK *b);
+CSF_FADDR CsfGetAttrBlock(MAP *m, CSF_ATTR_ID id, ATTR_CNTRL_BLOCK *b);
+CSF_FADDR CsfGetAttrPosSize(MAP *m, CSF_ATTR_ID id, size_t *size);
+size_t CsfWriteSwapped(void *buf, size_t size, size_t n, FILE  *f);
+size_t CsfReadSwapped(void *buf, size_t size, size_t n, FILE  *f);
+size_t CsfWritePlain(void *buf, size_t size, size_t n, FILE  *f);
+size_t CsfReadPlain(void *buf, size_t size, size_t n, FILE  *f);
+void   CsfSwap(void *buf, size_t size, size_t n);
+char *CsfStringPad(char *s, size_t reqSize);
+
+CSF_FADDR CsfSeekAttrSpace(MAP *m, CSF_ATTR_ID id, size_t size);
+CSF_ATTR_ID CsfPutAttribute( MAP *m, CSF_ATTR_ID id, size_t size, size_t nitems, void *attr);
+CSF_ATTR_ID CsfGetAttribute(MAP *m, CSF_ATTR_ID id, size_t elSize, size_t *nmemb, void *attr);
+size_t      CsfAttributeSize(MAP *m, CSF_ATTR_ID id);
+CSF_ATTR_ID CsfUpdateAttribute(MAP *m, CSF_ATTR_ID id, size_t itemSize, size_t nitems, void *attr);
+
+int CsfValidSize(size_t size);
+
+#define CHECKHANDLE_GOTO(m, label)	\
+			if (! CsfIsValidMap(m))	\
+		   	{			\
+				M_ERROR(ILLHANDLE);	\
+				goto label;		\
+			}
+#define CHECKHANDLE_RETURN(m, value)	\
+			if (! CsfIsValidMap(m))	\
+		   	{			\
+				M_ERROR(ILLHANDLE);	\
+				return value;		\
+			}
+#define CHECKHANDLE(m)	\
+			if (! CsfIsValidMap(m))	\
+		   	{			\
+				M_ERROR(ILLHANDLE);	\
+			}
+
+#endif /* CSF__IMPL_H */
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/csfsup.c b/Utilities/GDAL/frmts/pcraster/libcsf/csfsup.c
new file mode 100644
index 0000000000..68bacca284
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/csfsup.c
@@ -0,0 +1,55 @@
+/*
+ * csfsup.c
+$Log: csfsup.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:34  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/06  13:21:04  cees
+ * added c2man docs
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csftypes.h"
+#include <string.h>    /* memset */
+
+/* set an array of cells to missing value
+ * SetMemMV sets an array of cells to missing value
+ */
+void SetMemMV(
+	void *buf,		/* write-only buffer with cells */
+	size_t nrElements,      /* number of cells */
+	CSF_CR cellRepr)         /* cell representation */
+{
+size_t index;
+
+	switch (cellRepr) {
+	  case CR_INT1: (void)memset(buf,MV_INT1,nrElements);break;
+	  case CR_INT2: for (index=0;index<nrElements;index++)
+					 ((INT2 *) buf)[index]=MV_INT2;
+			break;
+	  case CR_INT4: for (index=0;index<nrElements;index++)
+					 ((INT4 *) buf)[index]=MV_INT4;
+			break;
+	  default: (void)memset(buf,MV_UINT1,CSFSIZEOF(nrElements,cellRepr));
+	}
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/csftypes.h b/Utilities/GDAL/frmts/pcraster/libcsf/csftypes.h
new file mode 100644
index 0000000000..b2ca0513c9
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/csftypes.h
@@ -0,0 +1,416 @@
+#ifndef INCLUDED_CSFTYPES
+# define INCLUDED_CSFTYPES
+
+#ifdef USE_IN_GDAL
+#include "cpl_port.h"
+#endif
+
+
+#ifdef __cplusplus
+ extern "C" {
+ #define CSF_IN_GLOBAL_NS ::
+#else
+ #define CSF_IN_GLOBAL_NS
+#endif
+
+#ifdef CSF_V1
+# error include file CSF version 2 used while CSF_V1 is defined
+#endif
+
+
+/* MACHINE AND OPERATING SYSTEM DEPENDENT SECTION:
+ * (LOOK AND CONFIGURE YOUR MACHINE/OS)
+ *  - Retype all types for file i/o
+ *  - Define endian mode
+ *  - Compliance mode (POSIX/XOPEN)
+ */
+
+#ifdef THINK_C
+# error read notes in csftypes.h
+  /* Some history:
+   *   THINK C 3.0:
+   *     - does not know the keyword signed
+   *     - uses a 12-byte for double if compiled with
+   *       the in-line 68881 option
+   */
+#endif
+
+ /* the only 64 bit cpu we tested on:
+  * -ifdef __alpha
+  * don't use long
+  * -define CSF_4BYTE_INT_SIZE_SPECIFIER long
+  */
+#define CSF_4BYTE_INT_SIZE_SPECIFIER
+
+/* some old C++ compiler complains
+ * about signed
+ */
+#ifdef SIGNED_NOT_IMPL
+# define CSF_SIGNED_SPECIFIER
+#else
+# define CSF_SIGNED_SPECIFIER signed
+#endif
+
+/* the last-character figure is the
+ * size in bytes of the data type
+ */
+
+typedef CSF_SIGNED_SPECIFIER char INT1;
+typedef unsigned             char UINT1;
+
+#ifdef USE_IN_GDAL
+typedef GInt16   INT2;
+typedef GInt32   INT4;
+typedef GUInt16 UINT2;
+typedef GUInt32  UINT4;
+#else
+typedef CSF_SIGNED_SPECIFIER short                        int  INT2;
+typedef CSF_SIGNED_SPECIFIER CSF_4BYTE_INT_SIZE_SPECIFIER int  INT4;
+typedef unsigned             short                        int  UINT2;
+typedef unsigned             CSF_4BYTE_INT_SIZE_SPECIFIER int  UINT4;
+#endif
+
+#undef CSF_4BYTE_INT_SIZE_SPECIFIER
+#undef CSF_SIGNED_SPECIFIER
+
+typedef float               REAL4; /* IEEE-754 32-bit */
+typedef double              REAL8; /* IEEE-754 64-bit */
+
+/* 64 bit specifier not yet used
+ * but here is some code
+ */
+#if defined(__GCC__) || defined(__MINGW32__)
+/* assume gcc, gcc likes it like this */
+typedef          long long  CSF_INT8;
+typedef unsigned long long  CSF_UINT8;
+#else
+#if defined(WIN32)
+typedef          __int64    CSF_INT8;
+typedef unsigned __int64    CSF_UINT8;
+#endif
+#endif
+
+/* endian mode
+ * DEFINE WITH -D or find here
+ */
+#ifndef CPU_BIG_ENDIAN
+# ifndef CPU_LITTLE_ENDIAN
+
+#ifdef USE_IN_GDAL
+  /* GDAL CPL STYLE */
+# ifdef CPL_LSB
+#  define CPU_LITTLE_ENDIAN
+# endif
+# ifdef CPL_MSB
+#  define CPU_BIG_ENDIAN
+# endif
+
+#else
+ /* probe a few: */
+
+#ifdef _AIX
+/* IBM AIX defines this on RS/6000 */
+# define CPU_BIG_ENDIAN
+#endif
+
+#ifdef sparc
+/* both cc and gcc defines this in SunOS */
+# define CPU_BIG_ENDIAN
+#endif
+
+/*
+ * #ifdef mips
+ * worked once on the SGI machines
+ * but mips has both endian architectures
+ * # define CPU_BIG_ENDIAN
+ * #endif
+ */
+
+#ifdef __alpha
+/* DEC alpha defines this
+ * tested on OSF1 planet V4.0 1229 alpha
+ *   in combo with egcs/gcc 2.95.2
+ */
+#  define CPU_LITTLE_ENDIAN
+#endif
+
+#ifdef __i386__
+/* linux/gcc defines this on intel 80x86 platform */
+#  define CPU_LITTLE_ENDIAN
+#endif
+
+#ifdef _M_IX86
+/* Borland C defines this */
+/* Win32/MSC defines this on intel 80x86 platform */
+#  define CPU_LITTLE_ENDIAN
+#endif
+
+
+#ifdef __hppa
+/* cc and gcc defines this on HP PA risc platform */
+#  define CPU_BIG_ENDIAN
+#endif
+
+/* endif probing */
+# endif 
+
+/* endif no ENDIAN defined */
+# endif
+#endif
+
+/* POSIX or XOPEN compliance
+ *  The only need for the POSIX or even XOPEN superset
+ *  over ANSI-C are
+ *  M_PI    HP-UX complained about that and it seems in XOPEN
+ * solution:
+ *  M_PI is defined in situ in source files if it is not defined
+ *    (most times in math.h)
+ * we probe a few systems to find if we may define it
+ */
+
+#ifdef __hpux
+# ifndef _XOPEN_SOURCE
+#  define _XOPEN_SOURCE
+# endif
+#endif
+
+/* END OF MACHINE AND OPERATING SYSTEM DEPENDENT SECTION
+ * NO NEED TO EDIT ANYTHING BELOW THIS POINT
+ */
+
+
+
+/* PROJECTION
+ */
+/* version 1 types, no longer used
+ */
+#define PT_XY  0  /* XY-field (= PT_YINCT2B)
+                            */
+#define PT_UTM 1  /* Universal Transverse Mercator (= PT_YDECT2B)
+                            */
+#define PT_LATLON 2  /* Lattitude / Longitude (= PT_YDECT2B)
+                            */
+#define PT_CART        3  /* Carthesian (= PT_YDECT2B)
+                            */
+#define PT_RDM 4  /* Rijks Driehoek Metingen stelsel (= PT_YDECT2B)
+                            */
+/* the only difference we make is whether Y increases from
+ * top to bottom or decreases from top to bottom
+ */
+
+typedef enum CSF_PT {
+
+	/* * these two can be returned by or passed to a csf2 function */
+	PT_YINCT2B=0,    /* Y increase from top to bottom */
+	PT_YDECT2B=1,    /* Y decrease from top to bottom */
+
+	/* * this one CANNOT be returned by NOR passed to a csf2 function */
+	PT_UNDEFINED=100 /* just some value different from the rest */
+
+} CSF_PT;
+
+/* DATATYPE
+ * A.K.A. VALUESCALE
+ */
+
+/* historical errors don't use them:
+ * #define VS_NOTCLASSIFIED 2	
+ * #define VS_CONTINUES 	 2
+ *
+ * NOTE new VS_* types must be different from CR_* values
+ *      VS_BOOLEAN       0xE0 => 224
+ *      VS_NOMINAL       0xE2 => 226
+ *      VS_ORDINAL       0xF2 => 242
+ *      VS_SCALAR        0xEB => 235
+ *      VS_DIRECTION     0xFB => 251
+ *      VS_LDD           0xF0 => 240
+ *      VS_VECTOR        0xEC => 236
+ *      VS_VECTOR        0xEC vector, not used yet
+ */
+
+typedef enum CSF_VS {
+
+	/* * version 1 datatypes,
+	 * these can be returned by BUT NOT passed to a csf2 function
+	 */
+        VS_NOTDETERMINED=0, /* version 1  */
+        VS_CLASSIFIED   =1, /* version 1  */
+        VS_CONTINUOUS 	=2, /* version 1  */
+
+        /* * version 2 datatypes
+	 * these two can be returned by or passed to a csf2 function
+	 */
+        VS_BOOLEAN      =0xE0,/* boolean, always UINT1, values: 0,1 or MV_UINT1 */
+        VS_NOMINAL      =0xE2,/* nominal, UINT1 or INT4 */
+        VS_ORDINAL      =0xF2,/* ordinal, UINT1 or INT4 */
+        VS_SCALAR       =0xEB,/* scalar, REAL4 or (maybe) REAL8 */
+        VS_DIRECTION    =0xFB,/* directional REAL4 or (maybe) REAL8, -1 means no direction */
+        VS_LDD          =0xF0,/* local drain direction, always UINT1, values: 1-9 or MV_UINT1 */
+
+	/* * this one CANNOT be returned by NOR passed to a csf2 function */
+	VS_UNDEFINED    =100 /* just some value different from the rest */
+
+} CSF_VS;
+
+
+/* CELL REPRESENTATION
+   CR_UINT1      0x00 =>   0
+   CR_INT4       0x26 =>  38
+   CR_REAL4      0x5A =>  90
+   CR_INT1	 0x04 =>   4
+   CR_INT2	 0x15 =>  21
+   CR_UINT2	 0x11 =>  17
+   CR_UINT4	 0x22 =>  34
+   CR_REAL8	 0xDB => 219
+   for vector types
+   CR_VECT4	0x??
+   CR_VECT8	0x??
+ */
+
+typedef enum CSF_CR {
+
+       /* * preferred version 2 cell representations
+        */
+
+	CR_UINT1      =0x00, /*  boolean, ldd and small nominal and small ordinal */
+	CR_INT4       =0x26, /*  large nominal and large ordinal */
+	CR_REAL4      =0x5A, /*  single scalar and single directional */
+
+       /* * other version 2 cell representations
+        */
+
+	CR_REAL8      =0xDB, /* double scalar or directional, and also the only type that
+	                      * can hold all
+	                      * cell representation without loss of precision
+	                      */
+
+       /* * version 1 cell representations
+	* these can be returned by BUT NOT passed to a csf2 function
+        */
+
+	CR_INT1	      =0x04, /* . */
+	CR_INT2	      =0x15, /* . */
+	CR_UINT2      =0x11, /* . */
+	CR_UINT4      =0x22, /* . */
+
+	/* * this one CANNOT be returned by NOR passed to a csf2 function */
+
+        CR_UNDEFINED  =100 /* just some value different from the rest */
+} CSF_CR;
+
+
+
+/* how to get the cellsize from these type identifiers */
+#define CSF_SIZE_MASK 		((size_t)0x03)
+#define CSF_SIGN_MASK		((size_t)0x04)
+#define CSF_FLOAT_MASK		((size_t)0x08)
+#define CSF_FLOAT_SIGN_MASK	((size_t)0x0C)
+#define CSF_SIZE_MV_MASK	((size_t)0x30)
+#define CSF_SKIP_MASK		((size_t)0xC0)
+/* low nibble is uniq for every CR_VALUE: */
+#define CSF_UNIQ_MASK	((size_t)0x0F)
+#define CSF_POS_SIZE_MV_MASK 	((size_t)4)
+#define CSF_POS_SKIP_MASK       ((size_t)6)
+#define CSF_UNIQ_CR_MASK(type)  ((size_t)((type) & CSF_UNIQ_MASK))
+#define LOG_CELLSIZE(type)  	((size_t)((type) & CSF_SIZE_MASK))
+#define CELLSIZE(type)  	((size_t)(1 << LOG_CELLSIZE(type)))
+#define CSFSIZEOF(nr, type)	((size_t)(((size_t)nr) << LOG_CELLSIZE(type)))
+
+#include <float.h> /* FLT_MIN, DBL_MAX, DBL_MAX, FLT_MAX */
+
+#define MV_INT1   ((CSF_IN_GLOBAL_NS INT1)-128)
+#define MV_INT2   ((CSF_IN_GLOBAL_NS INT2)-32768)
+/* cl C4146 has it right
+  #define MV_INT4   ((CSF_IN_GLOBAL_NS INT4)-2147483648)
+   is dangerous
+ */
+#define MV_INT4   ((CSF_IN_GLOBAL_NS INT4)0x80000000L)
+
+#define MV_UINT1  ((CSF_IN_GLOBAL_NS UINT1)0xFF)
+#define MV_UINT2  ((CSF_IN_GLOBAL_NS UINT2)0xFFFF)
+#define MV_UINT4  ((CSF_IN_GLOBAL_NS UINT4)0xFFFFFFFFL)
+
+#define INT2_MIN  ((INT2)(MV_INT2+1))
+#define INT2_MAX  ((INT2)0x7FFF)
+
+#define UINT1_MIN ((UINT1)0)
+#define UINT1_MAX ((UINT1)(MV_UINT1-1))
+
+#define INT4_MIN   ((INT4)(MV_INT4+1))
+#define INT4_MAX   ((INT4)0x7FFFFFFFL)
+
+#define REAL4_MIN  ((REAL4)(FLT_MIN))
+#define REAL4_MAX  ((REAL4)(FLT_MAX))
+
+#define REAL8_MIN  ((REAL8)(DBL_MIN))
+#define REAL8_MAX  ((REAL8)(DBL_MAX))
+
+
+/* x is a pointer to a value */
+#define IS_MV_UINT1(x)	((*((const CSF_IN_GLOBAL_NS UINT1 *)x)) == MV_UINT1)
+#define IS_MV_UINT2(x)	((*((const CSF_IN_GLOBAL_NS UINT2 *)x)) == MV_UINT2)
+#define IS_MV_UINT4(x)	((*((const CSF_IN_GLOBAL_NS UINT4 *)x)) == MV_UINT4)
+#define IS_MV_INT1(x)	((*((const CSF_IN_GLOBAL_NS INT1 *)x)) == MV_INT1)
+#define IS_MV_INT2(x)	((*((const CSF_IN_GLOBAL_NS INT2 *)x)) == MV_INT2)
+#define IS_MV_INT4(x)	((*((const CSF_IN_GLOBAL_NS INT4 *)x)) == MV_INT4)
+
+/* MV_REAL4 and MV_REAL8 are bitpatterns with all 1's that
+ * are NAN's
+ * MV_REAL4 has the same bitpattern as a MV_UINT4
+ * MV_REAL8 has the same bitpattern as two MV_UINT4's
+ *          only the first 32 bits already identify a NAN, 
+ *          so that's what we test
+ */
+#ifdef CPU_LITTLE_ENDIAN
+# ifdef CPU_BIG_ENDIAN
+#  error CPU_BIG_ENDIAN and CPU_LITTLE_ENDIAN are both defined
+# endif
+# ifdef INTEL16
+#  define IS_MV_REAL4(x) (((const UINT2 *)(x))[1] == MV_UINT2)
+#  define IS_MV_REAL8(x) (((const UINT2 *)(x))[3] == MV_UINT2)
+# else
+#  define IS_MV_REAL4(x) ((*((const CSF_IN_GLOBAL_NS UINT4 *)(x))) == MV_UINT4)
+#  define IS_MV_REAL8(x) (((const CSF_IN_GLOBAL_NS UINT4 *)(x))[1] == MV_UINT4)
+# endif
+#else
+# ifdef CPU_BIG_ENDIAN
+#  ifdef CPU_LITTLE_ENDIAN
+#   error CPU_BIG_ENDIAN and CPU_LITTLE_ENDIAN are both defined
+#  endif
+#  define IS_MV_REAL4(x) (((const UINT4 *)(x))[0] == MV_UINT4)
+#  define IS_MV_REAL8(x) (((const UINT4 *)(x))[0] == MV_UINT4)
+# else
+#  error BYTE ORDER NOT SPECIFIED (CPU_LITTLE_ENDIAN or CPU_BIG_ENDIAN)
+# endif
+#endif
+
+
+/* some special values
+ */
+#define LDD_PIT          5
+#define DIR_NODIRECTION -1
+
+/* some special macro's
+ * x is a pointer
+ */
+#define SET_MV_UINT1(x)	( (*((UINT1 *)(x))) = MV_UINT1)
+#define SET_MV_UINT2(x)	( (*((UINT2 *)(x))) = MV_UINT2)
+#define SET_MV_UINT4(x)	( (*((UINT4 *)(x))) = MV_UINT4)
+#define SET_MV_INT1(x)	( (*(( INT1 *)(x))) = MV_INT1)
+#define SET_MV_INT2(x)	( (*(( INT2 *)(x))) = MV_INT2)
+#define SET_MV_INT4(x)	( (*(( INT4 *)(x))) = MV_INT4)
+#define	SET_MV_REAL4(x)	((*(CSF_IN_GLOBAL_NS UINT4 *)(x)) = MV_UINT4)
+#define	SET_MV_REAL8(x)	SET_MV_REAL4((x)),SET_MV_REAL4((((CSF_IN_GLOBAL_NS UINT4 *)(x))+1))
+
+/* copy of floats  by typecasting to
+ * an integer since MV_REAL? is a NAN
+ */
+#define	COPY_REAL4(dest,src) ( (*(UINT4 *)(dest)) = (*(const UINT4 *)(src)) )
+#define	COPY_REAL8(dest,src) COPY_REAL4((dest),(src)),\
+		COPY_REAL4( (((UINT4 *)(dest))+1),(((const UINT4 *)(src))+1) )
+
+#ifdef __cplusplus
+ };
+#endif
+
+#endif
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/delattr.c b/Utilities/GDAL/frmts/pcraster/libcsf/delattr.c
new file mode 100644
index 0000000000..bec1593b2e
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/delattr.c
@@ -0,0 +1,75 @@
+/*
+ * delattr.c
+$Log: delattr.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:36  cees
+Initial import Cees
+
+Revision 2.1  1996/12/29 19:35:21  cees
+src tree clean up
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* delete attribute from map
+ * MdelAttribute deletes an attribute
+ * from a map, if the attribute is available.
+ * returns
+ * the id argument if the attribute is succesfully deleted,
+ * or 0 in case of error or if the attribute is not found.
+ *
+ * Merrno
+ * NOACCESS
+ * WRITE_ERROR
+ */
+CSF_ATTR_ID MdelAttribute(
+	MAP *m,     /* map handle */
+	CSF_ATTR_ID id)   /* identification of attribute */
+{
+	ATTR_CNTRL_BLOCK b;
+	CSF_FADDR pos;
+
+	if (! WRITE_ENABLE(m))
+	{
+		M_ERROR(NOACCESS);
+		goto error;
+	}
+
+	pos = CsfGetAttrBlock(m, id, &b);  
+	if (pos == 0)
+		goto error;
+
+	b.attrs[CsfGetAttrIndex(id, &b)].attrId = ATTR_NOT_USED;
+	if (CsfWriteAttrBlock(m, pos, &b))
+	{
+		M_ERROR(WRITE_ERROR);
+		goto error;
+	}
+
+	return id ;
+
+error:	return 0 ;	/* not found or an error */
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/dumconv.c b/Utilities/GDAL/frmts/pcraster/libcsf/dumconv.c
new file mode 100644
index 0000000000..ff29c2c74f
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/dumconv.c
@@ -0,0 +1,70 @@
+
+/*
+ * dumconv.c 
+   $Log: dumconv.c,v $
+   Revision 1.3  2006/02/07 10:17:15  kdejong
+   Fixed endian compile problem
+   some rcs issues of Kor, I guess
+   Checked in by cees (cees@pcraster.nl) on account of Kor
+
+   Revision 1.3  2005/10/03 07:23:00  kor
+   Removed rcs id string
+
+   Revision 1.2  2005/09/29 18:43:22  cees
+   x86_64
+
+   Revision 1.1.1.1  2000/01/04 21:04:37  cees
+   Initial import Cees
+
+   Revision 2.0  1996/05/23 13:16:26  cees
+   csf2clean
+
+   Revision 1.1  1996/05/23 13:11:49  cees
+   Initial revision
+
+   Revision 1.2  1995/11/01 17:23:03  cees
+   .
+
+ * Revision 1.1  1994/09/09  12:17:59  cees
+ * Initial revision
+ *
+ */
+/********/
+/* USES */
+/********/
+
+/* libs ext. <>, our ""  */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* global header (opt.) and dumconv's prototypes "" */
+
+/* headers of this app. modules called */ 
+
+/***************/
+/* EXTERNALS   */
+/***************/
+
+/**********************/ 
+/* LOCAL DECLARATIONS */
+/**********************/ 
+
+/*********************/ 
+/* LOCAL DEFINITIONS */
+/*********************/ 
+
+/******************/
+/* IMPLEMENTATION */
+/******************/
+
+/* ARGSUSED */
+
+/* dummy conversion (LIBRARY_INTERNAL)
+ * does nothing
+ */
+void CsfDummyConversion(
+	size_t  nrCells,  
+	void   *buf )
+{
+	/* nothing */
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/endian.c b/Utilities/GDAL/frmts/pcraster/libcsf/endian.c
new file mode 100644
index 0000000000..1bcd1895c7
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/endian.c
@@ -0,0 +1,20 @@
+#include "csf.h"
+#include "csfimpl.h"
+
+/* test if map is in native endian mode
+ * test if map is in native endian mode
+ * returns nonzero if native, 0 if not native
+ */
+int    MnativeEndian(const MAP *map)
+{
+	return map->main.byteOrder == ORD_OK;
+}
+
+/* test if the Rput functions alter their cell buffers
+ * test if the Rput functions alter their cell buffers
+ * returns nonzero if they do not alter, 0 if they alter their buffers
+ */
+int    RputDoNotChangeValues(const MAP *map)
+{
+	return MnativeEndian(map) && map->appCR == map->raster.cellRepr;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/filename.c b/Utilities/GDAL/frmts/pcraster/libcsf/filename.c
new file mode 100644
index 0000000000..0d6fafb98d
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/filename.c
@@ -0,0 +1,41 @@
+/*
+ * filename.c
+$Log: filename.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:37  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* file name associated with map
+ * returns the file name associated with map
+ */
+const char *MgetFileName(
+	const MAP *m) /* map handle */
+{
+	return(m->fileName);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/gattrblk.c b/Utilities/GDAL/frmts/pcraster/libcsf/gattrblk.c
new file mode 100644
index 0000000000..c485721f88
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/gattrblk.c
@@ -0,0 +1,89 @@
+/*
+ * gattrblk.c
+$Log: gattrblk.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:37  cees
+Initial import Cees
+
+Revision 2.1  1996/12/29 19:35:21  cees
+src tree clean up
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.4  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.3  1995/01/11  14:14:44  cees
+ * added soem sinternal stuff
+ *
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* get the attribute control block (LIBRARY_INTERNAL)
+ * GetAttrBlock searches for the attribute control block
+ * that keeps the information for the given id.
+ * returns
+ * 0 if attribute is not found,
+ * or if found, the file position of the attribute
+ * control block.
+ */
+CSF_FADDR CsfGetAttrBlock(
+	MAP *m,     /* map handle */
+	CSF_ATTR_ID id,   /* identification of the attribute */
+	ATTR_CNTRL_BLOCK *b) /* write-only, attribute control block containing
+	                      * the id information.
+	                      */
+{
+	CSF_FADDR next;
+
+	next = m->main.attrTable;
+	while (next != 0 )
+	{
+		CsfReadAttrBlock(m, next, b);
+		if (CsfGetAttrIndex(id, b) != NR_ATTR_IN_BLOCK)
+			break;
+		next = b->next;
+	}
+	return(next);
+}
+
+/* get the attribute position and size (LIBRARY_INTERNAL)
+ * CsfGetAttrPosSize searches the attribute control block list
+ * that keeps the information for the given id.
+ * returns
+ * 0 if attribute is not found,
+ * or if found, the file position of the attribute.
+ */
+CSF_FADDR CsfGetAttrPosSize(
+	MAP *m,     /* map handle */
+	CSF_ATTR_ID id,   /* identification of the attribute */
+	size_t *size) /* write-only the size of the attribute */
+{
+	ATTR_CNTRL_BLOCK b;
+	int i;
+
+	if (CsfGetAttrBlock(m,id, &b) == 0)
+		return 0;
+
+	i = CsfGetAttrIndex(id, &b);
+	*size =	b.attrs[i].attrSize;
+	return b.attrs[i].attrOffset;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/gattridx.c b/Utilities/GDAL/frmts/pcraster/libcsf/gattridx.c
new file mode 100644
index 0000000000..0b6eef8642
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/gattridx.c
@@ -0,0 +1,51 @@
+/*
+ * gattridx.c
+$Log: gattridx.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:37  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/06  13:27:31  cees
+ * const'fied
+ * added c2man docs
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* search attribute index in block (LIBRARY_INTERNAL)
+ * returns index in block where id is found, NR_ATTR_IN_BLOCK if not found 
+ */
+int CsfGetAttrIndex(
+	CSF_ATTR_ID id,  /* id to be found */
+	const ATTR_CNTRL_BLOCK *b) /* block to inspect */
+{
+	int i = 0;
+
+	while(i < NR_ATTR_IN_BLOCK)
+	{
+		if (b->attrs[i].attrId == id) 
+			break;
+		i++;
+	}
+	return(i);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/gcellrep.c b/Utilities/GDAL/frmts/pcraster/libcsf/gcellrep.c
new file mode 100644
index 0000000000..b9307d6b93
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/gcellrep.c
@@ -0,0 +1,23 @@
+#include "csf.h"
+#include "csfimpl.h"
+
+/* get cell representation
+ * RgetCellRepr returns the in-file cell representation.
+ * returns the cell representation 
+ */
+CSF_CR RgetCellRepr(
+	const MAP *map) /* map handle */
+{
+	return(map->raster.cellRepr);
+}
+
+/* get cell representation as set by RuseAs
+ * RgetUseCellRepr returns the cell representation as set by RuseAs
+ * returns the cell representation as set by RuseAs
+ */
+
+CSF_CR RgetUseCellRepr(
+	const MAP *map) /* map handle */
+{
+   	return(map->appCR);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/gdattype.c b/Utilities/GDAL/frmts/pcraster/libcsf/gdattype.c
new file mode 100644
index 0000000000..b656b38b07
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/gdattype.c
@@ -0,0 +1,48 @@
+/*
+ * gdattype.c
+$Log: gdattype.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:37  cees
+Initial import Cees
+
+Revision 2.1  1996/12/29 19:35:21  cees
+src tree clean up
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/02  16:22:20  cees
+ * added c2man doc
+ * const'ified map handle
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* data type of the map
+ * MgetDataType returns the type of the map, which is always
+ * a raster until now.
+ * returns
+ * T_RASTER
+ */
+UINT4 MgetMapDataType(
+	const MAP *map) /* map handle */
+{
+	return (UINT4)(map->main.mapType);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/getattr.c b/Utilities/GDAL/frmts/pcraster/libcsf/getattr.c
new file mode 100644
index 0000000000..65e62f04e8
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/getattr.c
@@ -0,0 +1,81 @@
+/*
+ * getattr.c
+$Log: getattr.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:38  cees
+Initial import Cees
+
+Revision 2.1  1996/12/29 19:35:21  cees
+src tree clean up
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+ * Revision 1.3  1995/11/01  17:23:03  cees
+ * .
+ *
+ * Revision 1.2  1994/09/06  13:39:59  cees
+ * added c2man docs
+ * removed Merrno settin g if attr. is not avalaible
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* read an attribute (LIBRARY_INTERNAL)
+ * MgetAttribute reads an attribute if it is available.
+ * Be aware that you can't pass a simple pointer to some 
+ * (array of) structure(s) due to allignment en endian problems.
+ * At some time there will be a seperate get function for each attribute
+ * returns 0 if the attribute is not found, arg id if
+ * the attribute is found.
+ */
+CSF_ATTR_ID CsfGetAttribute(
+	 MAP *m, /* map handle */
+	 CSF_ATTR_ID id, /* id of attribute to be read */
+	 size_t  elSize, /* size of each data-element */
+	 size_t *nmemb, /* write-only. how many elSize mebers are read */
+	 void  *attr) /* write-only. buffer where attribute is read in.
+	               * Must be big enough to hold buffer.
+	               */
+{
+	ATTR_CNTRL_BLOCK b;
+	CSF_FADDR pos;
+	PRECOND(CsfValidSize(elSize));
+	CHECKHANDLE_GOTO(m, error);
+
+	if (! READ_ENABLE(m))
+	{
+		M_ERROR(NOACCESS);
+		goto error;
+	}
+
+	if (CsfGetAttrBlock(m, id, &b) != 0) 
+	{
+		int i = CsfGetAttrIndex(id, &b);
+		*nmemb =	b.attrs[i].attrSize;
+		POSTCOND( ((*nmemb) % elSize) == 0);
+		*nmemb /= elSize;
+		POSTCOND( (*nmemb) > 0);
+		pos =	b.attrs[i].attrOffset;
+		(void)fseek(m->fp, (long)pos, SEEK_SET); 
+		m->read(attr,elSize, (size_t)(*nmemb),m->fp);
+		return(id);
+	}
+	else 
+		*nmemb = 0;
+error:	return(0);	/* not available  or an error */
+} /* MgetAttribute */
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/getx0.c b/Utilities/GDAL/frmts/pcraster/libcsf/getx0.c
new file mode 100644
index 0000000000..00cfc45caf
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/getx0.c
@@ -0,0 +1,43 @@
+/*
+ * getx0.c
+$Log: getx0.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:40  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+
+#include "csf.h"
+#include "csfimpl.h"
+
+/* x value, upper left co-ordinate of map
+ * returns the x value, upper left co-ordinate of map
+ */
+REAL8 RgetXUL(
+	const MAP *map) /* map handle */
+{
+	CHECKHANDLE(map);
+	return(map->raster.xUL);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/gety0.c b/Utilities/GDAL/frmts/pcraster/libcsf/gety0.c
new file mode 100644
index 0000000000..5307896b38
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/gety0.c
@@ -0,0 +1,45 @@
+/*
+ * gety0.c
+$Log: gety0.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:44  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* y value, upper left co-ordinate of map
+ * RgetYUL returns the y value of the upper left co-ordinate of map.
+ * Whether this is the largest or smallest y value depends on the
+ * projection (See MgetProjection()).
+ * returns the y value, upper left co-ordinate of map
+ */
+REAL8 RgetYUL(
+	const MAP *map) /* map handle */
+{
+	CHECKHANDLE(map);
+	return(map->raster.yUL);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/ggisfid.c b/Utilities/GDAL/frmts/pcraster/libcsf/ggisfid.c
new file mode 100644
index 0000000000..1678f2d94e
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/ggisfid.c
@@ -0,0 +1,47 @@
+/*
+ * ggisfid.c
+$Log: ggisfid.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:44  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.4  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.3  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.2  1994/09/02  16:24:56  cees
+ * added c2man doc
+ * const'ified map handle
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* gis file id
+ * returns
+ * the "gis file id" field
+ */
+UINT4 MgetGisFileId(
+	const MAP *map) /* map handle */
+{
+	CHECKHANDLE(map);
+	return(map->main.gisFileId);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/gmaxval.c b/Utilities/GDAL/frmts/pcraster/libcsf/gmaxval.c
new file mode 100644
index 0000000000..481494833d
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/gmaxval.c
@@ -0,0 +1,37 @@
+#include "csf.h"
+#include "csfimpl.h"
+
+/* get maximum cell value
+ * RgetMaxVal returns the value stored in
+ * the header as the maximum value. 
+ * If the minMaxStatus is MM_WRONGVALUE
+ * then a missing value is returned.
+ * returns 0 if argument maxVal is returned with a missing
+ * value, nonzero if not.
+ *
+ * example
+ * .so examples/csfstat.tr
+ */
+int RgetMaxVal(
+	const MAP *map, /* map handle */
+	void *maxVal)   /* write-only. Maximum value or missing value */
+{
+	/* use buffer that can hold largest 
+	 * cell representation
+	 */
+	CSF_VAR_TYPE buf_1;
+	void *buf = (void *)(&buf_1);
+
+	CHECKHANDLE(map);
+	CsfGetVarType(buf, &(map->raster.maxVal), RgetCellRepr(map));
+
+	map->file2app((size_t)1, buf);
+
+	if (map->minMaxStatus == MM_WRONGVALUE)
+		SetMV(map, buf);
+
+	CsfGetVarType(maxVal, buf, map->appCR);
+
+	return((!IsMV(map,maxVal)) &&
+ 		map->minMaxStatus!=MM_WRONGVALUE);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/gminval.c b/Utilities/GDAL/frmts/pcraster/libcsf/gminval.c
new file mode 100644
index 0000000000..b7d624fb76
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/gminval.c
@@ -0,0 +1,38 @@
+#include "csf.h"
+#include "csfimpl.h"
+
+/* get minimum cell value
+ * RgetMinVal returns the value stored in
+ * the header as the minimum value. 
+ * If the minMaxStatus is MM_WRONGVALUE
+ * then a missing value is returned.
+ * returns 0 if argument minVal is returned with a missing
+ * value, nonzero if not.
+ *
+ * example
+ * .so examples/csfstat.tr
+ */
+
+int RgetMinVal(
+	const MAP *map, /* map handle */
+	void *minVal)   /* write-only. Minimum value or missing value */
+{
+	/* use buffer that can hold largest 
+	 * cell representation
+	 */
+	CSF_VAR_TYPE buf_1;
+	void *buf = (void *)(&buf_1);
+
+	CHECKHANDLE(map);
+	CsfGetVarType(buf, &(map->raster.minVal), RgetCellRepr(map));
+
+	map->file2app((size_t)1, buf);
+
+	if (map->minMaxStatus == MM_WRONGVALUE)
+		SetMV(map, buf);
+
+	CsfGetVarType(minVal, buf, map->appCR);
+
+	return((!IsMV(map,minVal)) &&
+ 		map->minMaxStatus!=MM_WRONGVALUE);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/gnrcols.c b/Utilities/GDAL/frmts/pcraster/libcsf/gnrcols.c
new file mode 100644
index 0000000000..89de9e1d7f
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/gnrcols.c
@@ -0,0 +1,45 @@
+/*
+ * gnrcols.c
+$Log: gnrcols.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:44  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* number of columns in a map 
+ * RgetNrCols returns the number of columns in a map 
+ * returns the number of columns in a map 
+ *
+ * example
+ * .so examples/csfdump1.tr
+ */
+size_t RgetNrCols(
+	const MAP *map) /* map handle */
+{
+	return(map->raster.nrCols);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/gnrrows.c b/Utilities/GDAL/frmts/pcraster/libcsf/gnrrows.c
new file mode 100644
index 0000000000..539e9d9ee7
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/gnrrows.c
@@ -0,0 +1,45 @@
+/*
+ * gnrrows.c
+$Log: gnrrows.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:44  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* number of rows in a map 
+ * RgetNrCols returns the number of rows in a map 
+ * returns the number of rows in a map 
+ *
+ * example
+ * .so examples/csfdump1.tr
+ */
+size_t RgetNrRows(
+	const MAP *map) /* map handle */
+{
+	return(map->raster.nrRows);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/gproj.c b/Utilities/GDAL/frmts/pcraster/libcsf/gproj.c
new file mode 100644
index 0000000000..bbcb068c31
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/gproj.c
@@ -0,0 +1,46 @@
+/*
+ * gproj.c
+$Log: gproj.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:44  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* get projection type
+ * MgetProjection returns the projection type of the map.
+ * In version 2, projections are simplified. We only discern between
+ * a projection with y increasing (PT_YINCT2B) and decreasing (PT_YDECT2B) 
+ * from top to bottom.
+ * The old constants are mapped to these two.
+ * returns PT_YINCT2B or PT_YDECT2B
+ */
+CSF_PT MgetProjection(
+	const MAP *map) /* map handle */
+{
+	return (map->main.projection) ? PT_YDECT2B : PT_YINCT2B;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/gputproj.c b/Utilities/GDAL/frmts/pcraster/libcsf/gputproj.c
new file mode 100644
index 0000000000..f2996c8a00
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/gputproj.c
@@ -0,0 +1,62 @@
+/*
+ * gputproj.c
+$Log: gputproj.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:44  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* change projection type of map
+ * MputProjection type changes the projection type of a map.
+ * In version 2, projections are simplified. We only discern between
+ * a projection with y increasing (PT_YINCT2B=0) and decreasing (PT_YDECT2B=1) 
+ * from top to bottom.
+ * All old constants that denote a projection with y decreasing are nonzero.
+ * And the old constant that denote a projection with y decreasing (PT_XY) is 0.
+ * returns the new projection (PT_YINCT2B or PT_YDECT2B) or MV_UINT2 if an 
+ * error occurred.
+ *
+ * Merrno
+ * NOACCESS
+ */
+CSF_PT MputProjection(
+	MAP *map,      /* map handle */
+	CSF_PT p)       /* projection type, all nonzero values are mapped to
+	                * 1 (PT_YDECT2B) 
+	                */
+{
+	CHECKHANDLE_GOTO(map, error);
+	if(! WRITE_ENABLE(map))
+	{
+		M_ERROR(NOACCESS);
+		goto error;
+	}
+	map->main.projection =  (p) ? PT_YDECT2B : PT_YINCT2B;
+	return map->main.projection; 
+error:	return(MV_UINT2);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/gvalscal.c b/Utilities/GDAL/frmts/pcraster/libcsf/gvalscal.c
new file mode 100644
index 0000000000..1fa95ae242
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/gvalscal.c
@@ -0,0 +1,44 @@
+/*
+ * gvalscal.c
+$Log: gvalscal.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:44  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/05  13:15:05  cees
+ * added c2man
+ * const'fied map handle
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* get value scale of map
+ * returns the value scale of a map which is one of the 
+ * constants prefixed by "VS_".
+ *
+ */
+CSF_VS RgetValueScale(
+	const MAP *map) /* map handle */
+{
+	return(map->raster.valueScale);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/gvartype.c b/Utilities/GDAL/frmts/pcraster/libcsf/gvartype.c
new file mode 100644
index 0000000000..7d4d92fd7e
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/gvartype.c
@@ -0,0 +1,50 @@
+/*
+ * gvartype.c
+$Log: gvartype.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:44  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/05  13:20:54  cees
+ * const'ified 2nd arg
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* perform a simple byte-copy of 8,4,2 or 1 byte (LIBRARY_INTERNAL)
+ */
+void CsfGetVarType( void *dest, const CSF_VAR_TYPE *src, CSF_CR cellRepr)
+{
+	switch (LOG_CELLSIZE(cellRepr))  /* 2log size */
+	{
+		case 3 : ((UINT4 *)dest)[1] = ((const UINT4 *)src)[1];
+		         ((UINT4 *)dest)[0] = ((const UINT4 *)src)[0];
+			 break;
+		case 2 : (*(UINT4 *)dest)    = (*(const UINT4 *)src);
+			break;
+		case 1 : (*(UINT2 *)dest)    = (*(const UINT2 *)src);
+			break;
+		default: POSTCOND(LOG_CELLSIZE(cellRepr) == 0);
+			 (*(UINT1 *)dest)    = (*(const UINT1 *)src);
+	}
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/gversion.c b/Utilities/GDAL/frmts/pcraster/libcsf/gversion.c
new file mode 100644
index 0000000000..5c8e435893
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/gversion.c
@@ -0,0 +1,42 @@
+/*
+ * gversion.c
+$Log: gversion.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:44  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/05  13:22:25  cees
+ * const'ified map handle
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* get CSF version
+ * returns CSF version
+ */
+UINT4 MgetVersion(
+ const MAP *map) /* map handle */
+{
+	CHECKHANDLE(map);
+	return (UINT4) (map->main.version);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/ismv.c b/Utilities/GDAL/frmts/pcraster/libcsf/ismv.c
new file mode 100644
index 0000000000..b3dbcadfac
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/ismv.c
@@ -0,0 +1,87 @@
+/*
+ * ismv.c
+$Log: ismv.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:44  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/05  13:27:26  cees
+ * changed for appCR field
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+/********************************************************/
+/*							*/
+/*	IsMV						*/
+/*							*/
+/********************************************************/
+/* check if cellValue is a missing value		*/
+/********************************************************/
+
+
+#include "csf.h"
+#include "csfimpl.h"
+
+/* test if a value is missing value
+ * returns 0 if not, nonzero if it is a missing value
+ */
+int  IsMV(
+	const MAP *map, /* map handle */
+	const void *cellValue) /* value to be tested */
+{
+	return(IsMVcellRepr(map->appCR, cellValue));
+}
+
+/* test if a value is missing value
+ * returns 0 if not, nonzero if it is a missing value
+ */
+int  IsMVcellRepr(
+	CSF_CR cellRepr,        /* cell representation of argument cellValue.
+	                        * That is one of the constants prefixed by CR_.
+	                        */
+	const void *cellValue) /* value to be tested */
+{
+
+	if (IS_SIGNED(cellRepr))
+	 	switch ( (cellRepr & CSF_SIZE_MV_MASK ) >> CSF_POS_SIZE_MV_MASK)
+		{
+		case 0:	return(*((const INT1 *)cellValue) == MV_INT1);
+		case 1:	return(*((const INT2 *)cellValue) == MV_INT2);
+		default:return(*((const INT4 *)cellValue) == MV_INT4);
+		}
+	else
+		if (IS_REAL(cellRepr))
+		{
+			if (cellRepr == CR_REAL4)
+				return(IS_MV_REAL4(cellValue));
+			else
+				return(IS_MV_REAL8(cellValue));
+		}
+		else
+		{
+			switch ( (cellRepr & CSF_SIZE_MV_MASK ) >> CSF_POS_SIZE_MV_MASK)
+			{
+			case 0:    return(*((const UINT1 *)cellValue) == MV_UINT1);
+			case 1:	   return(*((const UINT2 *)cellValue) == MV_UINT2);
+			default:   return(*((const UINT4 *)cellValue) == MV_UINT4);
+			}
+		}
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/kernlcsf.c b/Utilities/GDAL/frmts/pcraster/libcsf/kernlcsf.c
new file mode 100644
index 0000000000..c12e0560a8
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/kernlcsf.c
@@ -0,0 +1,147 @@
+/*
+ * kernlcsf.c
+ *    Functions to create  and maintain the csf-kernel
+ *     runtime structures
+ */
+
+/**************************************************************************/
+/*  KERNLCSF.C                                                            */
+/*                                                                        */
+/*                                                                        */
+/**************************************************************************/
+
+/********/
+/* USES */
+/********/
+#include "csf.h"
+#include "csfimpl.h"
+
+#include <stdlib.h>
+
+/***************/
+/* EXTERNALS   */
+/***************/
+
+static MAP **mapList    = NULL;
+static size_t mapListLen = 4;
+
+/*********************/
+/* LOCAL DEFINITIONS */
+/*********************/
+
+/**********************/
+/* LOCAL DECLARATIONS */
+/**********************/
+
+/******************/
+/* IMPLEMENTATION */
+/******************/
+
+
+/* close all open maps at exit  (LIBRARY_INTERNAL)
+ * passed through atexit to c-library
+ * exit code
+ */
+static void CsfCloseCsfKernel(void)
+{
+  size_t i;
+
+  for(i = 0; i < mapListLen; i++)
+   if(mapList[i] != NULL)
+    if(Mclose(mapList[i]))
+      (void)fprintf(stderr,"CSF_INTERNAL_ERROR: unable to close %s at exit\n",
+        mapList[i]->fileName);
+  CSF_FREE(mapList);
+  mapList = NULL;
+}
+
+/* boot the CSF runtime library (LIBRARY_INTERNAL)
+ * CsfBootCsfKernel creates the mapList and adds the function to
+ *  close all files at a system exit
+ *
+ * NOTE
+ * Note that CsfBootCsfKernel never returns if there isn't enough
+ * memory to allocate an array of mapListLen pointers, or if
+ * the atexit() call fails
+ */
+void CsfBootCsfKernel(void)
+{
+  POSTCOND(mapList == NULL);
+
+  mapList = (MAP **)calloc(mapListLen,sizeof(MAP *));
+  if (mapList == NULL) {
+    (void)fprintf(stderr,"CSF_INTERNAL_ERROR: Not enough memory to use CSF-files\n");
+    exit(1);
+  }
+
+  if (atexit(CsfCloseCsfKernel)) {
+    (void)fprintf(stderr,"CSF_INTERNAL_ERROR: Impossible to close CSF-files automatically at exit\n");
+    exit(1);
+  }
+}
+
+/* check if the kernel is booted (LIBRARY_INTERNAL)
+ * returns 0 if not, nonzero if already booted
+ */
+int CsfIsBootedCsfKernel(void)
+{
+  return(mapList != NULL);
+}
+
+/* put map in run time structure (LIBRARY_INTERNAL)
+ * Every map opened or created is
+ * registered in a list for verification
+ * if functions get a valid map handle
+ * passed and for automatic closing
+ * at exit if the application forgets it.
+ */
+void CsfRegisterMap(
+  MAP *m) /* map handle to be registered, the field m->mapListId is
+           * initialized
+           */
+{
+  size_t i=0;
+
+  while (mapList[i] != NULL && i < mapListLen)
+    i++;
+
+  if(i == mapListLen)
+  {
+    size_t j;
+    /* double size */
+    mapListLen *=2;
+    mapList=realloc(mapList,sizeof(MAP *)*mapListLen);
+    if (mapList == NULL) {
+     (void)fprintf(stderr,"CSF_INTERNAL_ERROR: Not enough memory to use CSF-files\n");
+      exit(1);
+    }
+    /* initialize new part, i at begin */
+    for(j=i; j < mapListLen; ++j)
+      mapList[j]=0;
+  }
+
+  mapList[i] =   m;
+  m->mapListId = i;
+}
+
+/* remove map from run time structure (LIBRARY_INTERNAL)
+ * The map handle will become invalid.
+ */
+void CsfUnloadMap(
+  MAP *m) /* map handle */
+{
+  POSTCOND(CsfIsValidMap(m));
+
+  mapList[m->mapListId] = NULL;
+  m->mapListId = -1;
+}
+
+/* check if the map handle is created via the csf kernel (LIBRARY_INTERNAL)
+ */
+int CsfIsValidMap(
+  const MAP *m) /* map handle */
+{
+  return(CsfIsBootedCsfKernel() && m != NULL
+          && m->mapListId >= 0 && ((size_t)m->mapListId) < mapListLen
+    && mapList[m->mapListId] == m);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/legend.c b/Utilities/GDAL/frmts/pcraster/libcsf/legend.c
new file mode 100644
index 0000000000..47f7884bd4
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/legend.c
@@ -0,0 +1,134 @@
+
+/*
+ * legend.c 
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* get the number of entries with a negative
+ * number for a type 1 legend
+ */
+static int NrLegendEntries(MAP *m)
+{
+	int size = CsfAttributeSize(m, ATTR_ID_LEGEND_V2);
+	if (size == 0)
+	{
+		if ( (size = -CsfAttributeSize(m, ATTR_ID_LEGEND_V1)) != 0 )
+			size -= CSF_LEGEND_ENTRY_SIZE;
+	}
+	return size/CSF_LEGEND_ENTRY_SIZE;
+}
+static int CmpEntries(
+	CSF_LEGEND *e1,
+	CSF_LEGEND *e2)
+{
+	return (int) ((e1->nr) - (e2->nr));
+}
+
+static void SortEntries(
+	CSF_LEGEND *l, /* version 2 legend */
+	size_t        nr) /* nr entries + name */
+{
+#ifndef USE_IN_PCR
+	typedef int (*QSORT_CMP)(const void *e1, const void *e2);
+#endif
+	PRECOND(nr >= 1);
+	qsort(l+1, (size_t)nr-1, sizeof(CSF_LEGEND), (QSORT_CMP)CmpEntries);
+}
+
+/* get the number of legend entries
+ * MgetNrLegendEntries tries to find a version 2 or version 1
+ * legend. The return number can be used to allocate the appropriate
+ * array for legend. 
+ * returns the number of entries in a legend plus 1 (for the name of the legend)
+ * or 0 if there is no legend
+ */
+size_t MgetNrLegendEntries(
+	MAP *m) /* the map pointer */
+{
+	return (size_t)ABS(NrLegendEntries(m));
+}
+
+/* read a legend
+ * MgetLegend reads a version 2 and 1 legend.
+ * Version 1 legend are converted to version 2: the first
+ * array entry holds an empty string in the description field.
+ * returns 
+ * 0 if no legend is available or in case of an error,
+ * nonzero otherwise
+ */
+int MgetLegend(
+	MAP *m,        /* Map handle */
+	CSF_LEGEND *l) /* array large enough to hold name and all entries, 
+	                * the entries are sorted 
+	                * struct CSF_LEGEND is typedef'ed in csfattr.h
+	                */
+{
+	CSF_ATTR_ID id = NrLegendEntries(m) < 0 ? ATTR_ID_LEGEND_V1 : ATTR_ID_LEGEND_V2;
+	size_t size;
+	CSF_FADDR pos = CsfGetAttrPosSize(m, id, &size);
+	size_t i,nr,start = 0;
+        if (pos == 0)
+        	return 0;
+        fseek(m->fp, (long)pos, SEEK_SET);
+        if (id == ATTR_ID_LEGEND_V1)
+        { 
+        	/* empty name */
+        	l[0].nr       = 0;
+        	l[0].descr[0] = '\0';
+        	start = 1; /* don't read in name */
+        }
+	nr = size/CSF_LEGEND_ENTRY_SIZE;
+	for(i = start; i < nr+start; i++)
+	{
+		m->read(&(l[i].nr), sizeof(INT4), (size_t)1, m->fp);
+		m->read(l[i].descr, sizeof(char), (size_t)CSF_LEGEND_DESCR_SIZE, m->fp);
+	}
+	SortEntries(l, nr+start);
+	return 1;
+}
+
+/* write a legend
+ * MputLegend writes a (version 2) legend to a map replacing
+ * the old one if existent.
+ * See  csfattr.h for the legend structure.
+ *
+ * returns 
+ * 0 in case of an error,
+ * nonzero otherwise
+ *
+ * Merrno
+ * NOACCESS
+ * WRITE_ERROR
+ */
+int MputLegend(
+	MAP *m,        /* Map handle */
+	CSF_LEGEND *l, /* read-write, array with name and entries, the entries
+	                * are sorted before writing to the file.
+	                * Strings are padded with zeros.
+	                */
+	size_t nrEntries) /* number of array elements. That is name plus real legend entries */
+{
+	int i = NrLegendEntries(m);
+	CSF_ATTR_ID id = i < 0 ? ATTR_ID_LEGEND_V1 : ATTR_ID_LEGEND_V2;
+	if (i)
+		if (! MdelAttribute(m, id))
+			return 0;
+	SortEntries(l, nrEntries);
+	if (CsfSeekAttrSpace(m, ATTR_ID_LEGEND_V2, (size_t)(nrEntries*CSF_LEGEND_ENTRY_SIZE)) == 0)
+			return 0;
+	for(i = 0; i < (int)nrEntries; i++)
+	{
+	     if(
+		m->write(&(l[i].nr), sizeof(INT4), (size_t)1, m->fp) != 1 ||
+		m->write(
+		 CsfStringPad(l[i].descr,(size_t)CSF_LEGEND_DESCR_SIZE), 
+		 sizeof(char), (size_t)CSF_LEGEND_DESCR_SIZE, m->fp) 
+		 != CSF_LEGEND_DESCR_SIZE )
+		 {
+		 	M_ERROR(WRITE_ERROR);
+		 	return 0;
+		 }
+	}
+	return 1;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/makefile.vc b/Utilities/GDAL/frmts/pcraster/libcsf/makefile.vc
new file mode 100644
index 0000000000..7acb17e900
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/makefile.vc
@@ -0,0 +1,29 @@
+GDAL_ROOT	=	..\..\..
+EXTRAFLAGS=-DUSE_IN_GDAL
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+
+OBJ	=	endian.obj swapio.obj vs2.obj vsdef.obj rextend.obj\
+vsvers.obj legend.obj strpad.obj strconst.obj vsis.obj ruseas.obj rmalloc.obj create2.obj \
+_getcell.obj _getrow.obj _gsomece.obj _putcell.obj _rputrow.obj\
+attravai.obj attrsize.obj csfglob.obj csfsup.obj delattr.obj\
+filename.obj gattrblk.obj gattridx.obj gcellrep.obj \
+ cellsize.obj gdattype.obj getattr.obj getx0.obj\
+gety0.obj ggisfid.obj gmaxval.obj gminval.obj gnrcols.obj gnrrows.obj\
+gproj.obj gputproj.obj gvalscal.obj gvartype.obj gversion.obj ismv.obj\
+kernlcsf.obj mclose.obj mopen.obj mperror.obj \
+pgisfid.obj pmaxval.obj pminval.obj putallmv.obj putattr.obj\
+putsomec.obj putx0.obj puty0.obj pvalscal.obj rattrblk.obj rcomp.obj\
+rcoords.obj rdup2.obj reseterr.obj \
+rrowcol.obj setmv.obj setvtmv.obj moreattr.obj\
+wattrblk.obj setangle.obj angle.obj dumconv.obj trackmm.obj
+
+GDAL_ROOT	=	..\..\..
+
+
+default:	$(OBJ)
+	copy *.obj ..\..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/mclose.c b/Utilities/GDAL/frmts/pcraster/libcsf/mclose.c
new file mode 100644
index 0000000000..33c1b0ea28
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/mclose.c
@@ -0,0 +1,96 @@
+/*******************************************************/
+/*  Mclose.c               */
+/*******************************************************/
+/*  close map and if write permission write all    */
+/*  resident data to file                           */
+/*******************************************************/
+
+#include "csf.h"
+#include "csfimpl.h"
+
+#include <string.h>  /* memset() */
+
+/* close a map
+ * the Mclose function closes a map
+ * if the map is being used for output 
+ * all header data is rewritten first
+ * returns Upon succesful completion 0 is returned. 
+ * Otherwise, a non-zero value is returned
+ *
+ * Merrno
+ * WRITE_ERROR (map descriptor still in tact if this happens)
+ */
+int Mclose(
+  MAP *m) /* map to close, map descriptor
+             * is deallocated
+             */
+{
+  CHECKHANDLE_GOTO(m, error);
+
+  if (m->minMaxStatus == MM_WRONGVALUE)
+  {
+    CsfSetVarTypeMV( &(m->raster.minVal), m->raster.cellRepr);
+    CsfSetVarTypeMV( &(m->raster.maxVal), m->raster.cellRepr);
+  }
+
+  /* if write permission , write all header data to file */
+  if (WRITE_ENABLE(m))
+  {
+    char filler[MAX_HEADER_FILL_SIZE];
+    (void)memset(filler, 0x0, (size_t)MAX_HEADER_FILL_SIZE);
+
+    if (m->main.byteOrder != ORD_OK) {
+     CsfSwap((void*)&(m->raster.minVal), CELLSIZE(m->raster.cellRepr),(size_t)1);
+     CsfSwap((void*)&(m->raster.maxVal), CELLSIZE(m->raster.cellRepr),(size_t)1);
+    }
+
+    fseek(m->fp,(long)ADDR_MAIN_HEADER,SEEK_SET);
+    if(m->write((void*)&(m->main.signature),sizeof(char), CSF_SIG_SPACE,m->fp)
+                                                       != CSF_SIG_SPACE ||
+       m->write((void*)&(m->main.version),sizeof(UINT2),(size_t)1,m->fp)!=1 ||
+       m->write((void*)&(m->main.gisFileId),sizeof(UINT4),(size_t)1,m->fp)!=1 ||
+       m->write((void*)&(m->main.projection),sizeof(UINT2),(size_t)1,m->fp)!=1 ||
+       m->write((void*)&(m->main.attrTable),sizeof(UINT4),(size_t)1,m->fp)!=1 ||
+       m->write((void*)&(m->main.mapType),sizeof(UINT2),(size_t)1,m->fp)!=1 ||
+         fwrite((void*)&(m->main.byteOrder),sizeof(UINT4),(size_t)1,m->fp)!=1 ||
+       m->write((void*)filler, sizeof(char), MAIN_HEADER_FILL_SIZE ,m->fp)
+                                                          != MAIN_HEADER_FILL_SIZE )
+    {  
+      M_ERROR(WRITE_ERROR);
+      goto error;
+    }
+    fseek(m->fp,ADDR_SECOND_HEADER, SEEK_SET);
+
+    if (    m->write((void*)&(m->raster.valueScale),sizeof(UINT2),(size_t)1,m->fp) !=1 ||
+      m->write((void*)&(m->raster.cellRepr), sizeof(UINT2),(size_t)1,m->fp) !=1 ||
+        fwrite((void*)&(m->raster.minVal), sizeof(CSF_VAR_TYPE),(size_t)1,m->fp) !=1 ||
+        fwrite((void*)&(m->raster.maxVal), sizeof(CSF_VAR_TYPE),(size_t)1,m->fp) !=1 ||
+      m->write((void*)&(m->raster.xUL), sizeof(REAL8),(size_t)1,m->fp) !=1 ||
+      m->write((void*)&(m->raster.yUL), sizeof(REAL8),(size_t)1,m->fp) !=1 ||
+      m->write((void*)&(m->raster.nrRows), sizeof(UINT4),(size_t)1,m->fp) !=1 ||
+      m->write((void*)&(m->raster.nrCols), sizeof(UINT4),(size_t)1,m->fp) !=1 ||
+      m->write((void*)&(m->raster.cellSize), sizeof(REAL8),(size_t)1,m->fp) !=1 ||
+      m->write((void*)&(m->raster.cellSizeDupl), sizeof(REAL8),(size_t)1,m->fp) !=1 ||
+      m->write((void*)&(m->raster.angle), sizeof(REAL8),(size_t)1,m->fp) !=1 ||
+            m->write((void*)filler, sizeof(char), RASTER_HEADER_FILL_SIZE ,m->fp)
+                                                          != RASTER_HEADER_FILL_SIZE )
+    {
+      M_ERROR(WRITE_ERROR);
+      goto error;
+    }
+  }
+
+  (void)fclose(m->fp);
+  CsfUnloadMap(m);
+
+  /* clear the space, to avoid typical errors such as
+     accessing the map after Mclose */
+      (void)memset((void *)m->fileName, 0x0, strlen(m->fileName));
+      CSF_FREE(m->fileName);
+
+      (void)memset((void *)m, 0x0, sizeof(MAP));
+  CSF_FREE(m);
+
+  return(0);
+error:  return(1);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/mopen.c b/Utilities/GDAL/frmts/pcraster/libcsf/mopen.c
new file mode 100644
index 0000000000..46de10e0d7
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/mopen.c
@@ -0,0 +1,202 @@
+
+/*******************************************************/
+/*	FUNCTIE   MOPEN.C	                       */
+/*******************************************************/
+/*******************************************************/
+
+
+#include <string.h>
+
+#include "csf.h"
+#include "csfimpl.h"
+
+
+static const char *openModes[3] = {
+	S_READ,
+	S_WRITE,
+	S_READ_WRITE
+	};
+
+/* Return the access mode of m
+ * MopenPerm returns the permission.
+ * Note that M_WRITE is deprecated
+ */
+enum MOPEN_PERM MopenPerm(
+	const MAP *m)
+{
+ return m->fileAccessMode;
+}
+
+/* open an existing CSF file
+ * Mopen opens a CSF file. It allocates space for
+ * the MAP runtime-structure, reads the header file
+ * and performs test to determine if it is a CSF file.
+ * The MinMaxStatus is set to MM_KEEPTRACK if the min/max
+ * header fields are not MV or MM_WRONGVALUE if one of them
+ * contains a MV.
+ * returns a pointer the MAP runtime structure if the file is
+ * successfully opened as a CSF file, NULL if not.
+ *
+ * Merrno
+ * NOCORE BADACCESMODE OPENFAILED NOT_CSF BAD_VERSION
+ *
+ * EXAMPLE
+ * .so examples/testcsf.tr
+ */
+MAP  *Mopen(
+	const char *fileName, /* file name */
+	enum MOPEN_PERM mode) /*  file permission */
+{
+ MAP *m;
+ UINT4 s; /* swap detection field */
+ 
+ if (! CsfIsBootedCsfKernel())
+ 	CsfBootCsfKernel();
+ 
+ m = (MAP *)CSF_MALLOC(sizeof(MAP));
+ 
+ if (m == NULL)
+ {
+ 	M_ERROR(NOCORE);
+ 	goto error_mapMalloc;
+ }
+ 
+ m->fileName = (char *)CSF_MALLOC(strlen(fileName)+1);
+ if (m->fileName == NULL)
+ {
+ 	M_ERROR(NOCORE);
+ 	goto error_fnameMalloc;
+ }
+ (void)strcpy(m->fileName,fileName);
+ 
+ /* check file mode validation */
+ if ( IS_BAD_ACCESS_MODE(mode))
+ {
+ 	M_ERROR(BADACCESMODE);
+ 	goto error_notOpen;
+ }
+ m->fileAccessMode = mode;
+ 
+ 
+ /*  check if file can be opened or exists */
+ m->fp = fopen(fileName, openModes[mode-1]);
+ if (m->fp == NULL)
+ {
+ 	M_ERROR(OPENFAILED);
+ 	goto error_notOpen;
+ }
+ 
+ /*  check if file could be C.S.F.-file 
+  *   (at least 256 bytes long) 
+  *  otherwise the signature comparison will
+  *  fail
+  */
+ 
+ (void)fseek(m->fp,0L, SEEK_END);
+ if (ftell(m->fp) < (long)ADDR_DATA)
+ {
+ 	M_ERROR(NOT_CSF);
+ 	goto error_open;
+ }
+
+ (void)fseek(m->fp, 14+CSF_SIG_SPACE, SEEK_SET);
+ (void)fread((void *)&s, sizeof(UINT4),(size_t)1,m->fp);
+ if (s != ORD_OK) {
+	m->write = CsfWriteSwapped;
+	m->read  = CsfReadSwapped;
+ }
+ else {
+#ifdef DEBUG
+	m->read  = (CSF_READ_FUNC)CsfReadPlain;
+	m->write = (CSF_READ_FUNC)CsfWritePlain;
+#else
+	m->read  = (CSF_READ_FUNC)fread;
+	m->write = (CSF_READ_FUNC)fwrite;
+#endif
+ }
+ 
+ (void)fseek(m->fp, ADDR_MAIN_HEADER, SEEK_SET);
+ m->read((void *)&(m->main.signature), sizeof(char), CSF_SIG_SPACE,m->fp);
+ m->read((void *)&(m->main.version),   sizeof(UINT2),(size_t)1,m->fp);
+ m->read((void *)&(m->main.gisFileId), sizeof(UINT4),(size_t)1,m->fp);
+ m->read((void *)&(m->main.projection),sizeof(UINT2),(size_t)1,m->fp);
+ m->read((void *)&(m->main.attrTable), sizeof(UINT4),(size_t)1,m->fp);
+ m->read((void *)&(m->main.mapType),  sizeof(UINT2),(size_t)1,m->fp);
+ m->read((void *)&(m->main.byteOrder), sizeof(UINT4),(size_t)1,m->fp);
+ /*                                             14+CSF_SIG_SPACE
+  */
+ 
+ (void)fseek(m->fp, ADDR_SECOND_HEADER, SEEK_SET);
+ m->read((void *)&(m->raster.valueScale), sizeof(UINT2),(size_t)1,m->fp);
+ m->read((void *)&(m->raster.cellRepr), sizeof(UINT2),(size_t)1,m->fp);
+
+ (void)fread((void *)&(m->raster.minVal), sizeof(CSF_VAR_TYPE),(size_t)1,m->fp);
+ (void)fread((void *)&(m->raster.maxVal), sizeof(CSF_VAR_TYPE),(size_t)1,m->fp);
+ if (s != ORD_OK) {
+  CsfSwap((void *)&(m->raster.minVal), CELLSIZE(m->raster.cellRepr),(size_t)1);
+  CsfSwap((void *)&(m->raster.maxVal), CELLSIZE(m->raster.cellRepr),(size_t)1);
+ }
+
+ m->read((void *)&(m->raster.xUL), sizeof(REAL8),(size_t)1,m->fp);
+ m->read((void *)&(m->raster.yUL), sizeof(REAL8),(size_t)1,m->fp);
+ m->read((void *)&(m->raster.nrRows), sizeof(UINT4),(size_t)1,m->fp);
+ m->read((void *)&(m->raster.nrCols), sizeof(UINT4),(size_t)1,m->fp);
+ m->read((void *)&(m->raster.cellSize), sizeof(REAL8),(size_t)1,m->fp);
+ m->read((void *)&(m->raster.cellSizeDupl), sizeof(REAL8),(size_t)1,m->fp);
+
+ m->read((void *)&(m->raster.angle), sizeof(REAL8),(size_t)1,m->fp);
+
+
+ /*  check signature C.S.F.file	
+  */
+ if(strncmp(m->main.signature,CSF_SIG,CSF_SIZE_SIG)!=0)
+ {
+ 	M_ERROR(NOT_CSF);
+ 	goto error_open;
+ }
+ /* should be read right
+  */
+ POSTCOND(m->main.byteOrder == ORD_OK);
+ /*  restore byteOrder C.S.F.file (Intel or Motorola)  */
+ m->main.byteOrder=s;
+ 
+ /*  check version C.S.F.file	
+  */
+ if (m->main.version != CSF_VERSION_1
+    && (m->main.version != CSF_VERSION_2))
+ {
+ 	M_ERROR(BAD_VERSION);
+ 	goto error_open;
+ }
+ 
+ if (m->main.version == CSF_VERSION_1)
+ 	m->raster.angle = 0.0;
+ 
+ CsfFinishMapInit(m);
+ 
+ CsfRegisterMap(m);
+ 
+ /* install cell value converters: (app2file,file2app)
+  */
+ m->app2file = CsfDummyConversion;
+ m->file2app = CsfDummyConversion;
+ m->appCR    = m->raster.cellRepr;
+ 
+ if (IsMV(m,&(m->raster.minVal)) ||
+     IsMV(m,&(m->raster.maxVal))   )
+ 	m->minMaxStatus = MM_WRONGVALUE;
+ else
+ 	m->minMaxStatus = MM_KEEPTRACK;
+ 
+ return(m);
+ 
+ error_open:
+ PRECOND(m->fp != NULL);
+ (void)fclose(m->fp);
+ error_notOpen: 
+ CSF_FREE(m->fileName);
+ error_fnameMalloc: 
+ CSF_FREE(m);
+error_mapMalloc:
+	 return(NULL);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/moreattr.c b/Utilities/GDAL/frmts/pcraster/libcsf/moreattr.c
new file mode 100644
index 0000000000..33cf17fc08
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/moreattr.c
@@ -0,0 +1,182 @@
+/*
+ * moreattr.c 
+ */
+
+#include "csf.h"
+#include "csfimpl.h"
+
+#include <string.h> /* strlen */
+
+/* get the size of the history attribute
+ * returns
+ *  the size of history buffer INCLUDING the termimating `\0`,
+ *  or 0 if not available or in case of error
+ */
+size_t MgetHistorySize(MAP *m) /* the map to get it from */
+{
+	return (size_t)CsfAttributeSize(m, ATTR_ID_HISTORY);
+}
+
+/* get the size of the description attribute
+ * returns
+ *  the size of description buffer INCLUDING the termimating `\0`,
+ *  or 0 if not available or in case of error
+ */
+size_t MgetDescriptionSize(MAP *m) /* the map to get it from */
+{
+	return (size_t)CsfAttributeSize(m, ATTR_ID_DESCRIPTION);
+}
+
+/* get the number of colour palette entries
+ * MgetNrColourPaletteEntries returns the number of rgb tupels
+ * of the colour palette. Each tupel is a sequence of 3 UINT2
+ * words describing red, green and blue.
+ * returns
+ *  the number of rgb tupels,
+ *  or 0 if not available or in case of error
+ */
+size_t MgetNrColourPaletteEntries(MAP *m) /* the map to get it from */
+{
+	size_t s = (size_t)CsfAttributeSize(m, ATTR_ID_COLOUR_PAL);
+	POSTCOND( (s % (3*sizeof(UINT2))) == 0);
+	return s / (3*sizeof(UINT2));
+}
+
+/* get the number of grey palette entries
+ * MgetNrGreyPaletteEntries returns the number of grey tupels
+ * of the grey palette. Each tupel is one UINT2
+ * words describing the intensity: low, 0 is black, high is white.
+ * returns
+ *  the number of grey tupels,
+ *  or 0 if not available or in case of error
+ */
+size_t MgetNrGreyPaletteEntries(MAP *m) /* the map to get it from */
+{
+	size_t s = (size_t)CsfAttributeSize(m, ATTR_ID_GREY_PAL);
+	POSTCOND( (s % (sizeof(UINT2))) == 0);
+	return s / (sizeof(UINT2));
+}
+
+/* get the description attribute
+ * returns
+ *  0 if not available or in case of error,
+ * nonzero otherwise
+ */
+int MgetDescription(MAP *m,    /* the map to get it from */
+		char *des) /* the  resulting description string */
+{
+	size_t size;
+	return CsfGetAttribute(m, ATTR_ID_DESCRIPTION, sizeof(char), &size, des);
+}
+
+/* get the history attribute
+ * returns
+ *  0 if not available or in case of error,
+ * nonzero otherwise
+ */
+int MgetHistory(MAP *m,    /* the map to get it from */
+		char *history) /* the  resulting history string */
+{
+	size_t size;
+	return CsfGetAttribute(m, ATTR_ID_HISTORY, sizeof(char), &size, history);
+}
+
+/* get the colour palette 
+ * MgetColourPalette fills the pal argument with a number of rgb tupels
+ * of the colour palette. Each tupel is a sequence of 3 UINT2
+ * words describing red, green and blue. Thus if the map has 8
+ * colour palette entries it puts 24 UINT2 values in pal.
+ * returns
+ *  0 if not available or in case of error,
+ * nonzero otherwise
+ */
+int MgetColourPalette(MAP *m, /* the map to get it from */
+	UINT2 *pal) /* the resulting palette */
+{
+	size_t size;
+	return CsfGetAttribute(m, ATTR_ID_COLOUR_PAL, sizeof(UINT2), &size, pal);
+}
+
+/* get the grey palette 
+ * MgetGreyPalette fills the pal argument with a number of grey tupels
+ * of the grey palette. Each tupel is one UINT2
+ * words describing the intensity: low, 0 is black, high is white.
+ * returns
+ *  0 if not available or in case of error,
+ * nonzero otherwise
+ */
+int MgetGreyPalette(MAP *m, /* the map to get it from */
+	UINT2 *pal) /* the resulting palette */
+{
+	size_t size;
+	return CsfGetAttribute(m, ATTR_ID_GREY_PAL, sizeof(UINT2), &size, pal);
+}
+
+/* put the description attribute
+ * MputDescription writes the description string to a map.
+ * An existing description is overwritten.
+ * returns
+ *  0 if not available or in case of error,
+ * nonzero otherwise
+ */
+int MputDescription(MAP *m,    /* the map to get it from */
+		char *des) /* the  new description string. 
+		            * Is a C-string, `\0`-terminated 
+		            */
+{
+	return CsfUpdateAttribute(m, ATTR_ID_DESCRIPTION, sizeof(char), 
+			strlen(des)+1, des);
+}
+
+/* put the history attribute
+ * MputHistory writes the history string to a map.
+ * An existing history is overwritten.
+ * returns
+ *  0 if not available or in case of error,
+ * nonzero otherwise
+ */
+int MputHistory(MAP *m,    /* the map to get it from */
+		char *history) /* the new history string 
+		                * Is a C-string, `\0`-terminated 
+		                */
+{
+	return CsfUpdateAttribute(m, ATTR_ID_HISTORY, sizeof(char), 
+			strlen(history)+1, history);
+}
+
+/* put the colour palette 
+ * MputColourPalette writes the pal argument that is filled 
+ * with a number of rgb tupels
+ * of the colour palette to the map. Each tupel is a sequence of 3 UINT2
+ * words describing red, green and blue. Thus if the map has 8
+ * colour palette entries it puts 24 UINT2 values in map palette.
+ * An existing colour palette is overwritten.
+ * returns
+ *  0 if not available or in case of error,
+ * nonzero otherwise
+ */
+int MputColourPalette(MAP *m, /* the map to get it from */
+	UINT2 *pal, /* the new palette */
+	size_t nrTupels) /* the number of 3 UINT2 words tupels of pal */
+{
+	return CsfUpdateAttribute(m, ATTR_ID_COLOUR_PAL, sizeof(UINT2), 
+		nrTupels*3, pal);
+}
+
+/* put the grey palette 
+ * MputColourPalette writes the pal argument that is filled 
+ * with a number of grey tupels
+ * of the grey palette. Each tupel is one UINT2
+ * words describing the intensity: low, 0 is black, high is white.
+ * An existing grey palette is overwritten.
+ * returns
+ *  0 if not available or in case of error,
+ * nonzero otherwise
+ */
+int MputGreyPalette(MAP *m, /* the map to get it from */
+	UINT2 *pal, /* the new grey palette */
+	size_t nrTupels) /* the number of UINT2 words tupels of pal */
+{
+	return CsfUpdateAttribute(m, ATTR_ID_GREY_PAL, sizeof(UINT2), 
+		nrTupels, pal);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/mperror.c b/Utilities/GDAL/frmts/pcraster/libcsf/mperror.c
new file mode 100644
index 0000000000..d28b2bd8ef
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/mperror.c
@@ -0,0 +1,84 @@
+/*
+ * mperror.c
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+
+static const char *errolist[ERRORNO]={
+"No error",
+"File could not be opened or does not exist",
+"File is not a PCRaster file",
+"Wrong C.S.F.-version",
+"Wrong byte order",
+"Not enough memory",
+"Illegal cell representation constant",
+"Acces denied",
+"Row number to big",
+"Collumn number to big",
+"Map is not a raster file",
+"Illegal conversion",
+"No space on device to write",
+"A write error occurred",
+"Illegal handle",
+"A read error occurred",
+"Illegal access mode constant",
+"Attribute not found",
+"Attribute already in file",
+"Cell size <= 0",
+"Conflict between cell representation and value scale",
+"Illegal value scale",
+"XXXXXXXXXXXXXXXXXXXX",
+"Angle < -0.5 pi or > 0.5 pi",
+"Can't read as a boolean map",
+"Can't write as a boolean map",
+"Can't write as a ldd map",
+"Can't use as a ldd map",
+"Can't write to version 1 cell representation",
+"Usetype is not version 2 cell representation, VS_LDD or VS_BOOLEAN"
+};
+
+/* write error message to stderr
+ * Mperror writes the error message belonging to the current Merrno
+ * value to stderr, prefixed by a userString, separated by a semicolon.
+ *
+ * example
+ * .so examples/csfdump1.tr
+ */
+void Mperror(
+	const char *userString) /* prefix string */
+{
+	(void)fprintf(stderr,"%s : %s\n", userString, errolist[Merrno]);
+}
+
+/* write error message to stderr and exits
+ * Mperror first writes the error message belonging to the current Merrno
+ * value to stderr, prefixed by userString, separated by a semicolon.
+ * Then Mperror exits by calling exit() with the given exit code.
+ *
+ * returns
+ * NEVER RETURNS!
+ *
+ * example
+ * .so examples/csfdump2.tr
+ */
+void MperrorExit(
+	const char *userString, /* prefix string */
+	int exitCode) /* exit code */
+{
+	Mperror(userString);
+	exit(exitCode);
+}
+
+/* error message 
+ * MstrError returns the error message belonging to the current Merrno
+ * value.
+ * returns the error message belonging to the current Merrno
+ *
+ * example
+ * .so examples/testcsf.tr
+ */
+const char *MstrError(void)
+{	
+	return(errolist[Merrno]);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/pcrtypes.h b/Utilities/GDAL/frmts/pcraster/libcsf/pcrtypes.h
new file mode 100644
index 0000000000..1a33758300
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/pcrtypes.h
@@ -0,0 +1,246 @@
+#ifndef INCLUDED_PCRTYPES
+# define INCLUDED_PCRTYPES
+
+#ifndef INCLUDED_CSFTYPES
+#include "csftypes.h"
+#define INCLUDED_CSFTYPES
+#endif
+
+#ifndef INCLUDED_STRING
+#include <string>
+#define INCLUDED_STRING
+#endif
+
+// memset
+// use string.h not cstring
+// VC6 does not have memset in std
+// better use Ansi-C string.h to be safe
+#ifndef INCLUDED_C_STRING
+#include <string.h>
+#define INCLUDED_C_STRING
+#endif
+
+namespace pcr {
+/*!
+  \brief     Tests if the value v is a missing value.
+  \param     v the value to be tested.
+  \return    True if value \a v is a missing value.
+
+    the generic isMV(const T& v) is not implemented, only the specializations
+
+  \todo      Zet alle dingen met een bepaald type,isMV, setMv, isType in
+             een zgn. struct trait
+             zie cast drama als isMV mist voor INT2 in BandMapTest::Open2
+             Zie numeric_limit discussie in Josuttis
+*/
+  template<typename T> bool isMV(const T& v);
+/*!
+  \brief     Tests if the value pointed to by v is a missing value.
+  \param     v Pointer to the value to be tested.
+  \return    True if the value pointed to by v is a missing value.
+*/
+  template<typename T> bool isMV(T* v) {
+    return isMV(*v);
+  }
+
+# define PCR_DEF_ISMV(type)  \
+  template<>                  \
+   inline bool isMV(const type& v) \
+   { return v == MV_##type; }
+   PCR_DEF_ISMV(UINT1)
+   PCR_DEF_ISMV(UINT2)
+   PCR_DEF_ISMV(UINT4)
+   PCR_DEF_ISMV(INT1)
+   PCR_DEF_ISMV(INT2)
+   PCR_DEF_ISMV(INT4)
+#  undef PCR_DEF_ISMV
+  template<> inline bool isMV(const REAL4& v)
+  { return IS_MV_REAL4(&v); }
+  template<> inline bool isMV(const REAL8& v)
+  { return IS_MV_REAL8(&v); }
+
+template<> inline bool isMV(std::string const& string)
+{
+  return string.empty();
+}
+
+ /*!
+    \brief     Sets the value v to a missing value.
+    \param     v value to be set.
+    the generic setMV(T& v) is not implemented, only the specializations
+  */
+  template<typename T> void setMV(T& v);
+ /*!
+    \brief     Sets the value pointed to by v to a missing value.
+    \param     v Pointer to the value to be set.
+  */
+  template<typename T> void setMV(T *v) {
+    setMV(*v);
+  }
+
+# define PCR_DEF_SETMV(type)  \
+  template<>                  \
+   inline void setMV(type& v) \
+   { v = MV_##type; }
+   PCR_DEF_SETMV(UINT1)
+   PCR_DEF_SETMV(UINT2)
+   PCR_DEF_SETMV(UINT4)
+   PCR_DEF_SETMV(INT1)
+   PCR_DEF_SETMV(INT2)
+   PCR_DEF_SETMV(INT4)
+#  undef PCR_DEF_SETMV
+
+  template<>
+   inline void setMV(REAL4& v)
+  {
+#   ifndef __i386__
+     SET_MV_REAL4((&v));
+#   else
+     // this fixes an optimization problem (release mode), if is v is a single
+     // element variable in function scope (stack-based)
+     // constraint the setting to memory (m)
+     // for correct alignment
+     asm ("movl $-1, %0" : "=m" (v));
+#   endif
+  }
+  template<>
+   inline void setMV(REAL8& v)
+  {
+#   ifndef __i386__
+     SET_MV_REAL8((&v));
+#   else
+    memset(&v,MV_UINT1,sizeof(REAL8));
+    // constraint the setting to memory (m)
+    // this fixes the same optimization problem, as for REAL4
+    // see com_mvoptest.cc, does not work: !
+    // int *v2= (int *)&v;
+    // asm ("movl $-1, %[dest]" : [dest] "=m" (v2[0]));
+    // asm ("movl $-1, %[dest]" : [dest] "=m" (v2[1]));
+#   endif
+  }
+
+template<>
+inline void setMV(std::string& string)
+{
+//  string.clear();
+  string = "";
+}
+
+/*! \brief set array \a v of size \a n to all MV's
+ *  the generic setMV(T& v) is implemented, the specializations
+ *  are optimizations
+ * \todo
+ *   check if stdlib has a 'wordsized' memset
+ *   or optimize for I86, for gcc look into include/asm/string
+ */
+template<typename T>
+ void setMV(T *v, size_t n)
+{
+  for(size_t i=0; i<n; i++)
+      pcr::setMV(v[i]);
+}
+
+ namespace detail {
+   template<typename T>
+    void setMVMemSet(T *v, size_t n) {
+      memset(v,MV_UINT1,n*sizeof(T));
+    }
+ };
+
+# define PCR_DEF_SETMV_MEMSET(type)    \
+  template<>                           \
+   inline void setMV(type* v,size_t n) \
+   { detail::setMVMemSet(v,n); }
+  PCR_DEF_SETMV_MEMSET(UINT1)
+  PCR_DEF_SETMV_MEMSET(UINT2)
+  PCR_DEF_SETMV_MEMSET(UINT4)
+  PCR_DEF_SETMV_MEMSET(REAL4)
+  PCR_DEF_SETMV_MEMSET(REAL8)
+# undef PCR_DEF_SETMV_MEMSET
+  template<>
+    inline void setMV(INT1 *v, size_t n) {
+      memset(v,MV_INT1,n);
+    }
+
+//! replace a value equal to \a nonStdMV with the standard MV
+/*!
+ * \todo
+ *   the isMV test is only needed for floats, to protect NAN evaluation
+ *   what happens on bcc/win32
+ */
+template<typename T>
+  struct AlterToStdMV {
+    T d_nonStdMV;
+    AlterToStdMV(T nonStdMV):
+      d_nonStdMV(nonStdMV) {};
+
+    void operator()(T& v) {
+      if (!isMV(v) && v == d_nonStdMV)
+        setMV(v);
+    }
+  };
+
+//! return the value or the standard missing value if value equal to \a nonStdMV
+/*!
+ * \todo
+ *   the isMV test is only needed for floats, to protect NAN evaluation
+ *   what happens on bcc/win32
+ */
+template<typename T>
+  struct ToStdMV {
+    T d_nonStdMV;
+    T d_mv;
+    ToStdMV(T nonStdMV):
+      d_nonStdMV(nonStdMV) {
+        setMV(d_mv);
+      }
+
+    T operator()(T const& v) {
+      if (!isMV(v) && v == d_nonStdMV) {
+        return d_mv;
+      }
+      return v;
+    }
+  };
+
+//! replace the standard MV with a value  equal to \a otherMV
+/*!
+ * \todo
+ *   the isMV test is only needed for floats, to protect NAN evaluation
+ *   what happens on bcc/win32
+ */
+template<typename T>
+  struct AlterFromStdMV {
+    T d_otherMV;
+    AlterFromStdMV(T otherMV):
+      d_otherMV(otherMV) {};
+
+    void operator()(T& v) {
+      if (isMV(v))
+        v = d_otherMV;
+    }
+  };
+
+//! return the value or \a otherMV if value equal to standard MV
+/*!
+ * \todo
+ *   the isMV test is only needed for floats, to protect NAN evaluation
+ *   what happens on bcc/win32
+ */
+template<typename T>
+  struct FromStdMV {
+    T d_otherMV;
+    FromStdMV(T otherMV):
+      d_otherMV(otherMV) {};
+
+    T operator()(const T& v) {
+      if (isMV(v))
+        return d_otherMV;
+      return v;
+    }
+  };
+
+};
+
+
+#endif
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/pgisfid.c b/Utilities/GDAL/frmts/pcraster/libcsf/pgisfid.c
new file mode 100644
index 0000000000..d835a3e7a6
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/pgisfid.c
@@ -0,0 +1,56 @@
+/*
+ * pgisfid.c
+$Log: pgisfid.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:52  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* put the gis file id
+ * returns
+ * the "gis file id" field or MV_UINT4
+ * in case of an error
+ *
+ * Merrno
+ * NOACCESS
+ */
+UINT4 MputGisFileId(
+	MAP *map,          /* map handle */
+	UINT4 gisFileId)   /*  new gis file id */
+{
+
+	CHECKHANDLE_GOTO(map, error);
+	if(! WRITE_ENABLE(map))
+	{
+		M_ERROR(NOACCESS);
+		goto error;
+	}
+	map->main.gisFileId = gisFileId;
+	return(gisFileId);
+error:	return(MV_UINT4);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/pmaxval.c b/Utilities/GDAL/frmts/pcraster/libcsf/pmaxval.c
new file mode 100644
index 0000000000..4be3e73b76
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/pmaxval.c
@@ -0,0 +1,75 @@
+/*
+ * pmaxval.c
+$Log: pmaxval.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.3  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.2  2005/09/29 18:43:23  cees
+x86_64
+
+Revision 1.1.1.1  2000/01/04 21:04:52  cees
+Initial import Cees
+
+Revision 2.1  1996/12/29 19:35:21  cees
+src tree clean up
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/05  15:59:34  cees
+ * added app2file conversion
+ * added c2man docs
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* set new maximum cell value
+ * RputMaxVal set a new value stored in
+ * the header as the maximum value. 
+ * minMaxStatus is set to MM_DONTKEEPTRACK
+ *
+ * NOTE
+ * Note that the header maximum set must be equal or 
+ * larger than the maximum value in the map.
+ *
+ * example
+ * .so examples/set_min.tr
+ */
+void RputMaxVal(
+	MAP *map, /* map handle */
+	const void *maxVal)   /* New maximum value */
+{
+	/* use buffer that can hold largest 
+	 * cell representation
+	 */
+	CSF_VAR_TYPE buf_1;
+	void *buf = (void *)(&buf_1);
+
+	CHECKHANDLE(map);
+
+	/* make a copy */
+	CsfGetVarType(buf, maxVal, map->appCR);
+
+	/* convert */
+	map->app2file((size_t)1, buf);
+
+	/* set */
+	CsfGetVarType( (void *)&(map->raster.maxVal), buf, RgetCellRepr(map));
+
+	map->minMaxStatus = MM_DONTKEEPTRACK;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/pminval.c b/Utilities/GDAL/frmts/pcraster/libcsf/pminval.c
new file mode 100644
index 0000000000..ba0dbfbf74
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/pminval.c
@@ -0,0 +1,76 @@
+/*
+ * pminval.c
+$Log: pminval.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.3  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.2  2005/09/29 18:43:23  cees
+x86_64
+
+Revision 1.1.1.1  2000/01/04 21:04:52  cees
+Initial import Cees
+
+Revision 2.1  1996/12/29 19:35:21  cees
+src tree clean up
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/05  15:59:34  cees
+ * added app2file conversion
+ * added c2man docs
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* set new minimum cell value
+ * RputMinVal set a new value stored in
+ * the header as the minimum value.
+ * minMaxStatus is set to MM_DONTKEEPTRACK
+ * 
+ * NOTE
+ * Note that the header minimum set must be equal or 
+ * smaller than the minimum value in the map.
+ *
+ * example
+ * .so examples/set_min.tr
+ */
+
+void RputMinVal(
+  MAP *map, /* map handle */
+  const void *minVal)   /* New minimum value */
+{
+  /* use buffer that can hold largest
+   * cell representation
+   */
+  CSF_VAR_TYPE buf_1;
+  void *buf = (void *)(&buf_1);
+
+  CHECKHANDLE(map);
+
+  /* make a copy */
+  CsfGetVarType(buf, minVal, map->appCR);
+
+  /* convert */
+  map->app2file((size_t)1,buf);
+
+  /* set */
+  CsfGetVarType( (void *)&(map->raster.minVal), buf, RgetCellRepr(map));
+
+  map->minMaxStatus = MM_DONTKEEPTRACK;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/putallmv.c b/Utilities/GDAL/frmts/pcraster/libcsf/putallmv.c
new file mode 100644
index 0000000000..6904f28767
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/putallmv.c
@@ -0,0 +1,56 @@
+#include <stdlib.h>
+
+#include "csf.h"
+#include "csfimpl.h"
+
+/* make all cells missing value in map
+ * RputAllMV writes a missing values to all the cells in a
+ * map. For this is allocates a buffer to hold one row at a
+ * time.
+ * returns 1 if succesfull, 0 in case of an error
+ */
+int RputAllMV(
+	MAP *m)
+{
+	size_t i,nc,nr;
+	void *buffer;
+	CSF_CR cr;
+
+	CHECKHANDLE_GOTO(m, error);
+	if(! WRITE_ENABLE(m))
+	{
+		M_ERROR(NOACCESS);
+		goto error;
+	}
+
+	cr = RgetCellRepr(m);
+	nc = RgetNrCols(m);
+
+	buffer = Rmalloc(m,nc);
+	if(buffer == NULL)
+	{
+		M_ERROR(NOCORE);
+		goto error;
+	}
+
+	/*  Fill buffer with determined Missingvalue*/
+	SetMemMV(buffer, nc, cr);
+
+	nr = RgetNrRows(m);
+	for(i = 0 ; i < nr; i++)
+	 if (RputRow(m, i, buffer) != nc)
+	 {
+	    M_ERROR(WRITE_ERROR);
+	    goto error_f;
+	 }
+	CSF_FREE(buffer);
+
+	CsfSetVarTypeMV( &(m->raster.minVal), cr);
+	CsfSetVarTypeMV( &(m->raster.maxVal), cr);
+
+	return(1);
+error_f:  
+	CSF_FREE(buffer);
+error:
+	return(0);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/putattr.c b/Utilities/GDAL/frmts/pcraster/libcsf/putattr.c
new file mode 100644
index 0000000000..45fb57638c
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/putattr.c
@@ -0,0 +1,214 @@
+#include "csf.h"
+#include "csfimpl.h"
+
+/* make block empty
+ */
+static void InitBlock(
+	ATTR_CNTRL_BLOCK *b) /* write-only */
+{
+	int i;
+ 	for (i = 0 ; i < NR_ATTR_IN_BLOCK; i++)
+	{
+		b->attrs[i].attrId   = END_OF_ATTRS;
+		b->attrs[i].attrSize   = 0;
+		b->attrs[i].attrOffset = 0;
+	}
+	b->next = 0;
+}
+
+/* replace an attribute (LIBRARY_INTERNAL)
+ *
+ */
+CSF_ATTR_ID CsfUpdateAttribute(
+	MAP *m,       		/* map handle */
+	CSF_ATTR_ID id,               /* attribute identification */
+	size_t itemSize,        /* size of each attribute element.
+	                         * 1 or sizeof(char) in case of a
+	                         * string 
+	                         */
+	size_t nitems,          /* number of attribute elements or
+	                         * strlen+1 in case of a variable character
+	                         * string field. Don't forget to pad a
+	                         * non-variable field with '\0'!
+	                         */
+	void *attr)       /* buffer containing attribute */
+{
+	PRECOND(CsfValidSize(itemSize));
+	if (CsfAttributeSize(m,id))
+		if (! MdelAttribute(m,id))
+			return 0;
+	return CsfPutAttribute(m,id,itemSize,nitems, attr);
+}
+
+
+
+/* write an attribute to a map (LIBRARY_INTERNAL)
+ * MputAttribute writes exactly the number of bytes specified
+ * by the size argument starting at the address of argument
+ * attr. Which means that you can't simply pass a structure or an 
+ * array of structures as argument attr, due to the alignment
+ * of fields within a structure and internal swapping. You can
+ * only pass an array of elementary types (UINT1, REAL4, etc.)
+ * or character string.
+ * If one wants to refresh an attribute, one should first
+ * call MdelAttribute to delete the attribute and then use
+ * MputAttribute to write the new value.
+ * returns argument id or 0 in case of error.
+ *
+ * Merrno
+ * ATTRDUPL
+ * NOACCESS
+ * WRITE_ERROR
+ */
+CSF_ATTR_ID CsfPutAttribute(
+	MAP *m,       		/* map handle */
+	CSF_ATTR_ID id,               /* attribute identification */
+	size_t itemSize,        /* size of each attribute element.
+	                         * 1 or sizeof(char) in case of a
+	                         * string 
+	                         */
+	size_t nitems,          /* number of attribute elements or
+	                         * strlen+1 in case of a variable character
+	                         * string field. Don't forget to pad a
+	                         * non-variable field with '\0'!
+	                         */
+	void *attr)       /* buffer containing attribute */
+{
+	size_t size = nitems * itemSize;
+	
+	PRECOND(CsfValidSize(itemSize));
+	PRECOND(size > 0);
+
+	if (CsfSeekAttrSpace(m,id,size) == 0)
+		goto error;
+
+	if (m->write(attr, itemSize, nitems, m->fp) != nitems) 
+	{
+		M_ERROR(WRITE_ERROR);
+		goto error;
+	}
+	return(id); 		/* succes */
+error:	return(0);	/* failure */
+} 
+
+/* seek to space for attribute  (LIBRARY_INTERNAL)
+ * CsfSeekAttrSpace seeks to the a point in the file where
+ * the attribute must be stored and update the attribute control 
+ * blocks accordingly.
+ * Writing can still fail since there is no check if that space is really
+ * avalaible on the device. After this call returns the file is already
+ * seeked to the point the functions returns.
+ * returns the file position or 0 in case of error.
+ *
+ * Merrno
+ * ATTRDUPL
+ * NOACCESS
+ * WRITE_ERROR
+ */
+CSF_FADDR CsfSeekAttrSpace( 
+	MAP *m,       		/* map handle */
+	CSF_ATTR_ID id,               /* attribute identification only for check if avalaible */
+	size_t size)            /* size to be seeked to */
+{
+	ATTR_CNTRL_BLOCK b;
+	CSF_FADDR currBlockPos, prevBlockPos=USED_UNINIT_ZERO, newPos, endBlock, resultPos=0;
+	int noPosFound;
+	int i;
+
+	if (MattributeAvail(m ,id))
+	{
+		M_ERROR(ATTRDUPL);
+		goto error;
+	}
+
+	if (! WRITE_ENABLE(m))
+	{
+		M_ERROR(NOACCESS);
+		goto error;
+	}
+
+	currBlockPos = m->main.attrTable;
+        noPosFound = 1;
+	while (noPosFound) 
+	{	
+		if (currBlockPos == 0)
+		{
+			if (m->main.attrTable == 0)
+			{ /* FIRST BLOCK */
+				newPos =( (CSF_FADDR)(m->raster.nrRows)*
+					   (CSF_FADDR)(m->raster.nrCols)*
+					  (CSF_FADDR)(CELLSIZE(RgetCellRepr(m))))
+					  + ADDR_DATA;
+				m->main.attrTable = newPos;
+			}
+			else
+			{ /* NEW/NEXT BLOCK */
+				newPos = b.attrs[LAST_ATTR_IN_BLOCK].attrOffset 
+					+
+					b.attrs[LAST_ATTR_IN_BLOCK].attrSize;
+				b.next = newPos;
+				if (CsfWriteAttrBlock(m, prevBlockPos, &b))
+				{
+					M_ERROR(WRITE_ERROR);
+					resultPos = 0;
+				}
+			}
+			InitBlock(&b);
+			b.attrs[0].attrOffset =
+				newPos + SIZE_OF_ATTR_CNTRL_BLOCK;
+			currBlockPos = newPos;
+			noPosFound = 0;
+		}
+		else
+			CsfReadAttrBlock(m, currBlockPos, &b);
+		i = 0; /* this is also the right index if a new block
+			   is added ! */
+		while (noPosFound  && i < NR_ATTR_IN_BLOCK) 
+			switch (b.attrs[i].attrId)
+			{
+				case END_OF_ATTRS:
+					POSTCOND(i >= 1);
+					/* i >= 1 , no block otherwise */
+					b.attrs[i].attrOffset =
+						b.attrs[i-1].attrOffset  +
+						b.attrs[i-1].attrSize;
+					noPosFound = 0;
+                                        break;
+				case ATTR_NOT_USED:
+					if (i == NR_ATTR_IN_BLOCK)
+						endBlock = b.next;
+					else
+						endBlock = b.attrs[i+1].attrOffset;
+					if (( endBlock - b.attrs[i].attrOffset)
+						>= size)
+						/* this position can
+							hold the attr */
+						noPosFound = 0;
+                                        else
+                                            i++;
+                                        break;
+				 default:
+                                            i++;
+			} /* switch */
+/*		if (b.next == 0) 
+                     ? When did I change this CW
+		       remember this block position since it may be have
+		       to rewritten 
+*/
+		prevBlockPos = currBlockPos;
+		if (noPosFound)
+			currBlockPos = b.next;
+ 	} /* while */
+
+	b.attrs[i].attrSize = size;
+	b.attrs[i].attrId   = id;
+	resultPos = b.attrs[i].attrOffset;
+
+	if (CsfWriteAttrBlock(m, currBlockPos, &b))
+	{
+		M_ERROR(WRITE_ERROR);
+		resultPos = 0;
+	}
+	fseek(m->fp, (long)resultPos, SEEK_SET); /* fsetpos() is better */
+error:	return resultPos;
+} /* CsfSeekAttrSpace */
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/putsomec.c b/Utilities/GDAL/frmts/pcraster/libcsf/putsomec.c
new file mode 100644
index 0000000000..c92b574ec3
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/putsomec.c
@@ -0,0 +1,265 @@
+/*
+ * putsomec.c
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+typedef void (*DF)(void *min, void *max, size_t n, const void *buf);
+
+/* DET: 
+ *  while (min is MV) && (i != nrCells)
+ *      min = max = buf[i++];
+ *  while (i != nrCells)
+ *   if (buf[i] is not MV)
+ *      if (buf[i] < min) min = buf[i];
+ *      if (buf[i] > max) max = buf[i];
+ *   i++
+ */
+#define DET(min, max, nrCells, buf, type) \
+	{\
+		size_t i=0;\
+		while (((*(type *)min) == MV_##type) && (i != nrCells))\
+			 (*(type *)max) = (*(type *)min) =\
+			 ((const type *)buf)[i++];\
+		while (i != nrCells) \
+		{\
+			if (((const type *)buf)[i] != MV_##type)\
+			{\
+				if (((const type *)buf)[i] < (*(type *)min) )\
+			  		(*(type *)min) = ((const type *)buf)[i];\
+				if (((const type *)buf)[i] > (*(type *)max) )\
+			  		(*(type *)max) = ((const type *)buf)[i];\
+			}\
+			i++;\
+		}\
+	}
+
+
+/* determines new minimum and new maximum
+ * DetMinMax({U}INT[124]|REAL[48]) analyzes
+ * an array of cells and adjust the min and max argument
+ * if necessary. If min and max are not yet set then they
+ * must be MV both. The function 
+ * assumes that both min and max are MV if min is MV.
+ */
+static void DetMinMaxINT1(
+INT1 *min,    /* read-write.  adjusted minimum */
+INT1 *max,   /* read-write.  adjusted maximum */
+size_t nrCells, /* number of cells in buf */
+const INT1 *buf) /* cell values to be examined */
+{
+	DET(min, max, nrCells, buf, INT1);
+}
+
+/* determines new minimum and new maximum
+ * DetMinMax* (* = all cell representation) analyzes
+ * an array of cells and adjust the min and max argument
+ * if necessary. If min and max are not yet set then they
+ * must be MV both. The function 
+ * assumes that both min and max are MV if min is MV.
+ */
+ static void DetMinMaxINT2(
+INT2 *min,   /* read-write.  adjusted minimum */
+INT2 *max,  /* read-write.  adjusted maximum */
+size_t nrCells,/* number of cells in buf */
+const INT2 *buf) /* cell values to be examined */
+{
+	DET(min, max,  nrCells, buf, INT2);
+}
+
+/* determines new minimum and new maximum
+ * DetMinMax* (* = all cell representation) analyzes
+ * an array of cells and adjust the min and max argument
+ * if necessary. If min and max are not yet set then they
+ * must be MV both. The function 
+ * assumes that both min and max are MV if min is MV.
+ */
+ static void DetMinMaxINT4(
+INT4 *min,   /* read-write.  adjusted minimum */
+INT4 *max,  /* read-write.  adjusted maximum */
+size_t nrCells,/* number of cells in buf */
+const INT4 *buf) /* cell values to be examined */
+{
+	DET(min, max,  nrCells, buf, INT4);
+}
+
+
+/* determines new minimum and new maximum
+ * DetMinMax* (* = all cell representation) analyzes
+ * an array of cells and adjust the min and max argument
+ * if necessary. If min and max are not yet set then they
+ * must be MV both. The function 
+ * assumes that both min and max are MV if min is MV.
+ */
+ static void DetMinMaxUINT1(
+UINT1 *min,   /* read-write.  adjusted minimum */
+UINT1 *max,  /* read-write.  adjusted maximum */
+size_t nrCells,/* number of cells in buf */
+const UINT1 *buf) /* cell values to be examined */
+{
+	DET(min, max,  nrCells, buf, UINT1);
+}
+
+/* determines new minimum and new maximum
+ * DetMinMax* (* = all cell representation) analyzes
+ * an array of cells and adjust the min and max argument
+ * if necessary. If min and max are not yet set then they
+ * must be MV both. The function 
+ * assumes that both min and max are MV if min is MV.
+ */
+ static void DetMinMaxUINT2(
+UINT2 *min,   /* read-write.  adjusted minimum */
+UINT2 *max,  /* read-write.  adjusted maximum */
+size_t nrCells,/* number of cells in buf */
+const UINT2 *buf) /* cell values to be examined */
+{
+	DET(min, max,  nrCells, buf, UINT2);
+}
+
+/* determines new minimum and new maximum
+ * DetMinMax* (* = all cell representation) analyzes
+ * an array of cells and adjust the min and max argument
+ * if necessary. If min and max are not yet set then they
+ * must be MV both. The function 
+ * assumes that both min and max are MV if min is MV.
+ */
+ static void DetMinMaxUINT4(
+UINT4 *min,   /* read-write.  adjusted minimum */
+UINT4 *max,  /* read-write.  adjusted maximum */
+size_t nrCells,/* number of cells in buf */
+const UINT4 *buf) /* cell values to be examined */
+{
+	DET(min, max,  nrCells, buf, UINT4);
+}
+
+
+/* determines new minimum and new maximum
+ * DetMinMax* (* = all cell representation) analyzes
+ * an array of cells and adjust the min and max argument
+ * if necessary. If min and max are not yet set then they
+ * must be MV both. The function 
+ * assumes that both min and max are MV if min is MV.
+ */
+static void DetMinMaxREAL4(
+REAL4 *min,   /* read-write.  adjusted minimum */
+REAL4 *max,  /* read-write.  adjusted maximum */
+size_t nrCells,/* number of cells in buf */
+const REAL4 *buf) /* cell values to be examined */
+{
+	size_t i = 0; 
+
+	if ( IS_MV_REAL4(min))
+	{
+	 while ( IS_MV_REAL4(min) && (i != nrCells))
+		*((UINT4 *)min) = ((const UINT4 *)buf)[i++];
+	 *max = *min;
+	}
+	while (i != nrCells) 
+	{
+		if (! IS_MV_REAL4(buf+i))
+		{
+			if (buf[i] < *min )
+			  	*min = buf[i];
+			if (buf[i] > *max)
+			  *max = buf[i];
+		}
+		i++;
+	}
+}
+
+	
+
+/* determines new minimum and new maximum
+ * DetMinMax* (* = all cell representation) analyzes
+ * an array of cells and adjust the min and max argument
+ * if necessary. If min and max are not yet set then they
+ * must be MV both. The function 
+ * assumes that both min and max are MV if min is MV.
+ */
+static void DetMinMaxREAL8(
+REAL8 *min,   /* read-write.  adjusted minimum */
+REAL8 *max,  /* read-write.  adjusted maximum */
+size_t nrCells,/* number of cells in buf */
+const REAL8 *buf) /* cell values to be examined */
+{
+	size_t i = 0; 
+
+	if ( IS_MV_REAL8(min))
+	{
+	 while ( IS_MV_REAL8(min) && (i != nrCells))
+	 {
+		((UINT4 *)min)[0] = ((const UINT4 *)buf)[2*i];
+		((UINT4 *)min)[1] = ((const UINT4 *)buf)[(2*i++)+1];
+	 }
+	 *max = *min;
+	}
+	while (i != nrCells) 
+	{
+		if (! IS_MV_REAL8(buf+i))
+		{
+			if (buf[i] < *min )
+			  	*min = buf[i];
+			if (buf[i] > *max)
+			  	*max = buf[i];
+		}
+		i++;
+	}
+}
+
+
+/* write a stream of cells
+ * RputSomeCells views a raster as one linear stream of
+ * cells, with row i+1 placed after row i. 
+ * In this stream any sequence can be written by specifying an
+ * offset and the number of cells to be written
+ * returns the number of cells written, just as fwrite
+ *
+ * example
+ * .so examples/somecell.tr
+ */
+size_t RputSomeCells(
+	MAP *map,	/* map handle */
+	size_t offset,   /* offset from pixel (row,col) = (0,0) */
+	size_t nrCells,  /* number of cells to be read */
+	void *buf)/* read-write. Buffer large enough to
+                   * hold nrCells cells in the in-file cell representation
+                   * or the in-app cell representation.
+                   * If these types are not equal then the buffer is
+                   * converted from the in-app to the in-file 
+                   * cell representation. 
+                   */
+{
+	CSF_FADDR  writeAt;
+	CSF_CR  cr = map->raster.cellRepr;
+
+	/* convert */
+	map->app2file(nrCells, buf);
+	
+
+	if (map->minMaxStatus == MM_KEEPTRACK)
+	{
+		const DF  detMinMaxFunc[12] = {
+			 (DF)DetMinMaxUINT1, (DF)DetMinMaxUINT2, 
+			 (DF)DetMinMaxUINT4, NULL /* 0x03  */  ,
+			 (DF)DetMinMaxINT1 , (DF)DetMinMaxINT2 , 
+			 (DF)DetMinMaxINT4 , NULL /* 0x07  */  ,
+			 NULL /* 0x08 */   , NULL /* 0x09 */   , 
+			 (DF)DetMinMaxREAL4, (DF)DetMinMaxREAL8 };
+
+		void *min = &(map->raster.minVal);
+		void *max = &(map->raster.maxVal);
+
+		PRECOND(CSF_UNIQ_CR_MASK(cr) < 12);
+		PRECOND(detMinMaxFunc[CSF_UNIQ_CR_MASK(cr)] != NULL);
+
+		detMinMaxFunc[CSF_UNIQ_CR_MASK(cr)](min, max, nrCells, buf);
+		
+	}
+	else
+		map->minMaxStatus = MM_WRONGVALUE;
+
+	writeAt  = ((CSF_FADDR)offset) << LOG_CELLSIZE(cr);
+	writeAt += ADDR_DATA;
+	fseek(map->fp, (long)writeAt, SEEK_SET); 
+	return(map->write(buf, (size_t)CELLSIZE(cr), (size_t)nrCells, map->fp));
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/putx0.c b/Utilities/GDAL/frmts/pcraster/libcsf/putx0.c
new file mode 100644
index 0000000000..73351afa74
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/putx0.c
@@ -0,0 +1,56 @@
+/*
+ * putx0.c
+$Log: putx0.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:57  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+
+/* change the x value of upper left co-ordinate
+ * RputXUL changes the x value of upper left co-ordinate.
+ * returns the new x value of upper left co-ordinate or 0
+ * case of an error.
+ *
+ * Merrno
+ * NOACCESS
+ */
+REAL8 RputXUL(
+	MAP *map, /* map handle */
+	REAL8 xUL) /* new x value of top left co-ordinate */
+{
+	CHECKHANDLE_GOTO(map, error);
+	if(! WRITE_ENABLE(map))
+	{
+		M_ERROR(NOACCESS);
+		goto error;
+	}
+	map->raster.xUL = xUL;
+	return(xUL);
+error:  return(0);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/puty0.c b/Utilities/GDAL/frmts/pcraster/libcsf/puty0.c
new file mode 100644
index 0000000000..46474a9677
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/puty0.c
@@ -0,0 +1,57 @@
+/*
+ * puty0.c
+$Log: puty0.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:57  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* change the y value of upper left co-ordinate
+ * RputYUL changes the y value of upper left co-ordinate.
+ * Whether this is the largest or smallest y value depends on the
+ * projection (See MgetProjection()).
+ * returns the new y value of upper left co-ordinate or 0
+ * case of an error.
+ *
+ * Merrno
+ * NOACCESS
+ */
+REAL8 RputYUL(
+	MAP *map, /* map handle */
+	REAL8 yUL) /* new y value of upper left co-ordinate */
+{
+	CHECKHANDLE_GOTO(map, error);
+	if(! WRITE_ENABLE(map))
+	{
+		M_ERROR(NOACCESS);
+		goto error;
+	}
+	map->raster.yUL = yUL;
+	return(yUL);
+error:  return(0);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/pvalscal.c b/Utilities/GDAL/frmts/pcraster/libcsf/pvalscal.c
new file mode 100644
index 0000000000..e0645575ae
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/pvalscal.c
@@ -0,0 +1,64 @@
+/*
+ * pvalscal.c
+$Log: pvalscal.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:57  cees
+Initial import Cees
+
+Revision 2.1  1996/12/29 19:35:21  cees
+src tree clean up
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* put new value scale
+ * RputValueScale changes the value scale
+ * of the map.
+ *
+ * returns
+ *  new value scale or VS_UNDEFINED in case of an error.
+ * 
+ * NOTE
+ * Note that there is no check if the cell representation
+ * is complaint.
+ *
+ * Merrno
+ * NOACCESS
+ */
+CSF_VS RputValueScale(
+	MAP *map,         /* map handle */
+	CSF_VS valueScale) /* new value scale */
+{
+	CHECKHANDLE_GOTO(map, error);
+	if(! WRITE_ENABLE(map))
+	{
+		 M_ERROR(NOACCESS);
+		 goto error;
+	}
+	map->raster.valueScale = valueScale;
+	return(valueScale);
+error:	return(VS_UNDEFINED);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/rattrblk.c b/Utilities/GDAL/frmts/pcraster/libcsf/rattrblk.c
new file mode 100644
index 0000000000..471d40623f
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/rattrblk.c
@@ -0,0 +1,53 @@
+/*
+ * rattrblk.c
+$Log: rattrblk.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.3  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.2  2005/09/29 18:43:23  cees
+x86_64
+
+Revision 1.1.1.1  2000/01/04 21:04:57  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* read attribute control block (LIBRARY_INTERNAL)
+ */
+void CsfReadAttrBlock(
+	MAP *m,              /* map handle */
+	CSF_FADDR pos,           /* file position of block to be read */
+	ATTR_CNTRL_BLOCK *b) /* write-only. attribute control block read */
+{
+	int i;
+	fseek(m->fp, (long)pos, SEEK_SET);
+	for(i=0; i < NR_ATTR_IN_BLOCK; i++)
+	{
+	 m->read((void *)&(b->attrs[i].attrId), sizeof(UINT2),(size_t)1,m->fp);
+	 m->read((void *)&(b->attrs[i].attrOffset), sizeof(CSF_FADDR),(size_t)1,m->fp);
+	 m->read((void *)&(b->attrs[i].attrSize), sizeof(UINT4),(size_t)1,m->fp);
+	}
+	m->read((void *)&(b->next), sizeof(CSF_FADDR),(size_t)1,m->fp);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/rcomp.c b/Utilities/GDAL/frmts/pcraster/libcsf/rcomp.c
new file mode 100644
index 0000000000..f4b20c5a00
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/rcomp.c
@@ -0,0 +1,81 @@
+/*
+ * rcomp.c
+ */
+
+/*****************************************************************/
+/*    FUNCTION :  RCOMPARE                                       */
+/*****************************************************************/
+/*             				                         */
+/*****************************************************************/
+
+
+#include "csf.h"
+#include "csfimpl.h"
+
+/* compare 2 maps for their location attributes
+ * Rcompare compares 2 maps for all location attributes:
+ *
+ * projection,
+ *
+ * xUL, yUL, angle,
+ *
+ * cell size and
+ *
+ * number of rows and columns
+ *
+ * returns 0 if one of these attributes differ or in case of an error, 1 
+ * if they are all equal.
+ *
+ * Merrno
+ * NOT_RASTER
+ */
+int Rcompare(
+	const MAP *m1, /* map handle 1 */
+	const MAP *m2) /* map handle 2 */
+{
+	CHECKHANDLE_GOTO(m1, error);
+
+	/* check if mapType is T_RASTER */
+	if ((m1->main.mapType != T_RASTER)
+	|| (m2->main.mapType != T_RASTER))
+	{
+		M_ERROR(NOT_RASTER);
+		goto error;
+	}
+
+	if (
+	    MgetProjection(m1) == MgetProjection(m2) && 
+	    m1->raster.xUL == m2->raster.xUL &&
+    	    m1->raster.yUL == m2->raster.yUL &&
+	    m1->raster.cellSize == m2->raster.cellSize &&
+	    m1->raster.cellSizeDupl == m2->raster.cellSizeDupl &&
+	    m1->raster.angle == m2->raster.angle &&
+	    m1->raster.nrRows == m2->raster.nrRows &&
+	    m1->raster.nrCols == m2->raster.nrCols 
+	)	return(1);
+error: 
+		return(0);
+}
+
+int RgetLocationAttributes(
+	CSF_RASTER_LOCATION_ATTRIBUTES *l, /* fill in this struct */
+	const MAP *m) /* map handle to copy from */
+{
+	CHECKHANDLE_GOTO(m, error);
+	*l = m->raster;
+	return 1;
+error:
+	return 0;
+}
+
+int RcompareLocationAttributes(
+	const CSF_RASTER_LOCATION_ATTRIBUTES *m1, /* */
+	const CSF_RASTER_LOCATION_ATTRIBUTES *m2) /* */
+{
+	return  (
+	    m1->projection == m2->projection && 
+	    m1->xUL == m2->xUL && m1->yUL == m2->yUL &&
+	    m1->cellSize == m2->cellSize &&
+	    m1->angle == m2->angle &&
+	    m1->nrRows == m2->nrRows && m1->nrCols == m2->nrCols );
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/rcoords.c b/Utilities/GDAL/frmts/pcraster/libcsf/rcoords.c
new file mode 100644
index 0000000000..590d67aa64
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/rcoords.c
@@ -0,0 +1,104 @@
+/*
+ * rcoords.c
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* compute true world co-ordinate of a pixel
+ * RrowCol2Coords computes the true world co-ordinate from a 
+ * row, column index. 
+ * The row, column co-ordinate 
+ * don't have to be on the map. They are just relative to upper left position.
+ * For example (row,col) = (-1,0) computes the (x,y) co-ordinate of
+ * the pixel that is right above upper left pixel. 
+ *
+ * returns
+ *  0  if the co-ordinate is outside the map.
+ *  1 if inside.
+ * -1 in case of an error.
+ *
+ * Merrno
+ * ILL_CELLSIZE
+ */
+int RgetCoords(
+	const MAP *m,	/* map handle */
+	int inCellPos,  /* nonzero if you want the co-ordinate
+			 * at the centre of the cell, 0 if you
+			 * want the upper left co-ordinate of the cell
+			 */
+	size_t row,      /* Row number (relates to y position). */
+	size_t col,      /* Column number (relates to x position). */
+	double *x,      /* write-only. Returns x of true co-ordinate */
+	double *y)      /* write-only. Returns y of true co-ordinate */
+{
+	return RrowCol2Coords(m,
+		(double)row+(inCellPos ? 0.5 : 0.0),
+		(double)col+(inCellPos ? 0.5 : 0.0),
+		x,y);
+}
+
+/* compute true world co-ordinate from row, column index
+ * RrowCol2Coords computes the true world co-ordinate from a 
+ * row, column index. The row,column co-ordinate can be fractions.
+ * For example (row,col) = (0.5,0.5) computes the (x,y) co-ordinate of
+ * the centre of the upper left pixel. Secondly, the row and column co-ordinate
+ * don't have to be on the map. They are just relative to upper left position.
+ * For example (row,col) = (-0.5,0.5) computes the (x,y) co-ordinate of
+ * the centre of the pixel that is right above upper left pixel. 
+ */
+void RasterRowCol2Coords(
+	const CSF_RASTER_LOCATION_ATTRIBUTES *m, /* raster handle */
+	double row,  /* Row number (relates to y position). */
+	double col,  /* Column number (relates to x position). */
+	double *x,   /* write-only. x co-ordinate */
+	double *y)   /* write-only. y co-ordinate */
+{
+	double cs     = m->cellSize;
+	double c      = m->angleCos;
+	double s      = m->angleSin;
+	double yRow   = cs * row;
+	double xCol   = cs * col;
+	double xCol_t = xCol * c - yRow * s;
+	double yRow_t = xCol * s + yRow * c;
+
+	*x = m->xUL + xCol_t;
+	if (m->projection == PT_YINCT2B)
+		*y = m->yUL + yRow_t;
+	else  /* all other projections */
+		*y = m->yUL - yRow_t;
+}
+
+/* compute true world co-ordinate from row, column index
+ * RasterRowCol2Coords computes the true world co-ordinate from a 
+ * row, column index. The row,column co-ordinate can be fractions.
+ * For example (row,col) = (0.5,0.5) computes the (x,y) co-ordinate of
+ * the centre of the upper left pixel. Secondly, the row and column co-ordinate
+ * don't have to be on the map. They are just relative to upper left position.
+ * For example (row,col) = (-0.5,0.5) computes the (x,y) co-ordinate of
+ * the centre of the pixel that is right above upper left pixel. 
+ *
+ * returns
+ *  0  if the co-ordinate is outside the map.
+ *  1 if inside.
+ * -1 in case of an error.
+ *
+ * Merrno
+ * ILL_CELLSIZE
+ */
+int RrowCol2Coords(const MAP *m, /* map handle */
+		    double row,  /* Row number (relates to y position). */
+		    double col,  /* Column number (relates to x position). */
+		    double *x,   /* write-only. x co-ordinate */
+		    double *y)   /* write-only. y co-ordinate */
+{
+	if (m->raster.cellSize <= 0 
+	    || m->raster.cellSize != m->raster.cellSizeDupl )
+	{
+		M_ERROR(ILL_CELLSIZE);
+		goto error;
+	}
+	RasterRowCol2Coords(&(m->raster),row,col,x,y);
+	return( (m->raster.nrRows > row) && (m->raster.nrCols > col) &&
+	        (row >= 0) && (col >= 0));
+error:  return(-1);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/rdup2.c b/Utilities/GDAL/frmts/pcraster/libcsf/rdup2.c
new file mode 100644
index 0000000000..e4d61e8029
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/rdup2.c
@@ -0,0 +1,52 @@
+
+/*
+ * rdup2.c 
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+
+/* create a new map by cloning another one
+ * Rdup creates a new empty map from the specifications of another map.
+ * No cell values are copied. It uses a call to Rcreate to create the
+ * map. See Rcreate for legal values of the args cellRepr and valueScale.
+ * returns the map handle of the newly created map or NULL in case of an
+ * error
+ *
+ * Merrno
+ * NOT_RASTER plus the Merrno codes of Rcreate
+ *
+ * EXAMPLE
+ * .so examples/dupbool.tr
+ */
+MAP  *Rdup(
+	const char *toFile, /* file name of map to be created */
+	const MAP *from,    /* map to clone from */
+	CSF_CR cellRepr,     /* cell representation of new map  */
+	CSF_VS dataType)   /* datatype/valuescale of new map  */
+{
+	MAP *newMap = NULL; /* set NULL for goto error */
+
+	CHECKHANDLE_GOTO(from, error);
+
+	/* check if mapType is T_RASTER */
+	if (from->main.mapType != T_RASTER)
+	{
+		M_ERROR(NOT_RASTER);
+		goto error;
+	}
+
+	newMap = Rcreate(toFile,
+	            (size_t)from->raster.nrRows,
+			 (size_t)from->raster.nrCols,
+        		 cellRepr, 
+        		 dataType, 
+        		 from->main.projection, 
+        		 from->raster.xUL,
+        		 from->raster.yUL,
+        		 from->raster.angle,
+        		 from->raster.cellSize);
+
+error:
+	return newMap ;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/reseterr.c b/Utilities/GDAL/frmts/pcraster/libcsf/reseterr.c
new file mode 100644
index 0000000000..059ce2dc2e
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/reseterr.c
@@ -0,0 +1,43 @@
+/*
+ * reseterr.c
+$Log: reseterr.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:04:59  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/08  17:16:23  cees
+ * added c2man docs + small code changes
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* reset Merrno variable
+ * ResetMerrno sets the Merrno variable to NOERROR (0).
+ *
+ * example
+ * .so examples/testcsf.tr
+ */
+void ResetMerrno(void)
+{
+	Merrno = NOERROR;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/rextend.c b/Utilities/GDAL/frmts/pcraster/libcsf/rextend.c
new file mode 100644
index 0000000000..016d758204
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/rextend.c
@@ -0,0 +1,104 @@
+/*
+ * rextend.c
+ */
+#include "csf.h"
+#include "csfimpl.h"
+#include <math.h>
+
+/* round up and down come up with a number such
+ * we have rounded to integer multiplication
+ * anyway this should hold:
+	POSTCOND(RoundUp( 5.3, 4) == 8 );
+	POSTCOND(RoundUp( 4  , 4) == 8 );
+	POSTCOND(RoundUp(-5.3, 4) == -4 );
+	POSTCOND(RoundUp(-4, 4) == 0 );
+	POSTCOND(RoundDown( 5.3, 4)  ==  4 );
+	POSTCOND(RoundDown( 4  , 4)  ==  0 );
+	POSTCOND(RoundDown(-5.3, 4)  == -8 );
+	POSTCOND(RoundDown(-4  , 4)  == -8 );
+ */
+
+static double RoundDown(
+	double v,
+	double round)
+{
+	double rVal = fmod(v, round);
+	double x;
+	if(rVal == 0)
+		return v-round;
+	if (v < 0)
+		x = v-round-rVal;
+	else
+		x = v-rVal;
+	return  x;
+}
+
+static double RoundUp(
+	double v,
+	double round)
+{
+	double rVal = fmod(v, round);
+	if(rVal == 0)
+		return v+round;
+	if (v < 0)
+	  return v-rVal;
+	else
+	  return v+round-rVal; 
+}
+
+/* compute (xUL,yUL) and nrRows, nrCols from some coordinates
+ * RcomputeExtend computes parameters to create a raster maps
+ * from minimum and maximum x and y coordinates, projection information,
+ * cellsize and units. The resulting parameters are computed that the 
+ * smallest raster map can be created that will include the two 
+ * coordinates given, assuming a default angle of 0.
+ * Which coordinates are the maximum or minimum are
+ * determined by the function itself.
+ */
+void RcomputeExtend(
+	REAL8 *xUL,     /* write-only, resulting xUL */
+	REAL8 *yUL,     /* write-only, resulting yUL */
+	size_t *nrRows, /* write-only, resulting nrRows */
+	size_t *nrCols, /* write-only, resulting nrCols */
+	double x_1,      /* first x-coordinate */ 
+	double y_1,      /* first y-coordinate */
+	double x_2,      /* second x-coordinate */
+	double y_2,      /* second y-coordinate */
+	CSF_PT projection, /* required projection */
+	REAL8 cellSize, /* required cellsize, > 0 */
+	double rounding) /* assure that (xUL/rounding), (yUL/rouding)
+	                  * (xLL/rounding) and (yLL/rounding) will
+	                 * will all be an integers values > 0 
+	                 */
+{
+    /*
+     * xUL ______
+	   |    |
+	   |    |
+	   |    |
+	   ------
+
+     */
+	double yLL,xUR = x_1 > x_2 ? x_1 : x_2;
+	*xUL = x_1 < x_2 ? x_1 : x_2;
+	*xUL = RoundDown(*xUL, rounding); /* Round down */
+	xUR  = RoundUp(   xUR, rounding); /* Round up */
+	POSTCOND(*xUL <= xUR);
+	*nrCols = (size_t)ceil((xUR - *xUL)/cellSize);
+	if (projection == PT_YINCT2B)
+	{
+		 yLL = y_1 > y_2 ? y_1 : y_2;  /* highest value at bottom */
+		*yUL = y_1 < y_2 ? y_1 : y_2;  /* lowest value at top */
+	        *yUL = RoundDown(*yUL, rounding);
+	         yLL = RoundUp(   yLL, rounding);
+	}
+	else
+	{
+		 yLL = y_1 < y_2 ? y_1 : y_2;  /* lowest value at bottom */
+		*yUL = y_1 > y_2 ? y_1 : y_2;  /* highest value at top */
+	        *yUL = RoundUp(  *yUL, rounding);
+	         yLL = RoundDown( yLL, rounding);
+	}
+	*nrRows = (size_t)ceil(fabs(yLL - *yUL)/cellSize);
+}
+
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/rmalloc.c b/Utilities/GDAL/frmts/pcraster/libcsf/rmalloc.c
new file mode 100644
index 0000000000..f2fcee8972
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/rmalloc.c
@@ -0,0 +1,34 @@
+/*
+ * rmalloc.c 
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* allocate dynamic memory large enough to hold in-file and app cells
+ * Rmalloc allocates memory to hold  nrOfCells 
+ * cells in both the in-file and app cell representation. Allocation
+ * is done by malloc for other users. Our own (utrecht university) applications
+ * calls ChkMalloc. Freeing memory allocated by Rmalloc is done by free (or Free).
+ *
+ * NOTE
+ * Note that a possible RuseAs call must be done BEFORE Rmalloc.
+ *
+ * returns
+ * a pointer the allocated memory or
+ * NULL
+ * if the request fails
+ *
+ * example
+ * .so examples/_row.tr
+ */
+void *Rmalloc(
+	const MAP *m,      /* map handle */
+	size_t nrOfCells)   /* number of cells allocated memory must hold */
+{
+	CSF_CR inFileCR = RgetCellRepr(m);
+	CSF_CR largestCellRepr = 
+		LOG_CELLSIZE(m->appCR) > LOG_CELLSIZE(inFileCR) 
+		 ?  m->appCR : inFileCR;
+
+	return CSF_MALLOC((size_t)CSFSIZEOF(nrOfCells, largestCellRepr));
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/rrowcol.c b/Utilities/GDAL/frmts/pcraster/libcsf/rrowcol.c
new file mode 100644
index 0000000000..4fa09bc1dc
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/rrowcol.c
@@ -0,0 +1,142 @@
+/*
+ * rrowcol.c
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+#include <math.h> /* floor */
+
+/* compute (fractional) row, column index from true world co-ordinate.
+ * RasterCoords2RowCol computes row, column index from true world co-ordinate. 
+ * The row and column co-ordinate are returned as fractions (See parameters
+ * section).
+ * The x,y co-ordinate
+ * don't have to be on the map. They are just relative to upper left position.
+ *
+ * returns
+ * 0 if the co-ordinate is outside the map,
+ * 1 if inside,
+ * -1 in case of an error
+ *
+ * Merrno
+ * ILL_CELLSIZE
+ */
+void RasterCoords2RowCol(
+ const CSF_RASTER_LOCATION_ATTRIBUTES *m,
+ double x,      /* x of true co-ordinate */
+ double y,      /* y of true co-ordinate */
+ double *row,   /* write-only. Row index (y-pos). floor(row) is row number,
+                 * if (row >= 0) then fmod(row, 1) is in-pixel displacement from pixel-top,
+                 * if (row <0) then fmod(row, 1) is in-pixel displacement from pixel-bottom. 
+                 */
+ double *col)   /* write-only. Column index (x-pos). floor(col) is column number,
+                 * if (col >= 0) then fmod(col, 1) is in-pixel displacement from pixel-left, 
+                 * if (col <0) then fmod(col, 1) is in-pixel displacement from pixel-right.
+                 */
+{
+	double cs = m->cellSize;
+	double xCol = (x - m->xUL) / cs; 
+	double yRow = (((m->projection == PT_YINCT2B) 
+	                ? (y - m->yUL)
+	                : (m->yUL - y)) / cs); 
+	/* rotate clockwise: */
+	double c = m->angleCos;    /* cos(t) == cos(-t) */
+	double s = -(m->angleSin); /* -sin(t) == sin(-t) */
+	*col  = xCol * c - yRow * s;
+	*row  = xCol * s + yRow * c;
+}
+
+int RasterCoords2RowColChecked(
+ const CSF_RASTER_LOCATION_ATTRIBUTES *m,
+ double x,      /* x of true co-ordinate */
+ double y,      /* y of true co-ordinate */
+ double *row, 
+ double *col)  
+{
+	double row_,col_; /* use copies, func( , , ,&dummy, &dummy) will fail */
+	RasterCoords2RowCol(m,x,y,&row_,&col_);
+  *row = row_;
+  *col = col_;
+	return( row_ >= 0 && col_ >= 0 && 
+	        (m->nrRows > row_) && (m->nrCols > col_) );
+}
+
+/* compute (fractional) row, column index from true world co-ordinate.
+ * Rcoord2RowCol computes row, column index from true world co-ordinate. 
+ * The row and column co-ordinate are returned as fractions (See parameters
+ * section).
+ * The x,y co-ordinate
+ * don't have to be on the map. They are just relative to upper left position.
+ *
+ * returns
+ * 0 if the co-ordinate is outside the map,
+ * 1 if inside,
+ * -1 in case of an error
+ *
+ * Merrno
+ * ILL_CELLSIZE
+ */
+int Rcoords2RowCol(
+ const MAP *m,  /* map handle */
+ double x,      /* x of true co-ordinate */
+ double y,      /* y of true co-ordinate */
+ double *row,   /* write-only. Row index (y-pos). floor(row) is row number,
+                 * if (row >= 0) then fmod(row, 1) is in-pixel displacement from pixel-top,
+                 * if (row <0) then fmod(row, 1) is in-pixel displacement from pixel-bottom. 
+                 */
+ double *col)   /* write-only. Column index (x-pos). floor(col) is column number,
+                 * if (col >= 0) then fmod(col, 1) is in-pixel displacement from pixel-left, 
+                 * if (col <0) then fmod(col, 1) is in-pixel displacement from pixel-right.
+                 */
+{
+	double row_,col_; /* use copies, func( , , ,&dummy, &dummy) will fail
+	                   * otherwise
+	                   */
+	if (m->raster.cellSize <= 0 
+	    || (m->raster.cellSize != m->raster.cellSizeDupl ) )
+	{ /* CW we should put this in Mopen */ 
+		M_ERROR(ILL_CELLSIZE);
+		goto error;
+	}
+
+	RasterCoords2RowCol(&(m->raster),x,y,&row_,&col_);
+        *row = row_;
+        *col = col_;
+	return( row_ >= 0 && col_ >= 0 && 
+	        (m->raster.nrRows > row_) && (m->raster.nrCols > col_) );
+error:  return(-1);
+}
+
+
+/* compute row, column number of true world co-ordinate
+ * RgetRowCol computes row, column number of true world co-ordinate.
+ *
+ * returns
+ * 0  if the co-ordinate is outside the map,
+ * 1 if inside,
+ * -1 in case of an error
+ *
+ * Merrno
+ * ILL_CELLSIZE
+ */
+int RgetRowCol(
+	const MAP *m, /* map handle */
+	double x,     /* x of true co-ordinate */
+	double y,     /* y of true co-ordinate */
+	size_t *row,   /* write-only. Row number (y-pos).
+	               * Undefined if (x,y) is outside of map
+	               */
+	size_t *col)   /* write-only. Column number (x-pos).
+	               * Undefined if (x,y) is outside of map
+	               */
+{
+	double row_d,col_d;
+	int    result;
+	result = Rcoords2RowCol(m,x,y,&row_d,&col_d);
+	if (result > 0)
+	{
+		*row = (size_t)floor(row_d);
+		*col = (size_t)floor(col_d);
+	}
+	return(result);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/ruseas.c b/Utilities/GDAL/frmts/pcraster/libcsf/ruseas.c
new file mode 100644
index 0000000000..fa824246b1
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/ruseas.c
@@ -0,0 +1,517 @@
+#include "csf.h"
+#include "csfimpl.h"
+
+static void UINT1tLdd(size_t nrCells, void *Buf)
+{
+	size_t i;
+	UINT1 *buf = (UINT1 *)Buf;
+
+	for(i=0; i < (size_t)nrCells; i++)
+		if (buf[i] != MV_UINT1)
+		{
+			buf[i] %= (UINT1)10;
+			if (buf[i] == 0)
+				buf[i] = MV_UINT1;
+		}
+}
+
+static void INT2tLdd(size_t nrCells, void *Buf)
+{
+	size_t i;
+	INT2  *inBuf  = (INT2  *)Buf;
+	UINT1 *outBuf = (UINT1 *)Buf;
+	for(i=0; i < (size_t)nrCells; i++)
+		if (inBuf[i] != MV_INT2)
+		{
+			outBuf[i] = (UINT1)(ABS(inBuf[i]) % 10);
+			if (outBuf[i] == 0)
+				outBuf[i] = MV_UINT1;
+		}
+		else
+			outBuf[i] = MV_UINT1;
+}
+
+#define TOBOOL(nr, buf, srcType)\
+{\
+	size_t i;\
+/* loop must be upward to prevent overwriting of values \
+ * not yet converted \
+ */ \
+ 	PRECOND(sizeof(srcType) >= sizeof(UINT1));\
+	for(i=0; i < (size_t)nr; i++)\
+		if (IS_MV_##srcType( ((srcType *)buf)+i) )\
+			((UINT1 *)buf)[i] = MV_UINT1;\
+		else\
+			((UINT1 *)buf)[i] = ((srcType *)buf)[i] != ZERO_##srcType;\
+}
+
+static void INT1tBoolean(size_t nrCells, void *buf)
+{ TOBOOL(nrCells, buf, INT1); }
+
+static void INT2tBoolean(size_t nrCells, void *buf)
+{ TOBOOL(nrCells, buf, INT2); }
+
+static void INT4tBoolean(size_t nrCells, void *buf)
+{ TOBOOL(nrCells, buf, INT4); }
+
+static void UINT1tBoolean(size_t nrCells, void *buf)
+{ TOBOOL(nrCells, buf, UINT1); }
+
+static void UINT2tBoolean(size_t nrCells, void *buf)
+{ TOBOOL(nrCells, buf, UINT2); }
+
+static void UINT4tBoolean(size_t nrCells, void *buf)
+{ TOBOOL(nrCells, buf, UINT4); }
+
+static void REAL4tBoolean(size_t nrCells, void *buf)
+{ TOBOOL(nrCells, buf, REAL4); }
+
+static void REAL8tBoolean(size_t nrCells, void *buf)
+{ TOBOOL(nrCells, buf, REAL8); }
+
+#define CONV_BIG_TO_SMALL(nr, buf, destType, srcType)\
+{\
+	size_t i;\
+/* loop must be upward to prevent overwriting of values \
+ * not yet converted: \
+ */ \
+ 	PRECOND(sizeof(srcType) >= sizeof(destType)); /* upward loop OK */ \
+	for(i=0; i < (size_t)nr; i++)\
+		if (IS_MV_##srcType( ((srcType *)buf)+i) )\
+		    SET_MV_##destType( ((destType *)buf)+i);\
+		else\
+			((destType *)buf)[i] = (destType)(((srcType *)buf)[i]);\
+}
+
+#define CONV_SMALL_TO_BIG(nr, buf, destType, srcType)\
+{\
+	size_t i = (size_t)nr;\
+/* loop must be downward to prevent overwriting of values \
+ * not yet converted: \
+ */ \
+ 	PRECOND(sizeof(srcType) <= sizeof(destType)); /* downward loop OK */ \
+	do { i--;\
+		if (IS_MV_##srcType( ((srcType *)buf)+i) )\
+		    SET_MV_##destType( ((destType *)buf)+i);\
+		else\
+			((destType *)buf)[i] = (destType)(((srcType *)buf)[i]);\
+	}while ( i != 0);\
+}
+
+static void UINT1tINT4(size_t nrCells, void *buf)
+{ CONV_SMALL_TO_BIG(nrCells, buf, INT4, UINT1);}
+
+static void UINT1tREAL4(size_t nrCells, void *buf)
+{ CONV_SMALL_TO_BIG(nrCells, buf, REAL4, UINT1);}
+
+static void UINT1tREAL8(size_t nrCells, void *buf)
+{ CONV_SMALL_TO_BIG(nrCells, buf, REAL8, UINT1);}
+
+static void INT4tUINT1(size_t nrCells, void *buf)
+{ CONV_BIG_TO_SMALL(nrCells, buf, UINT1, INT4);}
+
+static void INT2tUINT1(size_t nrCells, void *buf)
+{ CONV_BIG_TO_SMALL(nrCells, buf, UINT1, INT2);}
+
+static void UINT2tUINT1(size_t nrCells, void *buf)
+{ CONV_BIG_TO_SMALL(nrCells, buf, UINT1, UINT2);}
+
+static void INT4tREAL4(size_t nrCells, void *buf)
+{ CONV_BIG_TO_SMALL(nrCells, buf, REAL4, INT4);}
+
+static void INT4tREAL8(size_t nrCells, void *buf)
+{ CONV_SMALL_TO_BIG(nrCells, buf, REAL8, INT4);}
+
+static void REAL4tUINT1(size_t nrCells, void *buf)
+{ CONV_BIG_TO_SMALL(nrCells, buf, UINT1, REAL4);}
+
+static void REAL4tINT4(size_t nrCells, void *buf)
+{ CONV_BIG_TO_SMALL(nrCells, buf, INT4, REAL4);}
+
+static void REAL4tREAL8(size_t nrCells, void *buf)
+{ CONV_SMALL_TO_BIG(nrCells, buf, REAL8, REAL4);}
+
+static void REAL8tUINT1(size_t nrCells, void *buf)
+{ CONV_BIG_TO_SMALL(nrCells, buf, UINT1, REAL8);}
+
+static void REAL8tINT4(size_t nrCells, void *buf)
+{ CONV_BIG_TO_SMALL(nrCells, buf, INT4, REAL8);} 
+
+static void REAL8tREAL4(size_t nrCells, void *buf)
+{ CONV_BIG_TO_SMALL(nrCells, buf, REAL4, REAL8);}
+
+static void Transform2( size_t nrCells, void *buf, CSF_CR destCellRepr, CSF_CR currCellRepr);
+
+static void INT1tINT4(size_t nrCells, void *buf)
+{ Transform2(nrCells, buf, CR_INT4, CR_INT1);}
+
+static void INT1tREAL4(size_t nrCells, void *buf)
+{ Transform2(nrCells, buf, CR_REAL4, CR_INT1);}
+
+static void INT1tREAL8(size_t nrCells, void *buf)
+{ Transform2(nrCells, buf, CR_REAL8, CR_INT1);}
+
+static void INT2tINT4(size_t nrCells, void *buf)
+{ Transform2(nrCells, buf, CR_INT4, CR_INT2);}
+
+static void INT2tREAL4(size_t nrCells, void *buf)
+{ Transform2(nrCells, buf, CR_REAL4, CR_INT2);}
+
+static void INT2tREAL8(size_t nrCells, void *buf)
+{ Transform2(nrCells, buf, CR_REAL8, CR_INT2);}
+
+static void UINT2tINT4(size_t nrCells, void *buf)
+{ Transform2(nrCells, buf, CR_INT4, CR_UINT2);}
+
+static void UINT2tREAL4(size_t nrCells, void *buf)
+{ Transform2(nrCells, buf, CR_REAL4, CR_UINT2);}
+
+static void UINT2tREAL8(size_t nrCells, void *buf)
+{ Transform2(nrCells, buf, CR_REAL8, CR_UINT2);}
+
+static void UINT4tREAL4(size_t nrCells, void *buf)
+{ Transform2(nrCells, buf, CR_REAL4, CR_UINT4);}
+
+static void UINT4tREAL8(size_t nrCells, void *buf)
+{ Transform2(nrCells, buf, CR_REAL8, CR_UINT4);}
+
+
+static void ConvertToINT2( size_t nrCells, void *buf, CSF_CR src)
+{
+	if (IS_SIGNED(src))
+	{
+		POSTCOND(src == CR_INT1);
+		CONV_SMALL_TO_BIG(nrCells,buf, INT2, INT1);
+	}
+	else  
+	{
+		POSTCOND(src == CR_UINT1);
+		CONV_SMALL_TO_BIG(nrCells,buf, INT2, UINT1);
+	}
+}
+
+static void ConvertToINT4( size_t nrCells, void *buf, CSF_CR src)
+{
+	if (IS_SIGNED(src))
+	{
+		POSTCOND(src == CR_INT2);
+		CONV_SMALL_TO_BIG(nrCells,buf, INT4, INT2);
+	}
+	else  
+	{
+		POSTCOND(src == CR_UINT2);
+		CONV_SMALL_TO_BIG(nrCells,buf, INT4, UINT2);
+	}
+}
+
+
+static void UINT1tUINT2(
+size_t nrCells,
+void *buf)
+{
+	CONV_SMALL_TO_BIG(nrCells, buf, UINT2, UINT1);
+}
+
+static void UINT2tUINT4(
+size_t nrCells,
+void *buf)     
+{
+	CONV_SMALL_TO_BIG(nrCells, buf, UINT4, UINT2);
+}
+
+static void ConvertToREAL4( size_t nrCells, void *buf, CSF_CR src)
+{
+	size_t i;
+
+	i = (size_t)nrCells;
+
+	if (IS_SIGNED(src))
+	{
+		POSTCOND(src == CR_INT4);
+		INT4tREAL4(nrCells, buf);
+	}
+	else
+	{
+		POSTCOND(src == CR_UINT4);
+		{
+			do {
+				i--;
+				if ( ((UINT4 *)buf)[i] == MV_UINT4 )
+					((UINT4 *)buf)[i] = MV_UINT4;
+				else
+					((REAL4 *)buf)[i] = ((UINT4 *)buf)[i];
+			    }
+			while(i != 0);
+		}
+	}
+}
+
+static void Transform2(
+	size_t nrCells,
+	void  *buf,
+	CSF_CR destCellRepr, /* the output representation */
+	CSF_CR currCellRepr)  /* at start of while this is the representation
+				read in the MAP-file */
+{
+  /* subsequent looping changes the to the new
+   * converted type
+   */
+	while(currCellRepr != destCellRepr)
+	{
+		switch(currCellRepr)
+		{
+			case CR_INT1:    ConvertToINT2(nrCells, buf,
+						currCellRepr);
+					currCellRepr = CR_INT2;
+					break;
+			case CR_INT2:    ConvertToINT4(nrCells, buf, 
+						currCellRepr);
+					currCellRepr = CR_INT4;
+					break;
+			case CR_INT4:    ConvertToREAL4(nrCells, buf,
+						currCellRepr);
+					currCellRepr = CR_REAL4;
+					break;
+			case CR_UINT1: 	if (IS_SIGNED(destCellRepr))
+					{
+						ConvertToINT2(nrCells, buf,
+							currCellRepr);
+						currCellRepr = CR_INT2;
+					}
+					else
+					{
+						UINT1tUINT2(nrCells, buf);
+						currCellRepr = CR_UINT2;
+					}
+					break;
+			case CR_UINT2:   if (destCellRepr == CR_INT4)
+					{
+						ConvertToINT4(nrCells, buf,
+							currCellRepr);
+					 	currCellRepr = CR_INT4;
+					 }
+					 else
+					 {
+						UINT2tUINT4(nrCells, buf);
+						currCellRepr = CR_UINT4;
+					 }
+					 break;
+			case CR_UINT4:   ConvertToREAL4(nrCells, buf,
+						currCellRepr);
+					currCellRepr = CR_REAL4;
+					break;
+			default       :	POSTCOND(currCellRepr == CR_REAL4);
+					REAL4tREAL8(nrCells, buf);
+					currCellRepr = CR_REAL8;
+		}
+	}
+}
+
+/* OLD STUFF 
+void TransForm(
+	const MAP *map,
+	size_t nrCells,
+	void *buf)
+{
+	Transform2(nrCells, buf, map->types[READ_AS], map->types[STORED_AS]);
+}
+*/
+
+#define illegal NULL
+#define same    CsfDummyConversion
+
+static const CSF_CONV_FUNC ConvTable[8][8] = {
+/* ConvTable[source][destination] */
+/* INT1     , INT2      , INT4      , UINT1     , UINT2     , UINT4     , REAL4     , REAL8        */
+/* 0        , 1         , 2         , 3         , 4         , 5         , 6         , 7      ind   */
+/* 0x04     , 0x05      , 0x06      , 0x00      , 0x01      , 0x02      , 0x0A      , 0x0B low-nib */
+{  same     , illegal   , INT1tINT4 , illegal   , illegal   , illegal   , INT1tREAL4, INT1tREAL8}, /* INT1 */
+{ illegal   ,  same     , INT2tINT4 ,INT2tUINT1 , illegal   , illegal   , INT2tREAL4, INT2tREAL8}, /* INT2 */
+{ illegal   , illegal   ,  same     ,INT4tUINT1 , illegal   , illegal   , INT4tREAL4, INT4tREAL8}, /* INT4 */
+{ illegal   , illegal   ,UINT1tINT4 ,  same     ,UINT1tUINT2, illegal   ,UINT1tREAL4,UINT1tREAL8}, /* UINT1 */
+{ illegal   , illegal   ,UINT2tINT4 , UINT2tUINT1 ,  same     ,UINT2tUINT4,UINT2tREAL4,UINT2tREAL8}, /* UINT2 */
+{ illegal   , illegal   , illegal   , illegal   , illegal   ,  same     ,UINT4tREAL4,UINT4tREAL8}, /* UINT4 */
+{ illegal   , illegal   ,REAL4tINT4 ,REAL4tUINT1, illegal   , illegal   ,  same     ,REAL4tREAL8}, /* REAL4 */
+{ illegal   , illegal   ,REAL8tINT4 ,REAL8tUINT1, illegal   , illegal   ,REAL8tREAL4,  same     }  /* REAL8 */
+};
+
+static const CSF_CONV_FUNC boolConvTable[8] = {
+INT1tBoolean, INT2tBoolean, INT4tBoolean,
+UINT1tBoolean, UINT2tBoolean, UINT4tBoolean,
+REAL4tBoolean, REAL8tBoolean };
+
+
+static const char  convTableIndex[12] = {
+	   3 /* UINT1 */,  4 /* UINT2 */,  5 /* UINT4 */, -1 /* 0x03  */,
+	   0 /*  INT1 */,  1 /*  INT2 */,  2 /*  INT4 */, -1 /* 0x07  */,
+	  -1 /*  0x08 */, -1 /*  0x09 */,  6 /* REAL4 */,  7 /* REAL8 */
+};
+
+static CSF_CONV_FUNC ConvFuncBool(CSF_CR cr)
+{
+	PRECOND(CSF_UNIQ_CR_MASK(cr) < 12);
+	PRECOND(convTableIndex[CSF_UNIQ_CR_MASK(cr)] != -1);
+	
+	return boolConvTable[(int)convTableIndex[CSF_UNIQ_CR_MASK(cr)]];
+}
+
+static CSF_CONV_FUNC ConvFunc(CSF_CR destType, CSF_CR srcType)
+{
+
+	PRECOND(CSF_UNIQ_CR_MASK(destType) < 12);
+	PRECOND(CSF_UNIQ_CR_MASK(srcType) < 12);
+	PRECOND(convTableIndex[CSF_UNIQ_CR_MASK(srcType)] != -1);
+	PRECOND(convTableIndex[CSF_UNIQ_CR_MASK(destType)] != -1);
+	/* don't complain on illegal, it can be attached
+	 * to a app2file while there's no WRITE_MODE
+	 * if it's an error then it's catched in RputSomeCells
+	 */
+	return 
+         ConvTable[(int)convTableIndex[CSF_UNIQ_CR_MASK(srcType)]]
+ 	          [(int)convTableIndex[CSF_UNIQ_CR_MASK(destType)]];
+}
+
+static int HasInFileCellReprType2(CSF_CR cr)
+{
+	char  type2[12] = {
+	   1 /* UINT1 */,  0 /* UINT2 */,  0 /* UINT4 */,  0 /* 0x03  */,
+	   0 /*  INT1 */,  0 /*  INT2 */,  1 /*  INT4 */,  0 /* 0x07  */,
+	   0 /*  0x08 */,  0 /*  0x09 */,  1 /* REAL4 */,  1 /* REAL8 */};
+
+	PRECOND(CSF_UNIQ_CR_MASK(cr) < 12);
+	
+	return (int)type2[CSF_UNIQ_CR_MASK(cr)];
+}
+
+/* set the cell representation the application will use
+ * RuseAs enables an application to use cell values
+ * in a different format than they are stored in the map.
+ * Cell values are converted when getting (Rget*-functions) and
+ * putting (Rput*-functions) cells if necessary.
+ * Thus no conversions are applied if cell representation and/or 
+ * value scale already match.
+ * Any conversions between the version 2 cell representations, 
+ * (CR_UINT1, CR_INT4, CR_REAL4 and CR_REAL8) is possible. 
+ * Conversion from a non version 2 cell representation to a version
+ * 2 cell representation is only possible when you don't
+ * have write access to the cells.
+ * Conversion rules are exactly as described in K&R 2nd edition section A6.
+ * 
+ * Two special conversions are possible if you don't
+ * have write access to the cells or if the in-file cell representation is
+ * UINT1:
+ * (1) VS_BOOLEAN: successive calls to the Rget*-functions returns the result of
+ * value != 0 
+ * , that is 0 or 1 in UINT1 format. The in-file cell representation can be
+ * anything, except if the value scale is VS_DIRECTION or VS_LDD.
+ * (2) VS_LDD: successive calls to the Rget*-functions returns the result of
+ * value % 10 
+ * , that is 1 to 9 in UINT1 format (0's are set to MV_UINT1). 
+ * The in-file cell representation must be CR_UINT1 or CR_INT2 and 
+ * the value scale must be VS_LDD, VS_CLASSIFIED or VS_NOTDETERMINED.
+ *
+ * NOTE
+ * that you must use Rmalloc() to get enough space for both the in-file and
+ * app cell representation. 
+ *
+ * returns 
+ * 0 if conversion obeys rules given here. 1 if not (conversions 
+ * will not take place).
+ *
+ * Merrno
+ * CANT_USE_AS_BOOLEAN CANT_USE_WRITE_BOOLEAN
+ * CANT_USE_WRITE_LDD
+ * CANT_USE_AS_LDD
+ * CANT_USE_WRITE_OLDCR
+ * ILLEGAL_USE_TYPE
+ *
+ * EXAMPLE
+ * .so examples/maskdump.tr
+ */
+
+int RuseAs(
+	MAP *m,          /* map handle */
+	CSF_CR useType)   /* CR_UINT1,CR_INT4, CR_REAL4, CR_REAL8, VS_BOOLEAN or VS_LDD */
+{ 
+
+  CSF_CR inFileCR = RgetCellRepr(m);
+  CSF_VS inFileVS = RgetValueScale(m);
+  int hasInFileCellReprType2 =  HasInFileCellReprType2(inFileCR);
+
+  switch(useType)
+  {
+    case VS_BOOLEAN:
+  	switch(inFileVS) {
+	   case VS_LDD: case VS_DIRECTION:
+   			M_ERROR(CANT_USE_AS_BOOLEAN);
+   			return 1;
+   	   case VS_BOOLEAN:
+   	   		POSTCOND(inFileCR == CR_UINT1);
+   	   		m->appCR = CR_UINT1;
+   	   		m->file2app = same;
+   	   		m->app2file = same;
+   	   		return 0;
+   	   default:
+			if ( (! hasInFileCellReprType2) && WRITE_ENABLE(m) )
+			{ /* cellrepr is old one, we can't write that */
+					M_ERROR(CANT_USE_WRITE_BOOLEAN);
+					return 1;
+			}
+   			m->appCR = CR_UINT1;
+   			m->file2app  = ConvFuncBool(inFileCR);
+   			m->app2file = ConvFunc(inFileCR, CR_UINT1);
+   	   		return 0;
+   	} /* case useType == VS_BOOLEAN */
+   	break;
+
+   case VS_LDD:
+        switch(inFileVS) {
+         case VS_LDD:
+   	   		POSTCOND(inFileCR == CR_UINT1);
+   	   		m->appCR = CR_UINT1;
+   	   		m->file2app = same;
+   	   		m->app2file = same;
+   	   		return 0;
+        case VS_CLASSIFIED: 
+        case VS_NOTDETERMINED: 
+        	switch(inFileCR) {
+        	 case CR_UINT1:
+        	 	m->appCR = CR_UINT1;
+			m->file2app  = UINT1tLdd;
+			m->app2file = same;
+			return 0;
+		 case CR_INT2:
+		        if (WRITE_ENABLE(m))
+		        {  M_ERROR(CANT_USE_WRITE_LDD);
+			   return 1;
+		        }
+        	 	m->appCR = CR_UINT1;
+			m->file2app  = INT2tLdd;
+			m->app2file = illegal;
+			return 0;
+		}
+	default: M_ERROR(CANT_USE_AS_LDD);
+		 return 1;
+      }
+      /* case useType == VS_LDD */
+      break;
+     case CR_UINT1:
+     case CR_INT4 :
+     case CR_REAL4:
+     case CR_REAL8:
+		if ( (! hasInFileCellReprType2) && WRITE_ENABLE(m) )
+		{ /* cellrepr is old one, we can't write that */
+				M_ERROR(CANT_USE_WRITE_OLDCR);
+				return 1;
+		}
+		m->appCR = useType;
+		m->file2app  = ConvFunc(useType, inFileCR);
+		m->app2file = ConvFunc(inFileCR, useType);
+		POSTCOND(m->file2app != NULL);
+		return 0;
+    default:
+    		M_ERROR(ILLEGAL_USE_TYPE);
+		return 1;
+  }
+  /* NOTREACHED */
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/setangle.c b/Utilities/GDAL/frmts/pcraster/libcsf/setangle.c
new file mode 100644
index 0000000000..b278fef8a9
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/setangle.c
@@ -0,0 +1,79 @@
+
+/*
+ * setangle.c 
+   $Log: setangle.c,v $
+   Revision 1.3  2006/02/07 10:17:15  kdejong
+   Fixed endian compile problem
+   some rcs issues of Kor, I guess
+   Checked in by cees (cees@pcraster.nl) on account of Kor
+
+   Revision 1.3  2005/10/03 07:23:00  kor
+   Removed rcs id string
+
+   Revision 1.2  2000/02/05 21:25:48  cees
+   added LOCATION_ATTRIBUTER struct
+
+   Revision 1.1.1.1  2000/01/04 21:05:05  cees
+   Initial import Cees
+
+   Revision 2.0  1996/05/23 13:16:26  cees
+   csf2clean
+
+   Revision 1.1  1996/05/23 13:11:49  cees
+   Initial revision
+
+   Revision 1.2  1995/11/01 17:23:03  cees
+   .
+
+ * Revision 1.1  1994/09/07  13:23:08  cees
+ * Initial revision
+ *
+ */
+/********/
+/* USES */
+/********/
+
+/* libs ext. <>, our ""  */
+#include <math.h>
+#include "csf.h"
+#include "csfimpl.h"
+
+/* global header (opt.) and setangle's prototypes "" */
+
+
+/* headers of this app. modules called */ 
+
+/***************/
+/* EXTERNALS   */
+/***************/
+
+/**********************/ 
+/* LOCAL DECLARATIONS */
+/**********************/ 
+
+/*********************/ 
+/* LOCAL DEFINITIONS */
+/*********************/ 
+
+/******************/
+/* IMPLEMENTATION */
+/******************/
+
+/* Set the stuff in the header after header initialization (LIBRARY_INTERNAL)
+ * Implements some common code for Mopen, Rcreate and family:
+ *
+ * set the map angle cosine and sin in header
+ * these values are only used in the co-ordinate conversion
+ * routines. And since they do a counter clockwise rotation we
+ * take the sine and cosine of the negative angle.
+ *
+ * copy projection field into  raster, so raster can act as an 
+ * indepent structure, for transformations
+ */
+void CsfFinishMapInit(
+	MAP *m)		/* map handle */
+{
+	m->raster.angleCos   = cos(-(m->raster.angle));
+	m->raster.angleSin   = sin(-(m->raster.angle));
+	m->raster.projection = MgetProjection(m);
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/setmv.c b/Utilities/GDAL/frmts/pcraster/libcsf/setmv.c
new file mode 100644
index 0000000000..7ceeb282f5
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/setmv.c
@@ -0,0 +1,85 @@
+/*
+ * setmv.c
+$Log: setmv.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:05:06  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/09/06  13:04:27  cees
+ * changed for appCR field
+ * added c2man docs
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* set a memory location to a missing value
+ * SetMV sets a memory location to a missing value
+ * (using the application cell representation).
+ * SetMV is quite slow but handy as in the example
+ * below. In general one should use assignment for
+ * integers (e.g. v = MV_UINT1) or the macro's
+ * SET_MV_REAL4 and SET_MV_REAL8
+ *
+ * EXAMPLE
+ * .so examples/border.tr
+ */
+void SetMV(
+	const MAP *m, /* map handle */
+	void *c)      /* write-only. location set to missing value */
+{
+	SetMVcellRepr(m->appCR, c);
+}
+
+/* set a memory location to a missing value
+ * SetMVcellRepr sets a memory location to a missing value
+ * (using the application cell representation).
+ * In general one should use assignment for
+ * integers (e.g. v = MV_UINT1) or the macro's
+ * SET_MV_REAL4 and SET_MV_REAL8
+ *
+ */
+void SetMVcellRepr(
+	CSF_CR cellRepr, /* cell representation, one of the CR_* constants */
+	void *c)      /* write-only. location set to missing value */
+{
+	switch (cellRepr)
+	{
+		case CR_INT1  : *((INT1 *)c) = MV_INT1;
+				break;
+		case CR_INT2  : *((INT2 *)c) = MV_INT2;
+				break;
+		case CR_INT4  : *((INT4 *)c) = MV_INT4;
+				break;
+		case CR_UINT1 : *((UINT1 *)c) = MV_UINT1;
+				break;
+		case CR_UINT2 : *((UINT2 *)c) = MV_UINT2;
+				break;
+		case CR_REAL8 :
+				((UINT4 *)c)[1] = MV_UINT4;
+		default       : POSTCOND(
+					cellRepr == CR_REAL8 ||
+					cellRepr == CR_REAL4 ||
+					cellRepr == CR_UINT4 );
+				*((UINT4 *)c) = MV_UINT4;
+	}
+} 
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/setvtmv.c b/Utilities/GDAL/frmts/pcraster/libcsf/setvtmv.c
new file mode 100644
index 0000000000..8aeac74d4c
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/setvtmv.c
@@ -0,0 +1,51 @@
+/*
+ * setvtmv.c
+$Log: setvtmv.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.2  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.1.1.1  2000/01/04 21:05:06  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.2  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* (LIBRARY_INTERNAL)
+ */
+void CsfSetVarTypeMV( CSF_VAR_TYPE *var, CSF_CR cellRepr)
+{
+/* assuming unions are left-alligned */
+	if(IS_SIGNED(cellRepr))
+		switch(LOG_CELLSIZE(cellRepr))
+		{
+		 case 2	:	*(INT4 *)var = MV_INT4;
+		 		break;
+		 case 1	:	*(INT2 *)var = MV_INT2;
+		 		break;
+		 default:	POSTCOND(LOG_CELLSIZE(cellRepr) == 0);
+		 		*(INT1 *)var = MV_INT1;
+		}
+	else
+	{
+		((UINT4 *)var)[0] = MV_UINT4;
+		((UINT4 *)var)[1] = MV_UINT4;
+	}
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/strconst.c b/Utilities/GDAL/frmts/pcraster/libcsf/strconst.c
new file mode 100644
index 0000000000..4c78e69aa2
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/strconst.c
@@ -0,0 +1,97 @@
+
+/*
+ * strconst.c 
+ */
+/********/
+/* USES */
+/********/
+
+/* libs ext. <>, our ""  */
+#include "csf.h"
+
+/* global header (opt.) and strconst's prototypes "" */
+
+
+/* headers of this app. modules called */ 
+
+/***************/
+/* EXTERNALS   */
+/***************/
+
+/**********************/ 
+/* LOCAL DECLARATIONS */
+/**********************/ 
+
+/*********************/ 
+/* LOCAL DEFINITIONS */
+/*********************/ 
+static char errorBuf[64];
+/******************/
+/* IMPLEMENTATION */
+/******************/
+
+/* string with cell representation in plain english or acronym
+ * The string is in lower case except for INT1,INT2,UINT2 and UINT4
+ *, they return an acronym. If cr is not
+ * a valid constant, for example 999,  then the string is  
+ * "999 is no CR constant".
+ * The "no constant" message is stored in a static buffer
+ * used by both RstrCellRepr and RstrValueScale.
+ * returns
+ * string with cell representation
+ */
+const char *RstrCellRepr(CSF_CR cr) /* cell representation constant */
+{
+ switch(cr) {
+	case CR_INT1  : return "INT1";
+	case CR_INT2  : return "INT2";
+	case CR_INT4  : return "large integer";
+	case CR_UINT1 : return "small integer";
+	case CR_UINT2 : return "UINT2";
+	case CR_UINT4 : return "UINT4";
+	case CR_REAL4 : return "small real";
+	case CR_REAL8 : return "large real";
+	default       : (void)sprintf(errorBuf,"%u is no CR constant", (unsigned)cr);
+                        return errorBuf;
+ }
+}
+
+/* string with value scale
+ * The string is in lower case. If cr is not
+ * a valid constant, for example 999,  then the string is  
+ * "999 is no VS constant".
+ * The "no constant" message is stored in a static buffer
+ * used by both RstrCellRepr and RstrValueScale.
+ * returns
+ * string with value scale in lower case
+ */
+const char *RstrValueScale(CSF_VS vs) /* value scale constant */
+{
+ switch(vs) {
+	case VS_NOTDETERMINED : return "notdetermined";
+	case VS_CLASSIFIED    : return "classified";
+	case VS_CONTINUOUS    : return "continuous";
+	case VS_BOOLEAN       : return "boolean";
+	case VS_NOMINAL       : return "nominal";
+	case VS_ORDINAL       : return "ordinal";
+	case VS_SCALAR        : return "scalar";
+	case VS_DIRECTION     : return "directional";
+	case VS_LDD           : return "ldd";
+	default       : (void)sprintf(errorBuf,"%u is no VS constant", (unsigned)vs);
+                        return errorBuf;
+ }
+}
+
+/* string with projection 
+ * The string is in lower case. 
+ * string with name of projection 
+ */
+const char *MstrProjection(CSF_PT p) /* projection constant, 0 is
+                                     * top to bottom. non-0 is bottom
+                                     * to top
+                                     */
+{
+ 	return (p) ?
+ 	   "y increases from bottom to top"
+ 	  :"y increases from top to bottom";
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/strpad.c b/Utilities/GDAL/frmts/pcraster/libcsf/strpad.c
new file mode 100644
index 0000000000..f625159187
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/strpad.c
@@ -0,0 +1,37 @@
+/********/
+/* USES */
+/********/
+
+/* libs ext. <>, our ""  */
+#include <string.h>
+
+/* global header (opt.) and strpad's prototypes "" */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* headers of this app. modules called */ 
+
+/***************/
+/* EXTERNALS   */
+/***************/
+
+/**********************/ 
+/* LOCAL DECLARATIONS */
+/**********************/ 
+
+/*********************/ 
+/* LOCAL DEFINITIONS */
+/*********************/ 
+
+/******************/
+/* IMPLEMENTATION */
+/******************/
+/* pad a string attribute with zeros (LIBRARY_INTERNAL)
+ */
+char *CsfStringPad(char *s, size_t reqSize)
+{
+	size_t l = strlen(s);
+	PRECOND(l <= reqSize);
+	(void)memset(s+l, '\0', reqSize-l);
+	return s;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/swapio.c b/Utilities/GDAL/frmts/pcraster/libcsf/swapio.c
new file mode 100644
index 0000000000..93067f8cb5
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/swapio.c
@@ -0,0 +1,121 @@
+/********/
+/* USES */
+/********/
+
+/* libs ext. <>, our ""  */
+#include "csf.h"
+
+/* global header (opt.) and swapio's prototypes "" */
+#include "csfimpl.h" 
+
+
+/* headers of this app. modules called */ 
+
+/***************/
+/* EXTERNALS   */
+/***************/
+
+/**********************/ 
+/* LOCAL DECLARATIONS */
+/**********************/ 
+
+/* typedef for swap functions (LIBRARY_INTERNAL)
+ * typedef for swap functions 
+ */
+typedef void (*SWAP)(unsigned char *buf,  size_t n);
+/*********************/ 
+/* LOCAL DEFINITIONS */
+/*********************/ 
+
+/******************/
+/* IMPLEMENTATION */
+/******************/
+
+/* check valid size of element (LIBRARY_INTERNAL)
+ */
+int CsfValidSize(size_t size) 
+{
+ 	return size == 1 || size == 2 || size == 4 || size == 8;
+}
+
+#ifdef DEBUG
+ size_t CsfWritePlain(void *buf, size_t size, size_t n, FILE  *f)
+ {
+  PRECOND(CsfValidSize(size));
+  return fwrite(buf, size, n, f);
+ }
+ size_t CsfReadPlain(void *buf, size_t size, size_t n, FILE  *f)
+ {
+  PRECOND(CsfValidSize(size));
+  return fread(buf, size, n, f);
+ }
+#endif
+
+/* ARGSUSED */
+static void Swap1(unsigned char * buf,  size_t n)
+{
+	/* do nothing */
+}
+
+static void Swap2(unsigned char *b,  size_t n)
+{
+	unsigned char tmp;
+	size_t i;
+	for (i=0; i < n; i++)
+	{
+	 /* 01 => 10 */
+	 tmp = b[0]; b[0] = b[1]; b[1] = tmp;
+	 b += 2;
+	}
+}
+
+static void Swap4(unsigned char *b,  size_t n)
+{
+	unsigned char tmp;
+	size_t i;
+	for (i=0; i < n; i++)
+	{
+        /* 0123 => 3210 */
+	tmp = b[0]; b[0] = b[3]; b[3] = tmp;
+	tmp = b[1]; b[1] = b[2]; b[2] = tmp;
+	b += 4;
+        }
+}
+
+static void Swap8(unsigned char *b,  size_t n)
+{
+	unsigned char tmp;
+	size_t i;
+	for (i=0; i < n; i++)
+	{
+	/* 01234567 => 76543210 */
+	tmp = b[0]; b[0] = b[7]; b[7] = tmp;
+	tmp = b[1]; b[1] = b[6]; b[6] = tmp;
+	tmp = b[2]; b[2] = b[5]; b[5] = tmp;
+	tmp = b[3]; b[3] = b[4]; b[4] = tmp;
+	b += 8;
+	}
+}
+
+void CsfSwap(void *buf, size_t size, size_t n)
+{
+	SWAP l[9] = { NULL, Swap1, Swap2, NULL, Swap4,
+                      NULL, NULL,  NULL,  Swap8};
+        PRECOND(CsfValidSize(size));
+	PRECOND(l[size] != NULL);
+	
+	l[size]((unsigned char *)buf,n);
+}
+
+size_t CsfWriteSwapped(void *buf, size_t size, size_t n, FILE  *f)
+{
+	CsfSwap(buf,size, n);
+	return fwrite(buf, size, n,f);
+}
+
+size_t CsfReadSwapped(void *buf, size_t size, size_t n, FILE  *f)
+{
+	size_t r = fread(buf, size, n,f);
+	CsfSwap(buf,size, r);
+	return r;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/trackmm.c b/Utilities/GDAL/frmts/pcraster/libcsf/trackmm.c
new file mode 100644
index 0000000000..1c08922b6a
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/trackmm.c
@@ -0,0 +1,71 @@
+
+/*
+ * trackmm.c 
+   $Log: trackmm.c,v $
+   Revision 1.3  2006/02/07 10:17:15  kdejong
+   Fixed endian compile problem
+   some rcs issues of Kor, I guess
+   Checked in by cees (cees@pcraster.nl) on account of Kor
+
+   Revision 1.2  2005/10/03 07:23:00  kor
+   Removed rcs id string
+
+   Revision 1.1.1.1  2000/01/04 21:05:11  cees
+   Initial import Cees
+
+   Revision 2.1  1996/12/29 19:35:21  cees
+   src tree clean up
+
+   Revision 2.0  1996/05/23 13:16:26  cees
+   csf2clean
+
+   Revision 1.1  1996/05/23 13:11:49  cees
+   Initial revision
+
+   Revision 1.2  1995/11/01 17:23:03  cees
+   .
+
+ * Revision 1.1  1994/09/13  10:56:47  cees
+ * Initial revision
+ *
+ */
+
+/********/
+/* USES */
+/********/
+
+/* libs ext. <>, our ""  */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* global header (opt.) and trackmm's prototypes "" */
+
+
+/* headers of this app. modules called */ 
+
+/***************/
+/* EXTERNALS   */
+/***************/
+
+/**********************/ 
+/* LOCAL DECLARATIONS */
+/**********************/ 
+
+/*********************/ 
+/* LOCAL DEFINITIONS */
+/*********************/ 
+
+/******************/
+/* IMPLEMENTATION */
+/******************/
+
+/* disable automatic tracking of minimum and maximum value 
+ * A call to RdontTrackMinMax disables the automatic tracking
+ * of the min/max value in succesive cell writes. 
+ * If used, one must always
+ * use RputMinVal and RputMaxVal to set the correct values.
+ */
+void RdontTrackMinMax(MAP *m) /* map handle */
+{
+	m->minMaxStatus = MM_DONTKEEPTRACK;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/vs2.c b/Utilities/GDAL/frmts/pcraster/libcsf/vs2.c
new file mode 100644
index 0000000000..fd30002977
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/vs2.c
@@ -0,0 +1,49 @@
+/********/
+/* USES */
+/********/
+
+/* libs ext. <>, our ""  */
+
+/* global header (opt.) and vsis's prototypes "" */
+#include "csf.h" 
+#include "csfimpl.h" 
+
+
+/* headers of this app. modules called */ 
+
+/***************/
+/* EXTERNALS   */
+/***************/
+
+/**********************/ 
+/* LOCAL DECLARATIONS */
+/**********************/ 
+
+/*********************/ 
+/* LOCAL DEFINITIONS */
+/*********************/ 
+
+/******************/
+/* IMPLEMENTATION */
+/******************/
+
+/* test if valuescale/datatype is a CSF version 2 value scale
+ * RvalueScale2 tests if the map's value scale is a version
+ * 2 valuescale/datatype.
+ * returns
+ * 0 if the value is not in the above list, 1 if it does.
+ *
+ */
+int RvalueScale2(
+	CSF_VS    vs) /* value scale. ALL OF BELOW are accepted */
+{
+	switch(vs) {
+	  case VS_LDD: 
+	 case VS_BOOLEAN:
+	 case VS_NOMINAL:
+	 case VS_ORDINAL: 
+	 case VS_SCALAR: 
+	 case VS_DIRECTION: return 1;
+	 default :       return 0;
+      }
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/vsdef.c b/Utilities/GDAL/frmts/pcraster/libcsf/vsdef.c
new file mode 100644
index 0000000000..16626f004c
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/vsdef.c
@@ -0,0 +1,85 @@
+
+
+/*
+ * vsdef.c 
+   $Log: vsdef.c,v $
+   Revision 1.3  2006/02/07 10:17:15  kdejong
+   Fixed endian compile problem
+   some rcs issues of Kor, I guess
+   Checked in by cees (cees@pcraster.nl) on account of Kor
+
+   Revision 1.2  2005/10/03 07:23:00  kor
+   Removed rcs id string
+
+   Revision 1.1.1.1  2000/01/04 21:05:12  cees
+   Initial import Cees
+
+   Revision 2.0  1996/05/23 13:16:26  cees
+   csf2clean
+
+   Revision 1.1  1996/05/23 13:11:49  cees
+   Initial revision
+
+   Revision 1.2  1995/11/01 17:23:03  cees
+   .
+
+ * Revision 1.1  1995/05/04  14:35:24  cees
+ * Initial revision
+ *
+ * Revision 1.1  1994/09/02  14:30:00  cees
+ * Initial revision
+ *
+ */
+
+/********/
+/* USES */
+/********/
+
+/* libs ext. <>, our ""  */
+
+/* global header (opt.) and vsis's prototypes "" */
+#include "csf.h" 
+#include "csfimpl.h" 
+
+
+/* headers of this app. modules called */ 
+
+/***************/
+/* EXTERNALS   */
+/***************/
+
+/**********************/ 
+/* LOCAL DECLARATIONS */
+/**********************/ 
+
+/*********************/ 
+/* LOCAL DEFINITIONS */
+/*********************/ 
+
+/******************/
+/* IMPLEMENTATION */
+/******************/
+
+/* returns default cell representation of a value scale/cellRepr
+ * returns
+ *  the appropriate cell representation constant (CR_something)
+ *  or CR_UNDEFINED if vs is not a csf2 datatype
+ *
+ */
+CSF_CR RdefaultCellRepr(
+	CSF_VS     vs) /* value scale */
+{
+	switch(vs) {
+	 case VS_LDD: 
+	 case VS_BOOLEAN: return CR_UINT1;
+	 case VS_NOMINAL:
+	 case VS_ORDINAL: return CR_INT4;
+	 case VS_SCALAR: 
+	 case VS_DIRECTION: return CR_REAL4;
+         case VS_CLASSIFIED: return CR_UINT1;
+         case VS_CONTINUOUS: return CR_REAL4;
+         case VS_NOTDETERMINED:
+         default:
+         	 return  CR_UNDEFINED;
+      }
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/vsis.c b/Utilities/GDAL/frmts/pcraster/libcsf/vsis.c
new file mode 100644
index 0000000000..aa5423de44
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/vsis.c
@@ -0,0 +1,102 @@
+/*
+ * vsis.c 
+ */
+
+/********/
+/* USES */
+/********/
+
+/* libs ext. <>, our ""  */
+
+/* global header (opt.) and vsis's prototypes "" */
+#include "csf.h" 
+#include "csfimpl.h" 
+
+
+/* headers of this app. modules called */ 
+
+/***************/
+/* EXTERNALS   */
+/***************/
+
+/**********************/ 
+/* LOCAL DECLARATIONS */
+/**********************/ 
+
+/*********************/ 
+/* LOCAL DEFINITIONS */
+/*********************/ 
+
+/******************/
+/* IMPLEMENTATION */
+/******************/
+
+/* test value scale on compatibility CSF version 1 and 2
+ * RvalueScaleIs tests if the map's value scale is compatible
+ * with a certain value scale. Here is list of compatible but
+ * different value scales: 
+ *   
+ * VS_NOTDETERMINED: always returns 0
+ *
+ * VS_CLASSIFIED: VS_NOTDETERMINED
+ *
+ * VS_CONTINUOUS: VS_NOTDETERMINED
+ *
+ * VS_BOOLEAN: VS_CLASSIFIED, VS_NOTDETERMINED
+ *
+ * VS_NOMINAL: VS_CLASSIFIED, VS_NOTDETERMINED
+ *
+ * VS_ORDINAL: VS_CLASSIFIED, VS_NOTDETERMINED
+ *
+ * VS_LDD:  VS_CLASSIFIED, VS_NOTDETERMINED (only if cell representation is
+ * UINT1 or INT2)
+ *
+ * VS_SCALAR:  VS_CONTINUOUS, VS_NOTDETERMINED
+ *
+ * VS_DIRECTION: none 
+ *
+ * returns
+ * 0 if not compatible or if vs argument is VS_NOTDETERMINED or in case of
+ * error, nonzero if 
+ * compatible.
+ *
+ * Merrno
+ * BAD_VALUESCALE
+ *
+ * EXAMPLE
+ * .so examples/maskdump.tr
+ */
+int RvalueScaleIs(
+	const MAP *m, /* a version 1 map handle */
+	CSF_VS     vs) /* a version 2 value scale that is compatible with map's value
+	               * scale yes or no?
+	               */
+{
+	CSF_VS mapsVS = RgetValueScale(m);
+	
+	if (vs == VS_NOTDETERMINED)
+		return 0;
+
+	if (vs == mapsVS)
+		return 1;
+
+	switch(vs) {
+	  case VS_CLASSIFIED: return mapsVS == VS_NOTDETERMINED;
+	  case VS_CONTINUOUS: return mapsVS == VS_NOTDETERMINED;
+	  case VS_LDD: 
+	                { CSF_CR cr = RgetCellRepr(m);
+	                  if (cr !=  CR_UINT1 && cr != CR_INT2)
+	                   return 0;
+	                } /* fall through */
+	 case VS_BOOLEAN:
+	 case VS_NOMINAL:
+	 case VS_ORDINAL:  return mapsVS == VS_CLASSIFIED 
+			    || mapsVS == VS_NOTDETERMINED;
+	 case VS_SCALAR:   return mapsVS == VS_CONTINUOUS
+	                    || mapsVS == VS_NOTDETERMINED;
+	/* direction isn't compatible with anything */
+	 case VS_DIRECTION: return 0;
+	 default          : M_ERROR(BAD_VALUESCALE);
+	                    return 0;
+      }
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/vsvers.c b/Utilities/GDAL/frmts/pcraster/libcsf/vsvers.c
new file mode 100644
index 0000000000..70f75c5560
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/vsvers.c
@@ -0,0 +1,57 @@
+
+/*
+ * vsvers.c 
+ */
+
+/********/
+/* USES */
+/********/
+
+/* libs ext. <>, our ""  */
+#include "csf.h" 
+
+/* global header (opt.) and vsvers's prototypes "" */
+
+
+/* headers of this app. modules called */ 
+
+/***************/
+/* EXTERNALS   */
+/***************/
+
+/**********************/ 
+/* LOCAL DECLARATIONS */
+/**********************/ 
+
+/*********************/ 
+/* LOCAL DEFINITIONS */
+/*********************/ 
+
+/******************/
+/* IMPLEMENTATION */
+/******************/
+
+/* get version nr of value scale
+ * returns
+ * 0 for illegal value scale,
+ * 1 version 1 value scale,
+ * 2 version 2 value scale
+ */
+int RgetValueScaleVersion(
+	const MAP *m) /* map handle */
+{
+	UINT2 vs = RgetValueScale(m);
+	
+	switch(vs) {
+	  case VS_CLASSIFIED   : 
+	  case VS_CONTINUOUS   : 
+	  case VS_NOTDETERMINED: return 1;
+	  case VS_LDD      : 
+	  case VS_BOOLEAN  :
+	  case VS_NOMINAL  :
+	  case VS_ORDINAL  : 
+	  case VS_SCALAR   :
+	  case VS_DIRECTION: return 2;
+	  default          : return 0;
+      }
+}
diff --git a/Utilities/GDAL/frmts/pcraster/libcsf/wattrblk.c b/Utilities/GDAL/frmts/pcraster/libcsf/wattrblk.c
new file mode 100644
index 0000000000..bf3ce970fa
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/libcsf/wattrblk.c
@@ -0,0 +1,59 @@
+/*
+ * wattrblk.c
+$Log: wattrblk.c,v $
+Revision 1.3  2006/02/07 10:17:15  kdejong
+Fixed endian compile problem
+some rcs issues of Kor, I guess
+Checked in by cees (cees@pcraster.nl) on account of Kor
+
+Revision 1.3  2005/10/03 07:23:00  kor
+Removed rcs id string
+
+Revision 1.2  2005/09/29 18:43:23  cees
+x86_64
+
+Revision 1.1.1.1  2000/01/04 21:05:15  cees
+Initial import Cees
+
+Revision 2.0  1996/05/23 13:16:26  cees
+csf2clean
+
+Revision 1.1  1996/05/23 13:11:49  cees
+Initial revision
+
+Revision 1.3  1995/11/01 17:23:03  cees
+.
+
+ * Revision 1.2  1994/08/31  15:36:16  cees
+ * added c2man doc
+ *
+ * Revision 1.1  1994/08/26  13:33:23  cees
+ * Initial revision
+ *
+ */
+#include "csf.h"
+#include "csfimpl.h"
+
+/* write an attribute control block (LIBRARY_INTERNAL)
+ * returns 0 if successful,
+ * 1 if seeking or writing failed
+ */
+int CsfWriteAttrBlock(
+	MAP *m,          /* map handle */ 
+	CSF_FADDR pos,       /* file position where the block is written */
+	ATTR_CNTRL_BLOCK *b) /* attribute control block to be written */
+{
+ int i;
+
+ if ( fseek(m->fp,(long) pos, SEEK_SET) )
+ 	return 1;
+
+ for(i=0; i < NR_ATTR_IN_BLOCK; i++)
+  if ( m->write(&(b->attrs[i].attrId), sizeof(UINT2),(size_t)1,m->fp)    != 1 ||
+       m->write(&(b->attrs[i].attrOffset),sizeof(CSF_FADDR),(size_t)1,m->fp) != 1 ||
+       m->write(&(b->attrs[i].attrSize), sizeof(UINT4),(size_t)1,m->fp)  != 1 
+     )
+     return 1;
+ 
+ return m->write(&(b->next), sizeof(CSF_FADDR),(size_t)1,m->fp) != 1;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/makefile.vc b/Utilities/GDAL/frmts/pcraster/makefile.vc
new file mode 100644
index 0000000000..5701277cfc
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/makefile.vc
@@ -0,0 +1,30 @@
+
+# nmake makefile for gdal
+# should go as makefile.vc in frmts/pcraster
+
+OBJ = pcrasterdataset.obj pcrastermisc.obj pcrasterutil.obj \
+	pcrasterrasterband.obj
+
+!IFNDEF PCRASTER_EXTERNAL_LIB
+EXTRAFLAGS = 	-Ilibcsf
+!ENDIF
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj \
+	..\o
+!IFNDEF PCRASTER_EXTERNAL_LIB
+	cd libcsf
+	$(MAKE) /f makefile.vc
+	cd ..
+!ENDIF
+
+clean:
+	-del *.obj
+	cd libcsf
+	$(MAKE) /f makefile.vc clean
+	cd ..
+
diff --git a/Utilities/GDAL/frmts/pcraster/pcrasterdataset.cpp b/Utilities/GDAL/frmts/pcraster/pcrasterdataset.cpp
new file mode 100644
index 0000000000..9ca9b528cb
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/pcrasterdataset.cpp
@@ -0,0 +1,452 @@
+/******************************************************************************
+ * $Id: pcrasterdataset.cpp,v 1.9 2005/05/05 15:54:49 fwarmerdam Exp $
+ *
+ * Project:  PCRaster Integration
+ * Purpose:  PCRaster CSF 2.0 raster file driver
+ * Author:   Kor de Jong, k.dejong at geog.uu.nl
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, Kor de Jong
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: pcrasterdataset.cpp,v $
+ * Revision 1.9  2005/05/05 15:54:49  fwarmerdam
+ * PAM Enabled
+ *
+ * Revision 1.8  2004/11/25 15:00:41  kdejong
+ * Replace and by &&, moved free out of std namespace
+ *
+ * Revision 1.7  2004/11/22 10:40:23  kdejong
+ * Added PCRasterRasterBand::Minimum and Maximum. Improved documentation, layout. Removed unused code. Layout.
+ *
+ * Revision 1.6  2004/11/13 19:00:55  fwarmerdam
+ * Don't blow an assertion if we don't have enough header data, just
+ * return NULL (in open()).
+ *
+ * Revision 1.5  2004/11/13 12:08:44  kdejong
+ * Reading files with other cell representations than UINT1, INT4 or REAL4 will keep their original cell representation in memory.
+ *
+ * Revision 1.4  2004/11/11 15:50:36  kdejong
+ * Added write support, docs, improvements.
+ *
+ * Revision 1.3  2004/11/10 10:21:42  kdejong
+ * *** empty log message ***
+ *
+ * Revision 1.2  2004/11/10 10:09:19  kdejong
+ * Initial versions. Read only driver.
+ *
+ * Revision 1.1  2004/10/22 14:19:27  fwarmerdam
+ * New
+ *
+ */
+
+#include "gdal_pam.h"
+
+CPL_CVSID("$Id: pcrasterdataset.cpp,v 1.9 2005/05/05 15:54:49 fwarmerdam Exp $");
+
+#ifndef INCLUDED_PCRASTERDATASET
+#include "pcrasterdataset.h"
+#define INCLUDED_PCRASTERDATASET
+#endif
+
+// Library headers.
+#ifndef INCLUDED_CASSERT
+#include <cassert>
+#define INCLUDED_CASSERT
+#endif
+
+#ifndef INCLUDED_CSTDLIB
+#include <cstdlib>
+#define INCLUDED_CSTDLIB
+#endif
+
+#ifndef INCLUDED_IOSTREAM
+#include <iostream>
+#define INCLUDED_IOSTREAM
+#endif
+
+#ifndef INCLUDED_CPL_STRING
+#include "cpl_string.h"
+#define INCLUDED_CPL_STRING
+#endif
+
+// PCRaster library headers.
+
+// Module headers.
+#ifndef INCLUDED_PCRASTERRASTERBAND
+#include "pcrasterrasterband.h"
+#define INCLUDED_PCRASTERRASTERBAND
+#endif
+
+#ifndef INCLUDED_PCRASTERUTIL
+#include "pcrasterutil.h"
+#define INCLUDED_PCRASTERUTIL
+#endif
+
+
+
+/*!
+  \file
+  This file contains the implementation of the PCRasterDataset class.
+*/
+
+
+
+//------------------------------------------------------------------------------
+// DEFINITION OF STATIC PCRDATASET MEMBERS
+//------------------------------------------------------------------------------
+
+//! Tries to open the file described by \a info.
+/*!
+  \param     info Object with information about the dataset to open.
+  \return    Pointer to newly allocated GDALDataset or 0.
+
+  Returns 0 if the file could not be opened.
+*/
+GDALDataset* PCRasterDataset::open(GDALOpenInfo* info)
+{
+  PCRasterDataset* dataset = 0;
+
+  if(info->fp && info->nHeaderBytes >= static_cast<int>(CSF_SIZE_SIG) &&
+         strncmp((char*)info->pabyHeader, CSF_SIG, CSF_SIZE_SIG) == 0) {
+    MOPEN_PERM mode = M_READ;
+    if(info->eAccess == GA_Update) {
+      mode = M_READ_WRITE;
+    }
+
+    MAP* map = ::open(info->pszFilename, mode);
+
+    if(map) {
+      dataset = new PCRasterDataset(map);
+    }
+  }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+  if( dataset )
+  {
+      dataset->SetDescription( info->pszFilename );
+      dataset->TryLoadXML();
+  }
+
+  return dataset;
+}
+
+
+
+//! Writes a raster to \a filename as a PCRaster raster file.
+/*!
+  \warning   The source raster must have only 1 band. Currently, the values in
+             the source raster must be stored in one of the supported cell
+             representations (CR_UINT1, CR_INT4, CR_REAL4, CR_REAL8).
+
+  The meta data item PCRASTER_VALUESCALE will be checked to see what value
+  scale to use. Otherwise a value scale is determined using
+  GDALType2ValueScale(GDALDataType).
+
+  This function alwasy writes raster using CR_UINT1, CR_INT4 or CR_REAL4
+  cell representations.
+*/
+GDALDataset* PCRasterDataset::createCopy(char const* filename,
+         GDALDataset* source, int strict, char** options,
+         GDALProgressFunc progress, void* progressData)
+{
+  // Checks.
+  int nrBands = source->GetRasterCount();
+  if(nrBands != 1) {
+    CPLError(CE_Failure, CPLE_NotSupported,
+         "PCRaster driver: Too many bands ('%d'): must be 1 band", nrBands);
+    return 0;
+  }
+
+  GDALRasterBand* raster = source->GetRasterBand(1);
+
+  // Create PCRaster raster.
+  size_t nrRows = raster->GetYSize();
+  size_t nrCols = raster->GetXSize();
+  std::string string;
+
+  CSF_CR fileCellRepresentation = GDALType2CellRepresentation(
+         raster->GetRasterDataType(), false);
+
+  if(fileCellRepresentation == CR_UNDEFINED) {
+    CPLError(CE_Failure, CPLE_NotSupported,
+         "PCRaster driver: Cannot determine a valid cell representation");
+    return 0;
+  }
+
+  CSF_VS valueScale = VS_UNDEFINED;
+  if(source->GetMetadataItem("PCRASTER_VALUESCALE")) {
+    string = source->GetMetadataItem("PCRASTER_VALUESCALE");
+  }
+  if(!string.empty()) {
+    valueScale = string2ValueScale(string);
+  }
+  else {
+    valueScale = GDALType2ValueScale(raster->GetRasterDataType());
+  }
+
+  if(valueScale == VS_UNDEFINED) {
+    CPLError(CE_Failure, CPLE_NotSupported,
+         "PCRaster driver: Cannot determine a valid value scale");
+    return 0;
+  }
+
+  CSF_PT const projection = PT_YDECT2B;
+  REAL8  const angle = 0.0;
+  REAL8 west = 0.0;
+  REAL8 north = 0.0;
+  REAL8 cellSize = 1.0;
+
+  double transform[6];
+  if(source->GetGeoTransform(transform) == CE_None) {
+    if(transform[2] == 0.0 && transform[4] == 0.0) {
+      west = static_cast<REAL8>(transform[0]);
+      north = static_cast<REAL8>(transform[3]);
+      cellSize = static_cast<REAL8>(transform[1]);
+    }
+  }
+
+  // Determine in app cell representation.
+  CSF_CR appCellRepresentation = CR_UNDEFINED;
+  appCellRepresentation = GDALType2CellRepresentation(
+         raster->GetRasterDataType(), true);
+
+  if(appCellRepresentation == CR_UNDEFINED) {
+    CPLError(CE_Failure, CPLE_NotSupported,
+         "PCRaster driver: Cannot determine a valid cell representation");
+    return 0;
+  }
+
+  // Check whether value scale fits the cell representation. Adjust when
+  // needed.
+  valueScale = fitValueScale(valueScale, appCellRepresentation);
+
+  // Create a raster with the in file cell representation.
+  MAP* map = Rcreate(filename, nrRows, nrCols, fileCellRepresentation,
+                 valueScale, projection, west, north, angle, cellSize);
+
+  if(!map) {
+    CPLError(CE_Failure, CPLE_OpenFailed,
+         "PCRaster driver: Unable to create raster %s", filename);
+    return 0;
+  }
+
+  // Try to convert in app cell representation to the cell representation
+  // of the file.
+  if(RuseAs(map, appCellRepresentation)) {
+    CPLError(CE_Failure, CPLE_NotSupported,
+         "PCRaster driver: Cannot convert cells: %s", MstrError());
+    return 0;
+  }
+
+  int hasMissingValue;
+  double missingValue = raster->GetNoDataValue(&hasMissingValue);
+
+  // TODO conversie van INT2 naar INT4 ondersteunen. zie ruseas.c regel 503.
+  // conversie op r 159.
+
+  // Create buffer for one row of values.
+  void* buffer = Rmalloc(map, nrCols);
+
+  // Copy values from source to target.
+  CPLErr errorCode = CE_None;
+  for(size_t row = 0; errorCode == CE_None && row < nrRows; ++row) {
+
+    // Get row from source.
+    if(raster->RasterIO(GF_Read, 0, row, nrCols, 1, buffer, nrCols, 1,
+         raster->GetRasterDataType(), 0, 0) != CE_None) {
+      free(buffer);
+      CPLError(CE_Failure, CPLE_FileIO,
+         "PCRaster driver: Error reading from source raster");
+    }
+
+    if(hasMissingValue) {
+      alterToStdMV(buffer, nrCols, appCellRepresentation, missingValue);
+    }
+
+    // Write row in target.
+    RputRow(map, row, buffer);
+
+    if(!progress((row + 1) / (static_cast<double>(nrRows)), 0, progressData)) {
+      free(buffer);
+      CPLError(CE_Failure, CPLE_UserInterrupt,
+         "PCRaster driver: User terminated CreateCopy()");
+    }
+  }
+
+  Mclose(map);
+  map = 0;
+
+  free(buffer);
+  buffer = 0;
+
+  if( errorCode != CE_None )
+      return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Re-open dataset, and copy any auxilary pam information.         */
+/* -------------------------------------------------------------------- */
+  GDALPamDataset *poDS = (GDALPamDataset *) 
+        GDALOpen( filename, GA_Update );
+
+  if( poDS )
+      poDS->CloneInfo( source, GCIF_PAM_DEFAULT );
+  
+  return poDS;
+}
+
+
+
+//------------------------------------------------------------------------------
+// DEFINITION OF PCRDATASET MEMBERS
+//------------------------------------------------------------------------------
+
+//! Constructor.
+/*!
+  \param     map PCRaster map handle. It is ours to close.
+*/
+PCRasterDataset::PCRasterDataset(MAP* map)
+
+  : GDALPamDataset(),
+    d_map(map), d_west(0.0), d_north(0.0), d_cellSize(0.0)
+
+{
+  // Read header info.
+  nRasterXSize = RgetNrCols(d_map);
+  nRasterYSize = RgetNrRows(d_map);
+  d_west = static_cast<double>(RgetXUL(d_map));
+  d_north = static_cast<double>(RgetYUL(d_map));
+  d_cellSize = static_cast<double>(RgetCellSize(d_map));
+  d_cellRepresentation = RgetUseCellRepr(d_map);
+  assert(d_cellRepresentation != CR_UNDEFINED);
+  d_valueScale = RgetValueScale(d_map);
+  assert(d_valueScale != VS_UNDEFINED);
+  d_missingValue = ::missingValue(d_cellRepresentation);
+
+  // Create band information objects.
+  nBands = 1;
+  SetBand(1, new PCRasterRasterBand(this));
+
+  SetMetadataItem("PCRASTER_VALUESCALE", valueScale2String(
+         d_valueScale).c_str());
+}
+
+
+
+//! Destructor.
+/*!
+  \warning   The map given in the constructor is closed.
+*/
+PCRasterDataset::~PCRasterDataset()
+{
+    FlushCache();
+    Mclose(d_map);
+}
+
+
+
+//! Sets projections info.
+/*!
+  \param     transform Array to fill.
+
+  CSF 2.0 supports the notion of y coordinates which increase from north to
+  south. Support for this has been dropped and applications reading PCRaster
+  rasters will treat or already treat y coordinates as increasing from south
+  to north only.
+*/
+CPLErr PCRasterDataset::GetGeoTransform(double* transform)
+{
+  // x = west + nrCols * cellsize
+  transform[0] = d_west;
+  transform[1] = d_cellSize;
+  transform[2] = 0.0;
+
+  // y = north + nrRows * -cellsize
+  transform[3] = d_north;
+  transform[4] = 0.0;
+  transform[5] = -1.0 * d_cellSize;
+
+  return CE_None;
+}
+
+
+
+//! Returns the map handle.
+/*!
+  \return    Map handle.
+*/
+MAP* PCRasterDataset::map() const
+{
+  return d_map;
+}
+
+
+
+//! Returns the in-app cell representation.
+/*!
+  \return    cell representation
+  \warning   This might not be the same representation as use to store the values in the file.
+  \sa        valueScale()
+*/
+CSF_CR PCRasterDataset::cellRepresentation() const
+{
+  return d_cellRepresentation;
+}
+
+
+
+//! Returns the value scale of the data.
+/*!
+  \return    Value scale
+  \sa        cellRepresentation()
+*/
+CSF_VS PCRasterDataset::valueScale() const
+{
+  return d_valueScale;
+}
+
+
+
+//! Returns the value of the missing value.
+/*!
+  \return    Missing value
+*/
+double PCRasterDataset::missingValue() const
+{
+  return d_missingValue;
+}
+
+
+
+//------------------------------------------------------------------------------
+// DEFINITION OF FREE OPERATORS
+//------------------------------------------------------------------------------
+
+
+
+//------------------------------------------------------------------------------
+// DEFINITION OF FREE FUNCTIONS
+//------------------------------------------------------------------------------
+
+
+
diff --git a/Utilities/GDAL/frmts/pcraster/pcrasterdataset.h b/Utilities/GDAL/frmts/pcraster/pcrasterdataset.h
new file mode 100644
index 0000000000..8366ec5d24
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/pcrasterdataset.h
@@ -0,0 +1,138 @@
+#ifndef INCLUDED_PCRASTERDATASET
+#define INCLUDED_PCRASTERDATASET
+
+
+
+// Library headers.
+
+// PCRaster library headers.
+#ifndef INCLUDED_CSF
+#include "csf.h"
+#define INCLUDED_CSF
+#endif
+
+// Module headers.
+#include "gdal_pam.h"
+
+// namespace {
+  // PCRasterDataset declarations.
+// }
+namespace gdal {
+  class PCRasterDatasetTest;
+}
+
+
+
+// namespace {
+
+
+
+//! This class specialises the GDALDataset class for PCRaster datasets.
+/*!
+  PCRaster raster datasets are currently formatted by the CSF 2.0 data format.
+  A PCRasterDataset consists of one band.
+
+  More info about PCRaster can be found at http://www.pcraster.nl and
+  http://pcraster.geog.uu.nl
+
+  Additional documentation about this driver can be found in
+  frmts/frmts_various.html of the GDAL source code distribution.
+*/
+class PCRasterDataset: public GDALPamDataset
+{
+
+  friend class gdal::PCRasterDatasetTest;
+
+public:
+
+  static GDALDataset* open             (GDALOpenInfo* info);
+
+  static GDALDataset* createCopy       (char const* filename,
+                                        GDALDataset* source,
+                                        int strict,
+                                        char** options,
+                                        GDALProgressFunc progress,
+                                        void* progressData);
+
+private:
+
+  //! CSF map structure.
+  MAP*             d_map;
+
+  //! Left coordinate of raster.
+  double           d_west;
+
+  //! Top coordinate of raster.
+  double           d_north;
+
+  //! Cell size.
+  double           d_cellSize;
+
+  //! Cell representation.
+  CSF_CR           d_cellRepresentation;
+
+  //! Value scale.
+  CSF_VS           d_valueScale;
+
+  //! No data value.
+  double           d_missingValue;
+
+  //! Assignment operator. NOT IMPLEMENTED.
+  PCRasterDataset& operator=           (const PCRasterDataset&);
+
+  //! Copy constructor. NOT IMPLEMENTED.
+                   PCRasterDataset     (const PCRasterDataset&);
+
+public:
+
+  //----------------------------------------------------------------------------
+  // CREATORS
+  //----------------------------------------------------------------------------
+
+                   PCRasterDataset     (MAP* map);
+
+  /* virtual */    ~PCRasterDataset    ();
+
+  //----------------------------------------------------------------------------
+  // MANIPULATORS
+  //----------------------------------------------------------------------------
+
+  //----------------------------------------------------------------------------
+  // ACCESSORS
+  //----------------------------------------------------------------------------
+
+  MAP*             map                 () const;
+
+  CPLErr           GetGeoTransform     (double* transform);
+
+  CSF_CR           cellRepresentation  () const;
+
+  CSF_VS           valueScale          () const;
+
+  double           missingValue        () const;
+
+};
+
+
+
+//------------------------------------------------------------------------------
+// INLINE FUNCTIONS
+//------------------------------------------------------------------------------
+
+
+
+//------------------------------------------------------------------------------
+// FREE OPERATORS
+//------------------------------------------------------------------------------
+
+
+
+//------------------------------------------------------------------------------
+// FREE FUNCTIONS
+//------------------------------------------------------------------------------
+
+
+
+// } // namespace
+
+#endif
diff --git a/Utilities/GDAL/frmts/pcraster/pcrastermisc.cpp b/Utilities/GDAL/frmts/pcraster/pcrastermisc.cpp
new file mode 100644
index 0000000000..80e772a7b8
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/pcrastermisc.cpp
@@ -0,0 +1,47 @@
+// Library headers.
+#ifndef INCLUDED_IOSTREAM
+#include <iostream>
+#define INCLUDED_IOSTREAM
+#endif
+
+#ifndef INCLUDED_STRING
+#include <string>
+#define INCLUDED_STRING
+#endif
+
+// PCRaster library headers.
+
+// Module headers.
+#include "gdal_pam.h"
+
+#ifndef INCLUDED_PCRASTERDATASET
+#include "pcrasterdataset.h"
+#define INCLUDED_PCRASTERDATASET
+#endif
+
+
+
+CPL_C_START
+void GDALRegister_PCRaster(void);
+CPL_C_END
+
+
+
+void GDALRegister_PCRaster()
+{
+  if(!GDALGetDriverByName("PCRaster")) {
+
+    GDALDriver* driver = new GDALDriver();
+
+    driver->SetDescription("PCRaster");
+    driver->SetMetadataItem(GDAL_DMD_LONGNAME, "PCRaster Raster File");
+    driver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte Int32 Float32");
+    driver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "frmt_various.html#PCRaster");
+    driver->SetMetadataItem( GDAL_DMD_EXTENSION, "map" );
+
+    driver->pfnOpen = PCRasterDataset::open;
+    driver->pfnCreateCopy = PCRasterDataset::createCopy;
+
+    GetGDALDriverManager()->RegisterDriver(driver);
+  }
+}
diff --git a/Utilities/GDAL/frmts/pcraster/pcrasterrasterband.cpp b/Utilities/GDAL/frmts/pcraster/pcrasterrasterband.cpp
new file mode 100644
index 0000000000..22274d097e
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/pcrasterrasterband.cpp
@@ -0,0 +1,194 @@
+#ifndef INCLUDED_PCRASTERRASTERBAND
+#include "pcrasterrasterband.h"
+#define INCLUDED_PCRASTERRASTERBAND
+#endif
+
+// Library headers.
+#ifndef INCLUDED_CASSERT
+#include <cassert>
+#define INCLUDED_CASSERT
+#endif
+
+// PCRaster library headers.
+#ifndef INCLUDED_CSF
+#include "csf.h"
+#define INCLUDED_CSF
+#endif
+
+// Module headers.
+#ifndef INCLUDED_PCRASTERDATASET
+#include "pcrasterdataset.h"
+#define INCLUDED_PCRASTERDATASET
+#endif
+
+#ifndef INCLUDED_PCRASTERUTIL
+#include "pcrasterutil.h"
+#define INCLUDED_PCRASTERUTIL
+#endif
+
+
+
+/*!
+  \file
+  This file contains the implementation of the PCRasterRasterBand class.
+*/
+
+
+
+//------------------------------------------------------------------------------
+// DEFINITION OF STATIC PCRRASTERBAND MEMBERS
+//------------------------------------------------------------------------------
+
+
+
+//------------------------------------------------------------------------------
+// DEFINITION OF PCRRASTERBAND MEMBERS
+//------------------------------------------------------------------------------
+
+//! Constructor.
+/*!
+  \param     dataset The dataset we are a part of.
+*/
+PCRasterRasterBand::PCRasterRasterBand(PCRasterDataset* dataset)
+
+  : GDALPamRasterBand(), d_dataset(dataset)
+
+{
+  this->poDS = dataset;
+  this->nBand = 1;
+  this->eDataType = cellRepresentation2GDALType(dataset->cellRepresentation());
+  this->nBlockXSize = dataset->GetRasterXSize();
+  this->nBlockYSize = 1;
+}
+
+
+
+//! Destructor.
+/*!
+*/
+PCRasterRasterBand::~PCRasterRasterBand()
+{
+}
+
+
+
+double PCRasterRasterBand::GetNoDataValue(int* success)
+{
+  if(success) {
+    *success = 1;
+  }
+
+  return d_dataset->missingValue();
+}
+
+
+
+double PCRasterRasterBand::GetMinimum(int* success)
+{
+  double result;
+  int isValid;
+
+  switch(d_dataset->cellRepresentation()) {
+    case CR_UINT1: {
+      UINT1 min;
+      isValid = RgetMinVal(d_dataset->map(), &min);
+      result = static_cast<double>(min);
+      break;
+    }
+    case CR_INT4: {
+      INT4 min;
+      isValid = RgetMinVal(d_dataset->map(), &min);
+      result = static_cast<double>(min);
+      break;
+    }
+    case CR_REAL4: {
+      REAL4 min;
+      isValid = RgetMinVal(d_dataset->map(), &min);
+      result = static_cast<double>(min);
+      break;
+    }
+    default: {
+      result = 0.0;
+      isValid = 0;
+      break;
+    }
+  }
+
+  if(success) {
+    *success = isValid ? 1 : 0;
+  }
+
+  return result;
+}
+
+
+
+double PCRasterRasterBand::GetMaximum(int* success)
+{
+  double result;
+  int isValid;
+
+  switch(d_dataset->cellRepresentation()) {
+    case CR_UINT1: {
+      UINT1 max;
+      isValid = RgetMaxVal(d_dataset->map(), &max);
+      result = static_cast<double>(max);
+      break;
+    }
+    case CR_INT4: {
+      INT4 max;
+      isValid = RgetMaxVal(d_dataset->map(), &max);
+      result = static_cast<double>(max);
+      break;
+    }
+    case CR_REAL4: {
+      REAL4 max;
+      isValid = RgetMaxVal(d_dataset->map(), &max);
+      result = static_cast<double>(max);
+      break;
+    }
+    default: {
+      result = 0.0;
+      isValid = 0;
+      break;
+    }
+  }
+
+  if(success) {
+    *success = isValid ? 1 : 0;
+  }
+
+  return result;
+}
+
+
+
+CPLErr PCRasterRasterBand::IReadBlock(int nBlockXoff, int nBlockYoff,
+         void* buffer)
+{
+  size_t nrCellsRead = RgetRow(d_dataset->map(), nBlockYoff, buffer);
+
+  if(d_dataset->cellRepresentation() == CR_REAL4 ||
+         d_dataset->cellRepresentation() == CR_REAL8) {
+    // Missing value in the buffer is a NAN. Replace by valid value.
+    alterFromStdMV(buffer, nrCellsRead, d_dataset->cellRepresentation(),
+           d_dataset->missingValue());
+  }
+
+  return CE_None;
+}
+
+
+
+//------------------------------------------------------------------------------
+// DEFINITION OF FREE OPERATORS
+//------------------------------------------------------------------------------
+
+
+
+//------------------------------------------------------------------------------
+// DEFINITION OF FREE FUNCTIONS
+//------------------------------------------------------------------------------
+
+
+
diff --git a/Utilities/GDAL/frmts/pcraster/pcrasterrasterband.h b/Utilities/GDAL/frmts/pcraster/pcrasterrasterband.h
new file mode 100644
index 0000000000..f63ab6c544
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/pcrasterrasterband.h
@@ -0,0 +1,100 @@
+#ifndef INCLUDED_PCRASTERRASTERBAND
+#define INCLUDED_PCRASTERRASTERBAND
+
+
+
+// Library headers.
+
+// PCRaster library headers.
+
+// Module headers.
+#ifndef INCLUDED_GDAL_PAM
+#include "gdal_pam.h"
+#define INCLUDED_GDAL_PAM
+#endif
+
+
+
+// namespace {
+  // PCRasterRasterBand declarations.
+// }
+class PCRasterDataset;
+
+
+
+// namespace {
+
+
+
+//! This class specialises the GDALRasterBand class for PCRaster rasters.
+/*!
+*/
+class PCRasterRasterBand: public GDALPamRasterBand
+{
+
+private:
+
+  //! Dataset this band is part of. For use only.
+  PCRasterDataset const* d_dataset;
+
+  //! Assignment operator. NOT IMPLEMENTED.
+  PCRasterRasterBand& operator=        (const PCRasterRasterBand&);
+
+  //! Copy constructor. NOT IMPLEMENTED.
+                   PCRasterRasterBand  (const PCRasterRasterBand&);
+
+protected:
+
+  double           GetNoDataValue      (int* success);
+
+  double           GetMinimum          (int* success);
+
+  double           GetMaximum          (int* success);
+
+public:
+
+  //----------------------------------------------------------------------------
+  // CREATORS
+  //----------------------------------------------------------------------------
+
+                   PCRasterRasterBand  (PCRasterDataset* dataset);
+
+  /* virtual */    ~PCRasterRasterBand ();
+
+  //----------------------------------------------------------------------------
+  // MANIPULATORS
+  //----------------------------------------------------------------------------
+
+  //----------------------------------------------------------------------------
+  // ACCESSORS
+  //----------------------------------------------------------------------------
+
+  CPLErr           IReadBlock          (int nBlockXoff,
+                                        int nBlockYoff,
+                                        void* buffer);
+
+};
+
+
+
+//------------------------------------------------------------------------------
+// INLINE FUNCTIONS
+//------------------------------------------------------------------------------
+
+
+
+//------------------------------------------------------------------------------
+// FREE OPERATORS
+//------------------------------------------------------------------------------
+
+
+
+//------------------------------------------------------------------------------
+// FREE FUNCTIONS
+//------------------------------------------------------------------------------
+
+
+
+// } // namespace
+
+#endif
diff --git a/Utilities/GDAL/frmts/pcraster/pcrasterutil.cpp b/Utilities/GDAL/frmts/pcraster/pcrasterutil.cpp
new file mode 100644
index 0000000000..1eb5d24f75
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/pcrasterutil.cpp
@@ -0,0 +1,477 @@
+#ifndef INCLUDED_IOSTREAM
+#include <iostream>
+#define INCLUDED_IOSTREAM
+#endif
+
+#ifndef INCLUDED_CASSERT
+#include <cassert>
+#define INCLUDED_CASSERT
+#endif
+
+#ifndef INCLUDED_ALGORITHM
+#include <algorithm>
+#define INCLUDED_ALGORITHM
+#endif
+
+#ifndef INCLUDED_FLOAT
+#include <float.h>
+#define INCLUDED_FLOAT
+#endif
+
+#ifndef INCLUDED_PCRTYPES
+#include "pcrtypes.h"
+#define INCLUDED_PCRTYPES
+#endif
+
+#ifndef INCLUDED_PCRASTERUTIL
+#include "pcrasterutil.h"
+#define INCLUDED_PCRASTERUTIL
+#endif
+
+
+
+//! Converts PCRaster data type to GDAL data type.
+/*!
+  \param     cellRepresentation Cell representation.
+  \return    GDAL data type, GDT_Uknown if conversion is not possible.
+*/
+GDALDataType cellRepresentation2GDALType(CSF_CR cellRepresentation)
+{
+  GDALDataType type = GDT_Unknown;
+
+  switch(cellRepresentation) {
+    case CR_UINT1: {
+      type = GDT_Byte;
+      break;
+    }
+    case CR_INT4: {
+      type = GDT_Int32;
+      break;
+    }
+    case CR_REAL4: {
+      type = GDT_Float32;
+      break;
+    }
+    case CR_REAL8: {
+      type = GDT_Float64;
+      break;
+    }
+    default: {
+      break;
+    }
+  }
+
+  return type;
+}
+
+
+
+CSF_VS string2ValueScale(const std::string& string)
+{
+  CSF_VS valueScale = VS_UNDEFINED;
+
+  if(string == "VS_BOOLEAN") {
+    valueScale = VS_BOOLEAN;
+  }
+  else if(string == "VS_NOMINAL") {
+    valueScale = VS_NOMINAL;
+  }
+  else if(string == "VS_ORDINAL") {
+    valueScale = VS_ORDINAL;
+  }
+  else if(string == "VS_SCALAR") {
+    valueScale = VS_SCALAR;
+  }
+  else if(string == "VS_DIRECTION") {
+    valueScale = VS_DIRECTION;
+  }
+  else if(string == "VS_LDD") {
+    valueScale = VS_LDD;
+  }
+
+  return valueScale;
+}
+
+
+
+std::string valueScale2String(CSF_VS valueScale)
+{
+  std::string result = "VS_UNDEFINED";
+
+  switch(valueScale) {
+    case VS_BOOLEAN: {
+      result = "VS_BOOLEAN";
+      break;
+    }
+    case VS_NOMINAL: {
+      result = "VS_NOMINAL";
+      break;
+    }
+    case VS_ORDINAL: {
+      result = "VS_ORDINAL";
+      break;
+    }
+    case VS_SCALAR: {
+      result = "VS_SCALAR";
+      break;
+    }
+    case VS_DIRECTION: {
+      result = "VS_DIRECTION";
+      break;
+    }
+    case VS_LDD: {
+      result = "VS_LDD";
+      break;
+    }
+    default: {
+      break;
+    }
+  }
+
+  return result;
+}
+
+
+
+std::string cellRepresentation2String(CSF_CR cellRepresentation)
+{
+  std::string result = "CR_UNDEFINED";
+
+  switch(cellRepresentation) {
+    case CR_UINT1: {
+      result = "CR_UINT1";
+      break;
+    }
+    case CR_UINT2: {
+      result = "CR_UINT2";
+      break;
+    }
+    case CR_UINT4: {
+      result = "CR_UINT4";
+      break;
+    }
+    case CR_INT1: {
+     result = "CR_INT1";
+     break;
+   }
+    case CR_INT2: {
+     result = "CR_INT2";
+     break;
+   }
+    case CR_INT4: {
+     result = "CR_INT4";
+     break;
+   }
+    case CR_REAL4: {
+      result = "CR_REAL4";
+      break;
+    }
+    case CR_REAL8: {
+      result = "CR_REAL8";
+      break;
+    }
+    default: {
+      break;
+    }
+  }
+
+  return result;
+}
+
+
+
+//! Converts GDAL data type to PCRaster value scale.
+/*!
+  \param     type GDAL data type.
+  \return    Value scale.
+  \warning   \a type must be one of the standard numerical types and not
+             complex.
+
+  GDAL byte is regarded as PCRaster boolean, integral as nominal and float
+  as scalar. This function will never return VS_LDD, VS_ORDINAL or
+  VS_DIRECTION.
+*/
+CSF_VS GDALType2ValueScale(GDALDataType type)
+{
+  CSF_VS valueScale = VS_UNDEFINED;
+
+  switch(type) {
+    case GDT_Byte: {
+      // A foreign dataset is unlikely to support our LDD's.
+      valueScale = VS_BOOLEAN;
+      break;
+    }
+    case GDT_UInt16:
+    case GDT_UInt32:
+    case GDT_Int16:
+    case GDT_Int32: {
+      valueScale = VS_NOMINAL;
+      break;
+    }
+    case GDT_Float32: {
+      // A foreign dataset is unlikely to support our directional.
+      valueScale = VS_SCALAR;
+      break;
+    }
+    case GDT_Float64: {
+      // A foreign dataset is unlikely to support our directional.
+      valueScale = VS_SCALAR;
+      break;
+    }
+    default: {
+      assert(false);
+      break;
+    }
+  }
+
+  return valueScale;
+}
+
+
+
+//! Converts a GDAL type to a PCRaster cell representation.
+/*!
+  \param     type GDAL type.
+  \param     exact Whether an exact match or a CSF2.0 supported cell
+                   representation should be returned.
+  \return    Cell representation.
+  \warning   \a type must be one of the standard numerical types and not
+             complex.
+
+  If exact is false, conversion to CSF2.0 types will take place. This is
+  usefull for in file cell representations. If exact is true, and exact match
+  is made. This is usefull for in app cell representations.
+
+  If exact is false, this function always returns one of CR_UINT1, CR_INT4
+  or CR_REAL4.
+*/
+CSF_CR GDALType2CellRepresentation(GDALDataType type, bool exact)
+{
+  CSF_CR cellRepresentation = CR_UNDEFINED;
+
+  switch(type) {
+    case GDT_Byte: {
+      cellRepresentation = CR_UINT1;
+      break;
+    }
+    case GDT_UInt16: {
+      cellRepresentation = exact ? CR_UINT2: CR_UINT1;
+      break;
+    }
+    case GDT_UInt32: {
+      cellRepresentation = exact ? CR_UINT4: CR_UINT1;
+      break;
+    }
+    case GDT_Int16: {
+      cellRepresentation = exact ? CR_INT2: CR_INT4;
+      break;
+    }
+    case GDT_Int32: {
+      cellRepresentation = CR_INT4;
+      break;
+    }
+    case GDT_Float32: {
+      cellRepresentation = CR_REAL4;
+      break;
+    }
+    case GDT_Float64: {
+      cellRepresentation = exact ? CR_REAL8: CR_REAL4;
+      break;
+    }
+    default: {
+      break;
+    }
+  }
+
+  return cellRepresentation;
+}
+
+
+
+//! Determines a missing value to use for data of \a cellRepresentation.
+/*!
+  \param     cellRepresentation Cell representation of the data.
+  \return    Missing value.
+  \exception .
+  \warning   \a cellRepresentation must be CR_UINT1, CR_INT4 or CR_REAL4.
+  \sa        .
+*/
+double missingValue(CSF_CR cellRepresentation)
+{
+  double missingValue = 0.0;
+
+  switch(cellRepresentation) {
+    case CR_UINT1: {
+      missingValue = static_cast<double>(MV_UINT1);
+      break;
+    }
+    case CR_INT4: {
+      missingValue = static_cast<double>(MV_INT4);
+      break;
+    }
+    case CR_REAL4: {
+      // using <limits> breaks on gcc 2.95
+      // assert(std::numeric_limits<REAL4>::is_iec559);
+      // missingValue = -std::numeric_limits<REAL4>::max();
+      missingValue = -FLT_MAX;
+      break;
+    }
+    default: {
+      assert(false);
+      break;
+    }
+  }
+
+  return missingValue;
+}
+
+
+
+//! Opens the raster in \a filename using mode \a mode.
+/*!
+  \param     filename Filename of raster to open.
+  \return    Pointer to CSF MAP structure.
+  \exception .
+  \warning   .
+  \sa        .
+*/
+MAP* open(std::string const& filename, MOPEN_PERM mode)
+{
+  MAP* map = Mopen(filename.c_str(), mode);
+
+  return map;
+}
+
+
+
+void alterFromStdMV(void* buffer, size_t size, CSF_CR cellRepresentation,
+         double missingValue)
+{
+  switch(cellRepresentation) {
+    case(CR_UINT1): {
+      std::for_each(static_cast<UINT1*>(buffer),
+         static_cast<UINT1*>(buffer) + size,
+         pcr::AlterFromStdMV<UINT1>(static_cast<UINT1>(missingValue)));
+      break;
+    }
+    case(CR_INT4): {
+      std::for_each(static_cast<INT4*>(buffer),
+         static_cast<INT4*>(buffer) + size,
+         pcr::AlterFromStdMV<INT4>(static_cast<INT4>(missingValue)));
+      break;
+    }
+    case(CR_REAL4): {
+      std::for_each(static_cast<REAL4*>(buffer),
+         static_cast<REAL4*>(buffer) + size,
+         pcr::AlterFromStdMV<REAL4>(static_cast<REAL4>(missingValue)));
+      break;
+    }
+    default: {
+      assert(false);
+      break;
+    }
+  }
+}
+
+
+
+void alterToStdMV(void* buffer, size_t size, CSF_CR cellRepresentation,
+         double missingValue)
+{
+  switch(cellRepresentation) {
+    case(CR_UINT1): {
+      std::for_each(static_cast<UINT1*>(buffer),
+         static_cast<UINT1*>(buffer) + size,
+         pcr::AlterToStdMV<UINT1>(static_cast<UINT1>(missingValue)));
+      break;
+    }
+    case(CR_INT4): {
+      std::for_each(static_cast<INT4*>(buffer),
+         static_cast<INT4*>(buffer) + size,
+         pcr::AlterToStdMV<INT4>(static_cast<INT4>(missingValue)));
+      break;
+    }
+    case(CR_REAL4): {
+      std::for_each(static_cast<REAL4*>(buffer),
+         static_cast<REAL4*>(buffer) + size,
+         pcr::AlterToStdMV<REAL4>(static_cast<REAL4>(missingValue)));
+      break;
+    }
+    case(CR_REAL8): {
+      std::for_each(static_cast<REAL8*>(buffer),
+         static_cast<REAL8*>(buffer) + size,
+         pcr::AlterToStdMV<REAL8>(static_cast<REAL8>(missingValue)));
+      break;
+    }
+    default: {
+      assert(false);
+      break;
+    }
+  }
+}
+
+
+
+CSF_VS fitValueScale(CSF_VS valueScale, CSF_CR cellRepresentation)
+{
+  CSF_VS result = valueScale;
+
+  switch(cellRepresentation) {
+    case CR_UINT1: {
+      switch(valueScale) {
+        case VS_LDD: {
+          result = VS_LDD;
+          break;
+        }
+        default: {
+          result = VS_BOOLEAN;
+          break;
+        }
+      }
+      break;
+    }
+    case CR_INT4: {
+      switch(valueScale) {
+        case VS_BOOLEAN: {
+          result = VS_NOMINAL;
+          break;
+        }
+        case VS_SCALAR: {
+          result = VS_ORDINAL;
+          break;
+        }
+        case VS_DIRECTION: {
+          result = VS_ORDINAL;
+          break;
+        }
+        case VS_LDD: {
+          result = VS_NOMINAL;
+          break;
+        }
+        default: {
+          result = valueScale;
+          break;
+        }
+      }
+      break;
+    }
+    case CR_REAL4: {
+      switch(valueScale) {
+        case VS_DIRECTION: {
+          result = VS_DIRECTION;
+          break;
+        }
+        default: {
+          result = VS_SCALAR;
+          break;
+        }
+      }
+      break;
+    }
+    default: {
+      break;
+    }
+  }
+
+  return result;
+}
diff --git a/Utilities/GDAL/frmts/pcraster/pcrasterutil.h b/Utilities/GDAL/frmts/pcraster/pcrasterutil.h
new file mode 100644
index 0000000000..21bc372660
--- /dev/null
+++ b/Utilities/GDAL/frmts/pcraster/pcrasterutil.h
@@ -0,0 +1,64 @@
+// Library headers.
+#ifndef INCLUDED_STRING
+#include <string>
+#define INCLUDED_STRING
+#endif
+
+// PCRaster library headers.
+#ifndef INCLUDED_CSF
+#include "csf.h"
+#define INCLUDED_CSF
+#endif
+
+// Module headers.
+#ifndef INCLUDED_GDAL_PRIV
+#include "gdal_priv.h"
+#define INCLUDED_GDAL_PRIV
+#endif
+
+
+
+GDALDataType       cellRepresentation2GDALType(CSF_CR cellRepresentation);
+
+CSF_VS             string2ValueScale   (const std::string& string);
+
+std::string        valueScale2String   (CSF_VS valueScale);
+
+std::string        cellRepresentation2String(CSF_CR cellRepresentation);
+
+CSF_VS             GDALType2ValueScale (GDALDataType type);
+
+/*
+CSF_CR             string2PCRasterCellRepresentation(
+                                        const std::string& string);
+                                        */
+
+CSF_CR             GDALType2CellRepresentation(
+                                        GDALDataType type,
+                                        bool exact);
+
+void*              createBuffer        (size_t size,
+                                        CSF_CR type);
+
+void               deleteBuffer        (void* buffer,
+                                        CSF_CR type);
+
+bool               isContinuous        (CSF_VS valueScale);
+
+double             missingValue        (CSF_CR type);
+
+void               alterFromStdMV      (void* buffer,
+                                        size_t size,
+                                        CSF_CR cellRepresentation,
+                                        double missingValue);
+
+void               alterToStdMV        (void* buffer,
+                                        size_t size,
+                                        CSF_CR cellRepresentation,
+                                        double missingValue);
+
+MAP*               open                (std::string const& filename,
+                                        MOPEN_PERM mode);
+
+CSF_VS             fitValueScale       (CSF_VS valueScale,
+                                        CSF_CR cellRepresentation);
diff --git a/Utilities/GDAL/frmts/pgchip/GNUmakefile b/Utilities/GDAL/frmts/pgchip/GNUmakefile
new file mode 100644
index 0000000000..248038726b
--- /dev/null
+++ b/Utilities/GDAL/frmts/pgchip/GNUmakefile
@@ -0,0 +1,24 @@
+# USER CONFIGURATION
+# Set your postgis include path here :
+POSTGIS_INC     =-I/path/to/your/postgis/headers
+# END OF USER CONFIGURATION 
+
+
+include ../../GDALmake.opt
+
+OBJ	=	pgchipdataset.o pgchiprasterband.o pgchiputilities.o
+
+
+CPPFLAGS	:=	$(XTRA_OPT) $(PG_INC) $(POSTGIS_INC) $(GDAL_INCLUDE) $(CPPFLAGS )
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+../o/%.o:
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@
+
+all:	$(OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/pgchip/INSTALL b/Utilities/GDAL/frmts/pgchip/INSTALL
new file mode 100644
index 0000000000..36da77dedb
--- /dev/null
+++ b/Utilities/GDAL/frmts/pgchip/INSTALL
@@ -0,0 +1,37 @@
+REQUIREMENTS :
+
+* Make sure you have a full PostgreSQL/Postgis clean source installation 
+* You need to have Proj4 installed and configured in Pstgis to get the driver work
+
+
+INSTALL NOTES :
+
+1* Go to frmts directory under GDAL source tree
+
+2* Unpack pgchip archive in the frmts directory 
+
+3* Edit GNUMakefile to set your Postgis include path
+
+4* Add registration entry point declaration :
+    - Open gdal/gcore/gdal_frmts.h
+    - Add "void CPL_DLL GDALRegister_PGCHIP(void);" between the CPL_C_START and CPL_C_END tags
+
+5* Add a call to the registration function in frmts/gdalallregister.c
+    In the GDALAllRegister() function add the followinf lines :
+    #ifdef FRMT_pgchip
+        GDALRegister_PGCHIP();
+    #endif
+    
+6* Add the format short name to the GDAL_FORMATS macro in GDALmake.opt.in (and to GDALmake.opt) :
+    - Locate the variable GDAL_FORMATS and add "pgchip" (lowercase) to the list of formats
+    
+7* Add a format specific item to the EXTRAFLAGS macro in frmts/makefile.vc
+
+8* Recompile your GDAL library :
+    - make clean
+    -./configure
+    - make
+    - make install
+    
+9* Test your pgchip installation :
+    execute gdalinfo --formats and search for pgchip  
\ No newline at end of file
diff --git a/Utilities/GDAL/frmts/pgchip/README b/Utilities/GDAL/frmts/pgchip/README
new file mode 100644
index 0000000000..5fab1f71a0
--- /dev/null
+++ b/Utilities/GDAL/frmts/pgchip/README
@@ -0,0 +1,29 @@
+Important Drivers Restrictions :
+
+    * PGCHIP driver is currently under development which means it has NOT been fully tested and no stable release is downloadable.
+    
+    * The driver only supports GDT_Byte and GDT_UInt16 datatypes and deals with 1 or 4 bands (GREY_SCALE, PALETTE and RGBA)
+    
+    * The column name for the chip is not yet changeable and is "raster" by default
+    
+    * In order to specify the database you want to connect to, you have to give a connection string. The differents connection parameters (host,port,dbname) must be delimited with a "#" character. The name of the Postgis layer should be given at the end of the string after a "%layer=" argument. Example : 
+
+        $ gdalinfo PG:host=192.168.1.1#dbname=mydb%layer=myRasterTable
+        
+        
+How can I test the driver :
+
+    * You can choose to build your own application using the GDAL API or use the utility programs.
+    * Some examples with gdal_translate :
+    
+        Import BMP raster :
+        gdal_translate -of pgchip /DATA/myRaster.bmp PG:host=192.168.1.1#dbname=mydb#port=5432%layer=myRaster
+    
+        Then export to PNG :
+        gdal_translate -of png -ot UInt16 PG:host=192.168.1.1#dbname=mydb#port=5432%layer=myRaster /DATA/myRaster.png
+
+                
+Author information and bug report :
+
+    website : http://simon.benjamin.free.fr/pgchip/
+    email : noumayoss@gmail.com
diff --git a/Utilities/GDAL/frmts/pgchip/makefile.vc b/Utilities/GDAL/frmts/pgchip/makefile.vc
new file mode 100644
index 0000000000..87a08aea0d
--- /dev/null
+++ b/Utilities/GDAL/frmts/pgchip/makefile.vc
@@ -0,0 +1,15 @@
+
+OBJ	=	pgChipdataset.obj
+
+EXTRAFLAGS = 	-I..\iso8211
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/pgchip/pgchip.h b/Utilities/GDAL/frmts/pgchip/pgchip.h
new file mode 100644
index 0000000000..4e428fbd5c
--- /dev/null
+++ b/Utilities/GDAL/frmts/pgchip/pgchip.h
@@ -0,0 +1,123 @@
+/******************************************************************************
+ *
+ * File :    pgchip.h
+ * Project:  PGCHIP Driver
+ * Purpose:  Main header file for POSTGIS CHIP/GDAL Driver 
+ * Author:   Benjamin Simon, noumayoss@gmail.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Benjamin Simon, noumayoss@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * Revision 1.1  2005/08/29  bsimon
+ * New
+ *
+ */
+
+#include "gdal_priv.h"
+#include "libpq-fe.h"
+#include "liblwgeom.h"
+
+// External functions (what's again the reason for using explicit hex form ?)
+extern void deparse_hex_string(unsigned char *strOut,char *strIn,int length);
+extern void parse_hex_string(unsigned char *strOut,char *strIn,int length);
+
+/* color types */
+#define PGCHIP_COLOR_TYPE_GRAY 0
+#define PGCHIP_COLOR_TYPE_PALETTE 1
+#define PGCHIP_COLOR_TYPE_RGB_ALPHA 4
+
+//pg_chip color struct 
+typedef struct pgchip_color_nohex_struct
+{
+   unsigned char red; 
+   unsigned char green;
+   unsigned char blue;
+   unsigned char alpha;
+} pgchip_color;
+
+
+/************************************************************************/
+/* ==================================================================== */
+/*				PGCHIPDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class PGCHIPRasterBand;
+
+class PGCHIPDataset : public GDALDataset{
+
+    friend class PGCHIPRasterBand;
+
+    PGconn      *hPGConn;
+    char        *pszConnectionString;
+    char	*pszDBName;
+    char        *pszName;
+    char	*pszProjection;
+    int         bHavePostGIS;
+    
+    CHIP        *PGCHIP;
+    int         SRID;
+    int         nBitDepth;
+    
+    int                 nColorType; /* PGHIP_COLOR_TYPE_* */
+    GDALColorTable      *poColorTable;
+    int		bHaveNoData;
+    double 	dfNoDataValue;
+    
+    double              adfGeoTransform[6];
+    int                 bGeoTransformValid;
+    
+  public:
+	
+	PGCHIPDataset();
+        ~PGCHIPDataset();
+    
+    static GDALDataset *Open( GDALOpenInfo * );
+    
+    void        printChipInfo();
+
+    CPLErr 	GetGeoTransform( double * padfTransform );
+    virtual CPLErr      SetGeoTransform( double * );
+    
+    CPLErr 	SetProjection( const char *);
+    const char *GetProjectionRef();
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            PGCHIPRasterBand                          */
+/* ==================================================================== */
+/************************************************************************/
+
+class PGCHIPRasterBand : public GDALRasterBand{
+
+    friend class PGCHIPDataset;
+    
+  public:
+
+    PGCHIPRasterBand( PGCHIPDataset *, int );
+    
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual GDALColorTable *GetColorTable();
+            
+};
diff --git a/Utilities/GDAL/frmts/pgchip/pgchipdataset.cpp b/Utilities/GDAL/frmts/pgchip/pgchipdataset.cpp
new file mode 100644
index 0000000000..5def63f11b
--- /dev/null
+++ b/Utilities/GDAL/frmts/pgchip/pgchipdataset.cpp
@@ -0,0 +1,1074 @@
+/******************************************************************************
+ *
+ * File :    pgchipdataset.cpp
+ * Project:  PGCHIP Driver
+ * Purpose:  GDALDataset code for POSTGIS CHIP/GDAL Driver 
+ * Author:   Benjamin Simon, noumayoss@gmail.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Benjamin Simon, noumayoss@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * Revision 1.1  2005/08/29 bsimon
+ * New
+ *
+ */
+
+#include "pgchip.h"
+
+
+CPL_C_START
+void	GDALRegister_PGCHIP(void);
+CPL_C_END
+
+
+/************************************************************************/
+/* ==================================================================== */
+/*				PGCHIPDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+
+/************************************************************************/
+/*                            PGCHIPDataset()                           */
+/************************************************************************/
+
+PGCHIPDataset::PGCHIPDataset(){
+
+    hPGConn = NULL;
+    pszConnectionString = NULL;
+    pszDBName = NULL;
+    pszName = NULL;
+    bHavePostGIS = FALSE;
+    PGCHIP = NULL;
+    
+    bGeoTransformValid = FALSE;
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+    
+    SRID = -1;
+    pszProjection = CPLStrdup("");
+    
+    bHaveNoData = FALSE;
+    dfNoDataValue = -1;
+}
+
+/************************************************************************/
+/*                            ~PGCHIPDataset()                             */
+/************************************************************************/
+
+PGCHIPDataset::~PGCHIPDataset(){
+
+    CPLFree(pszProjection);
+    CPLFree(pszConnectionString);
+    CPLFree(pszDBName);
+    CPLFree(pszName);
+
+    
+    if(PGCHIP->data)
+        CPLFree(PGCHIP->data);
+        
+    if(PGCHIP)
+        CPLFree(PGCHIP);
+    
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr PGCHIPDataset::GetGeoTransform( double * padfTransform ){
+    
+    memcpy( padfTransform, adfGeoTransform, sizeof(adfGeoTransform[0]) * 6 );
+
+    if( bGeoTransformValid )
+        return CE_None;
+    else
+        return CE_Failure;
+}
+
+
+
+/************************************************************************/
+/*                          SetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr PGCHIPDataset::SetGeoTransform( double * padfTransform ){
+
+    CPLErr              eErr = CE_None;
+
+    memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 );
+
+    if ( pszConnectionString && bGeoTransformValid )
+    {
+    
+        /* NOT YET AVAILABLE */
+    
+    }
+
+    return eErr;
+}
+    
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *PGCHIPDataset::GetProjectionRef(){
+
+    char    szCommand[1024];
+    PGconn      *hPGConn;
+    PGresult    *hResult;
+    int SRID = -1;        
+    
+    hPGConn = this->hPGConn;
+    
+    SRID = this->PGCHIP->SRID;
+    
+/* -------------------------------------------------------------------- */
+/*      Reading proj                                                    */
+/* -------------------------------------------------------------------- */
+     
+    sprintf( szCommand,"SELECT srtext FROM spatial_ref_sys where SRID=%d",SRID);
+            
+    hResult = PQexec(hPGConn,szCommand);
+        
+    if(SRID == -1) {
+        return "";
+    }
+    else if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK 
+             && PQntuples(hResult) > 0 ){
+        
+        pszProjection = CPLStrdup(PQgetvalue(hResult,0,0)); 
+                
+        return( pszProjection );
+    }
+    
+    if( hResult )
+        PQclear( hResult );
+    
+    return NULL;
+}
+
+
+/************************************************************************/
+/*                           SetProjection()                            */
+/************************************************************************/
+
+CPLErr PGCHIPDataset::SetProjection( const char * pszNewProjection ){
+
+    char    szCommand[1024];
+    PGconn      *hPGConn;
+    PGresult    *hResult;
+    
+    hPGConn = this->hPGConn;
+    
+    
+    if( !EQUALN(pszNewProjection,"GEOGCS",6)
+        && !EQUALN(pszNewProjection,"PROJCS",6)
+        && !EQUALN(pszProjection,"+",1)
+        && !EQUAL(pszNewProjection,"") )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                "Only OGC WKT Projections supported for writing to Postgis.\n"
+                "%s not supported.",
+                  pszNewProjection );
+        
+        return CE_Failure;
+    }
+    
+    CPLFree( pszProjection );
+        
+/* -------------------------------------------------------------------- */
+/*      Reading SRID                                                    */
+/* -------------------------------------------------------------------- */
+     
+    this->SRID = -1;
+    
+    if( pszNewProjection[0]=='+')    
+        sprintf( szCommand,"SELECT SRID FROM spatial_ref_sys where proj4text=%s",pszNewProjection);
+    else
+        sprintf( szCommand,"SELECT SRID FROM spatial_ref_sys where srtext=%s",pszNewProjection);
+    
+    
+    hResult = PQexec(hPGConn,szCommand);
+        
+    
+    if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK 
+             && PQntuples(hResult) > 0 ){
+        
+        this->SRID = atoi(PQgetvalue(hResult,0,0)); 
+        
+        pszProjection = CPLStrdup( pszNewProjection );             
+        
+        PQclear( hResult );        
+        
+        return CE_None;
+    }
+    
+    // Try to find SRID via EPSG number
+    if (this->SRID == -1 && strcmp(pszNewProjection,"")!=0){
+                
+            char *buf;
+            char epsg[16];
+            memset(epsg,0,16);
+            char *workingproj = (char *)pszNewProjection;
+    
+            while( (buf = strstr(workingproj,"EPSG")) != 0){
+                workingproj = buf+4;
+            }
+            
+            int iChar = 0;
+            workingproj = workingproj + 3;
+            
+            while(workingproj[iChar] != '"'){
+                epsg[iChar] = workingproj[iChar];
+                iChar++;
+            }
+            
+            if(epsg[0] != 0){
+                this->SRID = atoi(epsg); 
+                pszProjection = CPLStrdup(pszNewProjection);    
+            }
+            
+            return CE_None;
+    }
+    else{
+         
+            CPLError( CE_Failure, CPLE_AppDefined,
+                "Projection %s not found in spatial_ref_sys table.\n",
+                  pszNewProjection );
+        
+            this->SRID = -1;
+            pszProjection = CPLStrdup("");    
+                  
+            if( hResult )
+                PQclear( hResult );
+                  
+            return CE_Failure;
+    }
+}
+
+
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *PGCHIPDataset::Open( GDALOpenInfo * poOpenInfo ){
+
+    char                szCommand[1024];
+    PGresult            *hResult = NULL;
+    PGCHIPDataset 	*poDS = NULL;
+    char                *chipStringHex;
+
+    unsigned char        *chipdata;
+    char                *layerName;
+    int                 t;
+       
+            
+    /* Chek Postgis connection string */
+    if( poOpenInfo->pszFilename == NULL)
+        return NULL;
+             
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+
+    poDS = new PGCHIPDataset();
+    poDS->pszConnectionString = CPLStrdup(poOpenInfo->pszFilename);
+    layerName = CPLStrdup(poOpenInfo->pszFilename);
+
+/* -------------------------------------------------------------------- */
+/*      Verify postgresql prefix.                                       */
+/* -------------------------------------------------------------------- */
+    if( !EQUALN(poDS->pszConnectionString,"PG:",3) )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                      "%s does not conform to PostgreSQL naming convention,"
+                      " PG:*\n" );
+        return NULL;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Try to establish connection.                                    */
+/* -------------------------------------------------------------------- */
+    int i=0;
+    while(poDS->pszConnectionString[i] != '\0'){
+        
+        if(poDS->pszConnectionString[i] == '#')
+            poDS->pszConnectionString[i] = ' ';
+        if(poDS->pszConnectionString[i] == '%')
+            poDS->pszConnectionString[i] = '\0';
+        i++;
+    }
+        
+    
+    poDS->hPGConn = PQconnectdb( poDS->pszConnectionString + 3 );
+    
+    if( poDS->hPGConn == NULL || PQstatus(poDS->hPGConn) == CONNECTION_BAD )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "PGconnectcb failed.\n%s", 
+                  PQerrorMessage(poDS->hPGConn) );
+        PQfinish(poDS->hPGConn);
+        poDS->hPGConn = NULL;
+        return NULL;
+    }
+    
+    
+/* -------------------------------------------------------------------- */
+/*      Try to establish the database name from the connection          */
+/*      string passed.                                                  */
+/* -------------------------------------------------------------------- */
+    if( strstr(poDS->pszConnectionString, "dbname=") != NULL )
+    {
+        int     i;
+
+        poDS->pszDBName = CPLStrdup( strstr(poDS->pszConnectionString, "dbname=") + 7 );
+                
+        for( i = 0; poDS->pszDBName[i] != '\0'; i++ )
+        {
+            if( poDS->pszDBName[i] == ' ' )                                   
+            {
+                poDS->pszDBName[i] = '\0';
+                break;
+            }
+        }
+    }
+    else if( getenv( "USER" ) != NULL )
+        poDS->pszDBName = CPLStrdup( getenv("USER") );
+    else
+        poDS->pszDBName = CPLStrdup( "unknown_dbname" );
+                   
+        
+/* -------------------------------------------------------------------- */
+/*      Test to see if this database instance has support for the       */
+/*      PostGIS Geometry type.  If so, disable sequential scanning      */
+/*      so we will get the value of the gist indexes.                   */
+/* -------------------------------------------------------------------- */
+       
+    
+    hResult = PQexec(poDS->hPGConn, 
+                         "SELECT oid FROM pg_type WHERE typname = 'geometry'" );
+   
+                         
+    if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK 
+        && PQntuples(hResult) > 0 )
+    {
+        poDS->bHavePostGIS = TRUE;
+    }
+
+    if( hResult )
+        PQclear( hResult );
+    
+    if(!poDS->bHavePostGIS){
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                      "Can't find geometry type, is Postgis correctly installed ?\n");
+        return NULL;
+    }
+    
+    
+/* -------------------------------------------------------------------- */
+/*  try opening the layer                                               */
+/* -------------------------------------------------------------------- */
+    
+    if( strstr(layerName, "layer=") != NULL )
+    {
+	poDS->pszName = CPLStrdup( strstr(layerName, "layer=") + 6 );
+    }
+    else        
+        poDS->pszName = CPLStrdup("unknown_layer");
+            
+    
+/* -------------------------------------------------------------------- */
+/*      Read the chip header                                            */
+/* -------------------------------------------------------------------- */
+    
+    
+    hResult = PQexec(poDS->hPGConn, "BEGIN");
+    
+    if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
+    {
+        PQclear( hResult );
+        sprintf( szCommand, 
+                 "SELECT raster FROM %s",
+                 poDS->pszName);
+                         
+        hResult = PQexec(poDS->hPGConn,szCommand);
+    }
+
+
+    if( !hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "%s", PQerrorMessage(poDS->hPGConn) );
+        return NULL;
+    }
+           
+    chipStringHex = PQgetvalue(hResult, 0, 0);
+    int stringlen = strlen((char *)chipStringHex);
+        
+    // Allocating memory for chip
+    chipdata = (unsigned char *) CPLMalloc(stringlen/2);
+                      	
+    for (t=0;t<stringlen/2;t++){
+	chipdata[t] = parse_hex( &chipStringHex[t*2]) ;
+    }
+    
+    // Chip assigment
+    poDS->PGCHIP = (CHIP *)chipdata;
+           
+    if( hResult )
+        PQclear( hResult );
+    
+    hResult = PQexec(poDS->hPGConn, "COMMIT");
+    PQclear( hResult );
+    
+    
+/* -------------------------------------------------------------------- */
+/*      Set some information from the file that is of interest.         */
+/* -------------------------------------------------------------------- */
+
+    poDS->nRasterXSize = poDS->PGCHIP->width;
+    poDS->nRasterYSize = poDS->PGCHIP->height;
+    poDS->nBands = (int)poDS->PGCHIP->future[0];
+    poDS->nBitDepth = (int)poDS->PGCHIP->future[1];
+    poDS->nColorType = (int)poDS->PGCHIP->future[2];
+    
+        
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    for( int iBand = 0; iBand < poDS->nBands; iBand++ )
+        poDS->SetBand( iBand+1, new PGCHIPRasterBand( poDS, iBand+1 ) );
+    
+    
+/* -------------------------------------------------------------------- */
+/*      Is there a palette?  Note: we should also read back and         */
+/*      apply transparency values if available.                         */
+/* -------------------------------------------------------------------- */
+    if( poDS->nColorType == PGCHIP_COLOR_TYPE_PALETTE )
+    {
+        unsigned char *pPalette;
+        int	nColorCount = 0;
+        int     sizePalette = 0;
+        int     offsetColor = -1;
+        GDALColorEntry oEntry;
+                
+        nColorCount = (int)poDS->PGCHIP->compression;
+        pPalette = (unsigned char *)chipdata + sizeof(CHIP);
+        sizePalette = nColorCount * sizeof(pgchip_color);
+        
+        poDS->poColorTable = new GDALColorTable();
+        
+        for( int iColor = 0; iColor < nColorCount; iColor++ )
+        {
+            oEntry.c1 = pPalette[offsetColor++];
+            oEntry.c2 = pPalette[offsetColor++];
+            oEntry.c3 = pPalette[offsetColor++];
+            oEntry.c4 = pPalette[offsetColor++];
+           
+            poDS->poColorTable->SetColorEntry( iColor, &oEntry );
+        }
+    }
+    
+    return( poDS );
+}
+
+
+/************************************************************************/
+/*                           PGCHIPCreateCopy()                         */
+/************************************************************************/
+static GDALDataset * PGCHIPCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, 
+                int bStrict, char ** papszOptions, 
+                GDALProgressFunc pfnProgress, void * pProgressData ){
+
+    
+    PGconn      *hPGConn;
+    char	*pszConnectionString;
+    char	*pszDBName;
+    char        *pszName;
+    int         bHavePostGIS;
+    char                *szCommand;
+    PGresult            *hResult;
+    char    *layerName;
+    char    *pszProjection;
+    int    SRID;
+    GDALColorTable	*poCT= NULL;
+    
+    int  nXSize = poSrcDS->GetRasterXSize();
+    int  nYSize = poSrcDS->GetRasterYSize();
+    int  nBands = poSrcDS->GetRasterCount();
+    
+    
+/* -------------------------------------------------------------------- */
+/*      Some some rudimentary checks                                    */
+/* -------------------------------------------------------------------- */
+    
+    /* check number of bands */
+    if( nBands != 1 && nBands != 4)
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "Under development : PGCHIP driver doesn't support %d bands.  Must be 1 or 4\n", nBands );
+
+        return NULL;
+    }
+    
+    
+    if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte 
+        && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16)
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "Under development : PGCHIP driver doesn't support data type %s. "
+                  "Only eight bit (Byte) and sixteen bit (UInt16) bands supported.\n", 
+                  GDALGetDataTypeName( 
+                      poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
+
+        return NULL;
+    }
+    
+    /* Check Postgis connection string */
+    if( pszFilename == NULL){
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "Connection string is NULL.\n");
+        return NULL;
+    }     
+    
+    
+/* -------------------------------------------------------------------- */
+/*      Setup some parameters.                                          */
+/* -------------------------------------------------------------------- */
+    
+    int nBitDepth;
+    GDALDataType eType;
+    int storageChunk;
+    int nColorType=0;
+       
+    
+    if( nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() == NULL ){
+        nColorType = PGCHIP_COLOR_TYPE_GRAY;
+    }
+    else if( nBands == 1 ){
+        nColorType = PGCHIP_COLOR_TYPE_PALETTE;
+    }
+    else if( nBands == 4 ){
+        nColorType = PGCHIP_COLOR_TYPE_RGB_ALPHA;
+    }
+    
+    if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16 )
+    {
+        eType = GDT_Byte;
+        nBitDepth = 8;
+    }
+    else 
+    {
+        eType = GDT_UInt16;
+        nBitDepth = 16;
+    }
+    
+    storageChunk = nBitDepth/8;
+      
+    printf("nBands = %d, nBitDepth = %d\n",nBands,nBitDepth);
+    
+/* -------------------------------------------------------------------- */
+/*      Verify postgresql prefix.                                       */
+/* -------------------------------------------------------------------- */
+    
+    if( !EQUALN(pszFilename,"PG:",3) )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                      "%s does not conform to PostgreSQL naming convention,"
+                      " PG:*\n" );
+        return NULL;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Try to establish connection.                                    */
+/* -------------------------------------------------------------------- */
+    
+    pszConnectionString = CPLStrdup(pszFilename);    
+    layerName = CPLStrdup(pszFilename);
+
+    int i=0;
+    while(pszConnectionString[i] != '\0'){
+        
+        if(pszConnectionString[i] == '#')
+            pszConnectionString[i] = ' ';
+        
+        i++;
+    }
+    
+    hPGConn = PQconnectdb( pszConnectionString + 3 );
+    
+    if( hPGConn == NULL || PQstatus(hPGConn) == CONNECTION_BAD )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "PGconnectcb failed.\n%s", 
+                  PQerrorMessage(hPGConn) );
+        PQfinish(hPGConn);
+        hPGConn = NULL;
+        return NULL;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Try to establish the database name from the connection          */
+/*      string passed.                                                  */
+/* -------------------------------------------------------------------- */
+        
+    if( strstr(pszFilename, "dbname=") != NULL )
+    {
+        int     i;
+
+        pszDBName = CPLStrdup( strstr(pszFilename, "dbname=") + 7 );
+
+        for( i = 0; pszDBName[i] != '\0'; i++ )
+        {
+            if( pszDBName[i] == ' ' )                                   
+            {
+                pszDBName[i] = '\0';
+                break;
+            }
+        }
+    }
+    else if( getenv( "USER" ) != NULL )
+        pszDBName = CPLStrdup( getenv("USER") );
+    else
+        pszDBName = CPLStrdup( "unknown_dbname" );
+        
+               
+/* -------------------------------------------------------------------- */
+/*      Test to see if this database instance has support for the       */
+/*      PostGIS Geometry type.  If so, disable sequential scanning      */
+/*      so we will get the value of the gist indexes.                   */
+/* -------------------------------------------------------------------- */
+       
+    hResult = PQexec(hPGConn, "BEGIN");
+
+    if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
+    {
+        PQclear( hResult );
+
+        hResult = PQexec(hPGConn, 
+                         "SELECT oid FROM pg_type WHERE typname = 'geometry'" );
+    }
+
+    if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK 
+        && PQntuples(hResult) > 0 )
+    {
+        bHavePostGIS = TRUE;
+    }
+    else {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "You don't seem to have Postgis installed. Check your settings.\n");
+        return NULL;         
+    }
+        
+    if( hResult )
+        PQclear( hResult );
+
+
+    hResult = PQexec(hPGConn, "COMMIT");
+    PQclear( hResult );
+    
+    
+/* -------------------------------------------------------------------- */
+/*     try opening Postgis Raster Layer                                 */
+/* -------------------------------------------------------------------- */
+    
+    if( strstr(layerName, "layer=") != NULL )
+    {
+	pszName = CPLStrdup( strstr(layerName, "layer=") + 6 );
+    }
+    else        
+        pszName = CPLStrdup("unknown_layer");
+            
+    CPLFree(layerName);
+    
+    
+    // First allocation is small
+    szCommand = (char *)CPLMalloc(1024);
+        
+    hResult = PQexec(hPGConn, "BEGIN");
+
+    if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
+    {
+        int bTableExists = FALSE;    
+    
+        PQclear( hResult );
+        sprintf( szCommand, 
+                 "select b.attname from pg_class a,pg_attribute b where a.oid=b.attrelid and a.relname='%s' and b.attname='raster';",
+                 pszName);
+                 
+        hResult = PQexec(hPGConn,szCommand);
+        
+        if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK 
+        && PQntuples(hResult) > 0 ){
+            bTableExists = TRUE;
+        }
+        
+        if(!bTableExists){
+            PQclear( hResult );
+            sprintf( szCommand, 
+                    "CREATE TABLE %s(raster chip)",
+                    pszName);
+                    
+            hResult = PQexec(hPGConn,szCommand);
+        }
+    }
+
+    if( hResult && (PQresultStatus(hResult) == PGRES_COMMAND_OK || PQresultStatus(hResult) == PGRES_TUPLES_OK)){
+        PQclear( hResult );
+    }
+    else {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "%s", PQerrorMessage(hPGConn) );
+        CPLFree(szCommand);
+        return NULL;
+    }
+    
+    hResult = PQexec(hPGConn, "COMMIT");
+    PQclear( hResult );
+        
+       
+/* -------------------------------------------------------------------- */
+/*      Projection, finding SRID                                        */
+/* -------------------------------------------------------------------- */    
+  
+    pszProjection = (char *)poSrcDS->GetProjectionRef();
+    SRID = -1;
+    
+    if( !EQUALN(pszProjection,"GEOGCS",6)
+        && !EQUALN(pszProjection,"PROJCS",6)
+        && !EQUALN(pszProjection,"+",6)
+        && !EQUAL(pszProjection,"") )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                "Only OGC WKT Projections supported for writing to Postgis.\n"
+                "%s not supported.",
+                  pszProjection );
+    }
+    
+    
+    if( pszProjection[0]=='+')    
+        sprintf( szCommand,"SELECT SRID FROM spatial_ref_sys where proj4text=%s",pszProjection);
+    else
+        sprintf( szCommand,"SELECT SRID FROM spatial_ref_sys where srtext=%s",pszProjection);
+            
+    hResult = PQexec(hPGConn,szCommand);
+        
+    if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK 
+             && PQntuples(hResult) > 0 ){
+        
+            SRID = atoi(PQgetvalue(hResult,0,0)); 
+        
+    }
+    
+    // Try to find SRID via EPSG number
+    if (SRID == -1 && strcmp(pszProjection,"") != 0){
+            
+            char *buf;
+            char epsg[16];
+            memset(epsg,0,16);
+            char *workingproj = CPLStrdup( pszProjection );
+                
+            while( (buf = strstr(workingproj,"EPSG")) != 0){
+                workingproj = buf+4;
+            }
+            
+            int iChar = 0;
+            workingproj = workingproj + 3;
+            
+            
+            while(workingproj[iChar] != '"'){
+                epsg[iChar] = workingproj[iChar];
+                iChar++;
+            }
+            
+            if(epsg[0] != 0){
+                SRID = atoi(epsg); 
+            }
+    }
+    else{
+            CPLError( CE_Failure, CPLE_AppDefined,
+                "Projection %s not found in spatial_ref_sys table. SRID will be set to -1.\n",
+                  pszProjection );
+        
+            SRID = -1;
+    }
+
+    if( hResult )
+        PQclear( hResult );
+        
+           
+/* -------------------------------------------------------------------- */
+/*      Write palette if there is one.  Technically, I think it is      */
+/*      possible to write 16bit palettes for PNG, but we will omit      */
+/*      this for now.                                                   */
+/* -------------------------------------------------------------------- */
+    
+    unsigned char	*pPalette = NULL;
+    int		bHaveNoData = FALSE;
+    double	dfNoDataValue = -1;
+    int nbColors = 0,bFoundTrans = FALSE;
+    int sizePalette = 0;
+    
+    if( nColorType == PGCHIP_COLOR_TYPE_PALETTE )
+    {
+        
+        GDALColorEntry  sEntry;
+        int		iColor;
+        int             offsetColor = -1;
+                        
+        poCT = poSrcDS->GetRasterBand(1)->GetColorTable();  
+        nbColors = poCT->GetColorEntryCount();
+                
+        sizePalette += sizeof(pgchip_color) * poCT->GetColorEntryCount();
+                
+        pPalette = (unsigned char *) CPLMalloc(sizePalette);
+               
+                                               
+        for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ )
+        {
+            poCT->GetColorEntryAsRGB( iColor, &sEntry );
+            if( sEntry.c4 != 255 )
+                bFoundTrans = TRUE;
+            
+            pPalette[offsetColor++]  = (unsigned char) sEntry.c1;
+            pPalette[offsetColor++]  = (unsigned char) sEntry.c2;
+            pPalette[offsetColor++]  = (unsigned char) sEntry.c3;
+            
+                       
+            if( bHaveNoData && iColor == (int) dfNoDataValue ){
+                pPalette[offsetColor++]  = 0;
+            }
+            else{
+                pPalette[offsetColor++]  = (unsigned char) sEntry.c4;
+            }
+        }
+    }
+    
+        
+/* -------------------------------------------------------------------- */
+/*     Initialize CHIP Structure                                        */
+/* -------------------------------------------------------------------- */  
+    
+    CHIP PGCHIP;
+    
+    memset(&PGCHIP,0,sizeof(PGCHIP));
+    
+    PGCHIP.factor = 1.0;
+    PGCHIP.endian_hint = 1;
+    PGCHIP.compression = nbColors; // To cope with palette extra information  : <header><palette><data>
+    PGCHIP.height = nYSize;
+    PGCHIP.width = nXSize;
+    PGCHIP.SRID = SRID;
+    PGCHIP.future[0] = nBands; //nBands is stored in future variable
+    PGCHIP.future[1] = nBitDepth; //nBitDepth is stored in future variable
+    PGCHIP.future[2] = nColorType; //nBitDepth is stored in future variable
+    PGCHIP.future[3] = nbColors; // Useless as we store nbColors in the "compression" integer
+    PGCHIP.data = NULL; // Serialized Form
+    
+    // PGCHIP.size changes if there is a palette.
+    // Is calculated by Postgis when inserting anyway
+    PGCHIP.size = sizeof(CHIP) + (nYSize * nXSize * storageChunk * nBands) + sizePalette;
+    
+    switch(storageChunk*nBands){
+        case 1 :
+            PGCHIP.datatype = 8;
+            break;
+        case 2 :
+            PGCHIP.datatype = 6;
+            break;
+        case 4 :
+            // Postgis sets data_size to 4 by default anyway
+            PGCHIP.datatype = 0;
+            break;
+        default :
+             CPLError( CE_Failure, CPLE_AppDefined,"Under development : ERROR STORAGE CHUNK SIZE NOT SUPPORTED\n");
+            break;   
+    }
+    
+                
+/* -------------------------------------------------------------------- */
+/*      Loop over image                                                 */
+/* -------------------------------------------------------------------- */
+       
+    CPLErr      eErr;
+    int lineSize = nXSize * storageChunk * nBands;
+    
+    // allocating data buffer
+    GByte *data = (GByte *) CPLMalloc( nYSize * lineSize);
+                    
+    for( int iLine = 0; iLine < nYSize; iLine++ ){
+        for( int iBand = 0; iBand < nBands; iBand++ ){
+            
+            GDALRasterBand * poBand = poSrcDS->GetRasterBand( iBand+1 );
+            
+            eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, 
+                                     data + (iBand*storageChunk) + iLine * lineSize, 
+                                     nXSize, 1, eType,
+                                     nBands * storageChunk, 
+                                     lineSize );  
+         }
+    }
+    
+        
+/* -------------------------------------------------------------------- */
+/*      Write Header, Palette and Data                                  */
+/* -------------------------------------------------------------------- */    
+    
+    char *result;
+    int j=0;
+    
+    // Calculating result length (*2 -> Hex form, +1 -> end string) 
+    int size_result = (PGCHIP.size * 2) + 1;
+        
+    // memory allocation
+    result = (char *) CPLMalloc( size_result * sizeof(char));
+            
+    // Assign chip
+    GByte *header = (GByte *)&PGCHIP;
+        
+    // Copy header into result string 
+    for(j=0;j<(int)sizeof(PGCHIP);j++){
+        deparse_hex( ((unsigned char *) header)[j], (unsigned char *)&result[j*2]);  
+    }  
+    
+    // Copy Palette into result string if required
+    int offsetPalette = (int)sizeof(PGCHIP) * 2;
+    if(nColorType == PGCHIP_COLOR_TYPE_PALETTE && sizePalette>0){
+        for(j=0;j<sizePalette;j++){
+            deparse_hex( ((unsigned char *) pPalette)[j], (unsigned char *)&result[offsetPalette + (j*2)]);     
+        }                   
+    }
+    
+    // Copy data into result string
+    int offsetData = offsetPalette + sizePalette * 2;
+    for(j=0;j<(nYSize * lineSize);j++){
+         deparse_hex( ((unsigned char *) data)[j], (unsigned char *)&result[offsetData + (j*2)]);
+    }
+   
+    
+    // end string
+    result[offsetData + j*2] = '\0';
+    
+                                         
+/* -------------------------------------------------------------------- */
+/*      Inserting Chip                                                  */
+/* -------------------------------------------------------------------- */
+     
+    // Second allocation to cope with data size
+    CPLFree(szCommand);
+    szCommand = (char *)CPLMalloc(PGCHIP.size*2 + 256);
+
+    hResult = PQexec(hPGConn, "BEGIN");
+
+    if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
+    {
+                        
+        PQclear( hResult );
+        sprintf( szCommand, 
+                 "INSERT INTO %s(raster) values('%s')",
+                 pszName,result);
+                 
+                
+        hResult = PQexec(hPGConn,szCommand);
+    
+    }
+    
+    if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK ){
+        PQclear( hResult );
+    }
+    else {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "%s", PQerrorMessage(hPGConn) );
+        CPLFree(szCommand);
+        return NULL;
+    }
+        
+    hResult = PQexec(hPGConn, "COMMIT");
+    PQclear( hResult );
+            
+    CPLFree( szCommand );
+    CPLFree( pPalette );
+    CPLFree( data );       
+    CPLFree( result );
+             
+    return (GDALDataset *)GDALOpen(pszFilename,GA_Update);
+}
+
+
+/************************************************************************/
+/*                          Display CHIP information                    */
+/************************************************************************/
+void     PGCHIPDataset::printChipInfo(){
+
+    if(this->PGCHIP != NULL){
+        printf("\n---< CHIP INFO >----\n");
+        printf("CHIP.datatype = %d\n",this->PGCHIP->datatype);
+        printf("CHIP.compression = %d\n",this->PGCHIP->compression);
+        printf("CHIP.size = %d\n",this->PGCHIP->size);
+        printf("CHIP.factor = %f\n",this->PGCHIP->factor);
+        printf("CHIP.width = %d\n",this->PGCHIP->width);
+        printf("CHIP.height = %d\n",this->PGCHIP->height);
+        printf("CHIP.nBands = %d\n",(int)this->PGCHIP->future[0]);
+        printf("CHIP.nBitDepth = %d\n",(int)this->PGCHIP->future[1]);
+        printf("--------------------\n");
+     }
+}
+
+
+/************************************************************************/
+/*                          GDALRegister_PGCHIP()                       */
+/************************************************************************/
+void GDALRegister_PGCHIP(){
+
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "PGCHIP" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "PGCHIP" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "Postgis CHIP raster" );
+                                   
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte UInt16" );
+         
+        poDriver->pfnOpen = PGCHIPDataset::Open;
+        poDriver->pfnCreateCopy = PGCHIPCreateCopy;
+        
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
+
+
+
+
+
+
diff --git a/Utilities/GDAL/frmts/pgchip/pgchiprasterband.cpp b/Utilities/GDAL/frmts/pgchip/pgchiprasterband.cpp
new file mode 100644
index 0000000000..b33626214d
--- /dev/null
+++ b/Utilities/GDAL/frmts/pgchip/pgchiprasterband.cpp
@@ -0,0 +1,218 @@
+/******************************************************************************
+ *
+ * File :    pgchiprasterband.cpp
+ * Project:  PGCHIP Driver
+ * Purpose:  GDALRasterBand code for POSTGIS CHIP/GDAL Driver 
+ * Author:   Benjamin Simon, noumayoss@gmail.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Benjamin Simon, noumayoss@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * Revision 1.1  2005/08/29 bsimon
+ * New
+ *
+ */
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            PGCHIPRasterBand                          */
+/* ==================================================================== */
+/************************************************************************/
+
+#include "pgchip.h"
+
+
+/************************************************************************/
+/*                           PGCHIPRasterBand()                         */
+/************************************************************************/
+
+PGCHIPRasterBand::PGCHIPRasterBand( PGCHIPDataset *poDS, int nBand ){
+
+    this->poDS = poDS;
+    this->nBand = nBand;
+    
+    if( poDS->nBitDepth == 16 )
+        eDataType = GDT_UInt16;
+    else
+        eDataType = GDT_Byte;
+
+    nBlockXSize = poDS->GetRasterXSize();
+    nBlockYSize = 1;
+    
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr PGCHIPRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage ){
+
+    char    szCommand[1024];
+    PGCHIPDataset *poGDS = (PGCHIPDataset *) poDS;
+    PGconn      *hPGConn;
+    PGresult    *hResult;
+        
+    int        chipDataSize;
+    int        bandSize;
+    int        nPixelSize;
+    int        nPixelOffset;
+    int        nXSize;
+    int        i;
+    
+    hPGConn = poGDS->hPGConn;
+        
+    // Must start on the very left
+    CPLAssert( nBlockXOff == 0 );
+           
+    
+    if( poGDS->nBitDepth == 16 )
+        nPixelSize = 2;
+    else
+        nPixelSize = 1;
+    
+    nPixelOffset = poGDS->nBands * nPixelSize;
+    nXSize = GetXSize();
+    bandSize = nPixelSize * nXSize;
+    int sizePalette = 0;        
+    
+    if(poGDS->PGCHIP->future[2] == PGCHIP_COLOR_TYPE_PALETTE){
+        sizePalette = (int)poGDS->PGCHIP->compression * sizeof(pgchip_color);
+    }
+    
+    // Determine size of whole Image Data
+    chipDataSize = poGDS->PGCHIP->size - sizeof(CHIP) - sizePalette;
+    
+    
+/* -------------------------------------------------------------------- */
+/*      Reading Chip (first pas only)                                   */
+/* -------------------------------------------------------------------- */
+    
+    if(poGDS->PGCHIP->data == NULL){
+    
+        hResult = PQexec(hPGConn, "BEGIN");
+    
+        if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK )
+        {
+                                   
+            PQclear( hResult );
+            sprintf( szCommand,"SELECT raster FROM %s",poGDS->pszName);
+            
+            hResult = PQexec(hPGConn,szCommand);
+        
+            char *chipData =  PQgetvalue(hResult,0,0);        
+            poGDS->PGCHIP->data = (char *) CPLMalloc(chipDataSize);
+            
+            char *data = chipData + (sizePalette + sizeof(CHIP))*2;
+                    
+            // Reading whole data
+            for(i=0 ; i<chipDataSize ; i++){
+                ((unsigned char *)poGDS->PGCHIP->data)[i] = parse_hex( &data[i*2]) ;
+            }
+            
+            PQclear( hResult );
+            
+        }
+        else {
+            CPLError( CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn) );
+            PQclear( hResult );
+        }
+                
+        hResult = PQexec(hPGConn, "COMMIT");
+        PQclear( hResult );
+        
+     }
+     
+/* -------------------------------------------------------------------- */
+/*      Extracting band from pointer                                    */
+/* -------------------------------------------------------------------- */
+     
+     if(poGDS->PGCHIP->data){
+     
+               
+        if( nPixelSize == 1 ){
+        
+            char *bufferData = ((char *)poGDS->PGCHIP->data) + (nBlockYOff * poGDS->nBands * bandSize) + ((nBand-1) * nPixelSize);
+                
+            for(i = 0; i < nXSize; i++ ){
+                ((char *) pImage)[i] = bufferData[i*nPixelOffset];
+            }
+        }
+        else {
+        
+            GUInt16 *bufferData = (GUInt16 *)(((char *)poGDS->PGCHIP->data) + (nBlockYOff * poGDS->nBands * bandSize) + ((nBand-1) * nPixelSize));
+             
+            for(i = 0; i < nXSize; i++ ){
+                
+                ((GUInt16 *) pImage)[i] = bufferData[i*poGDS->nBands];
+                
+            }
+        }
+     
+     }  
+         
+    return CE_None;
+}
+
+
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+GDALColorInterp PGCHIPRasterBand::GetColorInterpretation(){
+
+    PGCHIPDataset	*poGDS = (PGCHIPDataset *) poDS;
+
+    if( poGDS->nColorType == PGCHIP_COLOR_TYPE_GRAY ){
+        return GCI_GrayIndex;
+    }
+    else  if( poGDS->nColorType == PGCHIP_COLOR_TYPE_PALETTE ){
+        return GCI_PaletteIndex;    
+    }
+    else if(poGDS->nColorType == PGCHIP_COLOR_TYPE_RGB_ALPHA){
+        if( nBand == 1 )
+            return GCI_RedBand;
+        else if( nBand == 2 )
+            return GCI_GreenBand;
+        else if( nBand == 3 )
+            return GCI_BlueBand;
+        else 
+            return GCI_AlphaBand;
+    }
+    
+    return GCI_GrayIndex;
+}
+
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *PGCHIPRasterBand::GetColorTable(){
+
+    PGCHIPDataset	*poGDS = (PGCHIPDataset *) poDS;
+
+    if( nBand == 1 )
+        return poGDS->poColorTable;
+    else
+        return NULL;
+}
diff --git a/Utilities/GDAL/frmts/pgchip/pgchiputilities.cpp b/Utilities/GDAL/frmts/pgchip/pgchiputilities.cpp
new file mode 100644
index 0000000000..9921497c08
--- /dev/null
+++ b/Utilities/GDAL/frmts/pgchip/pgchiputilities.cpp
@@ -0,0 +1,290 @@
+/******************************************************************************
+ *
+ * File :    pgchiputilities.cpp
+ * Project:  PGCHIP Driver
+ * Purpose:  Utility functions for POSTGIS CHIP/GDAL Driver 
+ * Author:   Benjamin Simon, noumayoss@gmail.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Benjamin Simon, noumayoss@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * Revision 1.1  2005/08/29 bsimon
+ * New
+ *
+ */
+ 
+#include "pgchip.h"
+
+/************************************************************************/
+/* ==================================================================== */
+/*				Utility Hex Functions                   */
+/* ==================================================================== */
+/************************************************************************/
+
+void deparse_hex(unsigned char str, unsigned char *result){
+
+	int	input_high;
+	int  input_low;
+
+	input_high = (str>>4);
+	input_low = (str & 0x0F);
+
+	switch (input_high)
+	{
+		case 0:
+			result[0] = '0';
+			break;
+		case 1:
+			result[0] = '1';
+			break;
+		case 2:
+			result[0] = '2';
+			break;
+		case 3:
+			result[0] = '3';
+			break;
+		case 4:
+			result[0] = '4';
+			break;
+		case 5:
+			result[0] = '5';
+			break;
+		case 6:
+			result[0] = '6';
+			break;
+		case 7:
+			result[0] = '7';
+			break;
+		case 8:
+			result[0] = '8';
+			break;
+		case 9:
+			result[0] = '9';
+			break;
+		case 10:
+			result[0] = 'A';
+			break;
+		case 11:
+			result[0] = 'B';
+			break;
+		case 12:
+			result[0] = 'C';
+			break;
+		case 13:
+			result[0] = 'D';
+			break;
+		case 14:
+			result[0] = 'E';
+			break;
+		case 15:
+			result[0] = 'F';
+			break;
+	}
+
+	switch (input_low)
+	{
+		case 0:
+			result[1] = '0';
+			break;
+		case 1:
+			result[1] = '1';
+			break;
+		case 2:
+			result[1] = '2';
+			break;
+		case 3:
+			result[1] = '3';
+			break;
+		case 4:
+			result[1] = '4';
+			break;
+		case 5:
+			result[1] = '5';
+			break;
+		case 6:
+			result[1] = '6';
+			break;
+		case 7:
+			result[1] = '7';
+			break;
+		case 8:
+			result[1] = '8';
+			break;
+		case 9:
+			result[1] = '9';
+			break;
+		case 10:
+			result[1] = 'A';
+			break;
+		case 11:
+			result[1] = 'B';
+			break;
+		case 12:
+			result[1] = 'C';
+			break;
+		case 13:
+			result[1] = 'D';
+			break;
+		case 14:
+			result[1] = 'E';
+			break;
+		case 15:
+			result[1] = 'F';
+			break;
+	}
+}
+
+//given a string with at least 2 chars in it, convert them to
+// a byte value.  No error checking done!
+unsigned char parse_hex(char *str){
+
+	//do this a little brute force to make it faster
+
+	unsigned char		result_high = 0;
+	unsigned char		result_low = 0;
+
+	switch (str[0])
+	{
+		case '0' :
+			result_high = 0;
+			break;
+		case '1' :
+			result_high = 1;
+			break;
+		case '2' :
+			result_high = 2;
+			break;
+		case '3' :
+			result_high = 3;
+			break;
+		case '4' :
+			result_high = 4;
+			break;
+		case '5' :
+			result_high = 5;
+			break;
+		case '6' :
+			result_high = 6;
+			break;
+		case '7' :
+			result_high = 7;
+			break;
+		case '8' :
+			result_high = 8;
+			break;
+		case '9' :
+			result_high = 9;
+			break;
+		case 'A' :
+			result_high = 10;
+			break;
+		case 'B' :
+			result_high = 11;
+			break;
+		case 'C' :
+			result_high = 12;
+			break;
+		case 'D' :
+			result_high = 13;
+			break;
+		case 'E' :
+			result_high = 14;
+			break;
+		case 'F' :
+			result_high = 15;
+			break;
+	}
+	switch (str[1])
+	{
+		case '0' :
+			result_low = 0;
+			break;
+		case '1' :
+			result_low = 1;
+			break;
+		case '2' :
+			result_low = 2;
+			break;
+		case '3' :
+			result_low = 3;
+			break;
+		case '4' :
+			result_low = 4;
+			break;
+		case '5' :
+			result_low = 5;
+			break;
+		case '6' :
+			result_low = 6;
+			break;
+		case '7' :
+			result_low = 7;
+			break;
+		case '8' :
+			result_low = 8;
+			break;
+		case '9' :
+			result_low = 9;
+			break;
+		case 'A' :
+			result_low = 10;
+			break;
+		case 'B' :
+			result_low = 11;
+			break;
+		case 'C' :
+			result_low = 12;
+			break;
+		case 'D' :
+			result_low = 13;
+			break;
+		case 'E' :
+			result_low = 14;
+			break;
+		case 'F' :
+			result_low = 15;
+			break;
+	}
+	return (unsigned char) ((result_high<<4) + result_low);
+}
+
+/* Parse an hex string */
+void parse_hex_string(unsigned char *strOut,char *strIn,int length){
+    
+    int i;
+    for(i=0;i<length;i++){
+        //printf("Before = %c\n",strIn[i]);
+        strOut[i] = parse_hex(&strIn[i]);
+        //printf("After = %c\n",strOut[i]);
+        }
+
+}
+
+/* Deparse an hex string */
+void deparse_hex_string(unsigned char *strOut,char *strIn,int length){
+    
+    int i;
+    
+    for(i=0;i<length;i++)
+        deparse_hex(strIn[i],&strOut[i]);
+
+}
diff --git a/Utilities/GDAL/frmts/pgchip/todo b/Utilities/GDAL/frmts/pgchip/todo
new file mode 100644
index 0000000000..e9a65ef223
--- /dev/null
+++ b/Utilities/GDAL/frmts/pgchip/todo
@@ -0,0 +1,9 @@
+TODO List :
+
+* Test Driver compatibility with various raster formats
+* Modify the connection string to cope with the name of the raster column
+* Improve the number of color interpretation options
+* Manage geoTrasnform
+* Deal with more datatypes
+* Improve SRID conversion
+* Test makefile.vc (Visual C)
diff --git a/Utilities/GDAL/frmts/png/GNUmakefile b/Utilities/GDAL/frmts/png/GNUmakefile
new file mode 100644
index 0000000000..f25feead6d
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/GNUmakefile
@@ -0,0 +1,30 @@
+
+include ../../GDALmake.opt
+
+ifeq ($(PNG_SETTING),internal)
+XTRA_OPT =	-DPNG_NO_GLOBAL_ARRAYS -Ilibpng
+OBJ	=	png.o pngerror.o pnggccrd.o pngget.o pngmem.o \
+		pngpread.o pngread.o pngrio.o pngrtran.o pngrutil.o \
+		pngset.o pngtrans.o pngvcrd.o pngwio.o pngwrite.o \
+		pngwtran.o pngwutil.o \
+		\
+		pngdataset.o
+else
+OBJ	=	pngdataset.o
+endif
+
+XTRA_OPT	:=	$(XTRA_OPT) -I../zlib
+
+CPPFLAGS	:=	$(XTRA_OPT) $(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o libpng/*.o $(O_OBJ)
+
+../o/%.o:	libpng/%.c
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@
+
+all:	$(OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/png/libpng/LICENSE b/Utilities/GDAL/frmts/png/libpng/LICENSE
new file mode 100644
index 0000000000..fe3ac4b96f
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/LICENSE
@@ -0,0 +1,109 @@
+
+This copy of the libpng notices is provided for your convenience.  In case of
+any discrepancy between this copy and the notices in the file png.h that is
+included in the libpng distribution, the latter shall prevail.
+
+COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
+
+If you modify libpng you may insert additional notices immediately following
+this sentence.
+
+libpng version 1.2.6, December 3, 2004, is
+Copyright (c) 2004 Glenn Randers-Pehrson, and is
+distributed according to the same disclaimer and license as libpng-1.2.5
+with the following individual added to the list of Contributing Authors
+
+   Cosmin Truta
+
+libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, are
+Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are
+distributed according to the same disclaimer and license as libpng-1.0.6
+with the following individuals added to the list of Contributing Authors
+
+   Simon-Pierre Cadieux
+   Eric S. Raymond
+   Gilles Vollant
+
+and with the following additions to the disclaimer:
+
+   There is no warranty against interference with your enjoyment of the
+   library or against infringement.  There is no warranty that our
+   efforts or the library will fulfill any of your particular purposes
+   or needs.  This library is provided with all faults, and the entire
+   risk of satisfactory quality, performance, accuracy, and effort is with
+   the user.
+
+libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
+Copyright (c) 1998, 1999 Glenn Randers-Pehrson, and are
+distributed according to the same disclaimer and license as libpng-0.96,
+with the following individuals added to the list of Contributing Authors:
+
+   Tom Lane
+   Glenn Randers-Pehrson
+   Willem van Schaik
+
+libpng versions 0.89, June 1996, through 0.96, May 1997, are
+Copyright (c) 1996, 1997 Andreas Dilger
+Distributed according to the same disclaimer and license as libpng-0.88,
+with the following individuals added to the list of Contributing Authors:
+
+   John Bowler
+   Kevin Bracey
+   Sam Bushell
+   Magnus Holmgren
+   Greg Roelofs
+   Tom Tanner
+
+libpng versions 0.5, May 1995, through 0.88, January 1996, are
+Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+
+For the purposes of this copyright and license, "Contributing Authors"
+is defined as the following set of individuals:
+
+   Andreas Dilger
+   Dave Martindale
+   Guy Eric Schalnat
+   Paul Schmidt
+   Tim Wegner
+
+The PNG Reference Library is supplied "AS IS".  The Contributing Authors
+and Group 42, Inc. disclaim all warranties, expressed or implied,
+including, without limitation, the warranties of merchantability and of
+fitness for any purpose.  The Contributing Authors and Group 42, Inc.
+assume no liability for direct, indirect, incidental, special, exemplary,
+or consequential damages, which may result from the use of the PNG
+Reference Library, even if advised of the possibility of such damage.
+
+Permission is hereby granted to use, copy, modify, and distribute this
+source code, or portions hereof, for any purpose, without fee, subject
+to the following restrictions:
+
+1. The origin of this source code must not be misrepresented.
+
+2. Altered versions must be plainly marked as such and must not
+   be misrepresented as being the original source.
+
+3. This Copyright notice may not be removed or altered from any
+   source or altered source distribution.
+
+The Contributing Authors and Group 42, Inc. specifically permit, without
+fee, and encourage the use of this source code as a component to
+supporting the PNG file format in commercial products.  If you use this
+source code in a product, acknowledgment is not required but would be
+appreciated.
+
+
+A "png_get_copyright" function is available, for convenient use in "about"
+boxes and the like:
+
+   printf("%s",png_get_copyright(NULL));
+
+Also, the PNG logo (in PNG format, of course) is supplied in the
+files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31).
+
+Libpng is OSI Certified Open Source Software.  OSI Certified Open Source is a
+certification mark of the Open Source Initiative.
+
+Glenn Randers-Pehrson
+glennrp at users.sourceforge.net
+December 3, 2004
diff --git a/Utilities/GDAL/frmts/png/libpng/makefile.vc b/Utilities/GDAL/frmts/png/libpng/makefile.vc
new file mode 100644
index 0000000000..01a483d7a3
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/makefile.vc
@@ -0,0 +1,22 @@
+
+OBJ	=	\
+	png.obj pngerror.obj pnggccrd.obj pngget.obj pngmem.obj \
+	pngpread.obj pngread.obj pngrio.obj pngrtran.obj pngrutil.obj \
+	pngset.obj pngtrans.obj pngvcrd.obj pngwio.obj pngwrite.obj \
+	pngwtran.obj pngwutil.obj
+
+GDAL_ROOT	=	..\..\..
+
+EXTRAFLAGS = 	-I..\..\zlib
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\..\o
+
+libpng.lib:	$(OBJ)
+	LIB /OUT:libpng.lib $(OBJ)
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/png/libpng/png.c b/Utilities/GDAL/frmts/png/libpng/png.c
new file mode 100644
index 0000000000..608ea2ce7c
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/png.c
@@ -0,0 +1,828 @@
+
+/* png.c - location for general purpose libpng functions
+ *
+ * libpng version 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+#define PNG_INTERNAL
+#define PNG_NO_EXTERN
+#include "png.h"
+
+/* Generate a compiler error if there is an old png.h in the search path. */
+typedef version_1_2_8 Your_png_h_is_not_version_1_2_8;
+
+/* Version information for C files.  This had better match the version
+ * string defined in png.h.  */
+
+#ifdef PNG_USE_GLOBAL_ARRAYS
+/* png_libpng_ver was changed to a function in version 1.0.5c */
+const char png_libpng_ver[18] = PNG_LIBPNG_VER_STRING;
+
+/* png_sig was changed to a function in version 1.0.5c */
+/* Place to hold the signature string for a PNG file. */
+const png_byte FARDATA png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10};
+
+/* Invoke global declarations for constant strings for known chunk types */
+PNG_IHDR;
+PNG_IDAT;
+PNG_IEND;
+PNG_PLTE;
+PNG_bKGD;
+PNG_cHRM;
+PNG_gAMA;
+PNG_hIST;
+PNG_iCCP;
+PNG_iTXt;
+PNG_oFFs;
+PNG_pCAL;
+PNG_sCAL;
+PNG_pHYs;
+PNG_sBIT;
+PNG_sPLT;
+PNG_sRGB;
+PNG_tEXt;
+PNG_tIME;
+PNG_tRNS;
+PNG_zTXt;
+
+/* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+/* start of interlace block */
+const int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
+
+/* offset to next interlace block */
+const int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
+
+/* start of interlace block in the y direction */
+const int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
+
+/* offset to next interlace block in the y direction */
+const int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
+
+/* width of interlace block (used in assembler routines only) */
+#ifdef PNG_HAVE_ASSEMBLER_COMBINE_ROW
+const int FARDATA png_pass_width[] = {8, 4, 4, 2, 2, 1, 1};
+#endif
+
+/* Height of interlace block.  This is not currently used - if you need
+ * it, uncomment it here and in png.h
+const int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1};
+*/
+
+/* Mask to determine which pixels are valid in a pass */
+const int FARDATA png_pass_mask[] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff};
+
+/* Mask to determine which pixels to overwrite while displaying */
+const int FARDATA png_pass_dsp_mask[]
+   = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff};
+
+#endif /* PNG_USE_GLOBAL_ARRAYS */
+
+/* Tells libpng that we have already handled the first "num_bytes" bytes
+ * of the PNG file signature.  If the PNG data is embedded into another
+ * stream we can set num_bytes = 8 so that libpng will not attempt to read
+ * or write any of the magic bytes before it starts on the IHDR.
+ */
+
+void PNGAPI
+png_set_sig_bytes(png_structp png_ptr, int num_bytes)
+{
+   png_debug(1, "in png_set_sig_bytes\n");
+   if (num_bytes > 8)
+      png_error(png_ptr, "Too many bytes for PNG signature.");
+
+   png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes);
+}
+
+/* Checks whether the supplied bytes match the PNG signature.  We allow
+ * checking less than the full 8-byte signature so that those apps that
+ * already read the first few bytes of a file to determine the file type
+ * can simply check the remaining bytes for extra assurance.  Returns
+ * an integer less than, equal to, or greater than zero if sig is found,
+ * respectively, to be less than, to match, or be greater than the correct
+ * PNG signature (this is the same behaviour as strcmp, memcmp, etc).
+ */
+int PNGAPI
+png_sig_cmp(png_bytep sig, png_size_t start, png_size_t num_to_check)
+{
+   png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
+   if (num_to_check > 8)
+      num_to_check = 8;
+   else if (num_to_check < 1)
+      return (0);
+
+   if (start > 7)
+      return (0);
+
+   if (start + num_to_check > 8)
+      num_to_check = 8 - start;
+
+   return ((int)(png_memcmp(&sig[start], &png_signature[start], num_to_check)));
+}
+
+/* (Obsolete) function to check signature bytes.  It does not allow one
+ * to check a partial signature.  This function might be removed in the
+ * future - use png_sig_cmp().  Returns true (nonzero) if the file is a PNG.
+ */
+int PNGAPI
+png_check_sig(png_bytep sig, int num)
+{
+  return ((int)!png_sig_cmp(sig, (png_size_t)0, (png_size_t)num));
+}
+
+/* Function to allocate memory for zlib and clear it to 0. */
+#ifdef PNG_1_0_X
+voidpf PNGAPI
+#else
+voidpf /* private */
+#endif
+png_zalloc(voidpf png_ptr, uInt items, uInt size)
+{
+   png_voidp ptr;
+   png_structp p=png_ptr;
+   png_uint_32 save_flags=p->flags;
+   png_uint_32 num_bytes;
+
+   if (items > PNG_UINT_32_MAX/size)
+   {
+     png_warning (png_ptr, "Potential overflow in png_zalloc()");
+     return (NULL);
+   }
+   num_bytes = (png_uint_32)items * size;
+
+   p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK;
+   ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes);
+   p->flags=save_flags;
+
+#if defined(PNG_1_0_X) && !defined(PNG_NO_ZALLOC_ZERO)
+   if (ptr == NULL)
+       return ((voidpf)ptr);
+
+   if (num_bytes > (png_uint_32)0x8000L)
+   {
+      png_memset(ptr, 0, (png_size_t)0x8000L);
+      png_memset((png_bytep)ptr + (png_size_t)0x8000L, 0,
+         (png_size_t)(num_bytes - (png_uint_32)0x8000L));
+   }
+   else
+   {
+      png_memset(ptr, 0, (png_size_t)num_bytes);
+   }
+#endif
+   return ((voidpf)ptr);
+}
+
+/* function to free memory for zlib */
+#ifdef PNG_1_0_X
+void PNGAPI
+#else
+void /* private */
+#endif
+png_zfree(voidpf png_ptr, voidpf ptr)
+{
+   png_free((png_structp)png_ptr, (png_voidp)ptr);
+}
+
+/* Reset the CRC variable to 32 bits of 1's.  Care must be taken
+ * in case CRC is > 32 bits to leave the top bits 0.
+ */
+void /* PRIVATE */
+png_reset_crc(png_structp png_ptr)
+{
+   png_ptr->crc = crc32(0, Z_NULL, 0);
+}
+
+/* Calculate the CRC over a section of data.  We can only pass as
+ * much data to this routine as the largest single buffer size.  We
+ * also check that this data will actually be used before going to the
+ * trouble of calculating it.
+ */
+void /* PRIVATE */
+png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length)
+{
+   int need_crc = 1;
+
+   if (png_ptr->chunk_name[0] & 0x20)                     /* ancillary */
+   {
+      if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
+          (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
+         need_crc = 0;
+   }
+   else                                                    /* critical */
+   {
+      if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE)
+         need_crc = 0;
+   }
+
+   if (need_crc)
+      png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length);
+}
+
+/* Allocate the memory for an info_struct for the application.  We don't
+ * really need the png_ptr, but it could potentially be useful in the
+ * future.  This should be used in favour of malloc(png_sizeof(png_info))
+ * and png_info_init() so that applications that want to use a shared
+ * libpng don't have to be recompiled if png_info changes size.
+ */
+png_infop PNGAPI
+png_create_info_struct(png_structp png_ptr)
+{
+   png_infop info_ptr;
+
+   png_debug(1, "in png_create_info_struct\n");
+   if(png_ptr == NULL) return (NULL);
+#ifdef PNG_USER_MEM_SUPPORTED
+   info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO,
+      png_ptr->malloc_fn, png_ptr->mem_ptr);
+#else
+   info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO);
+#endif
+   if (info_ptr != NULL)
+      png_info_init_3(&info_ptr, png_sizeof(png_info));
+
+   return (info_ptr);
+}
+
+/* This function frees the memory associated with a single info struct.
+ * Normally, one would use either png_destroy_read_struct() or
+ * png_destroy_write_struct() to free an info struct, but this may be
+ * useful for some applications.
+ */
+void PNGAPI
+png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr)
+{
+   png_infop info_ptr = NULL;
+
+   png_debug(1, "in png_destroy_info_struct\n");
+   if (info_ptr_ptr != NULL)
+      info_ptr = *info_ptr_ptr;
+
+   if (info_ptr != NULL)
+   {
+      png_info_destroy(png_ptr, info_ptr);
+
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn,
+          png_ptr->mem_ptr);
+#else
+      png_destroy_struct((png_voidp)info_ptr);
+#endif
+      *info_ptr_ptr = NULL;
+   }
+}
+
+/* Initialize the info structure.  This is now an internal function (0.89)
+ * and applications using it are urged to use png_create_info_struct()
+ * instead.
+ */
+#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
+#undef png_info_init
+void PNGAPI
+png_info_init(png_infop info_ptr)
+{
+   /* We only come here via pre-1.0.12-compiled applications */
+   png_info_init_3(&info_ptr, 0);
+}
+#endif
+
+void PNGAPI
+png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size)
+{
+   png_infop info_ptr = *ptr_ptr;
+
+   png_debug(1, "in png_info_init_3\n");
+
+   if(png_sizeof(png_info) > png_info_struct_size)
+     {
+       png_destroy_struct(info_ptr);
+       info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO);
+       *ptr_ptr = info_ptr;
+     }
+
+   /* set everything to 0 */
+   png_memset(info_ptr, 0, png_sizeof (png_info));
+}
+
+#ifdef PNG_FREE_ME_SUPPORTED
+void PNGAPI
+png_data_freer(png_structp png_ptr, png_infop info_ptr,
+   int freer, png_uint_32 mask)
+{
+   png_debug(1, "in png_data_freer\n");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+   if(freer == PNG_DESTROY_WILL_FREE_DATA)
+      info_ptr->free_me |= mask;
+   else if(freer == PNG_USER_WILL_FREE_DATA)
+      info_ptr->free_me &= ~mask;
+   else
+      png_warning(png_ptr,
+         "Unknown freer parameter in png_data_freer.");
+}
+#endif
+
+void PNGAPI
+png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask,
+   int num)
+{
+   png_debug(1, "in png_free_data\n");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+#if defined(PNG_TEXT_SUPPORTED)
+/* free text item num or (if num == -1) all text items */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_TEXT) & info_ptr->free_me)
+#else
+if (mask & PNG_FREE_TEXT)
+#endif
+{
+   if (num != -1)
+   {
+     if (info_ptr->text && info_ptr->text[num].key)
+     {
+         png_free(png_ptr, info_ptr->text[num].key);
+         info_ptr->text[num].key = NULL;
+     }
+   }
+   else
+   {
+       int i;
+       for (i = 0; i < info_ptr->num_text; i++)
+           png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i);
+       png_free(png_ptr, info_ptr->text);
+       info_ptr->text = NULL;
+       info_ptr->num_text=0;
+   }
+}
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED)
+/* free any tRNS entry */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_TRNS) & info_ptr->free_me)
+#else
+if ((mask & PNG_FREE_TRNS) && (png_ptr->flags & PNG_FLAG_FREE_TRNS))
+#endif
+{
+    png_free(png_ptr, info_ptr->trans);
+    info_ptr->valid &= ~PNG_INFO_tRNS;
+#ifndef PNG_FREE_ME_SUPPORTED
+    png_ptr->flags &= ~PNG_FLAG_FREE_TRNS;
+#endif
+    info_ptr->trans = NULL;
+}
+#endif
+
+#if defined(PNG_sCAL_SUPPORTED)
+/* free any sCAL entry */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_SCAL) & info_ptr->free_me)
+#else
+if (mask & PNG_FREE_SCAL)
+#endif
+{
+#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED)
+    png_free(png_ptr, info_ptr->scal_s_width);
+    png_free(png_ptr, info_ptr->scal_s_height);
+    info_ptr->scal_s_width = NULL;
+    info_ptr->scal_s_height = NULL;
+#endif
+    info_ptr->valid &= ~PNG_INFO_sCAL;
+}
+#endif
+
+#if defined(PNG_pCAL_SUPPORTED)
+/* free any pCAL entry */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_PCAL) & info_ptr->free_me)
+#else
+if (mask & PNG_FREE_PCAL)
+#endif
+{
+    png_free(png_ptr, info_ptr->pcal_purpose);
+    png_free(png_ptr, info_ptr->pcal_units);
+    info_ptr->pcal_purpose = NULL;
+    info_ptr->pcal_units = NULL;
+    if (info_ptr->pcal_params != NULL)
+    {
+        int i;
+        for (i = 0; i < (int)info_ptr->pcal_nparams; i++)
+        {
+          png_free(png_ptr, info_ptr->pcal_params[i]);
+          info_ptr->pcal_params[i]=NULL;
+        }
+        png_free(png_ptr, info_ptr->pcal_params);
+        info_ptr->pcal_params = NULL;
+    }
+    info_ptr->valid &= ~PNG_INFO_pCAL;
+}
+#endif
+
+#if defined(PNG_iCCP_SUPPORTED)
+/* free any iCCP entry */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_ICCP) & info_ptr->free_me)
+#else
+if (mask & PNG_FREE_ICCP)
+#endif
+{
+    png_free(png_ptr, info_ptr->iccp_name);
+    png_free(png_ptr, info_ptr->iccp_profile);
+    info_ptr->iccp_name = NULL;
+    info_ptr->iccp_profile = NULL;
+    info_ptr->valid &= ~PNG_INFO_iCCP;
+}
+#endif
+
+#if defined(PNG_sPLT_SUPPORTED)
+/* free a given sPLT entry, or (if num == -1) all sPLT entries */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_SPLT) & info_ptr->free_me)
+#else
+if (mask & PNG_FREE_SPLT)
+#endif
+{
+   if (num != -1)
+   {
+      if(info_ptr->splt_palettes)
+      {
+          png_free(png_ptr, info_ptr->splt_palettes[num].name);
+          png_free(png_ptr, info_ptr->splt_palettes[num].entries);
+          info_ptr->splt_palettes[num].name = NULL;
+          info_ptr->splt_palettes[num].entries = NULL;
+      }
+   }
+   else
+   {
+       if(info_ptr->splt_palettes_num)
+       {
+         int i;
+         for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
+            png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, i);
+
+         png_free(png_ptr, info_ptr->splt_palettes);
+         info_ptr->splt_palettes = NULL;
+         info_ptr->splt_palettes_num = 0;
+       }
+       info_ptr->valid &= ~PNG_INFO_sPLT;
+   }
+}
+#endif
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_UNKN) & info_ptr->free_me)
+#else
+if (mask & PNG_FREE_UNKN)
+#endif
+{
+   if (num != -1)
+   {
+       if(info_ptr->unknown_chunks)
+       {
+          png_free(png_ptr, info_ptr->unknown_chunks[num].data);
+          info_ptr->unknown_chunks[num].data = NULL;
+       }
+   }
+   else
+   {
+       int i;
+
+       if(info_ptr->unknown_chunks_num)
+       {
+         for (i = 0; i < (int)info_ptr->unknown_chunks_num; i++)
+            png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, i);
+
+         png_free(png_ptr, info_ptr->unknown_chunks);
+         info_ptr->unknown_chunks = NULL;
+         info_ptr->unknown_chunks_num = 0;
+       }
+   }
+}
+#endif
+
+#if defined(PNG_hIST_SUPPORTED)
+/* free any hIST entry */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_HIST)  & info_ptr->free_me)
+#else
+if ((mask & PNG_FREE_HIST) && (png_ptr->flags & PNG_FLAG_FREE_HIST))
+#endif
+{
+    png_free(png_ptr, info_ptr->hist);
+    info_ptr->hist = NULL;
+    info_ptr->valid &= ~PNG_INFO_hIST;
+#ifndef PNG_FREE_ME_SUPPORTED
+    png_ptr->flags &= ~PNG_FLAG_FREE_HIST;
+#endif
+}
+#endif
+
+/* free any PLTE entry that was internally allocated */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_PLTE) & info_ptr->free_me)
+#else
+if ((mask & PNG_FREE_PLTE) && (png_ptr->flags & PNG_FLAG_FREE_PLTE))
+#endif
+{
+    png_zfree(png_ptr, info_ptr->palette);
+    info_ptr->palette = NULL;
+    info_ptr->valid &= ~PNG_INFO_PLTE;
+#ifndef PNG_FREE_ME_SUPPORTED
+    png_ptr->flags &= ~PNG_FLAG_FREE_PLTE;
+#endif
+    info_ptr->num_palette = 0;
+}
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+/* free any image bits attached to the info structure */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_ROWS) & info_ptr->free_me)
+#else
+if (mask & PNG_FREE_ROWS)
+#endif
+{
+    if(info_ptr->row_pointers)
+    {
+       int row;
+       for (row = 0; row < (int)info_ptr->height; row++)
+       {
+          png_free(png_ptr, info_ptr->row_pointers[row]);
+          info_ptr->row_pointers[row]=NULL;
+       }
+       png_free(png_ptr, info_ptr->row_pointers);
+       info_ptr->row_pointers=NULL;
+    }
+    info_ptr->valid &= ~PNG_INFO_IDAT;
+}
+#endif
+
+#ifdef PNG_FREE_ME_SUPPORTED
+   if(num == -1)
+     info_ptr->free_me &= ~mask;
+   else
+     info_ptr->free_me &= ~(mask & ~PNG_FREE_MUL);
+#endif
+}
+
+/* This is an internal routine to free any memory that the info struct is
+ * pointing to before re-using it or freeing the struct itself.  Recall
+ * that png_free() checks for NULL pointers for us.
+ */
+void /* PRIVATE */
+png_info_destroy(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_info_destroy\n");
+
+   png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+   if (png_ptr->num_chunk_list)
+   {
+       png_free(png_ptr, png_ptr->chunk_list);
+       png_ptr->chunk_list=NULL;
+       png_ptr->num_chunk_list=0;
+   }
+#endif
+
+   png_info_init_3(&info_ptr, png_sizeof(png_info));
+}
+
+/* This function returns a pointer to the io_ptr associated with the user
+ * functions.  The application should free any memory associated with this
+ * pointer before png_write_destroy() or png_read_destroy() are called.
+ */
+png_voidp PNGAPI
+png_get_io_ptr(png_structp png_ptr)
+{
+   return (png_ptr->io_ptr);
+}
+
+#if !defined(PNG_NO_STDIO)
+/* Initialize the default input/output functions for the PNG file.  If you
+ * use your own read or write routines, you can call either png_set_read_fn()
+ * or png_set_write_fn() instead of png_init_io().  If you have defined
+ * PNG_NO_STDIO, you must use a function of your own because "FILE *" isn't
+ * necessarily available.
+ */
+void PNGAPI
+png_init_io(png_structp png_ptr, png_FILE_p fp)
+{
+   png_debug(1, "in png_init_io\n");
+   png_ptr->io_ptr = (png_voidp)fp;
+}
+#endif
+
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+/* Convert the supplied time into an RFC 1123 string suitable for use in
+ * a "Creation Time" or other text-based time string.
+ */
+png_charp PNGAPI
+png_convert_to_rfc1123(png_structp png_ptr, png_timep ptime)
+{
+   static PNG_CONST char short_months[12][4] =
+        {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+   if (png_ptr->time_buffer == NULL)
+   {
+      png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29*
+         png_sizeof(char)));
+   }
+
+#if defined(_WIN32_WCE)
+   {
+      wchar_t time_buf[29];
+      wsprintf(time_buf, TEXT("%d %S %d %02d:%02d:%02d +0000"),
+          ptime->day % 32, short_months[(ptime->month - 1) % 12],
+        ptime->year, ptime->hour % 24, ptime->minute % 60,
+          ptime->second % 61);
+      WideCharToMultiByte(CP_ACP, 0, time_buf, -1, png_ptr->time_buffer, 29,
+          NULL, NULL);
+   }
+#else
+#ifdef USE_FAR_KEYWORD
+   {
+      char near_time_buf[29];
+      sprintf(near_time_buf, "%d %s %d %02d:%02d:%02d +0000",
+          ptime->day % 32, short_months[(ptime->month - 1) % 12],
+          ptime->year, ptime->hour % 24, ptime->minute % 60,
+          ptime->second % 61);
+      png_memcpy(png_ptr->time_buffer, near_time_buf,
+          29*png_sizeof(char));
+   }
+#else
+   sprintf(png_ptr->time_buffer, "%d %s %d %02d:%02d:%02d +0000",
+       ptime->day % 32, short_months[(ptime->month - 1) % 12],
+       ptime->year, ptime->hour % 24, ptime->minute % 60,
+       ptime->second % 61);
+#endif
+#endif /* _WIN32_WCE */
+   return ((png_charp)png_ptr->time_buffer);
+}
+#endif /* PNG_TIME_RFC1123_SUPPORTED */
+
+#if 0
+/* Signature string for a PNG file. */
+png_bytep PNGAPI
+png_sig_bytes(void)
+{
+   return ((png_bytep)"\211\120\116\107\015\012\032\012");
+}
+#endif
+
+png_charp PNGAPI
+png_get_copyright(png_structp png_ptr)
+{
+   if (&png_ptr != NULL)  /* silence compiler warning about unused png_ptr */
+   return ((png_charp) "\n libpng version 1.2.8 - December 3, 2004\n\
+   Copyright (c) 1998-2004 Glenn Randers-Pehrson\n\
+   Copyright (c) 1996-1997 Andreas Dilger\n\
+   Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.\n");
+   return ((png_charp) "");
+}
+
+/* The following return the library version as a short string in the
+ * format 1.0.0 through 99.99.99zz.  To get the version of *.h files
+ * used with your application, print out PNG_LIBPNG_VER_STRING, which
+ * is defined in png.h.
+ * Note: now there is no difference between png_get_libpng_ver() and
+ * png_get_header_ver().  Due to the version_nn_nn_nn typedef guard,
+ * it is guaranteed that png.c uses the correct version of png.h.
+ */
+png_charp PNGAPI
+png_get_libpng_ver(png_structp png_ptr)
+{
+   /* Version of *.c files used when building libpng */
+   if (&png_ptr != NULL)  /* silence compiler warning about unused png_ptr */
+      return ((png_charp) PNG_LIBPNG_VER_STRING);
+   return ((png_charp) "");
+}
+
+png_charp PNGAPI
+png_get_header_ver(png_structp png_ptr)
+{
+   /* Version of *.h files used when building libpng */
+   if (&png_ptr != NULL)  /* silence compiler warning about unused png_ptr */
+      return ((png_charp) PNG_LIBPNG_VER_STRING);
+   return ((png_charp) "");
+}
+
+png_charp PNGAPI
+png_get_header_version(png_structp png_ptr)
+{
+   /* Returns longer string containing both version and date */
+   if (&png_ptr != NULL)  /* silence compiler warning about unused png_ptr */
+      return ((png_charp) PNG_HEADER_VERSION_STRING);
+   return ((png_charp) "");
+}
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+int PNGAPI
+png_handle_as_unknown(png_structp png_ptr, png_bytep chunk_name)
+{
+   /* check chunk_name and return "keep" value if it's on the list, else 0 */
+   int i;
+   png_bytep p;
+   if((png_ptr == NULL && chunk_name == NULL) || png_ptr->num_chunk_list<=0)
+      return 0;
+   p=png_ptr->chunk_list+png_ptr->num_chunk_list*5-5;
+   for (i = png_ptr->num_chunk_list; i; i--, p-=5)
+      if (!png_memcmp(chunk_name, p, 4))
+        return ((int)*(p+4));
+   return 0;
+}
+#endif
+
+/* This function, added to libpng-1.0.6g, is untested. */
+int PNGAPI
+png_reset_zstream(png_structp png_ptr)
+{
+   return (inflateReset(&png_ptr->zstream));
+}
+
+/* This function was added to libpng-1.0.7 */
+png_uint_32 PNGAPI
+png_access_version_number(void)
+{
+   /* Version of *.c files used when building libpng */
+   return((png_uint_32) PNG_LIBPNG_VER);
+}
+
+
+#if !defined(PNG_1_0_X)
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+    /* GRR:  could add this:   && defined(PNG_MMX_CODE_SUPPORTED) */
+/* this INTERNAL function was added to libpng 1.2.0 */
+void /* PRIVATE */
+png_init_mmx_flags (png_structp png_ptr)
+{
+    png_ptr->mmx_rowbytes_threshold = 0;
+    png_ptr->mmx_bitdepth_threshold = 0;
+
+#  if (defined(PNG_USE_PNGVCRD) || defined(PNG_USE_PNGGCCRD))
+
+    png_ptr->asm_flags |= PNG_ASM_FLAG_MMX_SUPPORT_COMPILED;
+
+    if (png_mmx_support() > 0) {
+        png_ptr->asm_flags |= PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU
+#    ifdef PNG_HAVE_ASSEMBLER_COMBINE_ROW
+                              | PNG_ASM_FLAG_MMX_READ_COMBINE_ROW
+#    endif
+#    ifdef PNG_HAVE_ASSEMBLER_READ_INTERLACE
+                              | PNG_ASM_FLAG_MMX_READ_INTERLACE
+#    endif
+#    ifndef PNG_HAVE_ASSEMBLER_READ_FILTER_ROW
+                              ;
+#    else
+                              | PNG_ASM_FLAG_MMX_READ_FILTER_SUB
+                              | PNG_ASM_FLAG_MMX_READ_FILTER_UP
+                              | PNG_ASM_FLAG_MMX_READ_FILTER_AVG
+                              | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ;
+
+        png_ptr->mmx_rowbytes_threshold = PNG_MMX_ROWBYTES_THRESHOLD_DEFAULT;
+        png_ptr->mmx_bitdepth_threshold = PNG_MMX_BITDEPTH_THRESHOLD_DEFAULT;
+#    endif
+    } else {
+        png_ptr->asm_flags &= ~( PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU
+                               | PNG_MMX_READ_FLAGS
+                               | PNG_MMX_WRITE_FLAGS );
+    }
+
+#  else /* !((PNGVCRD || PNGGCCRD) && PNG_ASSEMBLER_CODE_SUPPORTED)) */
+
+    /* clear all MMX flags; no support is compiled in */
+    png_ptr->asm_flags &= ~( PNG_MMX_FLAGS );
+
+#  endif /* ?(PNGVCRD || PNGGCCRD) */
+}
+
+#endif /* !(PNG_ASSEMBLER_CODE_SUPPORTED) */
+
+/* this function was added to libpng 1.2.0 */
+#if !defined(PNG_USE_PNGGCCRD) && \
+    !(defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_USE_PNGVCRD))
+int PNGAPI
+png_mmx_support(void)
+{
+    return -1;
+}
+#endif
+#endif /* PNG_1_0_X */
+
+#ifdef PNG_SIZE_T
+/* Added at libpng version 1.2.6 */
+   PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size));
+png_size_t PNGAPI
+png_convert_size(size_t size)
+{
+  if (size > (png_size_t)-1)
+     PNG_ABORT();  /* We haven't got access to png_ptr, so no png_error() */
+  return ((png_size_t)size);
+}
+#endif /* PNG_SIZE_T */
diff --git a/Utilities/GDAL/frmts/png/libpng/png.h b/Utilities/GDAL/frmts/png/libpng/png.h
new file mode 100644
index 0000000000..e87a3011cd
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/png.h
@@ -0,0 +1,3419 @@
+/* png.h - header file for PNG reference library
+ *
+ * libpng version 1.2.8 - December 3, 2004
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * Authors and maintainers:
+ *  libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat
+ *  libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger
+ *  libpng versions 0.97, January 1998, through 1.2.8 - December 3, 2004: Glenn
+ *  See also "Contributing Authors", below.
+ *
+ * Note about libpng version numbers:
+ *
+ *    Due to various miscommunications, unforeseen code incompatibilities
+ *    and occasional factors outside the authors' control, version numbering
+ *    on the library has not always been consistent and straightforward.
+ *    The following table summarizes matters since version 0.89c, which was
+ *    the first widely used release:
+ *
+ *    source                 png.h  png.h  shared-lib
+ *    version                string   int  version
+ *    -------                ------ -----  ----------
+ *    0.89c "1.0 beta 3"     0.89      89  1.0.89
+ *    0.90  "1.0 beta 4"     0.90      90  0.90  [should have been 2.0.90]
+ *    0.95  "1.0 beta 5"     0.95      95  0.95  [should have been 2.0.95]
+ *    0.96  "1.0 beta 6"     0.96      96  0.96  [should have been 2.0.96]
+ *    0.97b "1.00.97 beta 7" 1.00.97   97  1.0.1 [should have been 2.0.97]
+ *    0.97c                  0.97      97  2.0.97
+ *    0.98                   0.98      98  2.0.98
+ *    0.99                   0.99      98  2.0.99
+ *    0.99a-m                0.99      99  2.0.99
+ *    1.00                   1.00     100  2.1.0 [100 should be 10000]
+ *    1.0.0      (from here on, the   100  2.1.0 [100 should be 10000]
+ *    1.0.1       png.h string is   10001  2.1.0
+ *    1.0.1a-e    identical to the  10002  from here on, the shared library
+ *    1.0.2       source version)   10002  is 2.V where V is the source code
+ *    1.0.2a-b                      10003  version, except as noted.
+ *    1.0.3                         10003
+ *    1.0.3a-d                      10004
+ *    1.0.4                         10004
+ *    1.0.4a-f                      10005
+ *    1.0.5 (+ 2 patches)           10005
+ *    1.0.5a-d                      10006
+ *    1.0.5e-r                      10100 (not source compatible)
+ *    1.0.5s-v                      10006 (not binary compatible)
+ *    1.0.6 (+ 3 patches)           10006 (still binary incompatible)
+ *    1.0.6d-f                      10007 (still binary incompatible)
+ *    1.0.6g                        10007
+ *    1.0.6h                        10007  10.6h (testing xy.z so-numbering)
+ *    1.0.6i                        10007  10.6i
+ *    1.0.6j                        10007  2.1.0.6j (incompatible with 1.0.0)
+ *    1.0.7beta11-14        DLLNUM  10007  2.1.0.7beta11-14 (binary compatible)
+ *    1.0.7beta15-18           1    10007  2.1.0.7beta15-18 (binary compatible)
+ *    1.0.7rc1-2               1    10007  2.1.0.7rc1-2 (binary compatible)
+ *    1.0.7                    1    10007  (still compatible)
+ *    1.0.8beta1-4             1    10008  2.1.0.8beta1-4
+ *    1.0.8rc1                 1    10008  2.1.0.8rc1
+ *    1.0.8                    1    10008  2.1.0.8
+ *    1.0.9beta1-6             1    10009  2.1.0.9beta1-6
+ *    1.0.9rc1                 1    10009  2.1.0.9rc1
+ *    1.0.9beta7-10            1    10009  2.1.0.9beta7-10
+ *    1.0.9rc2                 1    10009  2.1.0.9rc2
+ *    1.0.9                    1    10009  2.1.0.9
+ *    1.0.10beta1              1    10010  2.1.0.10beta1
+ *    1.0.10rc1                1    10010  2.1.0.10rc1
+ *    1.0.10                   1    10010  2.1.0.10
+ *    1.0.11beta1-3            1    10011  2.1.0.11beta1-3
+ *    1.0.11rc1                1    10011  2.1.0.11rc1
+ *    1.0.11                   1    10011  2.1.0.11
+ *    1.0.12beta1-2            2    10012  2.1.0.12beta1-2
+ *    1.0.12rc1                2    10012  2.1.0.12rc1
+ *    1.0.12                   2    10012  2.1.0.12
+ *    1.1.0a-f                 -    10100  2.1.1.0a-f (branch abandoned)
+ *    1.2.0beta1-2             2    10200  2.1.2.0beta1-2
+ *    1.2.0beta3-5             3    10200  3.1.2.0beta3-5
+ *    1.2.0rc1                 3    10200  3.1.2.0rc1
+ *    1.2.0                    3    10200  3.1.2.0
+ *    1.2.1beta1-4             3    10201  3.1.2.1beta1-4
+ *    1.2.1rc1-2               3    10201  3.1.2.1rc1-2
+ *    1.2.1                    3    10201  3.1.2.1
+ *    1.2.2beta1-6            12    10202  12.so.0.1.2.2beta1-6
+ *    1.0.13beta1             10    10013  10.so.0.1.0.13beta1
+ *    1.0.13rc1               10    10013  10.so.0.1.0.13rc1
+ *    1.2.2rc1                12    10202  12.so.0.1.2.2rc1
+ *    1.0.13                  10    10013  10.so.0.1.0.13
+ *    1.2.2                   12    10202  12.so.0.1.2.2
+ *    1.2.3rc1-6              12    10203  12.so.0.1.2.3rc1-6
+ *    1.2.3                   12    10203  12.so.0.1.2.3
+ *    1.2.4beta1-3            13    10204  12.so.0.1.2.4beta1-3
+ *    1.0.14rc1               13    10014  10.so.0.1.0.14rc1
+ *    1.2.4rc1                13    10204  12.so.0.1.2.4rc1
+ *    1.0.14                  10    10014  10.so.0.1.0.14
+ *    1.2.4                   13    10204  12.so.0.1.2.4
+ *    1.2.5beta1-2            13    10205  12.so.0.1.2.5beta1-2
+ *    1.0.15rc1-3             10    10015  10.so.0.1.0.15rc1-3
+ *    1.2.5rc1-3              13    10205  12.so.0.1.2.5rc1-3
+ *    1.0.15                  10    10015  10.so.0.1.0.15
+ *    1.2.5                   13    10205  12.so.0.1.2.5
+ *    1.2.6beta1-4            13    10206  12.so.0.1.2.6beta1-4
+ *    1.0.16                  10    10016  10.so.0.1.0.16
+ *    1.2.6                   13    10206  12.so.0.1.2.6
+ *    1.2.7beta1-2            13    10207  12.so.0.1.2.7beta1-2
+ *    1.0.17rc1               10    10017  12.so.0.1.0.17rc1
+ *    1.2.7rc1                13    10207  12.so.0.1.2.7rc1
+ *    1.0.17                  10    10017  12.so.0.1.0.17
+ *    1.2.7                   13    10207  12.so.0.1.2.7
+ *    1.2.8beta1-5            13    10208  12.so.0.1.2.8beta1-5
+ *    1.0.18rc1-5             10    10018  12.so.0.1.0.18rc1-5
+ *    1.2.8rc1-5              13    10208  12.so.0.1.2.8rc1-5
+ *    1.0.18                  10    10018  12.so.0.1.0.18
+ *    1.2.8                   13    10208  12.so.0.1.2.8
+ *
+ *    Henceforth the source version will match the shared-library major
+ *    and minor numbers; the shared-library major version number will be
+ *    used for changes in backward compatibility, as it is intended.  The
+ *    PNG_LIBPNG_VER macro, which is not used within libpng but is available
+ *    for applications, is an unsigned integer of the form xyyzz corresponding
+ *    to the source version x.y.z (leading zeros in y and z).  Beta versions
+ *    were given the previous public release number plus a letter, until
+ *    version 1.0.6j; from then on they were given the upcoming public
+ *    release number plus "betaNN" or "rcN".
+ *
+ *    Binary incompatibility exists only when applications make direct access
+ *    to the info_ptr or png_ptr members through png.h, and the compiled
+ *    application is loaded with a different version of the library.
+ *
+ *    DLLNUM will change each time there are forward or backward changes
+ *    in binary compatibility (e.g., when a new feature is added).
+ *
+ * See libpng.txt or libpng.3 for more information.  The PNG specification
+ * is available as a W3C Recommendation and as an ISO Specification,
+ * <http://www.w3.org/TR/2003/REC-PNG-20031110/
+ */
+
+/*
+ * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
+ *
+ * If you modify libpng you may insert additional notices immediately following
+ * this sentence.
+ *
+ * libpng versions 1.2.6, August 15, 2004, through 1.2.8, December 3, 2004, are
+ * Copyright (c) 2004 Glenn Randers-Pehrson, and are
+ * distributed according to the same disclaimer and license as libpng-1.2.5
+ * with the following individual added to the list of Contributing Authors:
+ *
+ *    Cosmin Truta
+ *
+ * libpng versions 1.0.7, July 1, 2000, through 1.2.5, October 3, 2002, are
+ * Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are
+ * distributed according to the same disclaimer and license as libpng-1.0.6
+ * with the following individuals added to the list of Contributing Authors:
+ *
+ *    Simon-Pierre Cadieux
+ *    Eric S. Raymond
+ *    Gilles Vollant
+ *
+ * and with the following additions to the disclaimer:
+ *
+ *    There is no warranty against interference with your enjoyment of the
+ *    library or against infringement.  There is no warranty that our
+ *    efforts or the library will fulfill any of your particular purposes
+ *    or needs.  This library is provided with all faults, and the entire
+ *    risk of satisfactory quality, performance, accuracy, and effort is with
+ *    the user.
+ *
+ * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
+ * Copyright (c) 1998, 1999, 2000 Glenn Randers-Pehrson, and are
+ * distributed according to the same disclaimer and license as libpng-0.96,
+ * with the following individuals added to the list of Contributing Authors:
+ *
+ *    Tom Lane
+ *    Glenn Randers-Pehrson
+ *    Willem van Schaik
+ *
+ * libpng versions 0.89, June 1996, through 0.96, May 1997, are
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Distributed according to the same disclaimer and license as libpng-0.88,
+ * with the following individuals added to the list of Contributing Authors:
+ *
+ *    John Bowler
+ *    Kevin Bracey
+ *    Sam Bushell
+ *    Magnus Holmgren
+ *    Greg Roelofs
+ *    Tom Tanner
+ *
+ * libpng versions 0.5, May 1995, through 0.88, January 1996, are
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ *
+ * For the purposes of this copyright and license, "Contributing Authors"
+ * is defined as the following set of individuals:
+ *
+ *    Andreas Dilger
+ *    Dave Martindale
+ *    Guy Eric Schalnat
+ *    Paul Schmidt
+ *    Tim Wegner
+ *
+ * The PNG Reference Library is supplied "AS IS".  The Contributing Authors
+ * and Group 42, Inc. disclaim all warranties, expressed or implied,
+ * including, without limitation, the warranties of merchantability and of
+ * fitness for any purpose.  The Contributing Authors and Group 42, Inc.
+ * assume no liability for direct, indirect, incidental, special, exemplary,
+ * or consequential damages, which may result from the use of the PNG
+ * Reference Library, even if advised of the possibility of such damage.
+ *
+ * Permission is hereby granted to use, copy, modify, and distribute this
+ * source code, or portions hereof, for any purpose, without fee, subject
+ * to the following restrictions:
+ *
+ * 1. The origin of this source code must not be misrepresented.
+ *
+ * 2. Altered versions must be plainly marked as such and
+ * must not be misrepresented as being the original source.
+ *
+ * 3. This Copyright notice may not be removed or altered from
+ *    any source or altered source distribution.
+ *
+ * The Contributing Authors and Group 42, Inc. specifically permit, without
+ * fee, and encourage the use of this source code as a component to
+ * supporting the PNG file format in commercial products.  If you use this
+ * source code in a product, acknowledgment is not required but would be
+ * appreciated.
+ */
+
+/*
+ * A "png_get_copyright" function is available, for convenient use in "about"
+ * boxes and the like:
+ *
+ * printf("%s",png_get_copyright(NULL));
+ *
+ * Also, the PNG logo (in PNG format, of course) is supplied in the
+ * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31).
+ */
+
+/*
+ * Libpng is OSI Certified Open Source Software.  OSI Certified is a
+ * certification mark of the Open Source Initiative.
+ */
+
+/*
+ * The contributing authors would like to thank all those who helped
+ * with testing, bug fixes, and patience.  This wouldn't have been
+ * possible without all of you.
+ *
+ * Thanks to Frank J. T. Wojcik for helping with the documentation.
+ */
+
+/*
+ * Y2K compliance in libpng:
+ * =========================
+ *
+ *    December 3, 2004
+ *
+ *    Since the PNG Development group is an ad-hoc body, we can't make
+ *    an official declaration.
+ *
+ *    This is your unofficial assurance that libpng from version 0.71 and
+ *    upward through 1.2.8 are Y2K compliant.  It is my belief that earlier
+ *    versions were also Y2K compliant.
+ *
+ *    Libpng only has three year fields.  One is a 2-byte unsigned integer
+ *    that will hold years up to 65535.  The other two hold the date in text
+ *    format, and will hold years up to 9999.
+ *
+ *    The integer is
+ *        "png_uint_16 year" in png_time_struct.
+ *
+ *    The strings are
+ *        "png_charp time_buffer" in png_struct and
+ *        "near_time_buffer", which is a local character string in png.c.
+ *
+ *    There are seven time-related functions:
+ *        png.c: png_convert_to_rfc_1123() in png.c
+ *          (formerly png_convert_to_rfc_1152() in error)
+ *        png_convert_from_struct_tm() in pngwrite.c, called in pngwrite.c
+ *        png_convert_from_time_t() in pngwrite.c
+ *        png_get_tIME() in pngget.c
+ *        png_handle_tIME() in pngrutil.c, called in pngread.c
+ *        png_set_tIME() in pngset.c
+ *        png_write_tIME() in pngwutil.c, called in pngwrite.c
+ *
+ *    All handle dates properly in a Y2K environment.  The
+ *    png_convert_from_time_t() function calls gmtime() to convert from system
+ *    clock time, which returns (year - 1900), which we properly convert to
+ *    the full 4-digit year.  There is a possibility that applications using
+ *    libpng are not passing 4-digit years into the png_convert_to_rfc_1123()
+ *    function, or that they are incorrectly passing only a 2-digit year
+ *    instead of "year - 1900" into the png_convert_from_struct_tm() function,
+ *    but this is not under our control.  The libpng documentation has always
+ *    stated that it works with 4-digit years, and the APIs have been
+ *    documented as such.
+ *
+ *    The tIME chunk itself is also Y2K compliant.  It uses a 2-byte unsigned
+ *    integer to hold the year, and can hold years as large as 65535.
+ *
+ *    zlib, upon which libpng depends, is also Y2K compliant.  It contains
+ *    no date-related code.
+ *
+ *       Glenn Randers-Pehrson
+ *       libpng maintainer
+ *       PNG Development Group
+ */
+
+#ifndef PNG_H
+#define PNG_H
+
+/* This is not the place to learn how to use libpng.  The file libpng.txt
+ * describes how to use libpng, and the file example.c summarizes it
+ * with some code on which to build.  This file is useful for looking
+ * at the actual function definitions and structure components.
+ */
+
+/* Version information for png.h - this should match the version in png.c */
+#define PNG_LIBPNG_VER_STRING "1.2.8"
+#define PNG_HEADER_VERSION_STRING \
+   " libpng version 1.2.8 - December 3, 2004 (header)\n"
+
+#define PNG_LIBPNG_VER_SONUM   0
+#define PNG_LIBPNG_VER_DLLNUM  13
+
+/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
+#define PNG_LIBPNG_VER_MAJOR   1
+#define PNG_LIBPNG_VER_MINOR   2
+#define PNG_LIBPNG_VER_RELEASE 8
+/* This should match the numeric part of the final component of
+ * PNG_LIBPNG_VER_STRING, omitting any leading zero: */
+
+#define PNG_LIBPNG_VER_BUILD  0
+
+/* Release Status */
+#define PNG_LIBPNG_BUILD_ALPHA    1
+#define PNG_LIBPNG_BUILD_BETA     2
+#define PNG_LIBPNG_BUILD_RC       3
+#define PNG_LIBPNG_BUILD_STABLE   4
+#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7
+  
+/* Release-Specific Flags */
+#define PNG_LIBPNG_BUILD_PATCH    8 /* Can be OR'ed with
+                                       PNG_LIBPNG_BUILD_STABLE only */
+#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with
+                                       PNG_LIBPNG_BUILD_SPECIAL */
+#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with
+                                       PNG_LIBPNG_BUILD_PRIVATE */
+
+#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE
+
+/* Careful here.  At one time, Guy wanted to use 082, but that would be octal.
+ * We must not include leading zeros.
+ * Versions 0.7 through 1.0.0 were in the range 0 to 100 here (only
+ * version 1.0.0 was mis-numbered 100 instead of 10000).  From
+ * version 1.0.1 it's    xxyyzz, where x=major, y=minor, z=release */
+#define PNG_LIBPNG_VER 10208 /* 1.2.8 */
+
+#ifndef PNG_VERSION_INFO_ONLY
+/* include the compression library's header */
+#include "zlib.h"
+#endif
+
+/* include all user configurable info, including optional assembler routines */
+#include "pngconf.h"
+
+/*
+ * Added at libpng-1.2.8 */
+/* Ref MSDN: Private as priority over Special
+ * VS_FF_PRIVATEBUILD File *was not* built using standard release
+ * procedures. If this value is given, the StringFileInfo block must
+ * contain a PrivateBuild string. 
+ *
+ * VS_FF_SPECIALBUILD File *was* built by the original company using
+ * standard release procedures but is a variation of the standard
+ * file of the same version number. If this value is given, the
+ * StringFileInfo block must contain a SpecialBuild string. 
+ */
+
+#if defined(PNG_USER_PRIVATEBUILD)
+#  define PNG_LIBPNG_BUILD_TYPE PNG_LIBPNG_BUILD_BASE_TYPE | \
+          PNG_LIBPNG_BUILD_PRIVATE
+#else
+#  if defined(PNG_LIBPNG_SPECIALBUILD)
+#    define PNG_LIBPNG_BUILD_TYPE PNG_LIBPNG_BUILD_BASE_TYPE | \
+            PNG_LIBPNG_BUILD_SPECIAL
+#  else
+#    define PNG_LIBPNG_BUILD_TYPE PNG_LIBPNG_BUILD_BASE_TYPE
+#  endif
+#endif
+
+#ifndef PNG_VERSION_INFO_ONLY
+
+/* Inhibit C++ name-mangling for libpng functions but not for system calls. */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* This file is arranged in several sections.  The first section contains
+ * structure and type definitions.  The second section contains the external
+ * library functions, while the third has the internal library functions,
+ * which applications aren't expected to use directly.
+ */
+
+#ifndef PNG_NO_TYPECAST_NULL
+#define int_p_NULL                (int *)NULL
+#define png_bytep_NULL            (png_bytep)NULL
+#define png_bytepp_NULL           (png_bytepp)NULL
+#define png_doublep_NULL          (png_doublep)NULL
+#define png_error_ptr_NULL        (png_error_ptr)NULL
+#define png_flush_ptr_NULL        (png_flush_ptr)NULL
+#define png_free_ptr_NULL         (png_free_ptr)NULL
+#define png_infopp_NULL           (png_infopp)NULL
+#define png_malloc_ptr_NULL       (png_malloc_ptr)NULL
+#define png_read_status_ptr_NULL  (png_read_status_ptr)NULL
+#define png_rw_ptr_NULL           (png_rw_ptr)NULL
+#define png_structp_NULL          (png_structp)NULL
+#define png_uint_16p_NULL         (png_uint_16p)NULL
+#define png_voidp_NULL            (png_voidp)NULL
+#define png_write_status_ptr_NULL (png_write_status_ptr)NULL
+#else
+#define int_p_NULL                NULL
+#define png_bytep_NULL            NULL
+#define png_bytepp_NULL           NULL
+#define png_doublep_NULL          NULL
+#define png_error_ptr_NULL        NULL
+#define png_flush_ptr_NULL        NULL
+#define png_free_ptr_NULL         NULL
+#define png_infopp_NULL           NULL
+#define png_malloc_ptr_NULL       NULL
+#define png_read_status_ptr_NULL  NULL
+#define png_rw_ptr_NULL           NULL
+#define png_structp_NULL          NULL
+#define png_uint_16p_NULL         NULL
+#define png_voidp_NULL            NULL
+#define png_write_status_ptr_NULL NULL
+#endif
+
+/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */
+#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN)
+/* Version information for C files, stored in png.c.  This had better match
+ * the version above.
+ */
+#ifdef PNG_USE_GLOBAL_ARRAYS
+PNG_EXPORT_VAR (const char) png_libpng_ver[18];
+  /* need room for 99.99.99beta99z */
+#else
+#define png_libpng_ver png_get_header_ver(NULL)
+#endif
+
+#ifdef PNG_USE_GLOBAL_ARRAYS
+/* This was removed in version 1.0.5c */
+/* Structures to facilitate easy interlacing.  See png.c for more details */
+PNG_EXPORT_VAR (const int FARDATA) png_pass_start[7];
+PNG_EXPORT_VAR (const int FARDATA) png_pass_inc[7];
+PNG_EXPORT_VAR (const int FARDATA) png_pass_ystart[7];
+PNG_EXPORT_VAR (const int FARDATA) png_pass_yinc[7];
+PNG_EXPORT_VAR (const int FARDATA) png_pass_mask[7];
+PNG_EXPORT_VAR (const int FARDATA) png_pass_dsp_mask[7];
+#ifdef PNG_HAVE_ASSEMBLER_COMBINE_ROW
+PNG_EXPORT_VAR (const int FARDATA) png_pass_width[7];
+#endif
+/* This isn't currently used.  If you need it, see png.c for more details.
+PNG_EXPORT_VAR (const int FARDATA) png_pass_height[7];
+*/
+#endif
+
+#endif /* PNG_NO_EXTERN */
+
+/* Three color definitions.  The order of the red, green, and blue, (and the
+ * exact size) is not important, although the size of the fields need to
+ * be png_byte or png_uint_16 (as defined below).
+ */
+typedef struct png_color_struct
+{
+   png_byte red;
+   png_byte green;
+   png_byte blue;
+} png_color;
+typedef png_color FAR * png_colorp;
+typedef png_color FAR * FAR * png_colorpp;
+
+typedef struct png_color_16_struct
+{
+   png_byte index;    /* used for palette files */
+   png_uint_16 red;   /* for use in red green blue files */
+   png_uint_16 green;
+   png_uint_16 blue;
+   png_uint_16 gray;  /* for use in grayscale files */
+} png_color_16;
+typedef png_color_16 FAR * png_color_16p;
+typedef png_color_16 FAR * FAR * png_color_16pp;
+
+typedef struct png_color_8_struct
+{
+   png_byte red;   /* for use in red green blue files */
+   png_byte green;
+   png_byte blue;
+   png_byte gray;  /* for use in grayscale files */
+   png_byte alpha; /* for alpha channel files */
+} png_color_8;
+typedef png_color_8 FAR * png_color_8p;
+typedef png_color_8 FAR * FAR * png_color_8pp;
+
+/*
+ * The following two structures are used for the in-core representation
+ * of sPLT chunks.
+ */
+typedef struct png_sPLT_entry_struct
+{
+   png_uint_16 red;
+   png_uint_16 green;
+   png_uint_16 blue;
+   png_uint_16 alpha;
+   png_uint_16 frequency;
+} png_sPLT_entry;
+typedef png_sPLT_entry FAR * png_sPLT_entryp;
+typedef png_sPLT_entry FAR * FAR * png_sPLT_entrypp;
+
+/*  When the depth of the sPLT palette is 8 bits, the color and alpha samples
+ *  occupy the LSB of their respective members, and the MSB of each member
+ *  is zero-filled.  The frequency member always occupies the full 16 bits.
+ */
+
+typedef struct png_sPLT_struct
+{
+   png_charp name;           /* palette name */
+   png_byte depth;           /* depth of palette samples */
+   png_sPLT_entryp entries;  /* palette entries */
+   png_int_32 nentries;      /* number of palette entries */
+} png_sPLT_t;
+typedef png_sPLT_t FAR * png_sPLT_tp;
+typedef png_sPLT_t FAR * FAR * png_sPLT_tpp;
+
+#ifdef PNG_TEXT_SUPPORTED
+/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file,
+ * and whether that contents is compressed or not.  The "key" field
+ * points to a regular zero-terminated C string.  The "text", "lang", and
+ * "lang_key" fields can be regular C strings, empty strings, or NULL pointers.
+ * However, the * structure returned by png_get_text() will always contain
+ * regular zero-terminated C strings (possibly empty), never NULL pointers,
+ * so they can be safely used in printf() and other string-handling functions.
+ */
+typedef struct png_text_struct
+{
+   int  compression;       /* compression value:
+                             -1: tEXt, none
+                              0: zTXt, deflate
+                              1: iTXt, none
+                              2: iTXt, deflate  */
+   png_charp key;          /* keyword, 1-79 character description of "text" */
+   png_charp text;         /* comment, may be an empty string (ie "")
+                              or a NULL pointer */
+   png_size_t text_length; /* length of the text string */
+#ifdef PNG_iTXt_SUPPORTED
+   png_size_t itxt_length; /* length of the itxt string */
+   png_charp lang;         /* language code, 0-79 characters
+                              or a NULL pointer */
+   png_charp lang_key;     /* keyword translated UTF-8 string, 0 or more
+                              chars or a NULL pointer */
+#endif
+} png_text;
+typedef png_text FAR * png_textp;
+typedef png_text FAR * FAR * png_textpp;
+#endif
+
+/* Supported compression types for text in PNG files (tEXt, and zTXt).
+ * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */
+#define PNG_TEXT_COMPRESSION_NONE_WR -3
+#define PNG_TEXT_COMPRESSION_zTXt_WR -2
+#define PNG_TEXT_COMPRESSION_NONE    -1
+#define PNG_TEXT_COMPRESSION_zTXt     0
+#define PNG_ITXT_COMPRESSION_NONE     1
+#define PNG_ITXT_COMPRESSION_zTXt     2
+#define PNG_TEXT_COMPRESSION_LAST     3  /* Not a valid value */
+
+/* png_time is a way to hold the time in an machine independent way.
+ * Two conversions are provided, both from time_t and struct tm.  There
+ * is no portable way to convert to either of these structures, as far
+ * as I know.  If you know of a portable way, send it to me.  As a side
+ * note - PNG has always been Year 2000 compliant!
+ */
+typedef struct png_time_struct
+{
+   png_uint_16 year; /* full year, as in, 1995 */
+   png_byte month;   /* month of year, 1 - 12 */
+   png_byte day;     /* day of month, 1 - 31 */
+   png_byte hour;    /* hour of day, 0 - 23 */
+   png_byte minute;  /* minute of hour, 0 - 59 */
+   png_byte second;  /* second of minute, 0 - 60 (for leap seconds) */
+} png_time;
+typedef png_time FAR * png_timep;
+typedef png_time FAR * FAR * png_timepp;
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+/* png_unknown_chunk is a structure to hold queued chunks for which there is
+ * no specific support.  The idea is that we can use this to queue
+ * up private chunks for output even though the library doesn't actually
+ * know about their semantics.
+ */
+typedef struct png_unknown_chunk_t
+{
+    png_byte name[5];
+    png_byte *data;
+    png_size_t size;
+
+    /* libpng-using applications should NOT directly modify this byte. */
+    png_byte location; /* mode of operation at read time */
+}
+png_unknown_chunk;
+typedef png_unknown_chunk FAR * png_unknown_chunkp;
+typedef png_unknown_chunk FAR * FAR * png_unknown_chunkpp;
+#endif
+
+/* png_info is a structure that holds the information in a PNG file so
+ * that the application can find out the characteristics of the image.
+ * If you are reading the file, this structure will tell you what is
+ * in the PNG file.  If you are writing the file, fill in the information
+ * you want to put into the PNG file, then call png_write_info().
+ * The names chosen should be very close to the PNG specification, so
+ * consult that document for information about the meaning of each field.
+ *
+ * With libpng < 0.95, it was only possible to directly set and read the
+ * the values in the png_info_struct, which meant that the contents and
+ * order of the values had to remain fixed.  With libpng 0.95 and later,
+ * however, there are now functions that abstract the contents of
+ * png_info_struct from the application, so this makes it easier to use
+ * libpng with dynamic libraries, and even makes it possible to use
+ * libraries that don't have all of the libpng ancillary chunk-handing
+ * functionality.
+ *
+ * In any case, the order of the parameters in png_info_struct should NOT
+ * be changed for as long as possible to keep compatibility with applications
+ * that use the old direct-access method with png_info_struct.
+ *
+ * The following members may have allocated storage attached that should be
+ * cleaned up before the structure is discarded: palette, trans, text,
+ * pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile,
+ * splt_palettes, scal_unit, row_pointers, and unknowns.   By default, these
+ * are automatically freed when the info structure is deallocated, if they were
+ * allocated internally by libpng.  This behavior can be changed by means
+ * of the png_data_freer() function.
+ *
+ * More allocation details: all the chunk-reading functions that
+ * change these members go through the corresponding png_set_*
+ * functions.  A function to clear these members is available: see
+ * png_free_data().  The png_set_* functions do not depend on being
+ * able to point info structure members to any of the storage they are
+ * passed (they make their own copies), EXCEPT that the png_set_text
+ * functions use the same storage passed to them in the text_ptr or
+ * itxt_ptr structure argument, and the png_set_rows and png_set_unknowns
+ * functions do not make their own copies.
+ */
+typedef struct png_info_struct
+{
+   /* the following are necessary for every PNG file */
+   png_uint_32 width;       /* width of image in pixels (from IHDR) */
+   png_uint_32 height;      /* height of image in pixels (from IHDR) */
+   png_uint_32 valid;       /* valid chunk data (see PNG_INFO_ below) */
+   png_uint_32 rowbytes;    /* bytes needed to hold an untransformed row */
+   png_colorp palette;      /* array of color values (valid & PNG_INFO_PLTE) */
+   png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */
+   png_uint_16 num_trans;   /* number of transparent palette color (tRNS) */
+   png_byte bit_depth;      /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */
+   png_byte color_type;     /* see PNG_COLOR_TYPE_ below (from IHDR) */
+   /* The following three should have been named *_method not *_type */
+   png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */
+   png_byte filter_type;    /* must be PNG_FILTER_TYPE_BASE (from IHDR) */
+   png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
+
+   /* The following is informational only on read, and not used on writes. */
+   png_byte channels;       /* number of data channels per pixel (1, 2, 3, 4) */
+   png_byte pixel_depth;    /* number of bits per pixel */
+   png_byte spare_byte;     /* to align the data, and for future use */
+   png_byte signature[8];   /* magic bytes read by libpng from start of file */
+
+   /* The rest of the data is optional.  If you are reading, check the
+    * valid field to see if the information in these are valid.  If you
+    * are writing, set the valid field to those chunks you want written,
+    * and initialize the appropriate fields below.
+    */
+
+#if defined(PNG_gAMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED)
+   /* The gAMA chunk describes the gamma characteristics of the system
+    * on which the image was created, normally in the range [1.0, 2.5].
+    * Data is valid if (valid & PNG_INFO_gAMA) is non-zero.
+    */
+   float gamma; /* gamma value of image, if (valid & PNG_INFO_gAMA) */
+#endif
+
+#if defined(PNG_sRGB_SUPPORTED)
+    /* GR-P, 0.96a */
+    /* Data valid if (valid & PNG_INFO_sRGB) non-zero. */
+   png_byte srgb_intent; /* sRGB rendering intent [0, 1, 2, or 3] */
+#endif
+
+#if defined(PNG_TEXT_SUPPORTED)
+   /* The tEXt, and zTXt chunks contain human-readable textual data in
+    * uncompressed, compressed, and optionally compressed forms, respectively.
+    * The data in "text" is an array of pointers to uncompressed,
+    * null-terminated C strings. Each chunk has a keyword that describes the
+    * textual data contained in that chunk.  Keywords are not required to be
+    * unique, and the text string may be empty.  Any number of text chunks may
+    * be in an image.
+    */
+   int num_text; /* number of comments read/to write */
+   int max_text; /* current size of text array */
+   png_textp text; /* array of comments read/to write */
+#endif /* PNG_TEXT_SUPPORTED */
+
+#if defined(PNG_tIME_SUPPORTED)
+   /* The tIME chunk holds the last time the displayed image data was
+    * modified.  See the png_time struct for the contents of this struct.
+    */
+   png_time mod_time;
+#endif
+
+#if defined(PNG_sBIT_SUPPORTED)
+   /* The sBIT chunk specifies the number of significant high-order bits
+    * in the pixel data.  Values are in the range [1, bit_depth], and are
+    * only specified for the channels in the pixel data.  The contents of
+    * the low-order bits is not specified.  Data is valid if
+    * (valid & PNG_INFO_sBIT) is non-zero.
+    */
+   png_color_8 sig_bit; /* significant bits in color channels */
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) || \
+defined(PNG_READ_BACKGROUND_SUPPORTED)
+   /* The tRNS chunk supplies transparency data for paletted images and
+    * other image types that don't need a full alpha channel.  There are
+    * "num_trans" transparency values for a paletted image, stored in the
+    * same order as the palette colors, starting from index 0.  Values
+    * for the data are in the range [0, 255], ranging from fully transparent
+    * to fully opaque, respectively.  For non-paletted images, there is a
+    * single color specified that should be treated as fully transparent.
+    * Data is valid if (valid & PNG_INFO_tRNS) is non-zero.
+    */
+   png_bytep trans; /* transparent values for paletted image */
+   png_color_16 trans_values; /* transparent color for non-palette image */
+#endif
+
+#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   /* The bKGD chunk gives the suggested image background color if the
+    * display program does not have its own background color and the image
+    * is needs to composited onto a background before display.  The colors
+    * in "background" are normally in the same color space/depth as the
+    * pixel data.  Data is valid if (valid & PNG_INFO_bKGD) is non-zero.
+    */
+   png_color_16 background;
+#endif
+
+#if defined(PNG_oFFs_SUPPORTED)
+   /* The oFFs chunk gives the offset in "offset_unit_type" units rightwards
+    * and downwards from the top-left corner of the display, page, or other
+    * application-specific co-ordinate space.  See the PNG_OFFSET_ defines
+    * below for the unit types.  Valid if (valid & PNG_INFO_oFFs) non-zero.
+    */
+   png_int_32 x_offset; /* x offset on page */
+   png_int_32 y_offset; /* y offset on page */
+   png_byte offset_unit_type; /* offset units type */
+#endif
+
+#if defined(PNG_pHYs_SUPPORTED)
+   /* The pHYs chunk gives the physical pixel density of the image for
+    * display or printing in "phys_unit_type" units (see PNG_RESOLUTION_
+    * defines below).  Data is valid if (valid & PNG_INFO_pHYs) is non-zero.
+    */
+   png_uint_32 x_pixels_per_unit; /* horizontal pixel density */
+   png_uint_32 y_pixels_per_unit; /* vertical pixel density */
+   png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */
+#endif
+
+#if defined(PNG_hIST_SUPPORTED)
+   /* The hIST chunk contains the relative frequency or importance of the
+    * various palette entries, so that a viewer can intelligently select a
+    * reduced-color palette, if required.  Data is an array of "num_palette"
+    * values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST)
+    * is non-zero.
+    */
+   png_uint_16p hist;
+#endif
+
+#ifdef PNG_cHRM_SUPPORTED
+   /* The cHRM chunk describes the CIE color characteristics of the monitor
+    * on which the PNG was created.  This data allows the viewer to do gamut
+    * mapping of the input image to ensure that the viewer sees the same
+    * colors in the image as the creator.  Values are in the range
+    * [0.0, 0.8].  Data valid if (valid & PNG_INFO_cHRM) non-zero.
+    */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   float x_white;
+   float y_white;
+   float x_red;
+   float y_red;
+   float x_green;
+   float y_green;
+   float x_blue;
+   float y_blue;
+#endif
+#endif
+
+#if defined(PNG_pCAL_SUPPORTED)
+   /* The pCAL chunk describes a transformation between the stored pixel
+    * values and original physical data values used to create the image.
+    * The integer range [0, 2^bit_depth - 1] maps to the floating-point
+    * range given by [pcal_X0, pcal_X1], and are further transformed by a
+    * (possibly non-linear) transformation function given by "pcal_type"
+    * and "pcal_params" into "pcal_units".  Please see the PNG_EQUATION_
+    * defines below, and the PNG-Group's PNG extensions document for a
+    * complete description of the transformations and how they should be
+    * implemented, and for a description of the ASCII parameter strings.
+    * Data values are valid if (valid & PNG_INFO_pCAL) non-zero.
+    */
+   png_charp pcal_purpose;  /* pCAL chunk description string */
+   png_int_32 pcal_X0;      /* minimum value */
+   png_int_32 pcal_X1;      /* maximum value */
+   png_charp pcal_units;    /* Latin-1 string giving physical units */
+   png_charpp pcal_params;  /* ASCII strings containing parameter values */
+   png_byte pcal_type;      /* equation type (see PNG_EQUATION_ below) */
+   png_byte pcal_nparams;   /* number of parameters given in pcal_params */
+#endif
+
+/* New members added in libpng-1.0.6 */
+#ifdef PNG_FREE_ME_SUPPORTED
+   png_uint_32 free_me;     /* flags items libpng is responsible for freeing */
+#endif
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+   /* storage for unknown chunks that the library doesn't recognize. */
+   png_unknown_chunkp unknown_chunks;
+   png_size_t unknown_chunks_num;
+#endif
+
+#if defined(PNG_iCCP_SUPPORTED)
+   /* iCCP chunk data. */
+   png_charp iccp_name;     /* profile name */
+   png_charp iccp_profile;  /* International Color Consortium profile data */
+                            /* Note to maintainer: should be png_bytep */
+   png_uint_32 iccp_proflen;  /* ICC profile data length */
+   png_byte iccp_compression; /* Always zero */
+#endif
+
+#if defined(PNG_sPLT_SUPPORTED)
+   /* data on sPLT chunks (there may be more than one). */
+   png_sPLT_tp splt_palettes;
+   png_uint_32 splt_palettes_num;
+#endif
+
+#if defined(PNG_sCAL_SUPPORTED)
+   /* The sCAL chunk describes the actual physical dimensions of the
+    * subject matter of the graphic.  The chunk contains a unit specification
+    * a byte value, and two ASCII strings representing floating-point
+    * values.  The values are width and height corresponsing to one pixel
+    * in the image.  This external representation is converted to double
+    * here.  Data values are valid if (valid & PNG_INFO_sCAL) is non-zero.
+    */
+   png_byte scal_unit;         /* unit of physical scale */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   double scal_pixel_width;    /* width of one pixel */
+   double scal_pixel_height;   /* height of one pixel */
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   png_charp scal_s_width;     /* string containing height */
+   png_charp scal_s_height;    /* string containing width */
+#endif
+#endif
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+   /* Memory has been allocated if (valid & PNG_ALLOCATED_INFO_ROWS) non-zero */
+   /* Data valid if (valid & PNG_INFO_IDAT) non-zero */
+   png_bytepp row_pointers;        /* the image bits */
+#endif
+
+#if defined(PNG_FIXED_POINT_SUPPORTED) && defined(PNG_gAMA_SUPPORTED)
+   png_fixed_point int_gamma; /* gamma of image, if (valid & PNG_INFO_gAMA) */
+#endif
+
+#if defined(PNG_cHRM_SUPPORTED) && defined(PNG_FIXED_POINT_SUPPORTED)
+   png_fixed_point int_x_white;
+   png_fixed_point int_y_white;
+   png_fixed_point int_x_red;
+   png_fixed_point int_y_red;
+   png_fixed_point int_x_green;
+   png_fixed_point int_y_green;
+   png_fixed_point int_x_blue;
+   png_fixed_point int_y_blue;
+#endif
+
+} png_info;
+
+typedef png_info FAR * png_infop;
+typedef png_info FAR * FAR * png_infopp;
+
+/* Maximum positive integer used in PNG is (2^31)-1 */
+#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL)
+#define PNG_UINT_32_MAX ((png_uint_32)(-1))
+#define PNG_SIZE_MAX ((png_size_t)(-1))
+/* PNG_MAX_UINT is deprecated; use PNG_UINT_31_MAX instead. */
+#define PNG_MAX_UINT PNG_UINT_31_MAX
+
+/* These describe the color_type field in png_info. */
+/* color type masks */
+#define PNG_COLOR_MASK_PALETTE    1
+#define PNG_COLOR_MASK_COLOR      2
+#define PNG_COLOR_MASK_ALPHA      4
+
+/* color types.  Note that not all combinations are legal */
+#define PNG_COLOR_TYPE_GRAY 0
+#define PNG_COLOR_TYPE_PALETTE  (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
+#define PNG_COLOR_TYPE_RGB        (PNG_COLOR_MASK_COLOR)
+#define PNG_COLOR_TYPE_RGB_ALPHA  (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
+#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA)
+/* aliases */
+#define PNG_COLOR_TYPE_RGBA  PNG_COLOR_TYPE_RGB_ALPHA
+#define PNG_COLOR_TYPE_GA  PNG_COLOR_TYPE_GRAY_ALPHA
+
+/* This is for compression type. PNG 1.0-1.2 only define the single type. */
+#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */
+#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE
+
+/* This is for filter type. PNG 1.0-1.2 only define the single type. */
+#define PNG_FILTER_TYPE_BASE      0 /* Single row per-byte filtering */
+#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */
+#define PNG_FILTER_TYPE_DEFAULT   PNG_FILTER_TYPE_BASE
+
+/* These are for the interlacing type.  These values should NOT be changed. */
+#define PNG_INTERLACE_NONE        0 /* Non-interlaced image */
+#define PNG_INTERLACE_ADAM7       1 /* Adam7 interlacing */
+#define PNG_INTERLACE_LAST        2 /* Not a valid value */
+
+/* These are for the oFFs chunk.  These values should NOT be changed. */
+#define PNG_OFFSET_PIXEL          0 /* Offset in pixels */
+#define PNG_OFFSET_MICROMETER     1 /* Offset in micrometers (1/10^6 meter) */
+#define PNG_OFFSET_LAST           2 /* Not a valid value */
+
+/* These are for the pCAL chunk.  These values should NOT be changed. */
+#define PNG_EQUATION_LINEAR       0 /* Linear transformation */
+#define PNG_EQUATION_BASE_E       1 /* Exponential base e transform */
+#define PNG_EQUATION_ARBITRARY    2 /* Arbitrary base exponential transform */
+#define PNG_EQUATION_HYPERBOLIC   3 /* Hyperbolic sine transformation */
+#define PNG_EQUATION_LAST         4 /* Not a valid value */
+
+/* These are for the sCAL chunk.  These values should NOT be changed. */
+#define PNG_SCALE_UNKNOWN         0 /* unknown unit (image scale) */
+#define PNG_SCALE_METER           1 /* meters per pixel */
+#define PNG_SCALE_RADIAN          2 /* radians per pixel */
+#define PNG_SCALE_LAST            3 /* Not a valid value */
+
+/* These are for the pHYs chunk.  These values should NOT be changed. */
+#define PNG_RESOLUTION_UNKNOWN    0 /* pixels/unknown unit (aspect ratio) */
+#define PNG_RESOLUTION_METER      1 /* pixels/meter */
+#define PNG_RESOLUTION_LAST       2 /* Not a valid value */
+
+/* These are for the sRGB chunk.  These values should NOT be changed. */
+#define PNG_sRGB_INTENT_PERCEPTUAL 0
+#define PNG_sRGB_INTENT_RELATIVE   1
+#define PNG_sRGB_INTENT_SATURATION 2
+#define PNG_sRGB_INTENT_ABSOLUTE   3
+#define PNG_sRGB_INTENT_LAST       4 /* Not a valid value */
+
+/* This is for text chunks */
+#define PNG_KEYWORD_MAX_LENGTH     79
+
+/* Maximum number of entries in PLTE/sPLT/tRNS arrays */
+#define PNG_MAX_PALETTE_LENGTH    256
+
+/* These determine if an ancillary chunk's data has been successfully read
+ * from the PNG header, or if the application has filled in the corresponding
+ * data in the info_struct to be written into the output file.  The values
+ * of the PNG_INFO_<chunk> defines should NOT be changed.
+ */
+#define PNG_INFO_gAMA 0x0001
+#define PNG_INFO_sBIT 0x0002
+#define PNG_INFO_cHRM 0x0004
+#define PNG_INFO_PLTE 0x0008
+#define PNG_INFO_tRNS 0x0010
+#define PNG_INFO_bKGD 0x0020
+#define PNG_INFO_hIST 0x0040
+#define PNG_INFO_pHYs 0x0080
+#define PNG_INFO_oFFs 0x0100
+#define PNG_INFO_tIME 0x0200
+#define PNG_INFO_pCAL 0x0400
+#define PNG_INFO_sRGB 0x0800   /* GR-P, 0.96a */
+#define PNG_INFO_iCCP 0x1000   /* ESR, 1.0.6 */
+#define PNG_INFO_sPLT 0x2000   /* ESR, 1.0.6 */
+#define PNG_INFO_sCAL 0x4000   /* ESR, 1.0.6 */
+#define PNG_INFO_IDAT 0x8000L  /* ESR, 1.0.6 */
+
+/* This is used for the transformation routines, as some of them
+ * change these values for the row.  It also should enable using
+ * the routines for other purposes.
+ */
+typedef struct png_row_info_struct
+{
+   png_uint_32 width; /* width of row */
+   png_uint_32 rowbytes; /* number of bytes in row */
+   png_byte color_type; /* color type of row */
+   png_byte bit_depth; /* bit depth of row */
+   png_byte channels; /* number of channels (1, 2, 3, or 4) */
+   png_byte pixel_depth; /* bits per pixel (depth * channels) */
+} png_row_info;
+
+typedef png_row_info FAR * png_row_infop;
+typedef png_row_info FAR * FAR * png_row_infopp;
+
+/* These are the function types for the I/O functions and for the functions
+ * that allow the user to override the default I/O functions with his or her
+ * own.  The png_error_ptr type should match that of user-supplied warning
+ * and error functions, while the png_rw_ptr type should match that of the
+ * user read/write data functions.
+ */
+typedef struct png_struct_def png_struct;
+typedef png_struct FAR * png_structp;
+
+typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp));
+typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t));
+typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp));
+typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32,
+   int));
+typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32,
+   int));
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, png_infop));
+typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop));
+typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep,
+   png_uint_32, int));
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_LEGACY_SUPPORTED)
+typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp,
+    png_row_infop, png_bytep));
+#endif
+
+#if defined(PNG_USER_CHUNKS_SUPPORTED)
+typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, png_unknown_chunkp));
+#endif
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp));
+#endif
+
+/* Transform masks for the high-level interface */
+#define PNG_TRANSFORM_IDENTITY       0x0000    /* read and write */
+#define PNG_TRANSFORM_STRIP_16       0x0001    /* read only */
+#define PNG_TRANSFORM_STRIP_ALPHA    0x0002    /* read only */
+#define PNG_TRANSFORM_PACKING        0x0004    /* read and write */
+#define PNG_TRANSFORM_PACKSWAP       0x0008    /* read and write */
+#define PNG_TRANSFORM_EXPAND         0x0010    /* read only */
+#define PNG_TRANSFORM_INVERT_MONO    0x0020    /* read and write */
+#define PNG_TRANSFORM_SHIFT          0x0040    /* read and write */
+#define PNG_TRANSFORM_BGR            0x0080    /* read and write */
+#define PNG_TRANSFORM_SWAP_ALPHA     0x0100    /* read and write */
+#define PNG_TRANSFORM_SWAP_ENDIAN    0x0200    /* read and write */
+#define PNG_TRANSFORM_INVERT_ALPHA   0x0400    /* read and write */
+#define PNG_TRANSFORM_STRIP_FILLER   0x0800    /* WRITE only */
+
+/* Flags for MNG supported features */
+#define PNG_FLAG_MNG_EMPTY_PLTE     0x01
+#define PNG_FLAG_MNG_FILTER_64      0x04
+#define PNG_ALL_MNG_FEATURES        0x05
+
+typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_size_t));
+typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp));
+
+/* The structure that holds the information to read and write PNG files.
+ * The only people who need to care about what is inside of this are the
+ * people who will be modifying the library for their own special needs.
+ * It should NOT be accessed directly by an application, except to store
+ * the jmp_buf.
+ */
+
+struct png_struct_def
+{
+#ifdef PNG_SETJMP_SUPPORTED
+   jmp_buf jmpbuf;            /* used in png_error */
+#endif
+   png_error_ptr error_fn;    /* function for printing errors and aborting */
+   png_error_ptr warning_fn;  /* function for printing warnings */
+   png_voidp error_ptr;       /* user supplied struct for error functions */
+   png_rw_ptr write_data_fn;  /* function for writing output data */
+   png_rw_ptr read_data_fn;   /* function for reading input data */
+   png_voidp io_ptr;          /* ptr to application struct for I/O functions */
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
+   png_user_transform_ptr read_user_transform_fn; /* user read transform */
+#endif
+
+#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+   png_user_transform_ptr write_user_transform_fn; /* user write transform */
+#endif
+
+/* These were added in libpng-1.0.2 */
+#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+   png_voidp user_transform_ptr; /* user supplied struct for user transform */
+   png_byte user_transform_depth;    /* bit depth of user transformed pixels */
+   png_byte user_transform_channels; /* channels in user transformed pixels */
+#endif
+#endif
+
+   png_uint_32 mode;          /* tells us where we are in the PNG file */
+   png_uint_32 flags;         /* flags indicating various things to libpng */
+   png_uint_32 transformations; /* which transformations to perform */
+
+   z_stream zstream;          /* pointer to decompression structure (below) */
+   png_bytep zbuf;            /* buffer for zlib */
+   png_size_t zbuf_size;      /* size of zbuf */
+   int zlib_level;            /* holds zlib compression level */
+   int zlib_method;           /* holds zlib compression method */
+   int zlib_window_bits;      /* holds zlib compression window bits */
+   int zlib_mem_level;        /* holds zlib compression memory level */
+   int zlib_strategy;         /* holds zlib compression strategy */
+
+   png_uint_32 width;         /* width of image in pixels */
+   png_uint_32 height;        /* height of image in pixels */
+   png_uint_32 num_rows;      /* number of rows in current pass */
+   png_uint_32 usr_width;     /* width of row at start of write */
+   png_uint_32 rowbytes;      /* size of row in bytes */
+   png_uint_32 irowbytes;     /* size of current interlaced row in bytes */
+   png_uint_32 iwidth;        /* width of current interlaced row in pixels */
+   png_uint_32 row_number;    /* current row in interlace pass */
+   png_bytep prev_row;        /* buffer to save previous (unfiltered) row */
+   png_bytep row_buf;         /* buffer to save current (unfiltered) row */
+   png_bytep sub_row;         /* buffer to save "sub" row when filtering */
+   png_bytep up_row;          /* buffer to save "up" row when filtering */
+   png_bytep avg_row;         /* buffer to save "avg" row when filtering */
+   png_bytep paeth_row;       /* buffer to save "Paeth" row when filtering */
+   png_row_info row_info;     /* used for transformation routines */
+
+   png_uint_32 idat_size;     /* current IDAT size for read */
+   png_uint_32 crc;           /* current chunk CRC value */
+   png_colorp palette;        /* palette from the input file */
+   png_uint_16 num_palette;   /* number of color entries in palette */
+   png_uint_16 num_trans;     /* number of transparency values */
+   png_byte chunk_name[5];    /* null-terminated name of current chunk */
+   png_byte compression;      /* file compression type (always 0) */
+   png_byte filter;           /* file filter type (always 0) */
+   png_byte interlaced;       /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
+   png_byte pass;             /* current interlace pass (0 - 6) */
+   png_byte do_filter;        /* row filter flags (see PNG_FILTER_ below ) */
+   png_byte color_type;       /* color type of file */
+   png_byte bit_depth;        /* bit depth of file */
+   png_byte usr_bit_depth;    /* bit depth of users row */
+   png_byte pixel_depth;      /* number of bits per pixel */
+   png_byte channels;         /* number of channels in file */
+   png_byte usr_channels;     /* channels at start of write */
+   png_byte sig_bytes;        /* magic bytes read/written from start of file */
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+#ifdef PNG_LEGACY_SUPPORTED
+   png_byte filler;           /* filler byte for pixel expansion */
+#else
+   png_uint_16 filler;           /* filler bytes for pixel expansion */
+#endif
+#endif
+
+#if defined(PNG_bKGD_SUPPORTED)
+   png_byte background_gamma_type;
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+   float background_gamma;
+#  endif
+   png_color_16 background;   /* background color in screen gamma space */
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   png_color_16 background_1; /* background normalized to gamma 1.0 */
+#endif
+#endif /* PNG_bKGD_SUPPORTED */
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+   png_flush_ptr output_flush_fn;/* Function for flushing output */
+   png_uint_32 flush_dist;    /* how many rows apart to flush, 0 - no flush */
+   png_uint_32 flush_rows;    /* number of rows written since last flush */
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   int gamma_shift;      /* number of "insignificant" bits 16-bit gamma */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   float gamma;          /* file gamma value */
+   float screen_gamma;   /* screen gamma value (display_exponent) */
+#endif
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   png_bytep gamma_table;     /* gamma table for 8-bit depth files */
+   png_bytep gamma_from_1;    /* converts from 1.0 to screen */
+   png_bytep gamma_to_1;      /* converts from file to 1.0 */
+   png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */
+   png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */
+   png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED)
+   png_color_8 sig_bit;       /* significant bits in each available channel */
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+   png_color_8 shift;         /* shift for significant bit tranformation */
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \
+ || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   png_bytep trans;           /* transparency values for paletted files */
+   png_color_16 trans_values; /* transparency values for non-paletted files */
+#endif
+
+   png_read_status_ptr read_row_fn;   /* called after each row is decoded */
+   png_write_status_ptr write_row_fn; /* called after each row is encoded */
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+   png_progressive_info_ptr info_fn; /* called after header data fully read */
+   png_progressive_row_ptr row_fn;   /* called after each prog. row is decoded */
+   png_progressive_end_ptr end_fn;   /* called after image is complete */
+   png_bytep save_buffer_ptr;        /* current location in save_buffer */
+   png_bytep save_buffer;            /* buffer for previously read data */
+   png_bytep current_buffer_ptr;     /* current location in current_buffer */
+   png_bytep current_buffer;         /* buffer for recently used data */
+   png_uint_32 push_length;          /* size of current input chunk */
+   png_uint_32 skip_length;          /* bytes to skip in input data */
+   png_size_t save_buffer_size;      /* amount of data now in save_buffer */
+   png_size_t save_buffer_max;       /* total size of save_buffer */
+   png_size_t buffer_size;           /* total amount of available input data */
+   png_size_t current_buffer_size;   /* amount of data now in current_buffer */
+   int process_mode;                 /* what push library is currently doing */
+   int cur_palette;                  /* current push library palette index */
+
+#  if defined(PNG_TEXT_SUPPORTED)
+     png_size_t current_text_size;   /* current size of text input data */
+     png_size_t current_text_left;   /* how much text left to read in input */
+     png_charp current_text;         /* current text chunk buffer */
+     png_charp current_text_ptr;     /* current location in current_text */
+#  endif /* PNG_PROGRESSIVE_READ_SUPPORTED && PNG_TEXT_SUPPORTED */
+
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__)
+/* for the Borland special 64K segment handler */
+   png_bytepp offset_table_ptr;
+   png_bytep offset_table;
+   png_uint_16 offset_table_number;
+   png_uint_16 offset_table_count;
+   png_uint_16 offset_table_count_free;
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+   png_bytep palette_lookup;         /* lookup table for dithering */
+   png_bytep dither_index;           /* index translation for palette files */
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED) || defined(PNG_hIST_SUPPORTED)
+   png_uint_16p hist;                /* histogram */
+#endif
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   png_byte heuristic_method;        /* heuristic for row filter selection */
+   png_byte num_prev_filters;        /* number of weights for previous rows */
+   png_bytep prev_filters;           /* filter type(s) of previous row(s) */
+   png_uint_16p filter_weights;      /* weight(s) for previous line(s) */
+   png_uint_16p inv_filter_weights;  /* 1/weight(s) for previous line(s) */
+   png_uint_16p filter_costs;        /* relative filter calculation cost */
+   png_uint_16p inv_filter_costs;    /* 1/relative filter calculation cost */
+#endif
+
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+   png_charp time_buffer;            /* String to hold RFC 1123 time text */
+#endif
+
+/* New members added in libpng-1.0.6 */
+
+#ifdef PNG_FREE_ME_SUPPORTED
+   png_uint_32 free_me;       /* flags items libpng is responsible for freeing */
+#endif
+
+#if defined(PNG_USER_CHUNKS_SUPPORTED)
+   png_voidp user_chunk_ptr;
+   png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */
+#endif
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+   int num_chunk_list;
+   png_bytep chunk_list;
+#endif
+
+/* New members added in libpng-1.0.3 */
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+   png_byte rgb_to_gray_status;
+   /* These were changed from png_byte in libpng-1.0.6 */
+   png_uint_16 rgb_to_gray_red_coeff;
+   png_uint_16 rgb_to_gray_green_coeff;
+   png_uint_16 rgb_to_gray_blue_coeff;
+#endif
+
+/* New member added in libpng-1.0.4 (renamed in 1.0.9) */
+#if defined(PNG_MNG_FEATURES_SUPPORTED) || \
+    defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \
+    defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED)
+/* changed from png_byte to png_uint_32 at version 1.2.0 */
+#ifdef PNG_1_0_X
+   png_byte mng_features_permitted;
+#else
+   png_uint_32 mng_features_permitted;
+#endif /* PNG_1_0_X */
+#endif
+
+/* New member added in libpng-1.0.7 */
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   png_fixed_point int_gamma;
+#endif
+
+/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   png_byte filter_type;
+#endif
+
+#if defined(PNG_1_0_X) || (defined(PNG_DEBUG) && defined(PNG_USE_PNGGCCRD))
+/* New member added in libpng-1.0.10, ifdef'ed out in 1.2.0 */
+   png_uint_32 row_buf_size;
+#endif
+
+/* New members added in libpng-1.2.0 */
+#if !defined(PNG_1_0_X) && defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+   png_byte     mmx_bitdepth_threshold;
+   png_uint_32  mmx_rowbytes_threshold;
+   png_uint_32  asm_flags;
+#endif
+
+/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_voidp mem_ptr;                /* user supplied struct for mem functions */
+   png_malloc_ptr malloc_fn;         /* function for allocating memory */
+   png_free_ptr free_fn;             /* function for freeing memory */
+#endif
+
+/* New member added in libpng-1.0.13 and 1.2.0 */
+   png_bytep big_row_buf;         /* buffer to save current (unfiltered) row */
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+/* The following three members were added at version 1.0.14 and 1.2.4 */
+   png_bytep dither_sort;            /* working sort array */
+   png_bytep index_to_palette;       /* where the original index currently is */
+                                     /* in the palette */
+   png_bytep palette_to_index;       /* which original index points to this */
+                                     /* palette color */
+#endif
+
+/* New members added in libpng-1.0.16 and 1.2.6 */
+   png_byte compression_type;
+
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   png_uint_32 user_width_max;
+   png_uint_32 user_height_max;
+#endif
+
+};
+
+
+/* This triggers a compiler error in png.c, if png.c and png.h
+ * do not agree upon the version number.
+ */
+typedef png_structp version_1_2_8;
+
+typedef png_struct FAR * FAR * png_structpp;
+
+/* Here are the function definitions most commonly used.  This is not
+ * the place to find out how to use libpng.  See libpng.txt for the
+ * full explanation, see example.c for the summary.  This just provides
+ * a simple one line description of the use of each function.
+ */
+
+/* Returns the version number of the library */
+extern PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void));
+
+/* Tell lib we have already handled the first <num_bytes> magic bytes.
+ * Handling more than 8 bytes from the beginning of the file is an error.
+ */
+extern PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr,
+   int num_bytes));
+
+/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a
+ * PNG file.  Returns zero if the supplied bytes match the 8-byte PNG
+ * signature, and non-zero otherwise.  Having num_to_check == 0 or
+ * start > 7 will always fail (ie return non-zero).
+ */
+extern PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start,
+   png_size_t num_to_check));
+
+/* Simple signature checking function.  This is the same as calling
+ * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n).
+ */
+extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num));
+
+/* Allocate and initialize png_ptr struct for reading, and any other memory. */
+extern PNG_EXPORT(png_structp,png_create_read_struct)
+   PNGARG((png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn));
+
+/* Allocate and initialize png_ptr struct for writing, and any other memory */
+extern PNG_EXPORT(png_structp,png_create_write_struct)
+   PNGARG((png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn));
+
+#ifdef PNG_WRITE_SUPPORTED
+extern PNG_EXPORT(png_uint_32,png_get_compression_buffer_size)
+   PNGARG((png_structp png_ptr));
+#endif
+
+#ifdef PNG_WRITE_SUPPORTED
+extern PNG_EXPORT(void,png_set_compression_buffer_size)
+   PNGARG((png_structp png_ptr, png_uint_32 size));
+#endif
+
+/* Reset the compression stream */
+extern PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr));
+
+/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */
+#ifdef PNG_USER_MEM_SUPPORTED
+extern PNG_EXPORT(png_structp,png_create_read_struct_2)
+   PNGARG((png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+   png_malloc_ptr malloc_fn, png_free_ptr free_fn));
+extern PNG_EXPORT(png_structp,png_create_write_struct_2)
+   PNGARG((png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+   png_malloc_ptr malloc_fn, png_free_ptr free_fn));
+#endif
+
+/* Write a PNG chunk - size, type, (optional) data, CRC. */
+extern PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr,
+   png_bytep chunk_name, png_bytep data, png_size_t length));
+
+/* Write the start of a PNG chunk - length and chunk name. */
+extern PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr,
+   png_bytep chunk_name, png_uint_32 length));
+
+/* Write the data of a PNG chunk started with png_write_chunk_start(). */
+extern PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr,
+   png_bytep data, png_size_t length));
+
+/* Finish a chunk started with png_write_chunk_start() (includes CRC). */
+extern PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr));
+
+/* Allocate and initialize the info structure */
+extern PNG_EXPORT(png_infop,png_create_info_struct)
+   PNGARG((png_structp png_ptr));
+
+/* Initialize the info structure (old interface - DEPRECATED) */
+extern PNG_EXPORT(void,png_info_init) PNGARG((png_infop info_ptr));
+#undef png_info_init
+#define png_info_init(info_ptr) png_info_init_3(&info_ptr,\
+    png_sizeof(png_info));
+extern PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr,
+    png_size_t png_info_struct_size));
+
+/* Writes all the PNG information before the image. */
+extern PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+extern PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* read the information before the actual image data. */
+extern PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+#endif
+
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+extern PNG_EXPORT(png_charp,png_convert_to_rfc1123)
+   PNGARG((png_structp png_ptr, png_timep ptime));
+#endif
+
+#if !defined(_WIN32_WCE)
+/* "time.h" functions are not supported on WindowsCE */
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+/* convert from a struct tm to png_time */
+extern PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime,
+   struct tm FAR * ttime));
+
+/* convert from time_t to png_time.  Uses gmtime() */
+extern PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime,
+   time_t ttime));
+#endif /* PNG_WRITE_tIME_SUPPORTED */
+#endif /* _WIN32_WCE */
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */
+extern PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr));
+extern PNG_EXPORT(void,png_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr));
+extern PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr));
+extern PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* Use blue, green, red order for pixels. */
+extern PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+/* Expand the grayscale to 24-bit RGB if necessary. */
+extern PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+/* Reduce RGB to grayscale. */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr,
+   int error_action, double red, double green ));
+#endif
+extern PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr,
+   int error_action, png_fixed_point red, png_fixed_point green ));
+extern PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp
+   png_ptr));
+#endif
+
+extern PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth,
+   png_colorp palette));
+
+#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+extern PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+extern PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+extern PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */
+extern PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr,
+   png_uint_32 filler, int flags));
+/* The values of the PNG_FILLER_ defines should NOT be changed */
+#define PNG_FILLER_BEFORE 0
+#define PNG_FILLER_AFTER 1
+/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */
+#if !defined(PNG_1_0_X)
+extern PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr,
+   png_uint_32 filler, int flags));
+#endif
+#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* Swap bytes in 16-bit depth files. */
+extern PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */
+extern PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+/* Swap packing order of pixels in bytes. */
+extern PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+/* Converts files to legal bit depths. */
+extern PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr,
+   png_color_8p true_bits));
+#endif
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
+    defined(PNG_WRITE_INTERLACING_SUPPORTED)
+/* Have the code handle the interlacing.  Returns the number of passes. */
+extern PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+/* Invert monochrome files */
+extern PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+/* Handle alpha and tRNS by replacing with a background color. */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr,
+   png_color_16p background_color, int background_gamma_code,
+   int need_expand, double background_gamma));
+#endif
+#define PNG_BACKGROUND_GAMMA_UNKNOWN 0
+#define PNG_BACKGROUND_GAMMA_SCREEN  1
+#define PNG_BACKGROUND_GAMMA_FILE    2
+#define PNG_BACKGROUND_GAMMA_UNIQUE  3
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+/* strip the second byte of information from a 16-bit depth file. */
+extern PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+/* Turn on dithering, and reduce the palette to the number of colors available. */
+extern PNG_EXPORT(void,png_set_dither) PNGARG((png_structp png_ptr,
+   png_colorp palette, int num_palette, int maximum_colors,
+   png_uint_16p histogram, int full_dither));
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+/* Handle gamma correction. Screen_gamma=(display_exponent) */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr,
+   double screen_gamma, double default_file_gamma));
+#endif
+#endif
+
+#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \
+    defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED)
+/* Permit or disallow empty PLTE (0: not permitted, 1: permitted) */
+/* Deprecated and will be removed.  Use png_permit_mng_features() instead. */
+extern PNG_EXPORT(void,png_permit_empty_plte) PNGARG((png_structp png_ptr,
+   int empty_plte_permitted));
+#endif
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+/* Set how many lines between output flushes - 0 for no flushing */
+extern PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows));
+/* Flush the current PNG output buffer */
+extern PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr));
+#endif
+
+/* optional update palette with requested transformations */
+extern PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr));
+
+/* optional call to update the users info structure */
+extern PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* read one or more rows of image data. */
+extern PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr,
+   png_bytepp row, png_bytepp display_row, png_uint_32 num_rows));
+#endif
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* read a row of data. */
+extern PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr,
+   png_bytep row,
+   png_bytep display_row));
+#endif
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* read the whole image into memory at once. */
+extern PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr,
+   png_bytepp image));
+#endif
+
+/* write a row of image data */
+extern PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr,
+   png_bytep row));
+
+/* write a few rows of image data */
+extern PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr,
+   png_bytepp row, png_uint_32 num_rows));
+
+/* write the image data */
+extern PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr,
+   png_bytepp image));
+
+/* writes the end of the PNG file. */
+extern PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* read the end of the PNG file. */
+extern PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+#endif
+
+/* free any memory associated with the png_info_struct */
+extern PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr,
+   png_infopp info_ptr_ptr));
+
+/* free any memory associated with the png_struct and the png_info_structs */
+extern PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp
+   png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr));
+
+/* free all memory used by the read (old method - NOT DLL EXPORTED) */
+extern void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_infop end_info_ptr));
+
+/* free any memory associated with the png_struct and the png_info_structs */
+extern PNG_EXPORT(void,png_destroy_write_struct)
+   PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr));
+
+/* free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */
+extern void png_write_destroy PNGARG((png_structp png_ptr));
+
+/* set the libpng method of handling chunk CRC errors */
+extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr,
+   int crit_action, int ancil_action));
+
+/* Values for png_set_crc_action() to say how to handle CRC errors in
+ * ancillary and critical chunks, and whether to use the data contained
+ * therein.  Note that it is impossible to "discard" data in a critical
+ * chunk.  For versions prior to 0.90, the action was always error/quit,
+ * whereas in version 0.90 and later, the action for CRC errors in ancillary
+ * chunks is warn/discard.  These values should NOT be changed.
+ *
+ *      value                       action:critical     action:ancillary
+ */
+#define PNG_CRC_DEFAULT       0  /* error/quit          warn/discard data */
+#define PNG_CRC_ERROR_QUIT    1  /* error/quit          error/quit        */
+#define PNG_CRC_WARN_DISCARD  2  /* (INVALID)           warn/discard data */
+#define PNG_CRC_WARN_USE      3  /* warn/use data       warn/use data     */
+#define PNG_CRC_QUIET_USE     4  /* quiet/use data      quiet/use data    */
+#define PNG_CRC_NO_CHANGE     5  /* use current value   use current value */
+
+/* These functions give the user control over the scan-line filtering in
+ * libpng and the compression methods used by zlib.  These functions are
+ * mainly useful for testing, as the defaults should work with most users.
+ * Those users who are tight on memory or want faster performance at the
+ * expense of compression can modify them.  See the compression library
+ * header file (zlib.h) for an explination of the compression functions.
+ */
+
+/* set the filtering method(s) used by libpng.  Currently, the only valid
+ * value for "method" is 0.
+ */
+extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method,
+   int filters));
+
+/* Flags for png_set_filter() to say which filters to use.  The flags
+ * are chosen so that they don't conflict with real filter types
+ * below, in case they are supplied instead of the #defined constants.
+ * These values should NOT be changed.
+ */
+#define PNG_NO_FILTERS     0x00
+#define PNG_FILTER_NONE    0x08
+#define PNG_FILTER_SUB     0x10
+#define PNG_FILTER_UP      0x20
+#define PNG_FILTER_AVG     0x40
+#define PNG_FILTER_PAETH   0x80
+#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \
+                         PNG_FILTER_AVG | PNG_FILTER_PAETH)
+
+/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now.
+ * These defines should NOT be changed.
+ */
+#define PNG_FILTER_VALUE_NONE  0
+#define PNG_FILTER_VALUE_SUB   1
+#define PNG_FILTER_VALUE_UP    2
+#define PNG_FILTER_VALUE_AVG   3
+#define PNG_FILTER_VALUE_PAETH 4
+#define PNG_FILTER_VALUE_LAST  5
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* EXPERIMENTAL */
+/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_
+ * defines, either the default (minimum-sum-of-absolute-differences), or
+ * the experimental method (weighted-minimum-sum-of-absolute-differences).
+ *
+ * Weights are factors >= 1.0, indicating how important it is to keep the
+ * filter type consistent between rows.  Larger numbers mean the current
+ * filter is that many times as likely to be the same as the "num_weights"
+ * previous filters.  This is cumulative for each previous row with a weight.
+ * There needs to be "num_weights" values in "filter_weights", or it can be
+ * NULL if the weights aren't being specified.  Weights have no influence on
+ * the selection of the first row filter.  Well chosen weights can (in theory)
+ * improve the compression for a given image.
+ *
+ * Costs are factors >= 1.0 indicating the relative decoding costs of a
+ * filter type.  Higher costs indicate more decoding expense, and are
+ * therefore less likely to be selected over a filter with lower computational
+ * costs.  There needs to be a value in "filter_costs" for each valid filter
+ * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't
+ * setting the costs.  Costs try to improve the speed of decompression without
+ * unduly increasing the compressed image size.
+ *
+ * A negative weight or cost indicates the default value is to be used, and
+ * values in the range [0.0, 1.0) indicate the value is to remain unchanged.
+ * The default values for both weights and costs are currently 1.0, but may
+ * change if good general weighting/cost heuristics can be found.  If both
+ * the weights and costs are set to 1.0, this degenerates the WEIGHTED method
+ * to the UNWEIGHTED method, but with added encoding time/computation.
+ */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr,
+   int heuristic_method, int num_weights, png_doublep filter_weights,
+   png_doublep filter_costs));
+#endif
+#endif /*  PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
+
+/* Heuristic used for row filter selection.  These defines should NOT be
+ * changed.
+ */
+#define PNG_FILTER_HEURISTIC_DEFAULT    0  /* Currently "UNWEIGHTED" */
+#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1  /* Used by libpng < 0.95 */
+#define PNG_FILTER_HEURISTIC_WEIGHTED   2  /* Experimental feature */
+#define PNG_FILTER_HEURISTIC_LAST       3  /* Not a valid value */
+
+/* Set the library compression level.  Currently, valid values range from
+ * 0 - 9, corresponding directly to the zlib compression levels 0 - 9
+ * (0 - no compression, 9 - "maximal" compression).  Note that tests have
+ * shown that zlib compression levels 3-6 usually perform as well as level 9
+ * for PNG images, and do considerably fewer caclulations.  In the future,
+ * these values may not correspond directly to the zlib compression levels.
+ */
+extern PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr,
+   int level));
+
+extern PNG_EXPORT(void,png_set_compression_mem_level)
+   PNGARG((png_structp png_ptr, int mem_level));
+
+extern PNG_EXPORT(void,png_set_compression_strategy)
+   PNGARG((png_structp png_ptr, int strategy));
+
+extern PNG_EXPORT(void,png_set_compression_window_bits)
+   PNGARG((png_structp png_ptr, int window_bits));
+
+extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr,
+   int method));
+
+/* These next functions are called for input/output, memory, and error
+ * handling.  They are in the file pngrio.c, pngwio.c, and pngerror.c,
+ * and call standard C I/O routines such as fread(), fwrite(), and
+ * fprintf().  These functions can be made to use other I/O routines
+ * at run time for those applications that need to handle I/O in a
+ * different manner by calling png_set_???_fn().  See libpng.txt for
+ * more information.
+ */
+
+#if !defined(PNG_NO_STDIO)
+/* Initialize the input/output for the PNG file to the default functions. */
+extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp));
+#endif
+
+/* Replace the (error and abort), and warning functions with user
+ * supplied functions.  If no messages are to be printed you must still
+ * write and use replacement functions. The replacement error_fn should
+ * still do a longjmp to the last setjmp location if you are using this
+ * method of error handling.  If error_fn or warning_fn is NULL, the
+ * default function will be used.
+ */
+
+extern PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr,
+   png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn));
+
+/* Return the user pointer associated with the error functions */
+extern PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr));
+
+/* Replace the default data output functions with a user supplied one(s).
+ * If buffered output is not used, then output_flush_fn can be set to NULL.
+ * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time
+ * output_flush_fn will be ignored (and thus can be NULL).
+ */
+extern PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr,
+   png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn));
+
+/* Replace the default data input function with a user supplied one. */
+extern PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr,
+   png_voidp io_ptr, png_rw_ptr read_data_fn));
+
+/* Return the user pointer associated with the I/O functions */
+extern PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr));
+
+extern PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr,
+   png_read_status_ptr read_row_fn));
+
+extern PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr,
+   png_write_status_ptr write_row_fn));
+
+#ifdef PNG_USER_MEM_SUPPORTED
+/* Replace the default memory allocation functions with user supplied one(s). */
+extern PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr,
+   png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn));
+/* Return the user pointer associated with the memory functions */
+extern PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_LEGACY_SUPPORTED)
+extern PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp
+   png_ptr, png_user_transform_ptr read_user_transform_fn));
+#endif
+
+#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_LEGACY_SUPPORTED)
+extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp
+   png_ptr, png_user_transform_ptr write_user_transform_fn));
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_LEGACY_SUPPORTED)
+extern PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp
+   png_ptr, png_voidp user_transform_ptr, int user_transform_depth,
+   int user_transform_channels));
+/* Return the user pointer associated with the user transform functions */
+extern PNG_EXPORT(png_voidp,png_get_user_transform_ptr)
+   PNGARG((png_structp png_ptr));
+#endif
+
+#ifdef PNG_USER_CHUNKS_SUPPORTED
+extern PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr,
+   png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn));
+extern PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp
+   png_ptr));
+#endif
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+/* Sets the function callbacks for the push reader, and a pointer to a
+ * user-defined structure available to the callback functions.
+ */
+extern PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr,
+   png_voidp progressive_ptr,
+   png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn,
+   png_progressive_end_ptr end_fn));
+
+/* returns the user pointer associated with the push read functions */
+extern PNG_EXPORT(png_voidp,png_get_progressive_ptr)
+   PNGARG((png_structp png_ptr));
+
+/* function to be called when data becomes available */
+extern PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_bytep buffer, png_size_t buffer_size));
+
+/* function that combines rows.  Not very much different than the
+ * png_combine_row() call.  Is this even used?????
+ */
+extern PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr,
+   png_bytep old_row, png_bytep new_row));
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr,
+   png_uint_32 size));
+
+#if defined(PNG_1_0_X)
+#  define png_malloc_warn png_malloc
+#else
+/* Added at libpng version 1.2.4 */
+extern PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr,
+   png_uint_32 size));
+#endif
+
+/* frees a pointer allocated by png_malloc() */
+extern PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr));
+
+#if defined(PNG_1_0_X)
+/* Function to allocate memory for zlib. */
+extern PNG_EXPORT(voidpf,png_zalloc) PNGARG((voidpf png_ptr, uInt items,
+   uInt size));
+
+/* Function to free memory for zlib */
+extern PNG_EXPORT(void,png_zfree) PNGARG((voidpf png_ptr, voidpf ptr));
+#endif
+
+/* Free data that was allocated internally */
+extern PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 free_me, int num));
+#ifdef PNG_FREE_ME_SUPPORTED
+/* Reassign responsibility for freeing existing data, whether allocated
+ * by libpng or by the application */
+extern PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int freer, png_uint_32 mask));
+#endif
+/* assignments for png_data_freer */
+#define PNG_DESTROY_WILL_FREE_DATA 1
+#define PNG_SET_WILL_FREE_DATA 1
+#define PNG_USER_WILL_FREE_DATA 2
+/* Flags for png_ptr->free_me and info_ptr->free_me */
+#define PNG_FREE_HIST 0x0008
+#define PNG_FREE_ICCP 0x0010
+#define PNG_FREE_SPLT 0x0020
+#define PNG_FREE_ROWS 0x0040
+#define PNG_FREE_PCAL 0x0080
+#define PNG_FREE_SCAL 0x0100
+#define PNG_FREE_UNKN 0x0200
+#define PNG_FREE_LIST 0x0400
+#define PNG_FREE_PLTE 0x1000
+#define PNG_FREE_TRNS 0x2000
+#define PNG_FREE_TEXT 0x4000
+#define PNG_FREE_ALL  0x7fff
+#define PNG_FREE_MUL  0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */
+
+#ifdef PNG_USER_MEM_SUPPORTED
+extern PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr,
+   png_uint_32 size));
+extern PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr,
+   png_voidp ptr));
+#endif
+
+extern PNG_EXPORT(png_voidp,png_memcpy_check) PNGARG((png_structp png_ptr,
+   png_voidp s1, png_voidp s2, png_uint_32 size));
+
+extern PNG_EXPORT(png_voidp,png_memset_check) PNGARG((png_structp png_ptr,
+   png_voidp s1, int value, png_uint_32 size));
+
+#if defined(USE_FAR_KEYWORD)  /* memory model conversion function */
+extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr,
+   int check));
+#endif /* USE_FAR_KEYWORD */
+
+/* Fatal error in PNG image of libpng - can't continue */
+extern PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr,
+   png_const_charp error_message));
+
+/* The same, but the chunk name is prepended to the error string. */
+extern PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr,
+   png_const_charp error_message));
+
+/* Non-fatal error in libpng.  Can continue, but may have a problem. */
+extern PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr,
+   png_const_charp warning_message));
+
+/* Non-fatal error in libpng, chunk name is prepended to message. */
+extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr,
+   png_const_charp warning_message));
+
+/* The png_set_<chunk> functions are for storing values in the png_info_struct.
+ * Similarly, the png_get_<chunk> calls are used to read values from the
+ * png_info_struct, either storing the parameters in the passed variables, or
+ * setting pointers into the png_info_struct where the data is stored.  The
+ * png_get_<chunk> functions return a non-zero value if the data was available
+ * in info_ptr, or return zero and do not change any of the parameters if the
+ * data was not available.
+ *
+ * These functions should be used instead of directly accessing png_info
+ * to avoid problems with future changes in the size and internal layout of
+ * png_info_struct.
+ */
+/* Returns "flag" if chunk data is valid in info_ptr. */
+extern PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr,
+png_infop info_ptr, png_uint_32 flag));
+
+/* Returns number of bytes needed to hold a transformed row. */
+extern PNG_EXPORT(png_uint_32,png_get_rowbytes) PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+/* Returns row_pointers, which is an array of pointers to scanlines that was
+returned from png_read_png(). */
+extern PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+/* Set row_pointers, which is an array of pointers to scanlines for use
+by png_write_png(). */
+extern PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_bytepp row_pointers));
+#endif
+
+/* Returns number of color channels in image. */
+extern PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+
+#ifdef PNG_EASY_ACCESS_SUPPORTED
+/* Returns image width in pixels. */
+extern PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image height in pixels. */
+extern PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image bit_depth. */
+extern PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image color_type. */
+extern PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image filter_type. */
+extern PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image interlace_type. */
+extern PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image compression_type. */
+extern PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image resolution in pixels per meter, from pHYs chunk data. */
+extern PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns pixel aspect ratio, computed from pHYs chunk data.  */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+#endif
+
+/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */
+extern PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+#endif /* PNG_EASY_ACCESS_SUPPORTED */
+
+/* Returns pointer to signature string read from PNG header */
+extern PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+
+#if defined(PNG_bKGD_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_color_16p *background));
+#endif
+
+#if defined(PNG_bKGD_SUPPORTED)
+extern PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_color_16p background));
+#endif
+
+#if defined(PNG_cHRM_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, double *white_x, double *white_y, double *red_x,
+   double *red_y, double *green_x, double *green_y, double *blue_x,
+   double *blue_y));
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+extern PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point
+   *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y,
+   png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point
+   *int_blue_x, png_fixed_point *int_blue_y));
+#endif
+#endif
+
+#if defined(PNG_cHRM_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, double white_x, double white_y, double red_x,
+   double red_y, double green_x, double green_y, double blue_x, double blue_y));
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y,
+   png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point
+   int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x,
+   png_fixed_point int_blue_y));
+#endif
+#endif
+
+#if defined(PNG_gAMA_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, double *file_gamma));
+#endif
+extern PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_fixed_point *int_file_gamma));
+#endif
+
+#if defined(PNG_gAMA_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, double file_gamma));
+#endif
+extern PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_fixed_point int_file_gamma));
+#endif
+
+#if defined(PNG_hIST_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_16p *hist));
+#endif
+
+#if defined(PNG_hIST_SUPPORTED)
+extern PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_16p hist));
+#endif
+
+extern PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 *width, png_uint_32 *height,
+   int *bit_depth, int *color_type, int *interlace_method,
+   int *compression_method, int *filter_method));
+
+extern PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth,
+   int color_type, int interlace_method, int compression_method,
+   int filter_method));
+
+#if defined(PNG_oFFs_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y,
+   int *unit_type));
+#endif
+
+#if defined(PNG_oFFs_SUPPORTED)
+extern PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y,
+   int unit_type));
+#endif
+
+#if defined(PNG_pCAL_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1,
+   int *type, int *nparams, png_charp *units, png_charpp *params));
+#endif
+
+#if defined(PNG_pCAL_SUPPORTED)
+extern PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1,
+   int type, int nparams, png_charp units, png_charpp params));
+#endif
+
+#if defined(PNG_pHYs_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type));
+#endif
+
+#if defined(PNG_pHYs_SUPPORTED)
+extern PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type));
+#endif
+
+extern PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_colorp *palette, int *num_palette));
+
+extern PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_colorp palette, int num_palette));
+
+#if defined(PNG_sBIT_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_color_8p *sig_bit));
+#endif
+
+#if defined(PNG_sBIT_SUPPORTED)
+extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_color_8p sig_bit));
+#endif
+
+#if defined(PNG_sRGB_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int *intent));
+#endif
+
+#if defined(PNG_sRGB_SUPPORTED)
+extern PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int intent));
+extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int intent));
+#endif
+
+#if defined(PNG_iCCP_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_charpp name, int *compression_type,
+   png_charpp profile, png_uint_32 *proflen));
+   /* Note to maintainer: profile should be png_bytepp */
+#endif
+
+#if defined(PNG_iCCP_SUPPORTED)
+extern PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_charp name, int compression_type,
+   png_charp profile, png_uint_32 proflen));
+   /* Note to maintainer: profile should be png_bytep */
+#endif
+
+#if defined(PNG_sPLT_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_sPLT_tpp entries));
+#endif
+
+#if defined(PNG_sPLT_SUPPORTED)
+extern PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_sPLT_tp entries, int nentries));
+#endif
+
+#if defined(PNG_TEXT_SUPPORTED)
+/* png_get_text also returns the number of text chunks in *num_text */
+extern PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_textp *text_ptr, int *num_text));
+#endif
+
+/*
+ *  Note while png_set_text() will accept a structure whose text,
+ *  language, and  translated keywords are NULL pointers, the structure
+ *  returned by png_get_text will always contain regular
+ *  zero-terminated C strings.  They might be empty strings but
+ *  they will never be NULL pointers.
+ */
+
+#if defined(PNG_TEXT_SUPPORTED)
+extern PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_textp text_ptr, int num_text));
+#endif
+
+#if defined(PNG_tIME_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_timep *mod_time));
+#endif
+
+#if defined(PNG_tIME_SUPPORTED)
+extern PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_timep mod_time));
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_bytep *trans, int *num_trans,
+   png_color_16p *trans_values));
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED)
+extern PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_bytep trans, int num_trans,
+   png_color_16p trans_values));
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED)
+#endif
+
+#if defined(PNG_sCAL_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int *unit, double *width, double *height));
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+extern PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight));
+#endif
+#endif
+#endif /* PNG_sCAL_SUPPORTED */
+
+#if defined(PNG_sCAL_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int unit, double width, double height));
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int unit, png_charp swidth, png_charp sheight));
+#endif
+#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+/* provide a list of chunks and how they are to be handled, if the built-in
+   handling or default unknown chunk handling is not desired.  Any chunks not
+   listed will be handled in the default manner.  The IHDR and IEND chunks
+   must not be listed.
+      keep = 0: follow default behavour
+           = 1: do not keep
+           = 2: keep only if safe-to-copy
+           = 3: keep even if unsafe-to-copy
+*/
+extern PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp
+   png_ptr, int keep, png_bytep chunk_list, int num_chunks));
+extern PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns));
+extern PNG_EXPORT(void, png_set_unknown_chunk_location)
+   PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location));
+extern PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp
+   png_ptr, png_infop info_ptr, png_unknown_chunkpp entries));
+#endif
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep
+   chunk_name));
+#endif
+
+/* Png_free_data() will turn off the "valid" flag for anything it frees.
+   If you need to turn it off for a chunk that your application has freed,
+   you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); */
+extern PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int mask));
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+/* The "params" pointer is currently not used and is for future expansion. */
+extern PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr,
+                        png_infop info_ptr,
+                        int transforms,
+                        png_voidp params));
+extern PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr,
+                        png_infop info_ptr,
+                        int transforms,
+                        png_voidp params));
+#endif
+
+/* Define PNG_DEBUG at compile time for debugging information.  Higher
+ * numbers for PNG_DEBUG mean more debugging information.  This has
+ * only been added since version 0.95 so it is not implemented throughout
+ * libpng yet, but more support will be added as needed.
+ */
+#ifdef PNG_DEBUG
+#if (PNG_DEBUG > 0)
+#if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER)
+#include <crtdbg.h>
+#if (PNG_DEBUG > 1)
+#define png_debug(l,m)  _RPT0(_CRT_WARN,m)
+#define png_debug1(l,m,p1)  _RPT1(_CRT_WARN,m,p1)
+#define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m,p1,p2)
+#endif
+#else /* PNG_DEBUG_FILE || !_MSC_VER */
+#ifndef PNG_DEBUG_FILE
+#define PNG_DEBUG_FILE stderr
+#endif /* PNG_DEBUG_FILE */
+#if (PNG_DEBUG > 1)
+#define png_debug(l,m) \
+{ \
+     int num_tabs=l; \
+     fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \
+       (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \
+}
+#define png_debug1(l,m,p1) \
+{ \
+     int num_tabs=l; \
+     fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \
+       (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \
+}
+#define png_debug2(l,m,p1,p2) \
+{ \
+     int num_tabs=l; \
+     fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \
+       (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \
+}
+#endif /* (PNG_DEBUG > 1) */
+#endif /* _MSC_VER */
+#endif /* (PNG_DEBUG > 0) */
+#endif /* PNG_DEBUG */
+#ifndef png_debug
+#define png_debug(l, m)
+#endif
+#ifndef png_debug1
+#define png_debug1(l, m, p1)
+#endif
+#ifndef png_debug2
+#define png_debug2(l, m, p1, p2)
+#endif
+
+extern PNG_EXPORT(png_bytep,png_sig_bytes) PNGARG((void));
+
+extern PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr));
+extern PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr));
+extern PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr));
+extern PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr));
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp
+   png_ptr, png_uint_32 mng_features_permitted));
+#endif
+
+/* For use in png_set_keep_unknown, added to version 1.2.6 */
+#define PNG_HANDLE_CHUNK_AS_DEFAULT   0
+#define PNG_HANDLE_CHUNK_NEVER        1
+#define PNG_HANDLE_CHUNK_IF_SAFE      2
+#define PNG_HANDLE_CHUNK_ALWAYS       3
+
+/* Added to version 1.2.0 */
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+#define PNG_ASM_FLAG_MMX_SUPPORT_COMPILED  0x01  /* not user-settable */
+#define PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU    0x02  /* not user-settable */
+#define PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  0x04
+#define PNG_ASM_FLAG_MMX_READ_INTERLACE    0x08
+#define PNG_ASM_FLAG_MMX_READ_FILTER_SUB   0x10
+#define PNG_ASM_FLAG_MMX_READ_FILTER_UP    0x20
+#define PNG_ASM_FLAG_MMX_READ_FILTER_AVG   0x40
+#define PNG_ASM_FLAG_MMX_READ_FILTER_PAETH 0x80
+#define PNG_ASM_FLAGS_INITIALIZED          0x80000000  /* not user-settable */
+
+#define PNG_MMX_READ_FLAGS ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
+                           | PNG_ASM_FLAG_MMX_READ_INTERLACE    \
+                           | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
+                           | PNG_ASM_FLAG_MMX_READ_FILTER_UP    \
+                           | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
+                           | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH )
+#define PNG_MMX_WRITE_FLAGS ( 0 )
+
+#define PNG_MMX_FLAGS ( PNG_ASM_FLAG_MMX_SUPPORT_COMPILED \
+                      | PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU   \
+                      | PNG_MMX_READ_FLAGS                \
+                      | PNG_MMX_WRITE_FLAGS )
+
+#define PNG_SELECT_READ   1
+#define PNG_SELECT_WRITE  2
+
+#if !defined(PNG_1_0_X)
+/* pngget.c */
+extern PNG_EXPORT(png_uint_32,png_get_mmx_flagmask)
+   PNGARG((int flag_select, int *compilerID));
+
+/* pngget.c */
+extern PNG_EXPORT(png_uint_32,png_get_asm_flagmask)
+   PNGARG((int flag_select));
+
+/* pngget.c */
+extern PNG_EXPORT(png_uint_32,png_get_asm_flags)
+   PNGARG((png_structp png_ptr));
+
+/* pngget.c */
+extern PNG_EXPORT(png_byte,png_get_mmx_bitdepth_threshold)
+   PNGARG((png_structp png_ptr));
+
+/* pngget.c */
+extern PNG_EXPORT(png_uint_32,png_get_mmx_rowbytes_threshold)
+   PNGARG((png_structp png_ptr));
+
+/* pngset.c */
+extern PNG_EXPORT(void,png_set_asm_flags)
+   PNGARG((png_structp png_ptr, png_uint_32 asm_flags));
+
+/* pngset.c */
+extern PNG_EXPORT(void,png_set_mmx_thresholds)
+   PNGARG((png_structp png_ptr, png_byte mmx_bitdepth_threshold,
+   png_uint_32 mmx_rowbytes_threshold));
+
+#endif /* PNG_1_0_X */
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+
+#if !defined(PNG_1_0_X)
+/* png.c, pnggccrd.c, or pngvcrd.c */
+extern PNG_EXPORT(int,png_mmx_support) PNGARG((void));
+
+/* Strip the prepended error numbers ("#nnn ") from error and warning
+ * messages before passing them to the error or warning handler. */
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+extern PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp
+   png_ptr, png_uint_32 strip_mode));
+#endif
+
+#endif /* PNG_1_0_X */
+
+/* Added at libpng-1.2.6 */
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+extern PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp
+   png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max));
+extern PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp
+   png_ptr));
+extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp
+   png_ptr));
+#endif
+
+/* Maintainer: Put new public prototypes here ^, in libpng.3, and project defs */
+
+#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED
+/* With these routines we avoid an integer divide, which will be slower on
+ * most machines.  However, it does take more operations than the corresponding
+ * divide method, so it may be slower on a few RISC systems.  There are two
+ * shifts (by 8 or 16 bits) and an addition, versus a single integer divide.
+ *
+ * Note that the rounding factors are NOT supposed to be the same!  128 and
+ * 32768 are correct for the NODIV code; 127 and 32767 are correct for the
+ * standard method.
+ *
+ * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ]
+ */
+
+ /* fg and bg should be in `gamma 1.0' space; alpha is the opacity          */
+
+#  define png_composite(composite, fg, alpha, bg)                            \
+     { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) * (png_uint_16)(alpha) \
+                        +        (png_uint_16)(bg)*(png_uint_16)(255 -       \
+                        (png_uint_16)(alpha)) + (png_uint_16)128);           \
+       (composite) = (png_byte)((temp + (temp >> 8)) >> 8); }
+
+#  define png_composite_16(composite, fg, alpha, bg)                         \
+     { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) * (png_uint_32)(alpha) \
+                        + (png_uint_32)(bg)*(png_uint_32)(65535L -           \
+                        (png_uint_32)(alpha)) + (png_uint_32)32768L);        \
+       (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); }
+
+#else  /* standard method using integer division */
+
+#  define png_composite(composite, fg, alpha, bg)                            \
+     (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) +    \
+       (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) +       \
+       (png_uint_16)127) / 255)
+
+#  define png_composite_16(composite, fg, alpha, bg)                         \
+     (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \
+       (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) +      \
+       (png_uint_32)32767) / (png_uint_32)65535L)
+
+#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */
+
+/* These next functions are used internally in the code.  They generally
+ * shouldn't be used unless you are writing code to add or replace some
+ * functionality in libpng.  More information about most functions can
+ * be found in the files where the functions are located.
+ */
+
+#if defined(PNG_INTERNAL)
+
+/* Various modes of operation.  Note that after an init, mode is set to
+ * zero automatically when the structure is created.
+ */
+#define PNG_HAVE_IHDR               0x01
+#define PNG_HAVE_PLTE               0x02
+#define PNG_HAVE_IDAT               0x04
+#define PNG_AFTER_IDAT              0x08
+#define PNG_HAVE_IEND               0x10
+#define PNG_HAVE_gAMA               0x20
+#define PNG_HAVE_cHRM               0x40
+#define PNG_HAVE_sRGB               0x80
+#define PNG_HAVE_CHUNK_HEADER      0x100
+#define PNG_WROTE_tIME             0x200
+#define PNG_WROTE_INFO_BEFORE_PLTE 0x400
+#define PNG_BACKGROUND_IS_GRAY     0x800
+#define PNG_HAVE_PNG_SIGNATURE    0x1000
+
+/* flags for the transformations the PNG library does on the image data */
+#define PNG_BGR                0x0001
+#define PNG_INTERLACE          0x0002
+#define PNG_PACK               0x0004
+#define PNG_SHIFT              0x0008
+#define PNG_SWAP_BYTES         0x0010
+#define PNG_INVERT_MONO        0x0020
+#define PNG_DITHER             0x0040
+#define PNG_BACKGROUND         0x0080
+#define PNG_BACKGROUND_EXPAND  0x0100
+                          /*   0x0200 unused */
+#define PNG_16_TO_8            0x0400
+#define PNG_RGBA               0x0800
+#define PNG_EXPAND             0x1000
+#define PNG_GAMMA              0x2000
+#define PNG_GRAY_TO_RGB        0x4000
+#define PNG_FILLER             0x8000L
+#define PNG_PACKSWAP          0x10000L
+#define PNG_SWAP_ALPHA        0x20000L
+#define PNG_STRIP_ALPHA       0x40000L
+#define PNG_INVERT_ALPHA      0x80000L
+#define PNG_USER_TRANSFORM   0x100000L
+#define PNG_RGB_TO_GRAY_ERR  0x200000L
+#define PNG_RGB_TO_GRAY_WARN 0x400000L
+#define PNG_RGB_TO_GRAY      0x600000L  /* two bits, RGB_TO_GRAY_ERR|WARN */
+                       /*    0x800000L     Unused */
+#define PNG_ADD_ALPHA       0x1000000L  /* Added to libpng-1.2.7 */
+                       /*   0x2000000L  unused */
+                       /*   0x4000000L  unused */
+                       /*   0x8000000L  unused */
+                       /*  0x10000000L  unused */
+                       /*  0x20000000L  unused */
+                       /*  0x40000000L  unused */
+
+/* flags for png_create_struct */
+#define PNG_STRUCT_PNG   0x0001
+#define PNG_STRUCT_INFO  0x0002
+
+/* Scaling factor for filter heuristic weighting calculations */
+#define PNG_WEIGHT_SHIFT 8
+#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT))
+#define PNG_COST_SHIFT 3
+#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT))
+
+/* flags for the png_ptr->flags rather than declaring a byte for each one */
+#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY     0x0001
+#define PNG_FLAG_ZLIB_CUSTOM_LEVEL        0x0002
+#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL    0x0004
+#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS  0x0008
+#define PNG_FLAG_ZLIB_CUSTOM_METHOD       0x0010
+#define PNG_FLAG_ZLIB_FINISHED            0x0020
+#define PNG_FLAG_ROW_INIT                 0x0040
+#define PNG_FLAG_FILLER_AFTER             0x0080
+#define PNG_FLAG_CRC_ANCILLARY_USE        0x0100
+#define PNG_FLAG_CRC_ANCILLARY_NOWARN     0x0200
+#define PNG_FLAG_CRC_CRITICAL_USE         0x0400
+#define PNG_FLAG_CRC_CRITICAL_IGNORE      0x0800
+#define PNG_FLAG_FREE_PLTE                0x1000
+#define PNG_FLAG_FREE_TRNS                0x2000
+#define PNG_FLAG_FREE_HIST                0x4000
+#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS      0x8000L
+#define PNG_FLAG_KEEP_UNSAFE_CHUNKS       0x10000L
+#define PNG_FLAG_LIBRARY_MISMATCH         0x20000L
+#define PNG_FLAG_STRIP_ERROR_NUMBERS      0x40000L
+#define PNG_FLAG_STRIP_ERROR_TEXT         0x80000L
+#define PNG_FLAG_MALLOC_NULL_MEM_OK       0x100000L
+#define PNG_FLAG_ADD_ALPHA                0x200000L  /* Added to libpng-1.2.8 */
+#define PNG_FLAG_STRIP_ALPHA              0x400000L  /* Added to libpng-1.2.8 */
+                                  /*      0x800000L  unused */
+                                  /*     0x1000000L  unused */
+                                  /*     0x2000000L  unused */
+                                  /*     0x4000000L  unused */
+                                  /*     0x8000000L  unused */
+                                  /*    0x10000000L  unused */
+                                  /*    0x20000000L  unused */
+                                  /*    0x40000000L  unused */
+
+#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \
+                                     PNG_FLAG_CRC_ANCILLARY_NOWARN)
+
+#define PNG_FLAG_CRC_CRITICAL_MASK  (PNG_FLAG_CRC_CRITICAL_USE | \
+                                     PNG_FLAG_CRC_CRITICAL_IGNORE)
+
+#define PNG_FLAG_CRC_MASK           (PNG_FLAG_CRC_ANCILLARY_MASK | \
+                                     PNG_FLAG_CRC_CRITICAL_MASK)
+
+/* save typing and make code easier to understand */
+
+#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \
+   abs((int)((c1).green) - (int)((c2).green)) + \
+   abs((int)((c1).blue) - (int)((c2).blue)))
+
+/* Added to libpng-1.2.6 JB */
+#define PNG_ROWBYTES(pixel_bits, width) \
+    ((pixel_bits) >= 8 ? \
+    ((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \
+    (( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) )
+
+/* PNG_OUT_OF_RANGE returns true if value is outside the range
+   ideal-delta..ideal+delta.  Each argument is evaluated twice.
+   "ideal" and "delta" should be constants, normally simple
+   integers, "value" a variable. Added to libpng-1.2.6 JB */
+#define PNG_OUT_OF_RANGE(value, ideal, delta) \
+        ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) )
+
+/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */
+#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN)
+/* place to hold the signature string for a PNG file. */
+#ifdef PNG_USE_GLOBAL_ARRAYS
+   PNG_EXPORT_VAR (const png_byte FARDATA) png_sig[8];
+#else
+#define png_sig png_sig_bytes(NULL)
+#endif
+#endif /* PNG_NO_EXTERN */
+
+/* Constant strings for known chunk types.  If you need to add a chunk,
+ * define the name here, and add an invocation of the macro in png.c and
+ * wherever it's needed.
+ */
+#define PNG_IHDR const png_byte png_IHDR[5] = { 73,  72,  68,  82, '\0'}
+#define PNG_IDAT const png_byte png_IDAT[5] = { 73,  68,  65,  84, '\0'}
+#define PNG_IEND const png_byte png_IEND[5] = { 73,  69,  78,  68, '\0'}
+#define PNG_PLTE const png_byte png_PLTE[5] = { 80,  76,  84,  69, '\0'}
+#define PNG_bKGD const png_byte png_bKGD[5] = { 98,  75,  71,  68, '\0'}
+#define PNG_cHRM const png_byte png_cHRM[5] = { 99,  72,  82,  77, '\0'}
+#define PNG_gAMA const png_byte png_gAMA[5] = {103,  65,  77,  65, '\0'}
+#define PNG_hIST const png_byte png_hIST[5] = {104,  73,  83,  84, '\0'}
+#define PNG_iCCP const png_byte png_iCCP[5] = {105,  67,  67,  80, '\0'}
+#define PNG_iTXt const png_byte png_iTXt[5] = {105,  84,  88, 116, '\0'}
+#define PNG_oFFs const png_byte png_oFFs[5] = {111,  70,  70, 115, '\0'}
+#define PNG_pCAL const png_byte png_pCAL[5] = {112,  67,  65,  76, '\0'}
+#define PNG_sCAL const png_byte png_sCAL[5] = {115,  67,  65,  76, '\0'}
+#define PNG_pHYs const png_byte png_pHYs[5] = {112,  72,  89, 115, '\0'}
+#define PNG_sBIT const png_byte png_sBIT[5] = {115,  66,  73,  84, '\0'}
+#define PNG_sPLT const png_byte png_sPLT[5] = {115,  80,  76,  84, '\0'}
+#define PNG_sRGB const png_byte png_sRGB[5] = {115,  82,  71,  66, '\0'}
+#define PNG_tEXt const png_byte png_tEXt[5] = {116,  69,  88, 116, '\0'}
+#define PNG_tIME const png_byte png_tIME[5] = {116,  73,  77,  69, '\0'}
+#define PNG_tRNS const png_byte png_tRNS[5] = {116,  82,  78,  83, '\0'}
+#define PNG_zTXt const png_byte png_zTXt[5] = {122,  84,  88, 116, '\0'}
+
+#ifdef PNG_USE_GLOBAL_ARRAYS
+PNG_EXPORT_VAR (const png_byte FARDATA) png_IHDR[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_IDAT[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_IEND[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_PLTE[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_bKGD[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_cHRM[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_gAMA[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_hIST[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_iCCP[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_iTXt[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_oFFs[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_pCAL[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_sCAL[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_pHYs[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_sBIT[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_sPLT[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_sRGB[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_tEXt[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_tIME[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_tRNS[5];
+PNG_EXPORT_VAR (const png_byte FARDATA) png_zTXt[5];
+#endif /* PNG_USE_GLOBAL_ARRAYS */
+
+
+/* Inline macros to do direct reads of bytes from the input buffer.  These
+ * require that you are using an architecture that uses PNG byte ordering
+ * (MSB first) and supports unaligned data storage.  I think that PowerPC
+ * in big-endian mode and 680x0 are the only ones that will support this.
+ * The x86 line of processors definitely do not.  The png_get_int_32()
+ * routine also assumes we are using two's complement format for negative
+ * values, which is almost certainly true.
+ */
+#if defined(PNG_READ_BIG_ENDIAN_SUPPORTED)
+#  if defined(PNG_pCAL_SUPPORTED) || defined(PNG_oFFs_SUPPORTED)
+#    define png_get_int_32(buf) ( *((png_int_32p) (buf)))
+#  endif
+#  define png_get_uint_32(buf) ( *((png_uint_32p) (buf)))
+#  define png_get_uint_16(buf) ( *((png_uint_16p) (buf)))
+#else
+#  if defined(PNG_pCAL_SUPPORTED) || defined(PNG_oFFs_SUPPORTED)
+PNG_EXTERN png_int_32 png_get_int_32 PNGARG((png_bytep buf));
+#  endif
+PNG_EXTERN png_uint_32 png_get_uint_32 PNGARG((png_bytep buf));
+PNG_EXTERN png_uint_16 png_get_uint_16 PNGARG((png_bytep buf));
+#endif /* !PNG_READ_BIG_ENDIAN_SUPPORTED */
+PNG_EXTERN png_uint_32 png_get_uint_31 PNGARG((png_structp png_ptr,
+  png_bytep buf));
+
+/* Initialize png_ptr struct for reading, and allocate any other memory.
+ * (old interface - DEPRECATED - use png_create_read_struct instead).
+ */
+extern PNG_EXPORT(void,png_read_init) PNGARG((png_structp png_ptr));
+#undef png_read_init
+#define png_read_init(png_ptr) png_read_init_3(&png_ptr, \
+    PNG_LIBPNG_VER_STRING,  png_sizeof(png_struct));
+extern PNG_EXPORT(void,png_read_init_3) PNGARG((png_structpp ptr_ptr,
+    png_const_charp user_png_ver, png_size_t png_struct_size));
+extern PNG_EXPORT(void,png_read_init_2) PNGARG((png_structp png_ptr,
+    png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t
+    png_info_size));
+
+/* Initialize png_ptr struct for writing, and allocate any other memory.
+ * (old interface - DEPRECATED - use png_create_write_struct instead).
+ */
+extern PNG_EXPORT(void,png_write_init) PNGARG((png_structp png_ptr));
+#undef png_write_init
+#define png_write_init(png_ptr) png_write_init_3(&png_ptr, \
+    PNG_LIBPNG_VER_STRING, png_sizeof(png_struct));
+extern PNG_EXPORT(void,png_write_init_3) PNGARG((png_structpp ptr_ptr,
+    png_const_charp user_png_ver, png_size_t png_struct_size));
+extern PNG_EXPORT(void,png_write_init_2) PNGARG((png_structp png_ptr,
+    png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t
+    png_info_size));
+
+/* Allocate memory for an internal libpng struct */
+PNG_EXTERN png_voidp png_create_struct PNGARG((int type));
+
+/* Free memory from internal libpng struct */
+PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr));
+
+PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr
+  malloc_fn, png_voidp mem_ptr));
+PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr,
+   png_free_ptr free_fn, png_voidp mem_ptr));
+
+/* Free any memory that info_ptr points to and reset struct. */
+PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+#ifndef PNG_1_0_X
+/* Function to allocate memory for zlib. */
+PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, uInt size));
+
+/* Function to free memory for zlib */
+PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr));
+
+#ifdef PNG_SIZE_T
+/* Function to convert a sizeof an item to png_sizeof item */
+   PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size));
+#endif
+
+/* Next four functions are used internally as callbacks.  PNGAPI is required
+ * but not PNG_EXPORT.  PNGAPI added at libpng version 1.2.3. */
+
+PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr,
+   png_bytep data, png_size_t length));
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr,
+   png_bytep buffer, png_size_t length));
+#endif
+
+PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr,
+   png_bytep data, png_size_t length));
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+#if !defined(PNG_NO_STDIO)
+PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr));
+#endif
+#endif
+#else /* PNG_1_0_X */
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr,
+   png_bytep buffer, png_size_t length));
+#endif
+#endif /* PNG_1_0_X */
+
+/* Reset the CRC variable */
+PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr));
+
+/* Write the "data" buffer to whatever output you are using. */
+PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data,
+   png_size_t length));
+
+/* Read data from whatever input you are using into the "data" buffer */
+PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data,
+   png_size_t length));
+
+/* Read bytes into buf, and update png_ptr->crc */
+PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf,
+   png_size_t length));
+
+/* Decompress data in a chunk that uses compression */
+#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \
+    defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED)
+PNG_EXTERN png_charp png_decompress_chunk PNGARG((png_structp png_ptr,
+   int comp_type, png_charp chunkdata, png_size_t chunklength,
+   png_size_t prefix_length, png_size_t *data_length));
+#endif
+
+/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */
+PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip));
+
+/* Read the CRC from the file and compare it to the libpng calculated CRC */
+PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr));
+
+/* Calculate the CRC over a section of data.  Note that we are only
+ * passing a maximum of 64K on systems that have this as a memory limit,
+ * since this is the maximum buffer size we can specify.
+ */
+PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr,
+   png_size_t length));
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+PNG_EXTERN void png_flush PNGARG((png_structp png_ptr));
+#endif
+
+
+/* Place a 32-bit number into a buffer in PNG byte order (big-endian).
+ * The only currently known PNG chunks that use signed numbers are
+ * the ancillary extension chunks, oFFs and pCAL.
+ */
+PNG_EXTERN void png_save_uint_32 PNGARG((png_bytep buf, png_uint_32 i));
+
+#if defined(PNG_WRITE_pCAL_SUPPORTED) || defined(PNG_WRITE_oFFs_SUPPORTED)
+PNG_EXTERN void png_save_int_32 PNGARG((png_bytep buf, png_int_32 i));
+#endif
+
+/* Place a 16-bit number into a buffer in PNG byte order.
+ * The parameter is declared unsigned int, not png_uint_16,
+ * just to avoid potential problems on pre-ANSI C compilers.
+ */
+PNG_EXTERN void png_save_uint_16 PNGARG((png_bytep buf, unsigned int i));
+
+/* simple function to write the signature */
+PNG_EXTERN void png_write_sig PNGARG((png_structp png_ptr));
+
+/* write various chunks */
+
+/* Write the IHDR chunk, and update the png_struct with the necessary
+ * information.
+ */
+PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width,
+   png_uint_32 height,
+   int bit_depth, int color_type, int compression_method, int filter_method,
+   int interlace_method));
+
+PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette,
+   png_uint_32 num_pal));
+
+PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data,
+   png_size_t length));
+
+PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr));
+
+#if defined(PNG_WRITE_gAMA_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma));
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, png_fixed_point
+    file_gamma));
+#endif
+#endif
+
+#if defined(PNG_WRITE_sBIT_SUPPORTED)
+PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit,
+   int color_type));
+#endif
+
+#if defined(PNG_WRITE_cHRM_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr,
+   double white_x, double white_y,
+   double red_x, double red_y, double green_x, double green_y,
+   double blue_x, double blue_y));
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr,
+   png_fixed_point int_white_x, png_fixed_point int_white_y,
+   png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point
+   int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x,
+   png_fixed_point int_blue_y));
+#endif
+#endif
+
+#if defined(PNG_WRITE_sRGB_SUPPORTED)
+PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr,
+   int intent));
+#endif
+
+#if defined(PNG_WRITE_iCCP_SUPPORTED)
+PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr,
+   png_charp name, int compression_type,
+   png_charp profile, int proflen));
+   /* Note to maintainer: profile should be png_bytep */
+#endif
+
+#if defined(PNG_WRITE_sPLT_SUPPORTED)
+PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr,
+   png_sPLT_tp palette));
+#endif
+
+#if defined(PNG_WRITE_tRNS_SUPPORTED)
+PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans,
+   png_color_16p values, int number, int color_type));
+#endif
+
+#if defined(PNG_WRITE_bKGD_SUPPORTED)
+PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr,
+   png_color_16p values, int color_type));
+#endif
+
+#if defined(PNG_WRITE_hIST_SUPPORTED)
+PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist,
+   int num_hist));
+#endif
+
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
+    defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
+PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr,
+   png_charp key, png_charpp new_key));
+#endif
+
+#if defined(PNG_WRITE_tEXt_SUPPORTED)
+PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key,
+   png_charp text, png_size_t text_len));
+#endif
+
+#if defined(PNG_WRITE_zTXt_SUPPORTED)
+PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key,
+   png_charp text, png_size_t text_len, int compression));
+#endif
+
+#if defined(PNG_WRITE_iTXt_SUPPORTED)
+PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr,
+   int compression, png_charp key, png_charp lang, png_charp lang_key,
+   png_charp text));
+#endif
+
+#if defined(PNG_TEXT_SUPPORTED)  /* Added at version 1.0.14 and 1.2.4 */
+PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_textp text_ptr, int num_text));
+#endif
+
+#if defined(PNG_WRITE_oFFs_SUPPORTED)
+PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr,
+   png_int_32 x_offset, png_int_32 y_offset, int unit_type));
+#endif
+
+#if defined(PNG_WRITE_pCAL_SUPPORTED)
+PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose,
+   png_int_32 X0, png_int_32 X1, int type, int nparams,
+   png_charp units, png_charpp params));
+#endif
+
+#if defined(PNG_WRITE_pHYs_SUPPORTED)
+PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr,
+   png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit,
+   int unit_type));
+#endif
+
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr,
+   png_timep mod_time));
+#endif
+
+#if defined(PNG_WRITE_sCAL_SUPPORTED)
+#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO)
+PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr,
+   int unit, double width, double height));
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr,
+   int unit, png_charp width, png_charp height));
+#endif
+#endif
+#endif
+
+/* Called when finished processing a row of data */
+PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr));
+
+/* Internal use only.   Called before first row of data */
+PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr));
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr));
+#endif
+
+/* combine a row of data, dealing with alpha, etc. if requested */
+PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row,
+   int mask));
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+/* expand an interlaced row */
+/* OLD pre-1.0.9 interface:
+PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info,
+   png_bytep row, int pass, png_uint_32 transformations));
+ */
+PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr));
+#endif
+
+/* GRR TO DO (2.0 or whenever):  simplify other internal calling interfaces */
+
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+/* grab pixels out of a row for an interlaced pass */
+PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info,
+   png_bytep row, int pass));
+#endif
+
+/* unfilter a row */
+PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr,
+   png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter));
+
+/* Choose the best filter to use and filter the row data */
+PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr,
+   png_row_infop row_info));
+
+/* Write out the filtered row. */
+PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr,
+   png_bytep filtered_row));
+/* finish a row while reading, dealing with interlacing passes, etc. */
+PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr));
+
+/* initialize the row buffers, etc. */
+PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr));
+/* optional call to update the users info structure */
+PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+/* these are the functions that do the transformations */
+#if defined(PNG_READ_FILLER_SUPPORTED)
+PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info,
+   png_bytep row, png_uint_32 filler, png_uint_32 flags));
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
+    defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info,
+   png_bytep row, png_uint_32 flags));
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop
+   row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED)
+PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row,
+   png_color_8p sig_bits));
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+PNG_EXTERN void png_do_dither PNGARG((png_row_infop row_info,
+   png_bytep row, png_bytep palette_lookup, png_bytep dither_lookup));
+
+#  if defined(PNG_CORRECT_PALETTE_SUPPORTED)
+PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr,
+   png_colorp palette, int num_palette));
+#  endif
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_WRITE_PACK_SUPPORTED)
+PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info,
+   png_bytep row, png_uint_32 bit_depth));
+#endif
+
+#if defined(PNG_WRITE_SHIFT_SUPPORTED)
+PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row,
+   png_color_8p bit_depth));
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row,
+   png_color_16p trans_values, png_color_16p background,
+   png_color_16p background_1,
+   png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1,
+   png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1,
+   png_uint_16pp gamma_16_to_1, int gamma_shift));
+#else
+PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row,
+   png_color_16p trans_values, png_color_16p background));
+#endif
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row,
+   png_bytep gamma_table, png_uint_16pp gamma_16_table,
+   int gamma_shift));
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info,
+   png_bytep row, png_colorp palette, png_bytep trans, int num_trans));
+PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info,
+   png_bytep row, png_color_16p trans_value));
+#endif
+
+/* The following decodes the appropriate chunks, and does error correction,
+ * then calls the appropriate callback for the chunk if it is valid.
+ */
+
+/* decode the IHDR chunk */
+PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+
+#if defined(PNG_READ_bKGD_SUPPORTED)
+PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_cHRM_SUPPORTED)
+PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_gAMA_SUPPORTED)
+PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_hIST_SUPPORTED)
+PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_iCCP_SUPPORTED)
+extern void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif /* PNG_READ_iCCP_SUPPORTED */
+
+#if defined(PNG_READ_iTXt_SUPPORTED)
+PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_oFFs_SUPPORTED)
+PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_pCAL_SUPPORTED)
+PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_pHYs_SUPPORTED)
+PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_sBIT_SUPPORTED)
+PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_sCAL_SUPPORTED)
+PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_sPLT_SUPPORTED)
+extern void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif /* PNG_READ_sPLT_SUPPORTED */
+
+#if defined(PNG_READ_sRGB_SUPPORTED)
+PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_tEXt_SUPPORTED)
+PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_tIME_SUPPORTED)
+PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_tRNS_SUPPORTED)
+PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_zTXt_SUPPORTED)
+PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 length));
+
+PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr,
+   png_bytep chunk_name));
+
+/* handle the transformations for reading and writing */
+PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr));
+
+PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr));
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr,
+   png_uint_32 length));
+PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr,
+   png_bytep buffer, png_size_t buffer_length));
+PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr,
+   png_bytep buffer, png_size_t buffer_length));
+PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row));
+PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr));
+#if defined(PNG_READ_tEXt_SUPPORTED)
+PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+#endif
+
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info,
+   png_bytep row));
+PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+/* png.c */ /* PRIVATE */
+PNG_EXTERN void png_init_mmx_flags PNGARG((png_structp png_ptr));
+#endif
+/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */
+
+#endif /* PNG_INTERNAL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PNG_VERSION_INFO_ONLY */
+/* do not put anything past this line */
+#endif /* PNG_H */
diff --git a/Utilities/GDAL/frmts/png/libpng/pngconf.h b/Utilities/GDAL/frmts/png/libpng/pngconf.h
new file mode 100644
index 0000000000..ba50838454
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngconf.h
@@ -0,0 +1,1437 @@
+
+/* pngconf.h - machine configurable file for libpng
+ *
+ * libpng version 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+/* Any machine specific code is near the front of this file, so if you
+ * are configuring libpng for a machine, you may want to read the section
+ * starting here down to where it starts to typedef png_color, png_text,
+ * and png_info.
+ */
+
+#ifndef PNGCONF_H
+#define PNGCONF_H
+
+#define PNG_1_2_X
+
+/* 
+ * PNG_USER_CONFIG has to be defined on the compiler command line. This
+ * includes the resource compiler for Windows DLL configurations.
+ */
+#ifdef PNG_USER_CONFIG
+#include "pngusr.h"
+#endif
+
+/*
+ * Added at libpng-1.2.8
+ *  
+ * If you create a private DLL you need to define in "pngusr.h" the followings:
+ * #define PNG_USER_PRIVATEBUILD <Describes by whom and why this version of
+ *        the DLL was built>
+ *  e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons."
+ * #define PNG_USER_DLLFNAME_POSTFIX <two-letter postfix that serve to
+ *        distinguish your DLL from those of the official release. These
+ *        correspond to the trailing letters that come after the version
+ *        number and must match your private DLL name>
+ *  e.g. // private DLL "libpng13gx.dll"
+ *       #define PNG_USER_DLLFNAME_POSTFIX "gx"
+ * 
+ * The following macros are also at your disposal if you want to complete the 
+ * DLL VERSIONINFO structure.
+ * - PNG_USER_VERSIONINFO_COMMENTS
+ * - PNG_USER_VERSIONINFO_COMPANYNAME
+ * - PNG_USER_VERSIONINFO_LEGALTRADEMARKS
+ */
+
+#ifdef __STDC__
+#ifdef SPECIALBUILD
+#  pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\
+ are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.")
+#endif
+
+#ifdef PRIVATEBUILD
+# pragma message("PRIVATEBUILD is deprecated. Use\
+ PNG_USER_PRIVATEBUILD instead.")
+# define PNG_USER_PRIVATEBUILD PRIVATEBUILD
+#endif
+#endif /* __STDC__ */
+
+#ifndef PNG_VERSION_INFO_ONLY
+
+/* End of material added to libpng-1.2.8 */
+
+/* This is the size of the compression buffer, and thus the size of
+ * an IDAT chunk.  Make this whatever size you feel is best for your
+ * machine.  One of these will be allocated per png_struct.  When this
+ * is full, it writes the data to the disk, and does some other
+ * calculations.  Making this an extremely small size will slow
+ * the library down, but you may want to experiment to determine
+ * where it becomes significant, if you are concerned with memory
+ * usage.  Note that zlib allocates at least 32Kb also.  For readers,
+ * this describes the size of the buffer available to read the data in.
+ * Unless this gets smaller than the size of a row (compressed),
+ * it should not make much difference how big this is.
+ */
+
+#ifndef PNG_ZBUF_SIZE
+#  define PNG_ZBUF_SIZE 8192
+#endif
+
+/* Enable if you want a write-only libpng */
+
+#ifndef PNG_NO_READ_SUPPORTED
+#  define PNG_READ_SUPPORTED
+#endif
+
+/* Enable if you want a read-only libpng */
+
+#ifndef PNG_NO_WRITE_SUPPORTED
+#  define PNG_WRITE_SUPPORTED
+#endif
+
+/* Enabled by default in 1.2.0.  You can disable this if you don't need to
+   support PNGs that are embedded in MNG datastreams */
+#if !defined(PNG_1_0_X) && !defined(PNG_NO_MNG_FEATURES)
+#  ifndef PNG_MNG_FEATURES_SUPPORTED
+#    define PNG_MNG_FEATURES_SUPPORTED
+#  endif
+#endif
+
+#ifndef PNG_NO_FLOATING_POINT_SUPPORTED
+#  ifndef PNG_FLOATING_POINT_SUPPORTED
+#    define PNG_FLOATING_POINT_SUPPORTED
+#  endif
+#endif
+
+/* If you are running on a machine where you cannot allocate more
+ * than 64K of memory at once, uncomment this.  While libpng will not
+ * normally need that much memory in a chunk (unless you load up a very
+ * large file), zlib needs to know how big of a chunk it can use, and
+ * libpng thus makes sure to check any memory allocation to verify it
+ * will fit into memory.
+#define PNG_MAX_MALLOC_64K
+ */
+#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K)
+#  define PNG_MAX_MALLOC_64K
+#endif
+
+/* Special munging to support doing things the 'cygwin' way:
+ * 'Normal' png-on-win32 defines/defaults:
+ *   PNG_BUILD_DLL -- building dll
+ *   PNG_USE_DLL   -- building an application, linking to dll
+ *   (no define)   -- building static library, or building an
+ *                    application and linking to the static lib
+ * 'Cygwin' defines/defaults:
+ *   PNG_BUILD_DLL -- (ignored) building the dll
+ *   (no define)   -- (ignored) building an application, linking to the dll
+ *   PNG_STATIC    -- (ignored) building the static lib, or building an 
+ *                    application that links to the static lib.
+ *   ALL_STATIC    -- (ignored) building various static libs, or building an 
+ *                    application that links to the static libs.
+ * Thus,
+ * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and
+ * this bit of #ifdefs will define the 'correct' config variables based on
+ * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but
+ * unnecessary.
+ *
+ * Also, the precedence order is:
+ *   ALL_STATIC (since we can't #undef something outside our namespace)
+ *   PNG_BUILD_DLL
+ *   PNG_STATIC
+ *   (nothing) == PNG_USE_DLL
+ * 
+ * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent
+ *   of auto-import in binutils, we no longer need to worry about 
+ *   __declspec(dllexport) / __declspec(dllimport) and friends.  Therefore,
+ *   we don't need to worry about PNG_STATIC or ALL_STATIC when it comes
+ *   to __declspec() stuff.  However, we DO need to worry about 
+ *   PNG_BUILD_DLL and PNG_STATIC because those change some defaults
+ *   such as CONSOLE_IO and whether GLOBAL_ARRAYS are allowed.
+ */
+#if defined(__CYGWIN__)
+#  if defined(ALL_STATIC)
+#    if defined(PNG_BUILD_DLL)
+#      undef PNG_BUILD_DLL
+#    endif
+#    if defined(PNG_USE_DLL)
+#      undef PNG_USE_DLL
+#    endif
+#    if defined(PNG_DLL)
+#      undef PNG_DLL
+#    endif
+#    if !defined(PNG_STATIC)
+#      define PNG_STATIC
+#    endif
+#  else
+#    if defined (PNG_BUILD_DLL)
+#      if defined(PNG_STATIC)
+#        undef PNG_STATIC
+#      endif
+#      if defined(PNG_USE_DLL)
+#        undef PNG_USE_DLL
+#      endif
+#      if !defined(PNG_DLL)
+#        define PNG_DLL
+#      endif
+#    else
+#      if defined(PNG_STATIC)
+#        if defined(PNG_USE_DLL)
+#          undef PNG_USE_DLL
+#        endif
+#        if defined(PNG_DLL)
+#          undef PNG_DLL
+#        endif
+#      else
+#        if !defined(PNG_USE_DLL)
+#          define PNG_USE_DLL
+#        endif
+#        if !defined(PNG_DLL)
+#          define PNG_DLL
+#        endif
+#      endif  
+#    endif  
+#  endif
+#endif
+
+/* This protects us against compilers that run on a windowing system
+ * and thus don't have or would rather us not use the stdio types:
+ * stdin, stdout, and stderr.  The only one currently used is stderr
+ * in png_error() and png_warning().  #defining PNG_NO_CONSOLE_IO will
+ * prevent these from being compiled and used. #defining PNG_NO_STDIO
+ * will also prevent these, plus will prevent the entire set of stdio
+ * macros and functions (FILE *, printf, etc.) from being compiled and used,
+ * unless (PNG_DEBUG > 0) has been #defined.
+ *
+ * #define PNG_NO_CONSOLE_IO
+ * #define PNG_NO_STDIO
+ */
+
+#if defined(_WIN32_WCE)
+#  include <windows.h>
+   /* Console I/O functions are not supported on WindowsCE */
+#  define PNG_NO_CONSOLE_IO
+#  ifdef PNG_DEBUG
+#    undef PNG_DEBUG
+#  endif
+#endif
+
+#ifdef PNG_BUILD_DLL
+#  ifndef PNG_CONSOLE_IO_SUPPORTED
+#    ifndef PNG_NO_CONSOLE_IO
+#      define PNG_NO_CONSOLE_IO
+#    endif
+#  endif
+#endif
+
+#  ifdef PNG_NO_STDIO
+#    ifndef PNG_NO_CONSOLE_IO
+#      define PNG_NO_CONSOLE_IO
+#    endif
+#    ifdef PNG_DEBUG
+#      if (PNG_DEBUG > 0)
+#        include <stdio.h>
+#      endif
+#    endif
+#  else
+#    if !defined(_WIN32_WCE)
+/* "stdio.h" functions are not supported on WindowsCE */
+#      include <stdio.h>
+#    endif
+#  endif
+
+/* This macro protects us against machines that don't have function
+ * prototypes (ie K&R style headers).  If your compiler does not handle
+ * function prototypes, define this macro and use the included ansi2knr.
+ * I've always been able to use _NO_PROTO as the indicator, but you may
+ * need to drag the empty declaration out in front of here, or change the
+ * ifdef to suit your own needs.
+ */
+#ifndef PNGARG
+
+#ifdef OF /* zlib prototype munger */
+#  define PNGARG(arglist) OF(arglist)
+#else
+
+#ifdef _NO_PROTO
+#  define PNGARG(arglist) ()
+#  ifndef PNG_TYPECAST_NULL
+#     define PNG_TYPECAST_NULL
+#  endif
+#else
+#  define PNGARG(arglist) arglist
+#endif /* _NO_PROTO */
+
+#endif /* OF */
+
+#endif /* PNGARG */
+
+/* Try to determine if we are compiling on a Mac.  Note that testing for
+ * just __MWERKS__ is not good enough, because the Codewarrior is now used
+ * on non-Mac platforms.
+ */
+#ifndef MACOS
+#  if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \
+      defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC)
+#    define MACOS
+#  endif
+#endif
+
+/* enough people need this for various reasons to include it here */
+#if !defined(MACOS) && !defined(RISCOS) && !defined(_WIN32_WCE)
+#  include <sys/types.h>
+#endif
+
+#if !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED)
+#  define PNG_SETJMP_SUPPORTED
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+/* This is an attempt to force a single setjmp behaviour on Linux.  If
+ * the X config stuff didn't define _BSD_SOURCE we wouldn't need this.
+ */
+
+#  ifdef __linux__
+#    ifdef _BSD_SOURCE
+#      define PNG_SAVE_BSD_SOURCE
+#      undef _BSD_SOURCE
+#    endif
+#    ifdef _SETJMP_H
+     /* If you encounter a compiler error here, see the explanation
+      * near the end of INSTALL.
+      */
+         __png.h__ already includes setjmp.h;
+         __dont__ include it again.;
+#    endif
+#  endif /* __linux__ */
+
+   /* include setjmp.h for error handling */
+#  include <setjmp.h>
+
+#  ifdef __linux__
+#    ifdef PNG_SAVE_BSD_SOURCE
+#      define _BSD_SOURCE
+#      undef PNG_SAVE_BSD_SOURCE
+#    endif
+#  endif /* __linux__ */
+#endif /* PNG_SETJMP_SUPPORTED */
+
+#ifdef BSD
+#  include <strings.h>
+#else
+#  include <string.h>
+#endif
+
+/* Other defines for things like memory and the like can go here.  */
+#ifdef PNG_INTERNAL
+
+#include <stdlib.h>
+
+/* The functions exported by PNG_EXTERN are PNG_INTERNAL functions, which
+ * aren't usually used outside the library (as far as I know), so it is
+ * debatable if they should be exported at all.  In the future, when it is
+ * possible to have run-time registry of chunk-handling functions, some of
+ * these will be made available again.
+#define PNG_EXTERN extern
+ */
+#define PNG_EXTERN
+
+/* Other defines specific to compilers can go here.  Try to keep
+ * them inside an appropriate ifdef/endif pair for portability.
+ */
+
+#if defined(PNG_FLOATING_POINT_SUPPORTED)
+#  if defined(MACOS)
+     /* We need to check that <math.h> hasn't already been included earlier
+      * as it seems it doesn't agree with <fp.h>, yet we should really use
+      * <fp.h> if possible.
+      */
+#    if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__)
+#      include <fp.h>
+#    endif
+#  else
+#    include <math.h>
+#  endif
+#  if defined(_AMIGA) && defined(__SASC) && defined(_M68881)
+     /* Amiga SAS/C: We must include builtin FPU functions when compiling using
+      * MATH=68881
+      */
+#    include <m68881.h>
+#  endif
+#endif
+
+/* Codewarrior on NT has linking problems without this. */
+#if (defined(__MWERKS__) && defined(WIN32)) || defined(__STDC__)
+#  define PNG_ALWAYS_EXTERN
+#endif
+
+/* This provides the non-ANSI (far) memory allocation routines. */
+#if defined(__TURBOC__) && defined(__MSDOS__)
+#  include <mem.h>
+#  include <alloc.h>
+#endif
+
+/* I have no idea why is this necessary... */
+#if defined(_MSC_VER) && (defined(WIN32) || defined(_Windows) || \
+    defined(_WINDOWS) || defined(_WIN32) || defined(__WIN32__))
+#  include <malloc.h>
+#endif
+
+/* This controls how fine the dithering gets.  As this allocates
+ * a largish chunk of memory (32K), those who are not as concerned
+ * with dithering quality can decrease some or all of these.
+ */
+#ifndef PNG_DITHER_RED_BITS
+#  define PNG_DITHER_RED_BITS 5
+#endif
+#ifndef PNG_DITHER_GREEN_BITS
+#  define PNG_DITHER_GREEN_BITS 5
+#endif
+#ifndef PNG_DITHER_BLUE_BITS
+#  define PNG_DITHER_BLUE_BITS 5
+#endif
+
+/* This controls how fine the gamma correction becomes when you
+ * are only interested in 8 bits anyway.  Increasing this value
+ * results in more memory being used, and more pow() functions
+ * being called to fill in the gamma tables.  Don't set this value
+ * less then 8, and even that may not work (I haven't tested it).
+ */
+
+#ifndef PNG_MAX_GAMMA_8
+#  define PNG_MAX_GAMMA_8 11
+#endif
+
+/* This controls how much a difference in gamma we can tolerate before
+ * we actually start doing gamma conversion.
+ */
+#ifndef PNG_GAMMA_THRESHOLD
+#  define PNG_GAMMA_THRESHOLD 0.05
+#endif
+
+#endif /* PNG_INTERNAL */
+
+/* The following uses const char * instead of char * for error
+ * and warning message functions, so some compilers won't complain.
+ * If you do not want to use const, define PNG_NO_CONST here.
+ */
+
+#ifndef PNG_NO_CONST
+#  define PNG_CONST const
+#else
+#  define PNG_CONST
+#endif
+
+/* The following defines give you the ability to remove code from the
+ * library that you will not be using.  I wish I could figure out how to
+ * automate this, but I can't do that without making it seriously hard
+ * on the users.  So if you are not using an ability, change the #define
+ * to and #undef, and that part of the library will not be compiled.  If
+ * your linker can't find a function, you may want to make sure the
+ * ability is defined here.  Some of these depend upon some others being
+ * defined.  I haven't figured out all the interactions here, so you may
+ * have to experiment awhile to get everything to compile.  If you are
+ * creating or using a shared library, you probably shouldn't touch this,
+ * as it will affect the size of the structures, and this will cause bad
+ * things to happen if the library and/or application ever change.
+ */
+
+/* Any features you will not be using can be undef'ed here */
+
+/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user
+ * to turn it off with "*TRANSFORMS_NOT_SUPPORTED" or *PNG_NO_*_TRANSFORMS
+ * on the compile line, then pick and choose which ones to define without
+ * having to edit this file. It is safe to use the *TRANSFORMS_NOT_SUPPORTED
+ * if you only want to have a png-compliant reader/writer but don't need
+ * any of the extra transformations.  This saves about 80 kbytes in a
+ * typical installation of the library. (PNG_NO_* form added in version
+ * 1.0.1c, for consistency)
+ */
+
+/* The size of the png_text structure changed in libpng-1.0.6 when
+ * iTXt is supported.  It is turned off by default, to support old apps
+ * that malloc the png_text structure instead of calling png_set_text()
+ * and letting libpng malloc it.  It will be turned on by default in
+ * libpng-1.3.0.
+ */
+
+#ifndef PNG_iTXt_SUPPORTED
+#  if !defined(PNG_READ_iTXt_SUPPORTED) && !defined(PNG_NO_READ_iTXt)
+#    define PNG_NO_READ_iTXt
+#  endif
+#  if !defined(PNG_WRITE_iTXt_SUPPORTED) && !defined(PNG_NO_WRITE_iTXt)
+#    define PNG_NO_WRITE_iTXt
+#  endif
+#endif
+
+/* The following support, added after version 1.0.0, can be turned off here en
+ * masse by defining PNG_LEGACY_SUPPORTED in case you need binary compatibility
+ * with old applications that require the length of png_struct and png_info
+ * to remain unchanged.
+ */
+
+#ifdef PNG_LEGACY_SUPPORTED
+#  define PNG_NO_FREE_ME
+#  define PNG_NO_READ_UNKNOWN_CHUNKS
+#  define PNG_NO_WRITE_UNKNOWN_CHUNKS
+#  define PNG_NO_READ_USER_CHUNKS
+#  define PNG_NO_READ_iCCP
+#  define PNG_NO_WRITE_iCCP
+#  define PNG_NO_READ_iTXt
+#  define PNG_NO_WRITE_iTXt
+#  define PNG_NO_READ_sCAL
+#  define PNG_NO_WRITE_sCAL
+#  define PNG_NO_READ_sPLT
+#  define PNG_NO_WRITE_sPLT
+#  define PNG_NO_INFO_IMAGE
+#  define PNG_NO_READ_RGB_TO_GRAY
+#  define PNG_NO_READ_USER_TRANSFORM
+#  define PNG_NO_WRITE_USER_TRANSFORM
+#  define PNG_NO_USER_MEM
+#  define PNG_NO_READ_EMPTY_PLTE
+#  define PNG_NO_MNG_FEATURES
+#  define PNG_NO_FIXED_POINT_SUPPORTED
+#endif
+
+/* Ignore attempt to turn off both floating and fixed point support */
+#if !defined(PNG_FLOATING_POINT_SUPPORTED) || \
+    !defined(PNG_NO_FIXED_POINT_SUPPORTED)
+#  define PNG_FIXED_POINT_SUPPORTED
+#endif
+
+#ifndef PNG_NO_FREE_ME
+#  define PNG_FREE_ME_SUPPORTED
+#endif
+
+#if defined(PNG_READ_SUPPORTED)
+
+#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \
+      !defined(PNG_NO_READ_TRANSFORMS)
+#  define PNG_READ_TRANSFORMS_SUPPORTED
+#endif
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+#  ifndef PNG_NO_READ_EXPAND
+#    define PNG_READ_EXPAND_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_SHIFT
+#    define PNG_READ_SHIFT_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_PACK
+#    define PNG_READ_PACK_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_BGR
+#    define PNG_READ_BGR_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_SWAP
+#    define PNG_READ_SWAP_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_PACKSWAP
+#    define PNG_READ_PACKSWAP_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_INVERT
+#    define PNG_READ_INVERT_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_DITHER
+#    define PNG_READ_DITHER_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_BACKGROUND
+#    define PNG_READ_BACKGROUND_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_16_TO_8
+#    define PNG_READ_16_TO_8_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_FILLER
+#    define PNG_READ_FILLER_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_GAMMA
+#    define PNG_READ_GAMMA_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_GRAY_TO_RGB
+#    define PNG_READ_GRAY_TO_RGB_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_SWAP_ALPHA
+#    define PNG_READ_SWAP_ALPHA_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_INVERT_ALPHA
+#    define PNG_READ_INVERT_ALPHA_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_STRIP_ALPHA
+#    define PNG_READ_STRIP_ALPHA_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_USER_TRANSFORM
+#    define PNG_READ_USER_TRANSFORM_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_RGB_TO_GRAY
+#    define PNG_READ_RGB_TO_GRAY_SUPPORTED
+#  endif
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+
+#if !defined(PNG_NO_PROGRESSIVE_READ) && \
+ !defined(PNG_PROGRESSIVE_READ_NOT_SUPPORTED)  /* if you don't do progressive */
+#  define PNG_PROGRESSIVE_READ_SUPPORTED     /* reading.  This is not talking */
+#endif                               /* about interlacing capability!  You'll */
+              /* still have interlacing unless you change the following line: */
+
+#define PNG_READ_INTERLACING_SUPPORTED /* required for PNG-compliant decoders */
+
+#ifndef PNG_NO_READ_COMPOSITE_NODIV
+#  ifndef PNG_NO_READ_COMPOSITED_NODIV  /* libpng-1.0.x misspelling */
+#    define PNG_READ_COMPOSITE_NODIV_SUPPORTED   /* well tested on Intel, SGI */
+#  endif
+#endif
+
+/* Deprecated, will be removed from version 2.0.0.
+   Use PNG_MNG_FEATURES_SUPPORTED instead. */
+#ifndef PNG_NO_READ_EMPTY_PLTE
+#  define PNG_READ_EMPTY_PLTE_SUPPORTED
+#endif
+
+#endif /* PNG_READ_SUPPORTED */
+
+#if defined(PNG_WRITE_SUPPORTED)
+
+# if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \
+    !defined(PNG_NO_WRITE_TRANSFORMS)
+#  define PNG_WRITE_TRANSFORMS_SUPPORTED
+#endif
+
+#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+#  ifndef PNG_NO_WRITE_SHIFT
+#    define PNG_WRITE_SHIFT_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_PACK
+#    define PNG_WRITE_PACK_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_BGR
+#    define PNG_WRITE_BGR_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_SWAP
+#    define PNG_WRITE_SWAP_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_PACKSWAP
+#    define PNG_WRITE_PACKSWAP_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_INVERT
+#    define PNG_WRITE_INVERT_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_FILLER
+#    define PNG_WRITE_FILLER_SUPPORTED   /* same as WRITE_STRIP_ALPHA */
+#  endif
+#  ifndef PNG_NO_WRITE_SWAP_ALPHA
+#    define PNG_WRITE_SWAP_ALPHA_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_INVERT_ALPHA
+#    define PNG_WRITE_INVERT_ALPHA_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_USER_TRANSFORM
+#    define PNG_WRITE_USER_TRANSFORM_SUPPORTED
+#  endif
+#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */
+
+#define PNG_WRITE_INTERLACING_SUPPORTED  /* not required for PNG-compliant
+                                            encoders, but can cause trouble
+                                            if left undefined */
+
+#if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \
+     defined(PNG_FLOATING_POINT_SUPPORTED)
+#  define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+#endif
+
+#ifndef PNG_NO_WRITE_FLUSH
+#  define PNG_WRITE_FLUSH_SUPPORTED
+#endif
+
+/* Deprecated, see PNG_MNG_FEATURES_SUPPORTED, above */
+#ifndef PNG_NO_WRITE_EMPTY_PLTE
+#  define PNG_WRITE_EMPTY_PLTE_SUPPORTED
+#endif
+
+#endif /* PNG_WRITE_SUPPORTED */
+
+#ifndef PNG_1_0_X
+#  ifndef PNG_NO_ERROR_NUMBERS
+#    define PNG_ERROR_NUMBERS_SUPPORTED
+#  endif
+#endif /* PNG_1_0_X */
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+#  ifndef PNG_NO_USER_TRANSFORM_PTR
+#    define PNG_USER_TRANSFORM_PTR_SUPPORTED
+#  endif
+#endif
+
+#ifndef PNG_NO_STDIO
+#  define PNG_TIME_RFC1123_SUPPORTED
+#endif
+
+/* This adds extra functions in pngget.c for accessing data from the
+ * info pointer (added in version 0.99)
+ * png_get_image_width()
+ * png_get_image_height()
+ * png_get_bit_depth()
+ * png_get_color_type()
+ * png_get_compression_type()
+ * png_get_filter_type()
+ * png_get_interlace_type()
+ * png_get_pixel_aspect_ratio()
+ * png_get_pixels_per_meter()
+ * png_get_x_offset_pixels()
+ * png_get_y_offset_pixels()
+ * png_get_x_offset_microns()
+ * png_get_y_offset_microns()
+ */
+#if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED)
+#  define PNG_EASY_ACCESS_SUPPORTED
+#endif
+
+/* PNG_ASSEMBLER_CODE was enabled by default in version 1.2.0 
+   even when PNG_USE_PNGVCRD or PNG_USE_PNGGCCRD is not defined */
+#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_ASSEMBLER_CODE)
+#  ifndef PNG_ASSEMBLER_CODE_SUPPORTED
+#    define PNG_ASSEMBLER_CODE_SUPPORTED
+#  endif
+#  if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE)
+#    define PNG_MMX_CODE_SUPPORTED
+#  endif
+#endif
+
+/* If you are sure that you don't need thread safety and you are compiling
+   with PNG_USE_PNGCCRD for an MMX application, you can define this for
+   faster execution.  See pnggccrd.c.
+#define PNG_THREAD_UNSAFE_OK
+*/
+
+#if !defined(PNG_1_0_X)
+#if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED)
+#  define PNG_USER_MEM_SUPPORTED
+#endif
+#endif /* PNG_1_0_X */
+
+/* Added at libpng-1.2.6 */
+#if !defined(PNG_1_0_X)
+#ifndef PNG_SET_USER_LIMITS_SUPPORTED
+#if !defined(PNG_NO_SET_USER_LIMITS) && !defined(PNG_SET_USER_LIMITS_SUPPORTED)
+#  define PNG_SET_USER_LIMITS_SUPPORTED
+#endif
+#endif
+#endif /* PNG_1_0_X */
+
+/* Added at libpng-1.0.16 and 1.2.6.  To accept all valid PNGS no matter
+ * how large, set these limits to 0x7fffffffL
+ */
+#ifndef PNG_USER_WIDTH_MAX
+#  define PNG_USER_WIDTH_MAX 1000000L
+#endif
+#ifndef PNG_USER_HEIGHT_MAX
+#  define PNG_USER_HEIGHT_MAX 1000000L
+#endif
+
+/* These are currently experimental features, define them if you want */
+
+/* very little testing */
+/*
+#ifdef PNG_READ_SUPPORTED
+#  ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED
+#    define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED
+#  endif
+#endif
+*/
+
+/* This is only for PowerPC big-endian and 680x0 systems */
+/* some testing */
+/*
+#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED
+#  define PNG_READ_BIG_ENDIAN_SUPPORTED
+#endif
+*/
+
+/* Buggy compilers (e.g., gcc 2.7.2.2) need this */
+/*
+#define PNG_NO_POINTER_INDEXING
+*/
+
+/* These functions are turned off by default, as they will be phased out. */
+/*
+#define  PNG_USELESS_TESTS_SUPPORTED
+#define  PNG_CORRECT_PALETTE_SUPPORTED
+*/
+
+/* Any chunks you are not interested in, you can undef here.  The
+ * ones that allocate memory may be expecially important (hIST,
+ * tEXt, zTXt, tRNS, pCAL).  Others will just save time and make png_info
+ * a bit smaller.
+ */
+
+#if defined(PNG_READ_SUPPORTED) && \
+    !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \
+    !defined(PNG_NO_READ_ANCILLARY_CHUNKS)
+#  define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
+#endif
+
+#if defined(PNG_WRITE_SUPPORTED) && \
+    !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \
+    !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS)
+#  define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
+#endif
+
+#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
+
+#ifdef PNG_NO_READ_TEXT
+#  define PNG_NO_READ_iTXt
+#  define PNG_NO_READ_tEXt
+#  define PNG_NO_READ_zTXt
+#endif
+#ifndef PNG_NO_READ_bKGD
+#  define PNG_READ_bKGD_SUPPORTED
+#  define PNG_bKGD_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_cHRM
+#  define PNG_READ_cHRM_SUPPORTED
+#  define PNG_cHRM_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_gAMA
+#  define PNG_READ_gAMA_SUPPORTED
+#  define PNG_gAMA_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_hIST
+#  define PNG_READ_hIST_SUPPORTED
+#  define PNG_hIST_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_iCCP
+#  define PNG_READ_iCCP_SUPPORTED
+#  define PNG_iCCP_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_iTXt
+#  ifndef PNG_READ_iTXt_SUPPORTED
+#    define PNG_READ_iTXt_SUPPORTED
+#  endif
+#  ifndef PNG_iTXt_SUPPORTED
+#    define PNG_iTXt_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_READ_oFFs
+#  define PNG_READ_oFFs_SUPPORTED
+#  define PNG_oFFs_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_pCAL
+#  define PNG_READ_pCAL_SUPPORTED
+#  define PNG_pCAL_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_sCAL
+#  define PNG_READ_sCAL_SUPPORTED
+#  define PNG_sCAL_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_pHYs
+#  define PNG_READ_pHYs_SUPPORTED
+#  define PNG_pHYs_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_sBIT
+#  define PNG_READ_sBIT_SUPPORTED
+#  define PNG_sBIT_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_sPLT
+#  define PNG_READ_sPLT_SUPPORTED
+#  define PNG_sPLT_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_sRGB
+#  define PNG_READ_sRGB_SUPPORTED
+#  define PNG_sRGB_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_tEXt
+#  define PNG_READ_tEXt_SUPPORTED
+#  define PNG_tEXt_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_tIME
+#  define PNG_READ_tIME_SUPPORTED
+#  define PNG_tIME_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_tRNS
+#  define PNG_READ_tRNS_SUPPORTED
+#  define PNG_tRNS_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_zTXt
+#  define PNG_READ_zTXt_SUPPORTED
+#  define PNG_zTXt_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_UNKNOWN_CHUNKS
+#  define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+#  ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED
+#    define PNG_UNKNOWN_CHUNKS_SUPPORTED
+#  endif
+#  ifndef PNG_NO_HANDLE_AS_UNKNOWN
+#    define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+#  endif
+#endif
+#if !defined(PNG_NO_READ_USER_CHUNKS) && \
+     defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
+#  define PNG_READ_USER_CHUNKS_SUPPORTED
+#  define PNG_USER_CHUNKS_SUPPORTED
+#  ifdef PNG_NO_READ_UNKNOWN_CHUNKS
+#    undef PNG_NO_READ_UNKNOWN_CHUNKS
+#  endif
+#  ifdef PNG_NO_HANDLE_AS_UNKNOWN
+#    undef PNG_NO_HANDLE_AS_UNKNOWN
+#  endif
+#endif
+#ifndef PNG_NO_READ_OPT_PLTE
+#  define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */
+#endif                      /* optional PLTE chunk in RGB and RGBA images */
+#if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \
+    defined(PNG_READ_zTXt_SUPPORTED)
+#  define PNG_READ_TEXT_SUPPORTED
+#  define PNG_TEXT_SUPPORTED
+#endif
+
+#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */
+
+#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
+
+#ifdef PNG_NO_WRITE_TEXT
+#  define PNG_NO_WRITE_iTXt
+#  define PNG_NO_WRITE_tEXt
+#  define PNG_NO_WRITE_zTXt
+#endif
+#ifndef PNG_NO_WRITE_bKGD
+#  define PNG_WRITE_bKGD_SUPPORTED
+#  ifndef PNG_bKGD_SUPPORTED
+#    define PNG_bKGD_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_cHRM
+#  define PNG_WRITE_cHRM_SUPPORTED
+#  ifndef PNG_cHRM_SUPPORTED
+#    define PNG_cHRM_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_gAMA
+#  define PNG_WRITE_gAMA_SUPPORTED
+#  ifndef PNG_gAMA_SUPPORTED
+#    define PNG_gAMA_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_hIST
+#  define PNG_WRITE_hIST_SUPPORTED
+#  ifndef PNG_hIST_SUPPORTED
+#    define PNG_hIST_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_iCCP
+#  define PNG_WRITE_iCCP_SUPPORTED
+#  ifndef PNG_iCCP_SUPPORTED
+#    define PNG_iCCP_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_iTXt
+#  ifndef PNG_WRITE_iTXt_SUPPORTED
+#    define PNG_WRITE_iTXt_SUPPORTED
+#  endif
+#  ifndef PNG_iTXt_SUPPORTED
+#    define PNG_iTXt_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_oFFs
+#  define PNG_WRITE_oFFs_SUPPORTED
+#  ifndef PNG_oFFs_SUPPORTED
+#    define PNG_oFFs_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_pCAL
+#  define PNG_WRITE_pCAL_SUPPORTED
+#  ifndef PNG_pCAL_SUPPORTED
+#    define PNG_pCAL_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_sCAL
+#  define PNG_WRITE_sCAL_SUPPORTED
+#  ifndef PNG_sCAL_SUPPORTED
+#    define PNG_sCAL_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_pHYs
+#  define PNG_WRITE_pHYs_SUPPORTED
+#  ifndef PNG_pHYs_SUPPORTED
+#    define PNG_pHYs_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_sBIT
+#  define PNG_WRITE_sBIT_SUPPORTED
+#  ifndef PNG_sBIT_SUPPORTED
+#    define PNG_sBIT_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_sPLT
+#  define PNG_WRITE_sPLT_SUPPORTED
+#  ifndef PNG_sPLT_SUPPORTED
+#    define PNG_sPLT_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_sRGB
+#  define PNG_WRITE_sRGB_SUPPORTED
+#  ifndef PNG_sRGB_SUPPORTED
+#    define PNG_sRGB_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_tEXt
+#  define PNG_WRITE_tEXt_SUPPORTED
+#  ifndef PNG_tEXt_SUPPORTED
+#    define PNG_tEXt_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_tIME
+#  define PNG_WRITE_tIME_SUPPORTED
+#  ifndef PNG_tIME_SUPPORTED
+#    define PNG_tIME_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_tRNS
+#  define PNG_WRITE_tRNS_SUPPORTED
+#  ifndef PNG_tRNS_SUPPORTED
+#    define PNG_tRNS_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_zTXt
+#  define PNG_WRITE_zTXt_SUPPORTED
+#  ifndef PNG_zTXt_SUPPORTED
+#    define PNG_zTXt_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS
+#  define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+#  ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED
+#    define PNG_UNKNOWN_CHUNKS_SUPPORTED
+#  endif
+#  ifndef PNG_NO_HANDLE_AS_UNKNOWN
+#     ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+#       define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+#     endif
+#  endif
+#endif
+#if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \
+    defined(PNG_WRITE_zTXt_SUPPORTED)
+#  define PNG_WRITE_TEXT_SUPPORTED
+#  ifndef PNG_TEXT_SUPPORTED
+#    define PNG_TEXT_SUPPORTED
+#  endif
+#endif
+
+#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */
+
+/* Turn this off to disable png_read_png() and
+ * png_write_png() and leave the row_pointers member
+ * out of the info structure.
+ */
+#ifndef PNG_NO_INFO_IMAGE
+#  define PNG_INFO_IMAGE_SUPPORTED
+#endif
+
+/* need the time information for reading tIME chunks */
+#if defined(PNG_tIME_SUPPORTED)
+#  if !defined(_WIN32_WCE)
+     /* "time.h" functions are not supported on WindowsCE */
+#    include <time.h>
+#  endif
+#endif
+
+/* Some typedefs to get us started.  These should be safe on most of the
+ * common platforms.  The typedefs should be at least as large as the
+ * numbers suggest (a png_uint_32 must be at least 32 bits long), but they
+ * don't have to be exactly that size.  Some compilers dislike passing
+ * unsigned shorts as function parameters, so you may be better off using
+ * unsigned int for png_uint_16.  Likewise, for 64-bit systems, you may
+ * want to have unsigned int for png_uint_32 instead of unsigned long.
+ */
+
+typedef unsigned long png_uint_32;
+typedef long png_int_32;
+typedef unsigned short png_uint_16;
+typedef short png_int_16;
+typedef unsigned char png_byte;
+
+/* This is usually size_t.  It is typedef'ed just in case you need it to
+   change (I'm not sure if you will or not, so I thought I'd be safe) */
+#ifdef PNG_SIZE_T
+   typedef PNG_SIZE_T png_size_t;
+#  define png_sizeof(x) png_convert_size(sizeof (x))
+#else
+   typedef size_t png_size_t;
+#  define png_sizeof(x) sizeof (x)
+#endif
+
+/* The following is needed for medium model support.  It cannot be in the
+ * PNG_INTERNAL section.  Needs modification for other compilers besides
+ * MSC.  Model independent support declares all arrays and pointers to be
+ * large using the far keyword.  The zlib version used must also support
+ * model independent data.  As of version zlib 1.0.4, the necessary changes
+ * have been made in zlib.  The USE_FAR_KEYWORD define triggers other
+ * changes that are needed. (Tim Wegner)
+ */
+
+/* Separate compiler dependencies (problem here is that zlib.h always
+   defines FAR. (SJT) */
+#ifdef __BORLANDC__
+#  if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__)
+#    define LDATA 1
+#  else
+#    define LDATA 0
+#  endif
+   /* GRR:  why is Cygwin in here?  Cygwin is not Borland C... */
+#  if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__)
+#    define PNG_MAX_MALLOC_64K
+#    if (LDATA != 1)
+#      ifndef FAR
+#        define FAR __far
+#      endif
+#      define USE_FAR_KEYWORD
+#    endif   /* LDATA != 1 */
+     /* Possibly useful for moving data out of default segment.
+      * Uncomment it if you want. Could also define FARDATA as
+      * const if your compiler supports it. (SJT)
+#    define FARDATA FAR
+      */
+#  endif  /* __WIN32__, __FLAT__, __CYGWIN__ */
+#endif   /* __BORLANDC__ */
+
+
+/* Suggest testing for specific compiler first before testing for
+ * FAR.  The Watcom compiler defines both __MEDIUM__ and M_I86MM,
+ * making reliance oncertain keywords suspect. (SJT)
+ */
+
+/* MSC Medium model */
+#if defined(FAR)
+#  if defined(M_I86MM)
+#    define USE_FAR_KEYWORD
+#    define FARDATA FAR
+#    include <dos.h>
+#  endif
+#endif
+
+/* SJT: default case */
+#ifndef FAR
+#  define FAR
+#endif
+
+/* At this point FAR is always defined */
+#ifndef FARDATA
+#  define FARDATA
+#endif
+
+/* Typedef for floating-point numbers that are converted
+   to fixed-point with a multiple of 100,000, e.g., int_gamma */
+typedef png_int_32 png_fixed_point;
+
+/* Add typedefs for pointers */
+typedef void            FAR * png_voidp;
+typedef png_byte        FAR * png_bytep;
+typedef png_uint_32     FAR * png_uint_32p;
+typedef png_int_32      FAR * png_int_32p;
+typedef png_uint_16     FAR * png_uint_16p;
+typedef png_int_16      FAR * png_int_16p;
+typedef PNG_CONST char  FAR * png_const_charp;
+typedef char            FAR * png_charp;
+typedef png_fixed_point FAR * png_fixed_point_p;
+
+#ifndef PNG_NO_STDIO
+#if defined(_WIN32_WCE)
+typedef HANDLE                png_FILE_p;
+#else
+typedef FILE                * png_FILE_p;
+#endif
+#endif
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+typedef double          FAR * png_doublep;
+#endif
+
+/* Pointers to pointers; i.e. arrays */
+typedef png_byte        FAR * FAR * png_bytepp;
+typedef png_uint_32     FAR * FAR * png_uint_32pp;
+typedef png_int_32      FAR * FAR * png_int_32pp;
+typedef png_uint_16     FAR * FAR * png_uint_16pp;
+typedef png_int_16      FAR * FAR * png_int_16pp;
+typedef PNG_CONST char  FAR * FAR * png_const_charpp;
+typedef char            FAR * FAR * png_charpp;
+typedef png_fixed_point FAR * FAR * png_fixed_point_pp;
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+typedef double          FAR * FAR * png_doublepp;
+#endif
+
+/* Pointers to pointers to pointers; i.e., pointer to array */
+typedef char            FAR * FAR * FAR * png_charppp;
+
+#if defined(PNG_1_0_X) || defined(PNG_1_2_X)
+/* SPC -  Is this stuff deprecated? */
+/* It'll be removed as of libpng-1.3.0 - GR-P */
+/* libpng typedefs for types in zlib. If zlib changes
+ * or another compression library is used, then change these.
+ * Eliminates need to change all the source files.
+ */
+typedef charf *         png_zcharp;
+typedef charf * FAR *   png_zcharpp;
+typedef z_stream FAR *  png_zstreamp;
+#endif /* (PNG_1_0_X) || defined(PNG_1_2_X) */
+
+/*
+ * Define PNG_BUILD_DLL if the module being built is a Windows
+ * LIBPNG DLL.
+ *
+ * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL.
+ * It is equivalent to Microsoft predefined macro _DLL that is
+ * automatically defined when you compile using the share
+ * version of the CRT (C Run-Time library)
+ *
+ * The cygwin mods make this behavior a little different:
+ * Define PNG_BUILD_DLL if you are building a dll for use with cygwin
+ * Define PNG_STATIC if you are building a static library for use with cygwin,
+ *   -or- if you are building an application that you want to link to the
+ *   static library.
+ * PNG_USE_DLL is defined by default (no user action needed) unless one of
+ *   the other flags is defined.
+ */
+
+#if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL))
+#  define PNG_DLL
+#endif
+/* If CYGWIN, then disallow GLOBAL ARRAYS unless building a static lib.
+ * When building a static lib, default to no GLOBAL ARRAYS, but allow
+ * command-line override
+ */
+#if defined(__CYGWIN__)
+#  if !defined(PNG_STATIC)
+#    if defined(PNG_USE_GLOBAL_ARRAYS)
+#      undef PNG_USE_GLOBAL_ARRAYS
+#    endif
+#    if !defined(PNG_USE_LOCAL_ARRAYS)
+#      define PNG_USE_LOCAL_ARRAYS
+#    endif
+#  else
+#    if defined(PNG_USE_LOCAL_ARRAYS) || defined(PNG_NO_GLOBAL_ARRAYS)
+#      if defined(PNG_USE_GLOBAL_ARRAYS)
+#        undef PNG_USE_GLOBAL_ARRAYS
+#      endif
+#    endif
+#  endif
+#  if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS)
+#    define PNG_USE_LOCAL_ARRAYS
+#  endif
+#endif
+
+/* Do not use global arrays (helps with building DLL's)
+ * They are no longer used in libpng itself, since version 1.0.5c,
+ * but might be required for some pre-1.0.5c applications.
+ */
+#if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS)
+#  if defined(PNG_NO_GLOBAL_ARRAYS) || (defined(__GNUC__) && defined(PNG_DLL))
+#    define PNG_USE_LOCAL_ARRAYS
+#  else
+#    define PNG_USE_GLOBAL_ARRAYS
+#  endif
+#endif
+
+#if defined(__CYGWIN__)
+#  undef PNGAPI
+#  define PNGAPI __cdecl
+#  undef PNG_IMPEXP
+#  define PNG_IMPEXP
+#endif  
+
+/* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall",
+ * you may get warnings regarding the linkage of png_zalloc and png_zfree.
+ * Don't ignore those warnings; you must also reset the default calling
+ * convention in your compiler to match your PNGAPI, and you must build
+ * zlib and your applications the same way you build libpng.
+ */
+
+#if defined(__MINGW32__) && !defined(PNG_MODULEDEF)
+#  ifndef PNG_NO_MODULEDEF
+#    define PNG_NO_MODULEDEF
+#  endif
+#endif
+
+#if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF)
+#  define PNG_IMPEXP
+#endif
+
+#if defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \
+    (( defined(_Windows) || defined(_WINDOWS) || \
+       defined(WIN32) || defined(_WIN32) || defined(__WIN32__) ))
+
+#  ifndef PNGAPI
+#     if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800))
+#        define PNGAPI __cdecl
+#     else
+#        define PNGAPI _cdecl
+#     endif
+#  endif
+
+#  if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \
+       0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */)
+#     define PNG_IMPEXP
+#  endif
+
+#  if !defined(PNG_IMPEXP)
+
+#     define PNG_EXPORT_TYPE1(type,symbol)  PNG_IMPEXP type PNGAPI symbol
+#     define PNG_EXPORT_TYPE2(type,symbol)  type PNG_IMPEXP PNGAPI symbol
+
+      /* Borland/Microsoft */
+#     if defined(_MSC_VER) || defined(__BORLANDC__)
+#        if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500)
+#           define PNG_EXPORT PNG_EXPORT_TYPE1
+#        else
+#           define PNG_EXPORT PNG_EXPORT_TYPE2
+#           if defined(PNG_BUILD_DLL)
+#              define PNG_IMPEXP __export
+#           else
+#              define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in
+                                                 VC++ */
+#           endif                             /* Exists in Borland C++ for
+                                                 C++ classes (== huge) */
+#        endif
+#     endif
+
+#     if !defined(PNG_IMPEXP)
+#        if defined(PNG_BUILD_DLL)
+#           define PNG_IMPEXP __declspec(dllexport)
+#        else
+#           define PNG_IMPEXP __declspec(dllimport)
+#        endif
+#     endif
+#  endif  /* PNG_IMPEXP */
+#else /* !(DLL || non-cygwin WINDOWS) */
+#   if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__)
+#      ifndef PNGAPI
+#         define PNGAPI _System
+#      endif
+#   else
+#      if 0 /* ... other platforms, with other meanings */
+#      endif
+#   endif
+#endif
+
+#ifndef PNGAPI
+#  define PNGAPI
+#endif
+#ifndef PNG_IMPEXP
+#  define PNG_IMPEXP
+#endif
+
+#ifdef PNG_BUILDSYMS
+#  ifndef PNG_EXPORT
+#    define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END
+#  endif
+#  ifdef PNG_USE_GLOBAL_ARRAYS
+#    ifndef PNG_EXPORT_VAR
+#      define PNG_EXPORT_VAR(type) PNG_DATA_EXPORT
+#    endif
+#  endif
+#endif
+
+#ifndef PNG_EXPORT
+#  define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol
+#endif
+
+#ifdef PNG_USE_GLOBAL_ARRAYS
+#  ifndef PNG_EXPORT_VAR
+#    define PNG_EXPORT_VAR(type) extern PNG_IMPEXP type
+#  endif
+#endif
+
+/* User may want to use these so they are not in PNG_INTERNAL. Any library
+ * functions that are passed far data must be model independent.
+ */
+
+#ifndef PNG_ABORT
+#  define PNG_ABORT() abort()
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+#  define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
+#else
+#  define png_jmpbuf(png_ptr) \
+   (LIBPNG_WAS_COMPILED_WITH__PNG_SETJMP_NOT_SUPPORTED)
+#endif
+
+#if defined(USE_FAR_KEYWORD)  /* memory model independent fns */
+/* use this to make far-to-near assignments */
+#  define CHECK   1
+#  define NOCHECK 0
+#  define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK))
+#  define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK))
+#  define png_strcpy  _fstrcpy
+#  define png_strncpy _fstrncpy   /* Added to v 1.2.6 */
+#  define png_strlen  _fstrlen
+#  define png_memcmp  _fmemcmp    /* SJT: added */
+#  define png_memcpy  _fmemcpy
+#  define png_memset  _fmemset
+#else /* use the usual functions */
+#  define CVT_PTR(ptr)         (ptr)
+#  define CVT_PTR_NOCHECK(ptr) (ptr)
+#  define png_strcpy  strcpy
+#  define png_strncpy strncpy     /* Added to v 1.2.6 */
+#  define png_strlen  strlen
+#  define png_memcmp  memcmp      /* SJT: added */
+#  define png_memcpy  memcpy
+#  define png_memset  memset
+#endif
+/* End of memory model independent support */
+
+/* Just a little check that someone hasn't tried to define something
+ * contradictory.
+ */
+#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K)
+#  undef PNG_ZBUF_SIZE
+#  define PNG_ZBUF_SIZE 65536L
+#endif
+
+#ifdef PNG_READ_SUPPORTED
+/* Prior to libpng-1.0.9, this block was in pngasmrd.h */
+#if defined(PNG_INTERNAL)
+
+/* These are the default thresholds before the MMX code kicks in; if either
+ * rowbytes or bitdepth is below the threshold, plain C code is used.  These
+ * can be overridden at runtime via the png_set_mmx_thresholds() call in
+ * libpng 1.2.0 and later.  The values below were chosen by Intel.
+ */
+
+#ifndef PNG_MMX_ROWBYTES_THRESHOLD_DEFAULT
+#  define PNG_MMX_ROWBYTES_THRESHOLD_DEFAULT  128  /*  >=  */
+#endif
+#ifndef PNG_MMX_BITDEPTH_THRESHOLD_DEFAULT
+#  define PNG_MMX_BITDEPTH_THRESHOLD_DEFAULT  9    /*  >=  */   
+#endif
+
+/* Set this in the makefile for VC++ on Pentium, not here. */
+/* Platform must be Pentium.  Makefile must assemble and load pngvcrd.c .
+ * MMX will be detected at run time and used if present.
+ */
+#ifdef PNG_USE_PNGVCRD
+#  define PNG_HAVE_ASSEMBLER_COMBINE_ROW
+#  define PNG_HAVE_ASSEMBLER_READ_INTERLACE
+#  define PNG_HAVE_ASSEMBLER_READ_FILTER_ROW
+#endif
+
+/* Set this in the makefile for gcc/as on Pentium, not here. */
+/* Platform must be Pentium.  Makefile must assemble and load pnggccrd.c .
+ * MMX will be detected at run time and used if present.
+ */
+#ifdef PNG_USE_PNGGCCRD
+#  define PNG_HAVE_ASSEMBLER_COMBINE_ROW
+#  define PNG_HAVE_ASSEMBLER_READ_INTERLACE
+#  define PNG_HAVE_ASSEMBLER_READ_FILTER_ROW
+#endif
+/* - see pnggccrd.c for info about what is currently enabled */
+
+#endif /* PNG_INTERNAL */
+#endif /* PNG_READ_SUPPORTED */
+
+/* Added at libpng-1.2.8 */
+#endif /* PNG_VERSION_INFO_ONLY */
+
+#endif /* PNGCONF_H */
diff --git a/Utilities/GDAL/frmts/png/libpng/pngerror.c b/Utilities/GDAL/frmts/png/libpng/pngerror.c
new file mode 100644
index 0000000000..6fa4012378
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngerror.c
@@ -0,0 +1,295 @@
+
+/* pngerror.c - stub functions for i/o and memory allocation
+ *
+ * libpng version 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This file provides a location for all error handling.  Users who
+ * need special error handling are expected to write replacement functions
+ * and use png_set_error_fn() to use those functions.  See the instructions
+ * at each function.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+static void /* PRIVATE */
+png_default_error PNGARG((png_structp png_ptr,
+  png_const_charp error_message));
+static void /* PRIVATE */
+png_default_warning PNGARG((png_structp png_ptr,
+  png_const_charp warning_message));
+
+/* This function is called whenever there is a fatal error.  This function
+ * should not be changed.  If there is a need to handle errors differently,
+ * you should supply a replacement error function and use png_set_error_fn()
+ * to replace the error function at run-time.
+ */
+void PNGAPI
+png_error(png_structp png_ptr, png_const_charp error_message)
+{
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+   char msg[16];
+   if (png_ptr->flags&(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))
+   {
+     if (*error_message == '#')
+     {
+         int offset;
+         for (offset=1; offset<15; offset++)
+            if (*(error_message+offset) == ' ')
+                break;
+         if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT)
+         {
+            int i;
+            for (i=0; i<offset-1; i++)
+               msg[i]=error_message[i+1];
+            msg[i]='\0';
+            error_message=msg;
+         }
+         else
+            error_message+=offset;
+     }
+     else
+     {
+         if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT)
+         {
+            msg[0]='0';        
+            msg[1]='\0';
+            error_message=msg;
+         }
+     }
+   }
+#endif
+   if (png_ptr != NULL && png_ptr->error_fn != NULL)
+      (*(png_ptr->error_fn))(png_ptr, error_message);
+
+   /* If the custom handler doesn't exist, or if it returns,
+      use the default handler, which will not return. */
+   png_default_error(png_ptr, error_message);
+}
+
+/* This function is called whenever there is a non-fatal error.  This function
+ * should not be changed.  If there is a need to handle warnings differently,
+ * you should supply a replacement warning function and use
+ * png_set_error_fn() to replace the warning function at run-time.
+ */
+void PNGAPI
+png_warning(png_structp png_ptr, png_const_charp warning_message)
+{
+   int offset = 0;
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+   if (png_ptr->flags&(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))
+#endif
+   {
+     if (*warning_message == '#')
+     {
+         for (offset=1; offset<15; offset++)
+            if (*(warning_message+offset) == ' ')
+                break;
+     }
+   }
+   if (png_ptr != NULL && png_ptr->warning_fn != NULL)
+      (*(png_ptr->warning_fn))(png_ptr, warning_message+offset);
+   else
+      png_default_warning(png_ptr, warning_message+offset);
+}
+
+/* These utilities are used internally to build an error message that relates
+ * to the current chunk.  The chunk name comes from png_ptr->chunk_name,
+ * this is used to prefix the message.  The message is limited in length
+ * to 63 bytes, the name characters are output as hex digits wrapped in []
+ * if the character is invalid.
+ */
+#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
+static PNG_CONST char png_digit[16] = {
+   '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+   'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+static void /* PRIVATE */
+png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp
+   error_message)
+{
+   int iout = 0, iin = 0;
+
+   while (iin < 4)
+   {
+      int c = png_ptr->chunk_name[iin++];
+      if (isnonalpha(c))
+      {
+         buffer[iout++] = '[';
+         buffer[iout++] = png_digit[(c & 0xf0) >> 4];
+         buffer[iout++] = png_digit[c & 0x0f];
+         buffer[iout++] = ']';
+      }
+      else
+      {
+         buffer[iout++] = (png_byte)c;
+      }
+   }
+
+   if (error_message == NULL)
+      buffer[iout] = 0;
+   else
+   {
+      buffer[iout++] = ':';
+      buffer[iout++] = ' ';
+      png_strncpy(buffer+iout, error_message, 63);
+      buffer[iout+63] = 0;
+   }
+}
+
+void PNGAPI
+png_chunk_error(png_structp png_ptr, png_const_charp error_message)
+{
+   char msg[18+64];
+   png_format_buffer(png_ptr, msg, error_message);
+   png_error(png_ptr, msg);
+}
+
+void PNGAPI
+png_chunk_warning(png_structp png_ptr, png_const_charp warning_message)
+{
+   char msg[18+64];
+   png_format_buffer(png_ptr, msg, warning_message);
+   png_warning(png_ptr, msg);
+}
+
+/* This is the default error handling function.  Note that replacements for
+ * this function MUST NOT RETURN, or the program will likely crash.  This
+ * function is used by default, or if the program supplies NULL for the
+ * error function pointer in png_set_error_fn().
+ */
+static void /* PRIVATE */
+png_default_error(png_structp png_ptr, png_const_charp error_message)
+{
+#ifndef PNG_NO_CONSOLE_IO
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+   if (*error_message == '#')
+   {
+     int offset;
+     char error_number[16];
+     for (offset=0; offset<15; offset++)
+     {
+         error_number[offset] = *(error_message+offset+1);
+         if (*(error_message+offset) == ' ')
+             break;
+     }
+     if((offset > 1) && (offset < 15))
+     {
+       error_number[offset-1]='\0';
+       fprintf(stderr, "libpng error no. %s: %s\n", error_number,
+          error_message+offset);
+     }
+     else
+       fprintf(stderr, "libpng error: %s, offset=%d\n", error_message,offset);
+   }
+   else
+#endif
+   fprintf(stderr, "libpng error: %s\n", error_message);
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+#  ifdef USE_FAR_KEYWORD
+   {
+      jmp_buf jmpbuf;
+      png_memcpy(jmpbuf,png_ptr->jmpbuf,png_sizeof(jmp_buf));
+      longjmp(jmpbuf, 1);
+   }
+#  else
+   longjmp(png_ptr->jmpbuf, 1);
+# endif
+#else
+   /* make compiler happy */ ;
+   if (png_ptr)
+   PNG_ABORT();
+#endif
+#ifdef PNG_NO_CONSOLE_IO
+   /* make compiler happy */ ;
+   if (&error_message != NULL)
+      return;
+#endif
+}
+
+/* This function is called when there is a warning, but the library thinks
+ * it can continue anyway.  Replacement functions don't have to do anything
+ * here if you don't want them to.  In the default configuration, png_ptr is
+ * not used, but it is passed in case it may be useful.
+ */
+static void /* PRIVATE */
+png_default_warning(png_structp png_ptr, png_const_charp warning_message)
+{
+#ifndef PNG_NO_CONSOLE_IO
+#  ifdef PNG_ERROR_NUMBERS_SUPPORTED
+   if (*warning_message == '#')
+   {
+     int offset;
+     char warning_number[16];
+     for (offset=0; offset<15; offset++)
+     {
+        warning_number[offset]=*(warning_message+offset+1);
+        if (*(warning_message+offset) == ' ')
+            break;
+     }
+     if((offset > 1) && (offset < 15))
+     {
+       warning_number[offset-1]='\0';
+       fprintf(stderr, "libpng warning no. %s: %s\n", warning_number,
+          warning_message+offset);
+     }
+     else
+       fprintf(stderr, "libpng warning: %s\n", warning_message);
+   }
+   else
+#  endif
+     fprintf(stderr, "libpng warning: %s\n", warning_message);
+#else
+   /* make compiler happy */ ;
+   if (warning_message)
+     return;
+#endif
+   /* make compiler happy */ ;
+   if (png_ptr)
+      return;
+}
+
+/* This function is called when the application wants to use another method
+ * of handling errors and warnings.  Note that the error function MUST NOT
+ * return to the calling routine or serious problems will occur.  The return
+ * method used in the default routine calls longjmp(png_ptr->jmpbuf, 1)
+ */
+void PNGAPI
+png_set_error_fn(png_structp png_ptr, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warning_fn)
+{
+   png_ptr->error_ptr = error_ptr;
+   png_ptr->error_fn = error_fn;
+   png_ptr->warning_fn = warning_fn;
+}
+
+
+/* This function returns a pointer to the error_ptr associated with the user
+ * functions.  The application should free any memory associated with this
+ * pointer before png_write_destroy and png_read_destroy are called.
+ */
+png_voidp PNGAPI
+png_get_error_ptr(png_structp png_ptr)
+{
+   return ((png_voidp)png_ptr->error_ptr);
+}
+
+
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+void PNGAPI
+png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode)
+{
+   if(png_ptr != NULL)
+   {
+     png_ptr->flags &=
+       ((~(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode);
+   }
+}
+#endif
diff --git a/Utilities/GDAL/frmts/png/libpng/pnggccrd.c b/Utilities/GDAL/frmts/png/libpng/pnggccrd.c
new file mode 100644
index 0000000000..248e1b3bb4
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pnggccrd.c
@@ -0,0 +1,5408 @@
+/* pnggccrd.c - mixed C/assembler version of utilities to read a PNG file
+ *
+ * For Intel x86 CPU (Pentium-MMX or later) and GNU C compiler.
+ *
+ *     See http://www.intel.com/drg/pentiumII/appnotes/916/916.htm
+ *     and http://www.intel.com/drg/pentiumII/appnotes/923/923.htm
+ *     for Intel's performance analysis of the MMX vs. non-MMX code.
+ *
+ * libpng version 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * Copyright (c) 1998, Intel Corporation
+ *
+ * Based on MSVC code contributed by Nirav Chhatrapati, Intel Corp., 1998.
+ * Interface to libpng contributed by Gilles Vollant, 1999.
+ * GNU C port by Greg Roelofs, 1999-2001.
+ *
+ * Lines 2350-4300 converted in place with intel2gas 1.3.1:
+ *
+ *   intel2gas -mdI pnggccrd.c.partially-msvc -o pnggccrd.c
+ *
+ * and then cleaned up by hand.  See http://hermes.terminal.at/intel2gas/ .
+ *
+ * NOTE:  A sufficiently recent version of GNU as (or as.exe under DOS/Windows)
+ *        is required to assemble the newer MMX instructions such as movq.
+ *        For djgpp, see
+ *
+ *           ftp://ftp.simtel.net/pub/simtelnet/gnu/djgpp/v2gnu/bnu281b.zip
+ *
+ *        (or a later version in the same directory).  For Linux, check your
+ *        distribution's web site(s) or try these links:
+ *
+ *           http://rufus.w3.org/linux/RPM/binutils.html
+ *           http://www.debian.org/Packages/stable/devel/binutils.html
+ *           ftp://ftp.slackware.com/pub/linux/slackware/slackware/slakware/d1/
+ *             binutils.tgz
+ *
+ *        For other platforms, see the main GNU site:
+ *
+ *           ftp://ftp.gnu.org/pub/gnu/binutils/
+ *
+ *        Version 2.5.2l.15 is definitely too old...
+ */
+
+/*
+ * TEMPORARY PORTING NOTES AND CHANGELOG (mostly by Greg Roelofs)
+ * =====================================
+ *
+ * 19991006:
+ *  - fixed sign error in post-MMX cleanup code (16- & 32-bit cases)
+ *
+ * 19991007:
+ *  - additional optimizations (possible or definite):
+ *     x [DONE] write MMX code for 64-bit case (pixel_bytes == 8) [not tested]
+ *     - write MMX code for 48-bit case (pixel_bytes == 6)
+ *     - figure out what's up with 24-bit case (pixel_bytes == 3):
+ *        why subtract 8 from width_mmx in the pass 4/5 case?
+ *        (only width_mmx case) (near line 1606)
+ *     x [DONE] replace pixel_bytes within each block with the true
+ *        constant value (or are compilers smart enough to do that?)
+ *     - rewrite all MMX interlacing code so it's aligned with
+ *        the *beginning* of the row buffer, not the end.  This
+ *        would not only allow one to eliminate half of the memory
+ *        writes for odd passes (that is, pass == odd), it may also
+ *        eliminate some unaligned-data-access exceptions (assuming
+ *        there's a penalty for not aligning 64-bit accesses on
+ *        64-bit boundaries).  The only catch is that the "leftover"
+ *        pixel(s) at the end of the row would have to be saved,
+ *        but there are enough unused MMX registers in every case,
+ *        so this is not a problem.  A further benefit is that the
+ *        post-MMX cleanup code (C code) in at least some of the
+ *        cases could be done within the assembler block.
+ *  x [DONE] the "v3 v2 v1 v0 v7 v6 v5 v4" comments are confusing,
+ *     inconsistent, and don't match the MMX Programmer's Reference
+ *     Manual conventions anyway.  They should be changed to
+ *     "b7 b6 b5 b4 b3 b2 b1 b0," where b0 indicates the byte that
+ *     was lowest in memory (e.g., corresponding to a left pixel)
+ *     and b7 is the byte that was highest (e.g., a right pixel).
+ *
+ * 19991016:
+ *  - Brennan's Guide notwithstanding, gcc under Linux does *not*
+ *     want globals prefixed by underscores when referencing them--
+ *     i.e., if the variable is const4, then refer to it as const4,
+ *     not _const4.  This seems to be a djgpp-specific requirement.
+ *     Also, such variables apparently *must* be declared outside
+ *     of functions; neither static nor automatic variables work if
+ *     defined within the scope of a single function, but both
+ *     static and truly global (multi-module) variables work fine.
+ *
+ * 19991023:
+ *  - fixed png_combine_row() non-MMX replication bug (odd passes only?)
+ *  - switched from string-concatenation-with-macros to cleaner method of
+ *     renaming global variables for djgpp--i.e., always use prefixes in
+ *     inlined assembler code (== strings) and conditionally rename the
+ *     variables, not the other way around.  Hence _const4, _mask8_0, etc.
+ *
+ * 19991024:
+ *  - fixed mmxsupport()/png_do_read_interlace() first-row bug
+ *     This one was severely weird:  even though mmxsupport() doesn't touch
+ *     ebx (where "row" pointer was stored), it nevertheless managed to zero
+ *     the register (even in static/non-fPIC code--see below), which in turn
+ *     caused png_do_read_interlace() to return prematurely on the first row of
+ *     interlaced images (i.e., without expanding the interlaced pixels).
+ *     Inspection of the generated assembly code didn't turn up any clues,
+ *     although it did point at a minor optimization (i.e., get rid of
+ *     mmx_supported_local variable and just use eax).  Possibly the CPUID
+ *     instruction is more destructive than it looks?  (Not yet checked.)
+ *  - "info gcc" was next to useless, so compared fPIC and non-fPIC assembly
+ *     listings...  Apparently register spillage has to do with ebx, since
+ *     it's used to index the global offset table.  Commenting it out of the
+ *     input-reg lists in png_combine_row() eliminated compiler barfage, so
+ *     ifdef'd with __PIC__ macro:  if defined, use a global for unmask
+ *
+ * 19991107:
+ *  - verified CPUID clobberage:  12-char string constant ("GenuineIntel",
+ *     "AuthenticAMD", etc.) placed in ebx:ecx:edx.  Still need to polish.
+ *
+ * 19991120:
+ *  - made "diff" variable (now "_dif") global to simplify conversion of
+ *     filtering routines (running out of regs, sigh).  "diff" is still used
+ *     in interlacing routines, however.
+ *  - fixed up both versions of mmxsupport() (ORIG_THAT_USED_TO_CLOBBER_EBX
+ *     macro determines which is used); original not yet tested.
+ *
+ * 20000213:
+ *  - when compiling with gcc, be sure to use  -fomit-frame-pointer
+ *
+ * 20000319:
+ *  - fixed a register-name typo in png_do_read_interlace(), default (MMX) case,
+ *     pass == 4 or 5, that caused visible corruption of interlaced images
+ *
+ * 20000623:
+ *  - Various problems were reported with gcc 2.95.2 in the Cygwin environment,
+ *     many of the form "forbidden register 0 (ax) was spilled for class AREG."
+ *     This is explained at http://gcc.gnu.org/fom_serv/cache/23.html, and
+ *     Chuck Wilson supplied a patch involving dummy output registers.  See
+ *     http://sourceforge.net/bugs/?func=detailbug&bug_id=108741&group_id=5624
+ *     for the original (anonymous) SourceForge bug report.
+ *
+ * 20000706:
+ *  - Chuck Wilson passed along these remaining gcc 2.95.2 errors:
+ *       pnggccrd.c: In function `png_combine_row':
+ *       pnggccrd.c:525: more than 10 operands in `asm'
+ *       pnggccrd.c:669: more than 10 operands in `asm'
+ *       pnggccrd.c:828: more than 10 operands in `asm'
+ *       pnggccrd.c:994: more than 10 operands in `asm'
+ *       pnggccrd.c:1177: more than 10 operands in `asm'
+ *     They are all the same problem and can be worked around by using the
+ *     global _unmask variable unconditionally, not just in the -fPIC case.
+ *     Reportedly earlier versions of gcc also have the problem with more than
+ *     10 operands; they just don't report it.  Much strangeness ensues, etc.
+ *
+ * 20000729:
+ *  - enabled png_read_filter_row_mmx_up() (shortest remaining unconverted
+ *     MMX routine); began converting png_read_filter_row_mmx_sub()
+ *  - to finish remaining sections:
+ *     - clean up indentation and comments
+ *     - preload local variables
+ *     - add output and input regs (order of former determines numerical
+ *        mapping of latter)
+ *     - avoid all usage of ebx (including bx, bh, bl) register [20000823]
+ *     - remove "$" from addressing of Shift and Mask variables [20000823]
+ *
+ * 20000731:
+ *  - global union vars causing segfaults in png_read_filter_row_mmx_sub()?
+ *
+ * 20000822:
+ *  - ARGH, stupid png_read_filter_row_mmx_sub() segfault only happens with
+ *     shared-library (-fPIC) version!  Code works just fine as part of static
+ *     library.  Damn damn damn damn damn, should have tested that sooner.
+ *     ebx is getting clobbered again (explicitly this time); need to save it
+ *     on stack or rewrite asm code to avoid using it altogether.  Blargh!
+ *
+ * 20000823:
+ *  - first section was trickiest; all remaining sections have ebx -> edx now.
+ *     (-fPIC works again.)  Also added missing underscores to various Shift*
+ *     and *Mask* globals and got rid of leading "$" signs.
+ *
+ * 20000826:
+ *  - added visual separators to help navigate microscopic printed copies
+ *     (http://pobox.com/~newt/code/gpr-latest.zip, mode 10); started working
+ *     on png_read_filter_row_mmx_avg()
+ *
+ * 20000828:
+ *  - finished png_read_filter_row_mmx_avg():  only Paeth left! (930 lines...)
+ *     What the hell, did png_read_filter_row_mmx_paeth(), too.  Comments not
+ *     cleaned up/shortened in either routine, but functionality is complete
+ *     and seems to be working fine.
+ *
+ * 20000829:
+ *  - ahhh, figured out last(?) bit of gcc/gas asm-fu:  if register is listed
+ *     as an input reg (with dummy output variables, etc.), then it *cannot*
+ *     also appear in the clobber list or gcc 2.95.2 will barf.  The solution
+ *     is simple enough...
+ *
+ * 20000914:
+ *  - bug in png_read_filter_row_mmx_avg():  16-bit grayscale not handled
+ *     correctly (but 48-bit RGB just fine)
+ *
+ * 20000916:
+ *  - fixed bug in png_read_filter_row_mmx_avg(), bpp == 2 case; three errors:
+ *     - "_ShiftBpp.use = 24;"      should have been   "_ShiftBpp.use = 16;"
+ *     - "_ShiftRem.use = 40;"      should have been   "_ShiftRem.use = 48;"
+ *     - "psllq _ShiftRem, %%mm2"   should have been   "psrlq _ShiftRem, %%mm2"
+ *
+ * 20010101:
+ *  - added new png_init_mmx_flags() function (here only because it needs to
+ *     call mmxsupport(), which should probably become global png_mmxsupport());
+ *     modified other MMX routines to run conditionally (png_ptr->asm_flags)
+ *
+ * 20010103:
+ *  - renamed mmxsupport() to png_mmx_support(), with auto-set of mmx_supported,
+ *     and made it public; moved png_init_mmx_flags() to png.c as internal func
+ *
+ * 20010104:
+ *  - removed dependency on png_read_filter_row_c() (C code already duplicated
+ *     within MMX version of png_read_filter_row()) so no longer necessary to
+ *     compile it into pngrutil.o
+ *
+ * 20010310:
+ *  - fixed buffer-overrun bug in png_combine_row() C code (non-MMX)
+ *
+ * 20020304:
+ *  - eliminated incorrect use of width_mmx in pixel_bytes == 8 case
+ *
+ * 20040724:
+ *   - more tinkering with clobber list at lines 4529 and 5033, to get
+ *     it to compile on gcc-3.4.
+ *
+ * STILL TO DO:
+ *     - test png_do_read_interlace() 64-bit case (pixel_bytes == 8)
+ *     - write MMX code for 48-bit case (pixel_bytes == 6)
+ *     - figure out what's up with 24-bit case (pixel_bytes == 3):
+ *        why subtract 8 from width_mmx in the pass 4/5 case?
+ *        (only width_mmx case) (near line 1606)
+ *     - rewrite all MMX interlacing code so it's aligned with beginning
+ *        of the row buffer, not the end (see 19991007 for details)
+ *     x pick one version of mmxsupport() and get rid of the other
+ *     - add error messages to any remaining bogus default cases
+ *     - enable pixel_depth == 8 cases in png_read_filter_row()? (test speed)
+ *     x add support for runtime enable/disable/query of various MMX routines
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_USE_PNGGCCRD)
+
+int PNGAPI png_mmx_support(void);
+
+#ifdef PNG_USE_LOCAL_ARRAYS
+static const int FARDATA png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+static const int FARDATA png_pass_inc[7]   = {8, 8, 4, 4, 2, 2, 1};
+static const int FARDATA png_pass_width[7] = {8, 4, 4, 2, 2, 1, 1};
+#endif
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+/* djgpp, Win32, and Cygwin add their own underscores to global variables,
+ * so define them without: */
+#if defined(__DJGPP__) || defined(WIN32) || defined(__CYGWIN__)
+#  define _mmx_supported  mmx_supported
+#  define _const4         const4
+#  define _const6         const6
+#  define _mask8_0        mask8_0
+#  define _mask16_1       mask16_1
+#  define _mask16_0       mask16_0
+#  define _mask24_2       mask24_2
+#  define _mask24_1       mask24_1
+#  define _mask24_0       mask24_0
+#  define _mask32_3       mask32_3
+#  define _mask32_2       mask32_2
+#  define _mask32_1       mask32_1
+#  define _mask32_0       mask32_0
+#  define _mask48_5       mask48_5
+#  define _mask48_4       mask48_4
+#  define _mask48_3       mask48_3
+#  define _mask48_2       mask48_2
+#  define _mask48_1       mask48_1
+#  define _mask48_0       mask48_0
+#  define _LBCarryMask    LBCarryMask
+#  define _HBClearMask    HBClearMask
+#  define _ActiveMask     ActiveMask
+#  define _ActiveMask2    ActiveMask2
+#  define _ActiveMaskEnd  ActiveMaskEnd
+#  define _ShiftBpp       ShiftBpp
+#  define _ShiftRem       ShiftRem
+#ifdef PNG_THREAD_UNSAFE_OK
+#  define _unmask         unmask
+#  define _FullLength     FullLength
+#  define _MMXLength      MMXLength
+#  define _dif            dif
+#  define _patemp         patemp
+#  define _pbtemp         pbtemp
+#  define _pctemp         pctemp
+#endif
+#endif
+
+
+/* These constants are used in the inlined MMX assembly code.
+   Ignore gcc's "At top level: defined but not used" warnings. */
+
+/* GRR 20000706:  originally _unmask was needed only when compiling with -fPIC,
+ *  since that case uses the %ebx register for indexing the Global Offset Table
+ *  and there were no other registers available.  But gcc 2.95 and later emit
+ *  "more than 10 operands in `asm'" errors when %ebx is used to preload unmask
+ *  in the non-PIC case, so we'll just use the global unconditionally now.
+ */
+#ifdef PNG_THREAD_UNSAFE_OK
+static int _unmask;
+#endif
+
+static unsigned long long _mask8_0  = 0x0102040810204080LL;
+
+static unsigned long long _mask16_1 = 0x0101020204040808LL;
+static unsigned long long _mask16_0 = 0x1010202040408080LL;
+
+static unsigned long long _mask24_2 = 0x0101010202020404LL;
+static unsigned long long _mask24_1 = 0x0408080810101020LL;
+static unsigned long long _mask24_0 = 0x2020404040808080LL;
+
+static unsigned long long _mask32_3 = 0x0101010102020202LL;
+static unsigned long long _mask32_2 = 0x0404040408080808LL;
+static unsigned long long _mask32_1 = 0x1010101020202020LL;
+static unsigned long long _mask32_0 = 0x4040404080808080LL;
+
+static unsigned long long _mask48_5 = 0x0101010101010202LL;
+static unsigned long long _mask48_4 = 0x0202020204040404LL;
+static unsigned long long _mask48_3 = 0x0404080808080808LL;
+static unsigned long long _mask48_2 = 0x1010101010102020LL;
+static unsigned long long _mask48_1 = 0x2020202040404040LL;
+static unsigned long long _mask48_0 = 0x4040808080808080LL;
+
+static unsigned long long _const4   = 0x0000000000FFFFFFLL;
+//static unsigned long long _const5 = 0x000000FFFFFF0000LL;     // NOT USED
+static unsigned long long _const6   = 0x00000000000000FFLL;
+
+// These are used in the row-filter routines and should/would be local
+//  variables if not for gcc addressing limitations.
+// WARNING: Their presence probably defeats the thread safety of libpng.
+
+#ifdef PNG_THREAD_UNSAFE_OK
+static png_uint_32  _FullLength;
+static png_uint_32  _MMXLength;
+static int          _dif;
+static int          _patemp; // temp variables for Paeth routine
+static int          _pbtemp;
+static int          _pctemp;
+#endif
+
+void /* PRIVATE */
+png_squelch_warnings(void)
+{
+#ifdef PNG_THREAD_UNSAFE_OK
+   _dif = _dif;
+   _patemp = _patemp;
+   _pbtemp = _pbtemp;
+   _pctemp = _pctemp;
+   _MMXLength = _MMXLength;
+#endif
+   _const4  = _const4;
+   _const6  = _const6;
+   _mask8_0  = _mask8_0;
+   _mask16_1 = _mask16_1;
+   _mask16_0 = _mask16_0;
+   _mask24_2 = _mask24_2;
+   _mask24_1 = _mask24_1;
+   _mask24_0 = _mask24_0;
+   _mask32_3 = _mask32_3;
+   _mask32_2 = _mask32_2;
+   _mask32_1 = _mask32_1;
+   _mask32_0 = _mask32_0;
+   _mask48_5 = _mask48_5;
+   _mask48_4 = _mask48_4;
+   _mask48_3 = _mask48_3;
+   _mask48_2 = _mask48_2;
+   _mask48_1 = _mask48_1;
+   _mask48_0 = _mask48_0;
+}
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+
+
+static int _mmx_supported = 2;
+
+/*===========================================================================*/
+/*                                                                           */
+/*                       P N G _ C O M B I N E _ R O W                       */
+/*                                                                           */
+/*===========================================================================*/
+
+#if defined(PNG_HAVE_ASSEMBLER_COMBINE_ROW)
+
+#define BPP2  2
+#define BPP3  3 /* bytes per pixel (a.k.a. pixel_bytes) */
+#define BPP4  4
+#define BPP6  6 /* (defined only to help avoid cut-and-paste errors) */
+#define BPP8  8
+
+/* Combines the row recently read in with the previous row.
+   This routine takes care of alpha and transparency if requested.
+   This routine also handles the two methods of progressive display
+   of interlaced images, depending on the mask value.
+   The mask value describes which pixels are to be combined with
+   the row.  The pattern always repeats every 8 pixels, so just 8
+   bits are needed.  A one indicates the pixel is to be combined; a
+   zero indicates the pixel is to be skipped.  This is in addition
+   to any alpha or transparency value associated with the pixel.
+   If you want all pixels to be combined, pass 0xff (255) in mask. */
+
+/* Use this routine for the x86 platform - it uses a faster MMX routine
+   if the machine supports MMX. */
+
+void /* PRIVATE */
+png_combine_row(png_structp png_ptr, png_bytep row, int mask)
+{
+   png_debug(1, "in png_combine_row (pnggccrd.c)\n");
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+   if (_mmx_supported == 2) {
+#if !defined(PNG_1_0_X)
+       /* this should have happened in png_init_mmx_flags() already */
+       png_warning(png_ptr, "asm_flags may not have been initialized");
+#endif
+       png_mmx_support();
+   }
+#endif
+
+   if (mask == 0xff)
+   {
+      png_debug(2,"mask == 0xff:  doing single png_memcpy()\n");
+      png_memcpy(row, png_ptr->row_buf + 1,
+       (png_size_t)PNG_ROWBYTES(png_ptr->row_info.pixel_depth,png_ptr->width));
+   }
+   else   /* (png_combine_row() is never called with mask == 0) */
+   {
+      switch (png_ptr->row_info.pixel_depth)
+      {
+         case 1:        /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_inc, s_start, s_end;
+            int m;
+            int shift;
+            png_uint_32 i;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+                s_start = 0;
+                s_end = 7;
+                s_inc = 1;
+            }
+            else
+#endif
+            {
+                s_start = 7;
+                s_end = 0;
+                s_inc = -1;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  int value;
+
+                  value = (*sp >> shift) & 0x1;
+                  *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+
+         case 2:        /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_start, s_end, s_inc;
+            int m;
+            int shift;
+            png_uint_32 i;
+            int value;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+            else
+#endif
+            {
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0x3;
+                  *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+
+         case 4:        /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_start, s_end, s_inc;
+            int m;
+            int shift;
+            png_uint_32 i;
+            int value;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+            else
+#endif
+            {
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0xf;
+                  *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+
+         case 8:        /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK)
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && _mmx_supported */ )
+#else
+            if (_mmx_supported)
+#endif
+            {
+               png_uint_32 len;
+               int diff;
+               int dummy_value_a;   // fix 'forbidden register spilled' error
+               int dummy_value_d;
+               int dummy_value_c;
+               int dummy_value_S;
+               int dummy_value_D;
+               _unmask = ~mask;            // global variable for -fPIC version
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+               len  = png_ptr->width &~7;  // reduce to multiple of 8
+               diff = (int) (png_ptr->width & 7);  // amount lost
+
+               __asm__ __volatile__ (
+                  "movd      _unmask, %%mm7  \n\t" // load bit pattern
+                  "psubb     %%mm6, %%mm6    \n\t" // zero mm6
+                  "punpcklbw %%mm7, %%mm7    \n\t"
+                  "punpcklwd %%mm7, %%mm7    \n\t"
+                  "punpckldq %%mm7, %%mm7    \n\t" // fill reg with 8 masks
+
+                  "movq      _mask8_0, %%mm0 \n\t"
+                  "pand      %%mm7, %%mm0    \n\t" // nonzero if keep byte
+                  "pcmpeqb   %%mm6, %%mm0    \n\t" // zeros->1s, v versa
+
+// preload        "movl      len, %%ecx      \n\t" // load length of line
+// preload        "movl      srcptr, %%esi   \n\t" // load source
+// preload        "movl      dstptr, %%edi   \n\t" // load dest
+
+                  "cmpl      $0, %%ecx       \n\t" // len == 0 ?
+                  "je        mainloop8end    \n\t"
+
+                "mainloop8:                  \n\t"
+                  "movq      (%%esi), %%mm4  \n\t" // *srcptr
+                  "pand      %%mm0, %%mm4    \n\t"
+                  "movq      %%mm0, %%mm6    \n\t"
+                  "pandn     (%%edi), %%mm6  \n\t" // *dstptr
+                  "por       %%mm6, %%mm4    \n\t"
+                  "movq      %%mm4, (%%edi)  \n\t"
+                  "addl      $8, %%esi       \n\t" // inc by 8 bytes processed
+                  "addl      $8, %%edi       \n\t"
+                  "subl      $8, %%ecx       \n\t" // dec by 8 pixels processed
+                  "ja        mainloop8       \n\t"
+
+                "mainloop8end:               \n\t"
+// preload        "movl      diff, %%ecx     \n\t" // (diff is in eax)
+                  "movl      %%eax, %%ecx    \n\t"
+                  "cmpl      $0, %%ecx       \n\t"
+                  "jz        end8            \n\t"
+// preload        "movl      mask, %%edx     \n\t"
+                  "sall      $24, %%edx      \n\t" // make low byte, high byte
+
+                "secondloop8:                \n\t"
+                  "sall      %%edx           \n\t" // move high bit to CF
+                  "jnc       skip8           \n\t" // if CF = 0
+                  "movb      (%%esi), %%al   \n\t"
+                  "movb      %%al, (%%edi)   \n\t"
+
+                "skip8:                      \n\t"
+                  "incl      %%esi           \n\t"
+                  "incl      %%edi           \n\t"
+                  "decl      %%ecx           \n\t"
+                  "jnz       secondloop8     \n\t"
+
+                "end8:                       \n\t"
+                  "EMMS                      \n\t"  // DONE
+
+                  : "=a" (dummy_value_a),           // output regs (dummy)
+                    "=d" (dummy_value_d),
+                    "=c" (dummy_value_c),
+                    "=S" (dummy_value_S),
+                    "=D" (dummy_value_D)
+
+                  : "3" (srcptr),      // esi       // input regs
+                    "4" (dstptr),      // edi
+                    "0" (diff),        // eax
+// was (unmask)     "b"    RESERVED    // ebx       // Global Offset Table idx
+                    "2" (len),         // ecx
+                    "1" (mask)         // edx
+
+#if 0  /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */
+                  : "%mm0", "%mm4", "%mm6", "%mm7"  // clobber list
+#endif
+               );
+            }
+            else /* mmx _not supported - Use modified C routine */
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7;  /* reduce to mult. of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = len;  /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val+=diff /* *BPP1 */ ;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+
+            } /* end of else (_mmx_supported) */
+
+            break;
+         }       /* end 8 bpp */
+
+         case 16:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK)
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && _mmx_supported */ )
+#else
+            if (_mmx_supported)
+#endif
+            {
+               png_uint_32 len;
+               int diff;
+               int dummy_value_a;   // fix 'forbidden register spilled' error
+               int dummy_value_d;
+               int dummy_value_c;
+               int dummy_value_S;
+               int dummy_value_D;
+               _unmask = ~mask;            // global variable for -fPIC version
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+               len  = png_ptr->width &~7;  // reduce to multiple of 8
+               diff = (int) (png_ptr->width & 7); // amount lost //
+
+               __asm__ __volatile__ (
+                  "movd      _unmask, %%mm7   \n\t" // load bit pattern
+                  "psubb     %%mm6, %%mm6     \n\t" // zero mm6
+                  "punpcklbw %%mm7, %%mm7     \n\t"
+                  "punpcklwd %%mm7, %%mm7     \n\t"
+                  "punpckldq %%mm7, %%mm7     \n\t" // fill reg with 8 masks
+
+                  "movq      _mask16_0, %%mm0 \n\t"
+                  "movq      _mask16_1, %%mm1 \n\t"
+
+                  "pand      %%mm7, %%mm0     \n\t"
+                  "pand      %%mm7, %%mm1     \n\t"
+
+                  "pcmpeqb   %%mm6, %%mm0     \n\t"
+                  "pcmpeqb   %%mm6, %%mm1     \n\t"
+
+// preload        "movl      len, %%ecx       \n\t" // load length of line
+// preload        "movl      srcptr, %%esi    \n\t" // load source
+// preload        "movl      dstptr, %%edi    \n\t" // load dest
+
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        mainloop16end    \n\t"
+
+                "mainloop16:                  \n\t"
+                  "movq      (%%esi), %%mm4   \n\t"
+                  "pand      %%mm0, %%mm4     \n\t"
+                  "movq      %%mm0, %%mm6     \n\t"
+                  "movq      (%%edi), %%mm7   \n\t"
+                  "pandn     %%mm7, %%mm6     \n\t"
+                  "por       %%mm6, %%mm4     \n\t"
+                  "movq      %%mm4, (%%edi)   \n\t"
+
+                  "movq      8(%%esi), %%mm5  \n\t"
+                  "pand      %%mm1, %%mm5     \n\t"
+                  "movq      %%mm1, %%mm7     \n\t"
+                  "movq      8(%%edi), %%mm6  \n\t"
+                  "pandn     %%mm6, %%mm7     \n\t"
+                  "por       %%mm7, %%mm5     \n\t"
+                  "movq      %%mm5, 8(%%edi)  \n\t"
+
+                  "addl      $16, %%esi       \n\t" // inc by 16 bytes processed
+                  "addl      $16, %%edi       \n\t"
+                  "subl      $8, %%ecx        \n\t" // dec by 8 pixels processed
+                  "ja        mainloop16       \n\t"
+
+                "mainloop16end:               \n\t"
+// preload        "movl      diff, %%ecx      \n\t" // (diff is in eax)
+                  "movl      %%eax, %%ecx     \n\t"
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        end16            \n\t"
+// preload        "movl      mask, %%edx      \n\t"
+                  "sall      $24, %%edx       \n\t" // make low byte, high byte
+
+                "secondloop16:                \n\t"
+                  "sall      %%edx            \n\t" // move high bit to CF
+                  "jnc       skip16           \n\t" // if CF = 0
+                  "movw      (%%esi), %%ax    \n\t"
+                  "movw      %%ax, (%%edi)    \n\t"
+
+                "skip16:                      \n\t"
+                  "addl      $2, %%esi        \n\t"
+                  "addl      $2, %%edi        \n\t"
+                  "decl      %%ecx            \n\t"
+                  "jnz       secondloop16     \n\t"
+
+                "end16:                       \n\t"
+                  "EMMS                       \n\t" // DONE
+
+                  : "=a" (dummy_value_a),           // output regs (dummy)
+                    "=c" (dummy_value_c),
+                    "=d" (dummy_value_d),
+                    "=S" (dummy_value_S),
+                    "=D" (dummy_value_D)
+
+                  : "0" (diff),        // eax       // input regs
+// was (unmask)     " "    RESERVED    // ebx       // Global Offset Table idx
+                    "1" (len),         // ecx
+                    "2" (mask),        // edx
+                    "3" (srcptr),      // esi
+                    "4" (dstptr)       // edi
+
+#if 0  /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */
+                  : "%mm0", "%mm1", "%mm4"          // clobber list
+                  , "%mm5", "%mm6", "%mm7"
+#endif
+               );
+            }
+            else /* mmx _not supported - Use modified C routine */
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = BPP2 * png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = BPP2 * png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = BPP2 * png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7;  /* reduce to mult. of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = BPP2 * len;   /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val+=diff*BPP2;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+            } /* end of else (_mmx_supported) */
+
+            break;
+         }       /* end 16 bpp */
+
+         case 24:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK)
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && _mmx_supported */ )
+#else
+            if (_mmx_supported)
+#endif
+            {
+               png_uint_32 len;
+               int diff;
+               int dummy_value_a;   // fix 'forbidden register spilled' error
+               int dummy_value_d;
+               int dummy_value_c;
+               int dummy_value_S;
+               int dummy_value_D;
+               _unmask = ~mask;            // global variable for -fPIC version
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+               len  = png_ptr->width &~7;  // reduce to multiple of 8
+               diff = (int) (png_ptr->width & 7); // amount lost //
+
+               __asm__ __volatile__ (
+                  "movd      _unmask, %%mm7   \n\t" // load bit pattern
+                  "psubb     %%mm6, %%mm6     \n\t" // zero mm6
+                  "punpcklbw %%mm7, %%mm7     \n\t"
+                  "punpcklwd %%mm7, %%mm7     \n\t"
+                  "punpckldq %%mm7, %%mm7     \n\t" // fill reg with 8 masks
+
+                  "movq      _mask24_0, %%mm0 \n\t"
+                  "movq      _mask24_1, %%mm1 \n\t"
+                  "movq      _mask24_2, %%mm2 \n\t"
+
+                  "pand      %%mm7, %%mm0     \n\t"
+                  "pand      %%mm7, %%mm1     \n\t"
+                  "pand      %%mm7, %%mm2     \n\t"
+
+                  "pcmpeqb   %%mm6, %%mm0     \n\t"
+                  "pcmpeqb   %%mm6, %%mm1     \n\t"
+                  "pcmpeqb   %%mm6, %%mm2     \n\t"
+
+// preload        "movl      len, %%ecx       \n\t" // load length of line
+// preload        "movl      srcptr, %%esi    \n\t" // load source
+// preload        "movl      dstptr, %%edi    \n\t" // load dest
+
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        mainloop24end    \n\t"
+
+                "mainloop24:                  \n\t"
+                  "movq      (%%esi), %%mm4   \n\t"
+                  "pand      %%mm0, %%mm4     \n\t"
+                  "movq      %%mm0, %%mm6     \n\t"
+                  "movq      (%%edi), %%mm7   \n\t"
+                  "pandn     %%mm7, %%mm6     \n\t"
+                  "por       %%mm6, %%mm4     \n\t"
+                  "movq      %%mm4, (%%edi)   \n\t"
+
+                  "movq      8(%%esi), %%mm5  \n\t"
+                  "pand      %%mm1, %%mm5     \n\t"
+                  "movq      %%mm1, %%mm7     \n\t"
+                  "movq      8(%%edi), %%mm6  \n\t"
+                  "pandn     %%mm6, %%mm7     \n\t"
+                  "por       %%mm7, %%mm5     \n\t"
+                  "movq      %%mm5, 8(%%edi)  \n\t"
+
+                  "movq      16(%%esi), %%mm6 \n\t"
+                  "pand      %%mm2, %%mm6     \n\t"
+                  "movq      %%mm2, %%mm4     \n\t"
+                  "movq      16(%%edi), %%mm7 \n\t"
+                  "pandn     %%mm7, %%mm4     \n\t"
+                  "por       %%mm4, %%mm6     \n\t"
+                  "movq      %%mm6, 16(%%edi) \n\t"
+
+                  "addl      $24, %%esi       \n\t" // inc by 24 bytes processed
+                  "addl      $24, %%edi       \n\t"
+                  "subl      $8, %%ecx        \n\t" // dec by 8 pixels processed
+
+                  "ja        mainloop24       \n\t"
+
+                "mainloop24end:               \n\t"
+// preload        "movl      diff, %%ecx      \n\t" // (diff is in eax)
+                  "movl      %%eax, %%ecx     \n\t"
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        end24            \n\t"
+// preload        "movl      mask, %%edx      \n\t"
+                  "sall      $24, %%edx       \n\t" // make low byte, high byte
+
+                "secondloop24:                \n\t"
+                  "sall      %%edx            \n\t" // move high bit to CF
+                  "jnc       skip24           \n\t" // if CF = 0
+                  "movw      (%%esi), %%ax    \n\t"
+                  "movw      %%ax, (%%edi)    \n\t"
+                  "xorl      %%eax, %%eax     \n\t"
+                  "movb      2(%%esi), %%al   \n\t"
+                  "movb      %%al, 2(%%edi)   \n\t"
+
+                "skip24:                      \n\t"
+                  "addl      $3, %%esi        \n\t"
+                  "addl      $3, %%edi        \n\t"
+                  "decl      %%ecx            \n\t"
+                  "jnz       secondloop24     \n\t"
+
+                "end24:                       \n\t"
+                  "EMMS                       \n\t" // DONE
+
+                  : "=a" (dummy_value_a),           // output regs (dummy)
+                    "=d" (dummy_value_d),
+                    "=c" (dummy_value_c),
+                    "=S" (dummy_value_S),
+                    "=D" (dummy_value_D)
+
+                  : "3" (srcptr),      // esi       // input regs
+                    "4" (dstptr),      // edi
+                    "0" (diff),        // eax
+// was (unmask)     "b"    RESERVED    // ebx       // Global Offset Table idx
+                    "2" (len),         // ecx
+                    "1" (mask)         // edx
+
+#if 0  /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */
+                  : "%mm0", "%mm1", "%mm2"          // clobber list
+                  , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+               );
+            }
+            else /* mmx _not supported - Use modified C routine */
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = BPP3 * png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = BPP3 * png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = BPP3 * png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7;  /* reduce to mult. of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = BPP3 * len;   /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val+=diff*BPP3;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+            } /* end of else (_mmx_supported) */
+
+            break;
+         }       /* end 24 bpp */
+
+         case 32:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK)
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && _mmx_supported */ )
+#else
+            if (_mmx_supported)
+#endif
+            {
+               png_uint_32 len;
+               int diff;
+               int dummy_value_a;   // fix 'forbidden register spilled' error
+               int dummy_value_d;
+               int dummy_value_c;
+               int dummy_value_S;
+               int dummy_value_D;
+               _unmask = ~mask;            // global variable for -fPIC version
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+               len  = png_ptr->width &~7;  // reduce to multiple of 8
+               diff = (int) (png_ptr->width & 7); // amount lost //
+
+               __asm__ __volatile__ (
+                  "movd      _unmask, %%mm7   \n\t" // load bit pattern
+                  "psubb     %%mm6, %%mm6     \n\t" // zero mm6
+                  "punpcklbw %%mm7, %%mm7     \n\t"
+                  "punpcklwd %%mm7, %%mm7     \n\t"
+                  "punpckldq %%mm7, %%mm7     \n\t" // fill reg with 8 masks
+
+                  "movq      _mask32_0, %%mm0 \n\t"
+                  "movq      _mask32_1, %%mm1 \n\t"
+                  "movq      _mask32_2, %%mm2 \n\t"
+                  "movq      _mask32_3, %%mm3 \n\t"
+
+                  "pand      %%mm7, %%mm0     \n\t"
+                  "pand      %%mm7, %%mm1     \n\t"
+                  "pand      %%mm7, %%mm2     \n\t"
+                  "pand      %%mm7, %%mm3     \n\t"
+
+                  "pcmpeqb   %%mm6, %%mm0     \n\t"
+                  "pcmpeqb   %%mm6, %%mm1     \n\t"
+                  "pcmpeqb   %%mm6, %%mm2     \n\t"
+                  "pcmpeqb   %%mm6, %%mm3     \n\t"
+
+// preload        "movl      len, %%ecx       \n\t" // load length of line
+// preload        "movl      srcptr, %%esi    \n\t" // load source
+// preload        "movl      dstptr, %%edi    \n\t" // load dest
+
+                  "cmpl      $0, %%ecx        \n\t" // lcr
+                  "jz        mainloop32end    \n\t"
+
+                "mainloop32:                  \n\t"
+                  "movq      (%%esi), %%mm4   \n\t"
+                  "pand      %%mm0, %%mm4     \n\t"
+                  "movq      %%mm0, %%mm6     \n\t"
+                  "movq      (%%edi), %%mm7   \n\t"
+                  "pandn     %%mm7, %%mm6     \n\t"
+                  "por       %%mm6, %%mm4     \n\t"
+                  "movq      %%mm4, (%%edi)   \n\t"
+
+                  "movq      8(%%esi), %%mm5  \n\t"
+                  "pand      %%mm1, %%mm5     \n\t"
+                  "movq      %%mm1, %%mm7     \n\t"
+                  "movq      8(%%edi), %%mm6  \n\t"
+                  "pandn     %%mm6, %%mm7     \n\t"
+                  "por       %%mm7, %%mm5     \n\t"
+                  "movq      %%mm5, 8(%%edi)  \n\t"
+
+                  "movq      16(%%esi), %%mm6 \n\t"
+                  "pand      %%mm2, %%mm6     \n\t"
+                  "movq      %%mm2, %%mm4     \n\t"
+                  "movq      16(%%edi), %%mm7 \n\t"
+                  "pandn     %%mm7, %%mm4     \n\t"
+                  "por       %%mm4, %%mm6     \n\t"
+                  "movq      %%mm6, 16(%%edi) \n\t"
+
+                  "movq      24(%%esi), %%mm7 \n\t"
+                  "pand      %%mm3, %%mm7     \n\t"
+                  "movq      %%mm3, %%mm5     \n\t"
+                  "movq      24(%%edi), %%mm4 \n\t"
+                  "pandn     %%mm4, %%mm5     \n\t"
+                  "por       %%mm5, %%mm7     \n\t"
+                  "movq      %%mm7, 24(%%edi) \n\t"
+
+                  "addl      $32, %%esi       \n\t" // inc by 32 bytes processed
+                  "addl      $32, %%edi       \n\t"
+                  "subl      $8, %%ecx        \n\t" // dec by 8 pixels processed
+                  "ja        mainloop32       \n\t"
+
+                "mainloop32end:               \n\t"
+// preload        "movl      diff, %%ecx      \n\t" // (diff is in eax)
+                  "movl      %%eax, %%ecx     \n\t"
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        end32            \n\t"
+// preload        "movl      mask, %%edx      \n\t"
+                  "sall      $24, %%edx       \n\t" // low byte => high byte
+
+                "secondloop32:                \n\t"
+                  "sall      %%edx            \n\t" // move high bit to CF
+                  "jnc       skip32           \n\t" // if CF = 0
+                  "movl      (%%esi), %%eax   \n\t"
+                  "movl      %%eax, (%%edi)   \n\t"
+
+                "skip32:                      \n\t"
+                  "addl      $4, %%esi        \n\t"
+                  "addl      $4, %%edi        \n\t"
+                  "decl      %%ecx            \n\t"
+                  "jnz       secondloop32     \n\t"
+
+                "end32:                       \n\t"
+                  "EMMS                       \n\t" // DONE
+
+                  : "=a" (dummy_value_a),           // output regs (dummy)
+                    "=d" (dummy_value_d),
+                    "=c" (dummy_value_c),
+                    "=S" (dummy_value_S),
+                    "=D" (dummy_value_D)
+
+                  : "3" (srcptr),      // esi       // input regs
+                    "4" (dstptr),      // edi
+                    "0" (diff),        // eax
+// was (unmask)     "b"    RESERVED    // ebx       // Global Offset Table idx
+                    "2" (len),         // ecx
+                    "1" (mask)         // edx
+
+#if 0  /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */
+                  : "%mm0", "%mm1", "%mm2", "%mm3"  // clobber list
+                  , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+               );
+            }
+            else /* mmx _not supported - Use modified C routine */
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = BPP4 * png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = BPP4 * png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = BPP4 * png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7;  /* reduce to mult. of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = BPP4 * len;   /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val+=diff*BPP4;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+            } /* end of else (_mmx_supported) */
+
+            break;
+         }       /* end 32 bpp */
+
+         case 48:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK)
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && _mmx_supported */ )
+#else
+            if (_mmx_supported)
+#endif
+            {
+               png_uint_32 len;
+               int diff;
+               int dummy_value_a;   // fix 'forbidden register spilled' error
+               int dummy_value_d;
+               int dummy_value_c;
+               int dummy_value_S;
+               int dummy_value_D;
+               _unmask = ~mask;            // global variable for -fPIC version
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+               len  = png_ptr->width &~7;  // reduce to multiple of 8
+               diff = (int) (png_ptr->width & 7); // amount lost //
+
+               __asm__ __volatile__ (
+                  "movd      _unmask, %%mm7   \n\t" // load bit pattern
+                  "psubb     %%mm6, %%mm6     \n\t" // zero mm6
+                  "punpcklbw %%mm7, %%mm7     \n\t"
+                  "punpcklwd %%mm7, %%mm7     \n\t"
+                  "punpckldq %%mm7, %%mm7     \n\t" // fill reg with 8 masks
+
+                  "movq      _mask48_0, %%mm0 \n\t"
+                  "movq      _mask48_1, %%mm1 \n\t"
+                  "movq      _mask48_2, %%mm2 \n\t"
+                  "movq      _mask48_3, %%mm3 \n\t"
+                  "movq      _mask48_4, %%mm4 \n\t"
+                  "movq      _mask48_5, %%mm5 \n\t"
+
+                  "pand      %%mm7, %%mm0     \n\t"
+                  "pand      %%mm7, %%mm1     \n\t"
+                  "pand      %%mm7, %%mm2     \n\t"
+                  "pand      %%mm7, %%mm3     \n\t"
+                  "pand      %%mm7, %%mm4     \n\t"
+                  "pand      %%mm7, %%mm5     \n\t"
+
+                  "pcmpeqb   %%mm6, %%mm0     \n\t"
+                  "pcmpeqb   %%mm6, %%mm1     \n\t"
+                  "pcmpeqb   %%mm6, %%mm2     \n\t"
+                  "pcmpeqb   %%mm6, %%mm3     \n\t"
+                  "pcmpeqb   %%mm6, %%mm4     \n\t"
+                  "pcmpeqb   %%mm6, %%mm5     \n\t"
+
+// preload        "movl      len, %%ecx       \n\t" // load length of line
+// preload        "movl      srcptr, %%esi    \n\t" // load source
+// preload        "movl      dstptr, %%edi    \n\t" // load dest
+
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        mainloop48end    \n\t"
+
+                "mainloop48:                  \n\t"
+                  "movq      (%%esi), %%mm7   \n\t"
+                  "pand      %%mm0, %%mm7     \n\t"
+                  "movq      %%mm0, %%mm6     \n\t"
+                  "pandn     (%%edi), %%mm6   \n\t"
+                  "por       %%mm6, %%mm7     \n\t"
+                  "movq      %%mm7, (%%edi)   \n\t"
+
+                  "movq      8(%%esi), %%mm6  \n\t"
+                  "pand      %%mm1, %%mm6     \n\t"
+                  "movq      %%mm1, %%mm7     \n\t"
+                  "pandn     8(%%edi), %%mm7  \n\t"
+                  "por       %%mm7, %%mm6     \n\t"
+                  "movq      %%mm6, 8(%%edi)  \n\t"
+
+                  "movq      16(%%esi), %%mm6 \n\t"
+                  "pand      %%mm2, %%mm6     \n\t"
+                  "movq      %%mm2, %%mm7     \n\t"
+                  "pandn     16(%%edi), %%mm7 \n\t"
+                  "por       %%mm7, %%mm6     \n\t"
+                  "movq      %%mm6, 16(%%edi) \n\t"
+
+                  "movq      24(%%esi), %%mm7 \n\t"
+                  "pand      %%mm3, %%mm7     \n\t"
+                  "movq      %%mm3, %%mm6     \n\t"
+                  "pandn     24(%%edi), %%mm6 \n\t"
+                  "por       %%mm6, %%mm7     \n\t"
+                  "movq      %%mm7, 24(%%edi) \n\t"
+
+                  "movq      32(%%esi), %%mm6 \n\t"
+                  "pand      %%mm4, %%mm6     \n\t"
+                  "movq      %%mm4, %%mm7     \n\t"
+                  "pandn     32(%%edi), %%mm7 \n\t"
+                  "por       %%mm7, %%mm6     \n\t"
+                  "movq      %%mm6, 32(%%edi) \n\t"
+
+                  "movq      40(%%esi), %%mm7 \n\t"
+                  "pand      %%mm5, %%mm7     \n\t"
+                  "movq      %%mm5, %%mm6     \n\t"
+                  "pandn     40(%%edi), %%mm6 \n\t"
+                  "por       %%mm6, %%mm7     \n\t"
+                  "movq      %%mm7, 40(%%edi) \n\t"
+
+                  "addl      $48, %%esi       \n\t" // inc by 48 bytes processed
+                  "addl      $48, %%edi       \n\t"
+                  "subl      $8, %%ecx        \n\t" // dec by 8 pixels processed
+
+                  "ja        mainloop48       \n\t"
+
+                "mainloop48end:               \n\t"
+// preload        "movl      diff, %%ecx      \n\t" // (diff is in eax)
+                  "movl      %%eax, %%ecx     \n\t"
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        end48            \n\t"
+// preload        "movl      mask, %%edx      \n\t"
+                  "sall      $24, %%edx       \n\t" // make low byte, high byte
+
+                "secondloop48:                \n\t"
+                  "sall      %%edx            \n\t" // move high bit to CF
+                  "jnc       skip48           \n\t" // if CF = 0
+                  "movl      (%%esi), %%eax   \n\t"
+                  "movl      %%eax, (%%edi)   \n\t"
+
+                "skip48:                      \n\t"
+                  "addl      $4, %%esi        \n\t"
+                  "addl      $4, %%edi        \n\t"
+                  "decl      %%ecx            \n\t"
+                  "jnz       secondloop48     \n\t"
+
+                "end48:                       \n\t"
+                  "EMMS                       \n\t" // DONE
+
+                  : "=a" (dummy_value_a),           // output regs (dummy)
+                    "=d" (dummy_value_d),
+                    "=c" (dummy_value_c),
+                    "=S" (dummy_value_S),
+                    "=D" (dummy_value_D)
+
+                  : "3" (srcptr),      // esi       // input regs
+                    "4" (dstptr),      // edi
+                    "0" (diff),        // eax
+// was (unmask)     "b"    RESERVED    // ebx       // Global Offset Table idx
+                    "2" (len),         // ecx
+                    "1" (mask)         // edx
+
+#if 0  /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */
+                  : "%mm0", "%mm1", "%mm2", "%mm3"  // clobber list
+                  , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+               );
+            }
+            else /* mmx _not supported - Use modified C routine */
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = BPP6 * png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = BPP6 * png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = BPP6 * png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7;  /* reduce to mult. of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = BPP6 * len;   /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val+=diff*BPP6;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+            } /* end of else (_mmx_supported) */
+
+            break;
+         }       /* end 48 bpp */
+
+         case 64:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            register png_uint_32 i;
+            png_uint_32 initial_val = BPP8 * png_pass_start[png_ptr->pass];
+              /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+            register int stride = BPP8 * png_pass_inc[png_ptr->pass];
+              /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+            register int rep_bytes = BPP8 * png_pass_width[png_ptr->pass];
+              /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+            png_uint_32 len = png_ptr->width &~7;  /* reduce to mult. of 8 */
+            int diff = (int) (png_ptr->width & 7); /* amount lost */
+            register png_uint_32 final_val = BPP8 * len;   /* GRR bugfix */
+
+            srcptr = png_ptr->row_buf + 1 + initial_val;
+            dstptr = row + initial_val;
+
+            for (i = initial_val; i < final_val; i += stride)
+            {
+               png_memcpy(dstptr, srcptr, rep_bytes);
+               srcptr += stride;
+               dstptr += stride;
+            }
+            if (diff)  /* number of leftover pixels:  3 for pngtest */
+            {
+               final_val+=diff*BPP8;
+               for (; i < final_val; i += stride)
+               {
+                  if (rep_bytes > (int)(final_val-i))
+                     rep_bytes = (int)(final_val-i);
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+            }
+
+            break;
+         }       /* end 64 bpp */
+
+         default: /* png_ptr->row_info.pixel_depth != 1,2,4,8,16,24,32,48,64 */
+         {
+            /* this should never happen */
+            png_warning(png_ptr, "Invalid row_info.pixel_depth in pnggccrd");
+            break;
+         }
+      } /* end switch (png_ptr->row_info.pixel_depth) */
+
+   } /* end if (non-trivial mask) */
+
+} /* end png_combine_row() */
+
+#endif /* PNG_HAVE_ASSEMBLER_COMBINE_ROW */
+
+
+
+
+/*===========================================================================*/
+/*                                                                           */
+/*                 P N G _ D O _ R E A D _ I N T E R L A C E                 */
+/*                                                                           */
+/*===========================================================================*/
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+#if defined(PNG_HAVE_ASSEMBLER_READ_INTERLACE)
+
+/* png_do_read_interlace() is called after any 16-bit to 8-bit conversion
+ * has taken place.  [GRR: what other steps come before and/or after?]
+ */
+
+void /* PRIVATE */
+png_do_read_interlace(png_structp png_ptr)
+{
+   png_row_infop row_info = &(png_ptr->row_info);
+   png_bytep row = png_ptr->row_buf + 1;
+   int pass = png_ptr->pass;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+   png_uint_32 transformations = png_ptr->transformations;
+#endif
+
+   png_debug(1, "in png_do_read_interlace (pnggccrd.c)\n");
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+   if (_mmx_supported == 2) {
+#if !defined(PNG_1_0_X)
+       /* this should have happened in png_init_mmx_flags() already */
+       png_warning(png_ptr, "asm_flags may not have been initialized");
+#endif
+       png_mmx_support();
+   }
+#endif
+
+   if (row != NULL && row_info != NULL)
+   {
+      png_uint_32 final_width;
+
+      final_width = row_info->width * png_pass_inc[pass];
+
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_byte v;
+            png_uint_32 i;
+            int j;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 3);
+            dp = row + (png_size_t)((final_width - 1) >> 3);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (int)((row_info->width + 7) & 7);
+               dshift = (int)((final_width + 7) & 7);
+               s_start = 7;
+               s_end = 0;
+               s_inc = -1;
+            }
+            else
+#endif
+            {
+               sshift = 7 - (int)((row_info->width + 7) & 7);
+               dshift = 7 - (int)((final_width + 7) & 7);
+               s_start = 0;
+               s_end = 7;
+               s_inc = 1;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               v = (png_byte)((*sp >> sshift) & 0x1);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         case 2:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 2);
+            dp = row + (png_size_t)((final_width - 1) >> 2);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (png_size_t)(((row_info->width + 3) & 3) << 1);
+               dshift = (png_size_t)(((final_width + 3) & 3) << 1);
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+            else
+#endif
+            {
+               sshift = (png_size_t)((3 - ((row_info->width + 3) & 3)) << 1);
+               dshift = (png_size_t)((3 - ((final_width + 3) & 3)) << 1);
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               png_byte v;
+               int j;
+
+               v = (png_byte)((*sp >> sshift) & 0x3);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         case 4:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 1);
+            dp = row + (png_size_t)((final_width - 1) >> 1);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (png_size_t)(((row_info->width + 1) & 1) << 2);
+               dshift = (png_size_t)(((final_width + 1) & 1) << 2);
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            else
+#endif
+            {
+               sshift = (png_size_t)((1 - ((row_info->width + 1) & 1)) << 2);
+               dshift = (png_size_t)((1 - ((final_width + 1) & 1)) << 2);
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               png_byte v;
+               int j;
+
+               v = (png_byte)((*sp >> sshift) & 0xf);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+       /*====================================================================*/
+
+         default: /* 8-bit or larger (this is where the routine is modified) */
+         {
+#if 0
+//          static unsigned long long _const4 = 0x0000000000FFFFFFLL;  no good
+//          static unsigned long long const4 = 0x0000000000FFFFFFLL;   no good
+//          unsigned long long _const4 = 0x0000000000FFFFFFLL;         no good
+//          unsigned long long const4 = 0x0000000000FFFFFFLL;          no good
+#endif
+            png_bytep sptr, dp;
+            png_uint_32 i;
+            png_size_t pixel_bytes;
+            int width = (int)row_info->width;
+
+            pixel_bytes = (row_info->pixel_depth >> 3);
+
+            /* point sptr at the last pixel in the pre-expanded row: */
+            sptr = row + (width - 1) * pixel_bytes;
+
+            /* point dp at the last pixel position in the expanded row: */
+            dp = row + (final_width - 1) * pixel_bytes;
+
+            /* New code by Nirav Chhatrapati - Intel Corporation */
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_INTERLACE)
+                /* && _mmx_supported */ )
+#else
+            if (_mmx_supported)
+#endif
+            {
+               //--------------------------------------------------------------
+               if (pixel_bytes == 3)
+               {
+                  if (((pass == 0) || (pass == 1)) && width)
+                  {
+                     int dummy_value_c;   // fix 'forbidden register spilled'
+                     int dummy_value_S;
+                     int dummy_value_D;
+
+                     __asm__ __volatile__ (
+                        "subl $21, %%edi         \n\t"
+                                     // (png_pass_inc[pass] - 1)*pixel_bytes
+
+                     ".loop3_pass0:              \n\t"
+                        "movd (%%esi), %%mm0     \n\t" // x x x x x 2 1 0
+                        "pand _const4, %%mm0     \n\t" // z z z z z 2 1 0
+                        "movq %%mm0, %%mm1       \n\t" // z z z z z 2 1 0
+                        "psllq $16, %%mm0        \n\t" // z z z 2 1 0 z z
+                        "movq %%mm0, %%mm2       \n\t" // z z z 2 1 0 z z
+                        "psllq $24, %%mm0        \n\t" // 2 1 0 z z z z z
+                        "psrlq $8, %%mm1         \n\t" // z z z z z z 2 1
+                        "por %%mm2, %%mm0        \n\t" // 2 1 0 2 1 0 z z
+                        "por %%mm1, %%mm0        \n\t" // 2 1 0 2 1 0 2 1
+                        "movq %%mm0, %%mm3       \n\t" // 2 1 0 2 1 0 2 1
+                        "psllq $16, %%mm0        \n\t" // 0 2 1 0 2 1 z z
+                        "movq %%mm3, %%mm4       \n\t" // 2 1 0 2 1 0 2 1
+                        "punpckhdq %%mm0, %%mm3  \n\t" // 0 2 1 0 2 1 0 2
+                        "movq %%mm4, 16(%%edi)   \n\t"
+                        "psrlq $32, %%mm0        \n\t" // z z z z 0 2 1 0
+                        "movq %%mm3, 8(%%edi)    \n\t"
+                        "punpckldq %%mm4, %%mm0  \n\t" // 1 0 2 1 0 2 1 0
+                        "subl $3, %%esi          \n\t"
+                        "movq %%mm0, (%%edi)     \n\t"
+                        "subl $24, %%edi         \n\t"
+                        "decl %%ecx              \n\t"
+                        "jnz .loop3_pass0        \n\t"
+                        "EMMS                    \n\t" // DONE
+
+                        : "=c" (dummy_value_c),        // output regs (dummy)
+                          "=S" (dummy_value_S),
+                          "=D" (dummy_value_D)
+
+                        : "1" (sptr),      // esi      // input regs
+                          "2" (dp),        // edi
+                          "0" (width),     // ecx
+                          "rim" (_const4)  // %1(?)  (0x0000000000FFFFFFLL)
+
+#if 0  /* %mm0, ..., %mm4 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                        : "%mm0", "%mm1", "%mm2"       // clobber list
+                        , "%mm3", "%mm4"
+#endif
+                     );
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     int dummy_value_c;   // fix 'forbidden register spilled'
+                     int dummy_value_S;
+                     int dummy_value_D;
+
+                     __asm__ __volatile__ (
+                        "subl $9, %%edi          \n\t"
+                                     // (png_pass_inc[pass] - 1)*pixel_bytes
+
+                     ".loop3_pass2:              \n\t"
+                        "movd (%%esi), %%mm0     \n\t" // x x x x x 2 1 0
+                        "pand _const4, %%mm0     \n\t" // z z z z z 2 1 0
+                        "movq %%mm0, %%mm1       \n\t" // z z z z z 2 1 0
+                        "psllq $16, %%mm0        \n\t" // z z z 2 1 0 z z
+                        "movq %%mm0, %%mm2       \n\t" // z z z 2 1 0 z z
+                        "psllq $24, %%mm0        \n\t" // 2 1 0 z z z z z
+                        "psrlq $8, %%mm1         \n\t" // z z z z z z 2 1
+                        "por %%mm2, %%mm0        \n\t" // 2 1 0 2 1 0 z z
+                        "por %%mm1, %%mm0        \n\t" // 2 1 0 2 1 0 2 1
+                        "movq %%mm0, 4(%%edi)    \n\t"
+                        "psrlq $16, %%mm0        \n\t" // z z 2 1 0 2 1 0
+                        "subl $3, %%esi          \n\t"
+                        "movd %%mm0, (%%edi)     \n\t"
+                        "subl $12, %%edi         \n\t"
+                        "decl %%ecx              \n\t"
+                        "jnz .loop3_pass2        \n\t"
+                        "EMMS                    \n\t" // DONE
+
+                        : "=c" (dummy_value_c),        // output regs (dummy)
+                          "=S" (dummy_value_S),
+                          "=D" (dummy_value_D)
+
+                        : "1" (sptr),      // esi      // input regs
+                          "2" (dp),        // edi
+                          "0" (width),     // ecx
+                          "rim" (_const4)  // (0x0000000000FFFFFFLL)
+
+#if 0  /* %mm0, ..., %mm2 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                        : "%mm0", "%mm1", "%mm2"       // clobber list
+#endif
+                     );
+                  }
+                  else if (width) /* && ((pass == 4) || (pass == 5)) */
+                  {
+                     int width_mmx = ((width >> 1) << 1) - 8;   // GRR:  huh?
+                     if (width_mmx < 0)
+                         width_mmx = 0;
+                     width -= width_mmx;        // 8 or 9 pix, 24 or 27 bytes
+                     if (width_mmx)
+                     {
+                        // png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
+                        // sptr points at last pixel in pre-expanded row
+                        // dp points at last pixel position in expanded row
+                        int dummy_value_c;  // fix 'forbidden register spilled'
+                        int dummy_value_S;
+                        int dummy_value_D;
+
+                        __asm__ __volatile__ (
+                           "subl $3, %%esi          \n\t"
+                           "subl $9, %%edi          \n\t"
+                                        // (png_pass_inc[pass] + 1)*pixel_bytes
+
+                        ".loop3_pass4:              \n\t"
+                           "movq (%%esi), %%mm0     \n\t" // x x 5 4 3 2 1 0
+                           "movq %%mm0, %%mm1       \n\t" // x x 5 4 3 2 1 0
+                           "movq %%mm0, %%mm2       \n\t" // x x 5 4 3 2 1 0
+                           "psllq $24, %%mm0        \n\t" // 4 3 2 1 0 z z z
+                           "pand _const4, %%mm1     \n\t" // z z z z z 2 1 0
+                           "psrlq $24, %%mm2        \n\t" // z z z x x 5 4 3
+                           "por %%mm1, %%mm0        \n\t" // 4 3 2 1 0 2 1 0
+                           "movq %%mm2, %%mm3       \n\t" // z z z x x 5 4 3
+                           "psllq $8, %%mm2         \n\t" // z z x x 5 4 3 z
+                           "movq %%mm0, (%%edi)     \n\t"
+                           "psrlq $16, %%mm3        \n\t" // z z z z z x x 5
+                           "pand _const6, %%mm3     \n\t" // z z z z z z z 5
+                           "por %%mm3, %%mm2        \n\t" // z z x x 5 4 3 5
+                           "subl $6, %%esi          \n\t"
+                           "movd %%mm2, 8(%%edi)    \n\t"
+                           "subl $12, %%edi         \n\t"
+                           "subl $2, %%ecx          \n\t"
+                           "jnz .loop3_pass4        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "1" (sptr),      // esi      // input regs
+                             "2" (dp),        // edi
+                             "0" (width_mmx), // ecx
+                             "rim" (_const4), // 0x0000000000FFFFFFLL
+                             "rim" (_const6)  // 0x00000000000000FFLL
+
+#if 0  /* %mm0, ..., %mm3 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                           : "%mm0", "%mm1"               // clobber list
+                           , "%mm2", "%mm3"
+#endif
+                        );
+                     }
+
+                     sptr -= width_mmx*3;
+                     dp -= width_mmx*6;
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+
+                        png_memcpy(v, sptr, 3);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           png_memcpy(dp, v, 3);
+                           dp -= 3;
+                        }
+                        sptr -= 3;
+                     }
+                  }
+               } /* end of pixel_bytes == 3 */
+
+               //--------------------------------------------------------------
+               else if (pixel_bytes == 1)
+               {
+                  if (((pass == 0) || (pass == 1)) && width)
+                  {
+                     int width_mmx = ((width >> 2) << 2);
+                     width -= width_mmx;        // 0-3 pixels => 0-3 bytes
+                     if (width_mmx)
+                     {
+                        int dummy_value_c;  // fix 'forbidden register spilled'
+                        int dummy_value_S;
+                        int dummy_value_D;
+
+                        __asm__ __volatile__ (
+                           "subl $3, %%esi          \n\t"
+                           "subl $31, %%edi         \n\t"
+
+                        ".loop1_pass0:              \n\t"
+                           "movd (%%esi), %%mm0     \n\t" // x x x x 3 2 1 0
+                           "movq %%mm0, %%mm1       \n\t" // x x x x 3 2 1 0
+                           "punpcklbw %%mm0, %%mm0  \n\t" // 3 3 2 2 1 1 0 0
+                           "movq %%mm0, %%mm2       \n\t" // 3 3 2 2 1 1 0 0
+                           "punpcklwd %%mm0, %%mm0  \n\t" // 1 1 1 1 0 0 0 0
+                           "movq %%mm0, %%mm3       \n\t" // 1 1 1 1 0 0 0 0
+                           "punpckldq %%mm0, %%mm0  \n\t" // 0 0 0 0 0 0 0 0
+                           "punpckhdq %%mm3, %%mm3  \n\t" // 1 1 1 1 1 1 1 1
+                           "movq %%mm0, (%%edi)     \n\t"
+                           "punpckhwd %%mm2, %%mm2  \n\t" // 3 3 3 3 2 2 2 2
+                           "movq %%mm3, 8(%%edi)    \n\t"
+                           "movq %%mm2, %%mm4       \n\t" // 3 3 3 3 2 2 2 2
+                           "punpckldq %%mm2, %%mm2  \n\t" // 2 2 2 2 2 2 2 2
+                           "punpckhdq %%mm4, %%mm4  \n\t" // 3 3 3 3 3 3 3 3
+                           "movq %%mm2, 16(%%edi)   \n\t"
+                           "subl $4, %%esi          \n\t"
+                           "movq %%mm4, 24(%%edi)   \n\t"
+                           "subl $32, %%edi         \n\t"
+                           "subl $4, %%ecx          \n\t"
+                           "jnz .loop1_pass0        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "1" (sptr),      // esi      // input regs
+                             "2" (dp),        // edi
+                             "0" (width_mmx)  // ecx
+
+#if 0  /* %mm0, ..., %mm4 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                           : "%mm0", "%mm1", "%mm2"       // clobber list
+                           , "%mm3", "%mm4"
+#endif
+                        );
+                     }
+
+                     sptr -= width_mmx;
+                     dp -= width_mmx*8;
+                     for (i = width; i; i--)
+                     {
+                        int j;
+
+                       /* I simplified this part in version 1.0.4e
+                        * here and in several other instances where
+                        * pixel_bytes == 1  -- GR-P
+                        *
+                        * Original code:
+                        *
+                        * png_byte v[8];
+                        * png_memcpy(v, sptr, pixel_bytes);
+                        * for (j = 0; j < png_pass_inc[pass]; j++)
+                        * {
+                        *    png_memcpy(dp, v, pixel_bytes);
+                        *    dp -= pixel_bytes;
+                        * }
+                        * sptr -= pixel_bytes;
+                        *
+                        * Replacement code is in the next three lines:
+                        */
+
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           *dp-- = *sptr;
+                        }
+                        --sptr;
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     int width_mmx = ((width >> 2) << 2);
+                     width -= width_mmx;        // 0-3 pixels => 0-3 bytes
+                     if (width_mmx)
+                     {
+                        int dummy_value_c;  // fix 'forbidden register spilled'
+                        int dummy_value_S;
+                        int dummy_value_D;
+
+                        __asm__ __volatile__ (
+                           "subl $3, %%esi          \n\t"
+                           "subl $15, %%edi         \n\t"
+
+                        ".loop1_pass2:              \n\t"
+                           "movd (%%esi), %%mm0     \n\t" // x x x x 3 2 1 0
+                           "punpcklbw %%mm0, %%mm0  \n\t" // 3 3 2 2 1 1 0 0
+                           "movq %%mm0, %%mm1       \n\t" // 3 3 2 2 1 1 0 0
+                           "punpcklwd %%mm0, %%mm0  \n\t" // 1 1 1 1 0 0 0 0
+                           "punpckhwd %%mm1, %%mm1  \n\t" // 3 3 3 3 2 2 2 2
+                           "movq %%mm0, (%%edi)     \n\t"
+                           "subl $4, %%esi          \n\t"
+                           "movq %%mm1, 8(%%edi)    \n\t"
+                           "subl $16, %%edi         \n\t"
+                           "subl $4, %%ecx          \n\t"
+                           "jnz .loop1_pass2        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "1" (sptr),      // esi      // input regs
+                             "2" (dp),        // edi
+                             "0" (width_mmx)  // ecx
+
+#if 0  /* %mm0, %mm1 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                           : "%mm0", "%mm1"               // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= width_mmx;
+                     dp -= width_mmx*4;
+                     for (i = width; i; i--)
+                     {
+                        int j;
+
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           *dp-- = *sptr;
+                        }
+                        --sptr;
+                     }
+                  }
+                  else if (width)  /* && ((pass == 4) || (pass == 5)) */
+                  {
+                     int width_mmx = ((width >> 3) << 3);
+                     width -= width_mmx;        // 0-3 pixels => 0-3 bytes
+                     if (width_mmx)
+                     {
+                        int dummy_value_c;  // fix 'forbidden register spilled'
+                        int dummy_value_S;
+                        int dummy_value_D;
+
+                        __asm__ __volatile__ (
+                           "subl $7, %%esi          \n\t"
+                           "subl $15, %%edi         \n\t"
+
+                        ".loop1_pass4:              \n\t"
+                           "movq (%%esi), %%mm0     \n\t" // 7 6 5 4 3 2 1 0
+                           "movq %%mm0, %%mm1       \n\t" // 7 6 5 4 3 2 1 0
+                           "punpcklbw %%mm0, %%mm0  \n\t" // 3 3 2 2 1 1 0 0
+                           "punpckhbw %%mm1, %%mm1  \n\t" // 7 7 6 6 5 5 4 4
+                           "movq %%mm1, 8(%%edi)    \n\t"
+                           "subl $8, %%esi          \n\t"
+                           "movq %%mm0, (%%edi)     \n\t"
+                           "subl $16, %%edi         \n\t"
+                           "subl $8, %%ecx          \n\t"
+                           "jnz .loop1_pass4        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (none)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "1" (sptr),      // esi      // input regs
+                             "2" (dp),        // edi
+                             "0" (width_mmx)  // ecx
+
+#if 0  /* %mm0, %mm1 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                           : "%mm0", "%mm1"               // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= width_mmx;
+                     dp -= width_mmx*2;
+                     for (i = width; i; i--)
+                     {
+                        int j;
+
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           *dp-- = *sptr;
+                        }
+                        --sptr;
+                     }
+                  }
+               } /* end of pixel_bytes == 1 */
+
+               //--------------------------------------------------------------
+               else if (pixel_bytes == 2)
+               {
+                  if (((pass == 0) || (pass == 1)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1);
+                     width -= width_mmx;        // 0,1 pixels => 0,2 bytes
+                     if (width_mmx)
+                     {
+                        int dummy_value_c;  // fix 'forbidden register spilled'
+                        int dummy_value_S;
+                        int dummy_value_D;
+
+                        __asm__ __volatile__ (
+                           "subl $2, %%esi          \n\t"
+                           "subl $30, %%edi         \n\t"
+
+                        ".loop2_pass0:              \n\t"
+                           "movd (%%esi), %%mm0     \n\t" // x x x x 3 2 1 0
+                           "punpcklwd %%mm0, %%mm0  \n\t" // 3 2 3 2 1 0 1 0
+                           "movq %%mm0, %%mm1       \n\t" // 3 2 3 2 1 0 1 0
+                           "punpckldq %%mm0, %%mm0  \n\t" // 1 0 1 0 1 0 1 0
+                           "punpckhdq %%mm1, %%mm1  \n\t" // 3 2 3 2 3 2 3 2
+                           "movq %%mm0, (%%edi)     \n\t"
+                           "movq %%mm0, 8(%%edi)    \n\t"
+                           "movq %%mm1, 16(%%edi)   \n\t"
+                           "subl $4, %%esi          \n\t"
+                           "movq %%mm1, 24(%%edi)   \n\t"
+                           "subl $32, %%edi         \n\t"
+                           "subl $2, %%ecx          \n\t"
+                           "jnz .loop2_pass0        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "1" (sptr),      // esi      // input regs
+                             "2" (dp),        // edi
+                             "0" (width_mmx)  // ecx
+
+#if 0  /* %mm0, %mm1 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                           : "%mm0", "%mm1"               // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= (width_mmx*2 - 2); // sign fixed
+                     dp -= (width_mmx*16 - 2);  // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 2;
+                        png_memcpy(v, sptr, 2);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 2;
+                           png_memcpy(dp, v, 2);
+                        }
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;        // 0,1 pixels => 0,2 bytes
+                     if (width_mmx)
+                     {
+                        int dummy_value_c;  // fix 'forbidden register spilled'
+                        int dummy_value_S;
+                        int dummy_value_D;
+
+                        __asm__ __volatile__ (
+                           "subl $2, %%esi          \n\t"
+                           "subl $14, %%edi         \n\t"
+
+                        ".loop2_pass2:              \n\t"
+                           "movd (%%esi), %%mm0     \n\t" // x x x x 3 2 1 0
+                           "punpcklwd %%mm0, %%mm0  \n\t" // 3 2 3 2 1 0 1 0
+                           "movq %%mm0, %%mm1       \n\t" // 3 2 3 2 1 0 1 0
+                           "punpckldq %%mm0, %%mm0  \n\t" // 1 0 1 0 1 0 1 0
+                           "punpckhdq %%mm1, %%mm1  \n\t" // 3 2 3 2 3 2 3 2
+                           "movq %%mm0, (%%edi)     \n\t"
+                           "subl $4, %%esi          \n\t"
+                           "movq %%mm1, 8(%%edi)    \n\t"
+                           "subl $16, %%edi         \n\t"
+                           "subl $2, %%ecx          \n\t"
+                           "jnz .loop2_pass2        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "1" (sptr),      // esi      // input regs
+                             "2" (dp),        // edi
+                             "0" (width_mmx)  // ecx
+
+#if 0  /* %mm0, %mm1 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                           : "%mm0", "%mm1"               // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= (width_mmx*2 - 2); // sign fixed
+                     dp -= (width_mmx*8 - 2);   // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 2;
+                        png_memcpy(v, sptr, 2);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 2;
+                           png_memcpy(dp, v, 2);
+                        }
+                     }
+                  }
+                  else if (width)  // pass == 4 or 5
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;        // 0,1 pixels => 0,2 bytes
+                     if (width_mmx)
+                     {
+                        int dummy_value_c;  // fix 'forbidden register spilled'
+                        int dummy_value_S;
+                        int dummy_value_D;
+
+                        __asm__ __volatile__ (
+                           "subl $2, %%esi          \n\t"
+                           "subl $6, %%edi          \n\t"
+
+                        ".loop2_pass4:              \n\t"
+                           "movd (%%esi), %%mm0     \n\t" // x x x x 3 2 1 0
+                           "punpcklwd %%mm0, %%mm0  \n\t" // 3 2 3 2 1 0 1 0
+                           "subl $4, %%esi          \n\t"
+                           "movq %%mm0, (%%edi)     \n\t"
+                           "subl $8, %%edi          \n\t"
+                           "subl $2, %%ecx          \n\t"
+                           "jnz .loop2_pass4        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "1" (sptr),      // esi      // input regs
+                             "2" (dp),        // edi
+                             "0" (width_mmx)  // ecx
+
+#if 0  /* %mm0 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                           : "%mm0"                       // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= (width_mmx*2 - 2); // sign fixed
+                     dp -= (width_mmx*4 - 2);   // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 2;
+                        png_memcpy(v, sptr, 2);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 2;
+                           png_memcpy(dp, v, 2);
+                        }
+                     }
+                  }
+               } /* end of pixel_bytes == 2 */
+
+               //--------------------------------------------------------------
+               else if (pixel_bytes == 4)
+               {
+                  if (((pass == 0) || (pass == 1)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1);
+                     width -= width_mmx;        // 0,1 pixels => 0,4 bytes
+                     if (width_mmx)
+                     {
+                        int dummy_value_c;  // fix 'forbidden register spilled'
+                        int dummy_value_S;
+                        int dummy_value_D;
+
+                        __asm__ __volatile__ (
+                           "subl $4, %%esi          \n\t"
+                           "subl $60, %%edi         \n\t"
+
+                        ".loop4_pass0:              \n\t"
+                           "movq (%%esi), %%mm0     \n\t" // 7 6 5 4 3 2 1 0
+                           "movq %%mm0, %%mm1       \n\t" // 7 6 5 4 3 2 1 0
+                           "punpckldq %%mm0, %%mm0  \n\t" // 3 2 1 0 3 2 1 0
+                           "punpckhdq %%mm1, %%mm1  \n\t" // 7 6 5 4 7 6 5 4
+                           "movq %%mm0, (%%edi)     \n\t"
+                           "movq %%mm0, 8(%%edi)    \n\t"
+                           "movq %%mm0, 16(%%edi)   \n\t"
+                           "movq %%mm0, 24(%%edi)   \n\t"
+                           "movq %%mm1, 32(%%edi)   \n\t"
+                           "movq %%mm1, 40(%%edi)   \n\t"
+                           "movq %%mm1, 48(%%edi)   \n\t"
+                           "subl $8, %%esi          \n\t"
+                           "movq %%mm1, 56(%%edi)   \n\t"
+                           "subl $64, %%edi         \n\t"
+                           "subl $2, %%ecx          \n\t"
+                           "jnz .loop4_pass0        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "1" (sptr),      // esi      // input regs
+                             "2" (dp),        // edi
+                             "0" (width_mmx)  // ecx
+
+#if 0  /* %mm0, %mm1 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                           : "%mm0", "%mm1"               // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= (width_mmx*4 - 4); // sign fixed
+                     dp -= (width_mmx*32 - 4);  // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 4;
+                        png_memcpy(v, sptr, 4);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 4;
+                           png_memcpy(dp, v, 4);
+                        }
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1);
+                     width -= width_mmx;        // 0,1 pixels => 0,4 bytes
+                     if (width_mmx)
+                     {
+                        int dummy_value_c;  // fix 'forbidden register spilled'
+                        int dummy_value_S;
+                        int dummy_value_D;
+
+                        __asm__ __volatile__ (
+                           "subl $4, %%esi          \n\t"
+                           "subl $28, %%edi         \n\t"
+
+                        ".loop4_pass2:              \n\t"
+                           "movq (%%esi), %%mm0     \n\t" // 7 6 5 4 3 2 1 0
+                           "movq %%mm0, %%mm1       \n\t" // 7 6 5 4 3 2 1 0
+                           "punpckldq %%mm0, %%mm0  \n\t" // 3 2 1 0 3 2 1 0
+                           "punpckhdq %%mm1, %%mm1  \n\t" // 7 6 5 4 7 6 5 4
+                           "movq %%mm0, (%%edi)     \n\t"
+                           "movq %%mm0, 8(%%edi)    \n\t"
+                           "movq %%mm1, 16(%%edi)   \n\t"
+                           "movq %%mm1, 24(%%edi)   \n\t"
+                           "subl $8, %%esi          \n\t"
+                           "subl $32, %%edi         \n\t"
+                           "subl $2, %%ecx          \n\t"
+                           "jnz .loop4_pass2        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "1" (sptr),      // esi      // input regs
+                             "2" (dp),        // edi
+                             "0" (width_mmx)  // ecx
+
+#if 0  /* %mm0, %mm1 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                           : "%mm0", "%mm1"               // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= (width_mmx*4 - 4); // sign fixed
+                     dp -= (width_mmx*16 - 4);  // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 4;
+                        png_memcpy(v, sptr, 4);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 4;
+                           png_memcpy(dp, v, 4);
+                        }
+                     }
+                  }
+                  else if (width)  // pass == 4 or 5
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;        // 0,1 pixels => 0,4 bytes
+                     if (width_mmx)
+                     {
+                        int dummy_value_c;  // fix 'forbidden register spilled'
+                        int dummy_value_S;
+                        int dummy_value_D;
+
+                        __asm__ __volatile__ (
+                           "subl $4, %%esi          \n\t"
+                           "subl $12, %%edi         \n\t"
+
+                        ".loop4_pass4:              \n\t"
+                           "movq (%%esi), %%mm0     \n\t" // 7 6 5 4 3 2 1 0
+                           "movq %%mm0, %%mm1       \n\t" // 7 6 5 4 3 2 1 0
+                           "punpckldq %%mm0, %%mm0  \n\t" // 3 2 1 0 3 2 1 0
+                           "punpckhdq %%mm1, %%mm1  \n\t" // 7 6 5 4 7 6 5 4
+                           "movq %%mm0, (%%edi)     \n\t"
+                           "subl $8, %%esi          \n\t"
+                           "movq %%mm1, 8(%%edi)    \n\t"
+                           "subl $16, %%edi         \n\t"
+                           "subl $2, %%ecx          \n\t"
+                           "jnz .loop4_pass4        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "1" (sptr),      // esi      // input regs
+                             "2" (dp),        // edi
+                             "0" (width_mmx)  // ecx
+
+#if 0  /* %mm0, %mm1 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                           : "%mm0", "%mm1"               // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= (width_mmx*4 - 4); // sign fixed
+                     dp -= (width_mmx*8 - 4);   // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 4;
+                        png_memcpy(v, sptr, 4);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 4;
+                           png_memcpy(dp, v, 4);
+                        }
+                     }
+                  }
+               } /* end of pixel_bytes == 4 */
+
+               //--------------------------------------------------------------
+               else if (pixel_bytes == 8)
+               {
+// GRR TEST:  should work, but needs testing (special 64-bit version of rpng2?)
+                  // GRR NOTE:  no need to combine passes here!
+                  if (((pass == 0) || (pass == 1)) && width)
+                  {
+                     int dummy_value_c;  // fix 'forbidden register spilled'
+                     int dummy_value_S;
+                     int dummy_value_D;
+
+                     // source is 8-byte RRGGBBAA
+                     // dest is 64-byte RRGGBBAA RRGGBBAA RRGGBBAA RRGGBBAA ...
+                     __asm__ __volatile__ (
+                        "subl $56, %%edi         \n\t" // start of last block
+
+                     ".loop8_pass0:              \n\t"
+                        "movq (%%esi), %%mm0     \n\t" // 7 6 5 4 3 2 1 0
+                        "movq %%mm0, (%%edi)     \n\t"
+                        "movq %%mm0, 8(%%edi)    \n\t"
+                        "movq %%mm0, 16(%%edi)   \n\t"
+                        "movq %%mm0, 24(%%edi)   \n\t"
+                        "movq %%mm0, 32(%%edi)   \n\t"
+                        "movq %%mm0, 40(%%edi)   \n\t"
+                        "movq %%mm0, 48(%%edi)   \n\t"
+                        "subl $8, %%esi          \n\t"
+                        "movq %%mm0, 56(%%edi)   \n\t"
+                        "subl $64, %%edi         \n\t"
+                        "decl %%ecx              \n\t"
+                        "jnz .loop8_pass0        \n\t"
+                        "EMMS                    \n\t" // DONE
+
+                        : "=c" (dummy_value_c),        // output regs (dummy)
+                          "=S" (dummy_value_S),
+                          "=D" (dummy_value_D)
+
+                        : "1" (sptr),      // esi      // input regs
+                          "2" (dp),        // edi
+                          "0" (width)      // ecx
+
+#if 0  /* %mm0 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                        : "%mm0"                       // clobber list
+#endif
+                     );
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     // source is 8-byte RRGGBBAA
+                     // dest is 32-byte RRGGBBAA RRGGBBAA RRGGBBAA RRGGBBAA
+                     // (recall that expansion is _in place_:  sptr and dp
+                     //  both point at locations within same row buffer)
+                     {
+                        int dummy_value_c;  // fix 'forbidden register spilled'
+                        int dummy_value_S;
+                        int dummy_value_D;
+
+                        __asm__ __volatile__ (
+                           "subl $24, %%edi         \n\t" // start of last block
+
+                        ".loop8_pass2:              \n\t"
+                           "movq (%%esi), %%mm0     \n\t" // 7 6 5 4 3 2 1 0
+                           "movq %%mm0, (%%edi)     \n\t"
+                           "movq %%mm0, 8(%%edi)    \n\t"
+                           "movq %%mm0, 16(%%edi)   \n\t"
+                           "subl $8, %%esi          \n\t"
+                           "movq %%mm0, 24(%%edi)   \n\t"
+                           "subl $32, %%edi         \n\t"
+                           "decl %%ecx              \n\t"
+                           "jnz .loop8_pass2        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "1" (sptr),      // esi      // input regs
+                             "2" (dp),        // edi
+                             "0" (width)      // ecx
+
+#if 0  /* %mm0 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                           : "%mm0"                       // clobber list
+#endif
+                        );
+                     }
+                  }
+                  else if (width)  // pass == 4 or 5
+                  {
+                     // source is 8-byte RRGGBBAA
+                     // dest is 16-byte RRGGBBAA RRGGBBAA
+                     {
+                        int dummy_value_c;  // fix 'forbidden register spilled'
+                        int dummy_value_S;
+                        int dummy_value_D;
+
+                        __asm__ __volatile__ (
+                           "subl $8, %%edi          \n\t" // start of last block
+
+                        ".loop8_pass4:              \n\t"
+                           "movq (%%esi), %%mm0     \n\t" // 7 6 5 4 3 2 1 0
+                           "movq %%mm0, (%%edi)     \n\t"
+                           "subl $8, %%esi          \n\t"
+                           "movq %%mm0, 8(%%edi)    \n\t"
+                           "subl $16, %%edi         \n\t"
+                           "decl %%ecx              \n\t"
+                           "jnz .loop8_pass4        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "1" (sptr),      // esi      // input regs
+                             "2" (dp),        // edi
+                             "0" (width)      // ecx
+
+#if 0  /* %mm0 not supported by gcc 2.7.2.3 or egcs 1.1 */
+                           : "%mm0"                       // clobber list
+#endif
+                        );
+                     }
+                  }
+
+               } /* end of pixel_bytes == 8 */
+
+               //--------------------------------------------------------------
+               else if (pixel_bytes == 6)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, 6);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, 6);
+                        dp -= 6;
+                     }
+                     sptr -= 6;
+                  }
+               } /* end of pixel_bytes == 6 */
+
+               //--------------------------------------------------------------
+               else
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr-= pixel_bytes;
+                  }
+               }
+            } // end of _mmx_supported ========================================
+
+            else /* MMX not supported:  use modified C code - takes advantage
+                  *   of inlining of png_memcpy for a constant */
+                 /* GRR 19991007:  does it?  or should pixel_bytes in each
+                  *   block be replaced with immediate value (e.g., 1)? */
+                 /* GRR 19991017:  replaced with constants in each case */
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+            {
+               if (pixel_bytes == 1)
+               {
+                  for (i = width; i; i--)
+                  {
+                     int j;
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        *dp-- = *sptr;
+                     }
+                     --sptr;
+                  }
+               }
+               else if (pixel_bytes == 3)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, 3);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, 3);
+                        dp -= 3;
+                     }
+                     sptr -= 3;
+                  }
+               }
+               else if (pixel_bytes == 2)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, 2);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, 2);
+                        dp -= 2;
+                     }
+                     sptr -= 2;
+                  }
+               }
+               else if (pixel_bytes == 4)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, 4);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+#ifdef PNG_DEBUG
+                        if (dp < row || dp+3 > row+png_ptr->row_buf_size)
+                        {
+                           printf("dp out of bounds: row=%d, dp=%d, rp=%d\n",
+                             row, dp, row+png_ptr->row_buf_size);
+                           printf("row_buf=%d\n",png_ptr->row_buf_size);
+                        }
+#endif
+                        png_memcpy(dp, v, 4);
+                        dp -= 4;
+                     }
+                     sptr -= 4;
+                  }
+               }
+               else if (pixel_bytes == 6)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, 6);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, 6);
+                        dp -= 6;
+                     }
+                     sptr -= 6;
+                  }
+               }
+               else if (pixel_bytes == 8)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, 8);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, 8);
+                        dp -= 8;
+                     }
+                     sptr -= 8;
+                  }
+               }
+               else     /* GRR:  should never be reached */
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr -= pixel_bytes;
+                  }
+               }
+
+            } /* end if (MMX not supported) */
+            break;
+         }
+      } /* end switch (row_info->pixel_depth) */
+
+      row_info->width = final_width;
+
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width);
+   }
+
+} /* end png_do_read_interlace() */
+
+#endif /* PNG_HAVE_ASSEMBLER_READ_INTERLACE */
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+
+
+
+#if defined(PNG_HAVE_ASSEMBLER_READ_FILTER_ROW)
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+
+// These variables are utilized in the functions below.  They are declared
+// globally here to ensure alignment on 8-byte boundaries.
+
+union uAll {
+   long long use;
+   double  align;
+} _LBCarryMask = {0x0101010101010101LL},
+  _HBClearMask = {0x7f7f7f7f7f7f7f7fLL},
+  _ActiveMask, _ActiveMask2, _ActiveMaskEnd, _ShiftBpp, _ShiftRem;
+
+#ifdef PNG_THREAD_UNSAFE_OK
+//===========================================================================//
+//                                                                           //
+//           P N G _ R E A D _ F I L T E R _ R O W _ M M X _ A V G           //
+//                                                                           //
+//===========================================================================//
+
+// Optimized code for PNG Average filter decoder
+
+static void /* PRIVATE */
+png_read_filter_row_mmx_avg(png_row_infop row_info, png_bytep row,
+                            png_bytep prev_row)
+{
+   int bpp;
+   int dummy_value_c;   // fix 'forbidden register 2 (cx) was spilled' error
+   int dummy_value_S;
+   int dummy_value_D;
+
+   bpp = (row_info->pixel_depth + 7) >> 3;  // get # bytes per pixel
+   _FullLength  = row_info->rowbytes;       // # of bytes to filter
+
+   __asm__ __volatile__ (
+      // initialize address pointers and offset
+#ifdef __PIC__
+      "pushl %%ebx                 \n\t" // save index to Global Offset Table
+#endif
+//pre "movl row, %%edi             \n\t" // edi:  Avg(x)
+      "xorl %%ebx, %%ebx           \n\t" // ebx:  x
+      "movl %%edi, %%edx           \n\t"
+//pre "movl prev_row, %%esi        \n\t" // esi:  Prior(x)
+//pre "subl bpp, %%edx             \n\t" // (bpp is preloaded into ecx)
+      "subl %%ecx, %%edx           \n\t" // edx:  Raw(x-bpp)
+
+      "xorl %%eax,%%eax            \n\t"
+
+      // Compute the Raw value for the first bpp bytes
+      //    Raw(x) = Avg(x) + (Prior(x)/2)
+   "avg_rlp:                       \n\t"
+      "movb (%%esi,%%ebx,),%%al    \n\t" // load al with Prior(x)
+      "incl %%ebx                  \n\t"
+      "shrb %%al                   \n\t" // divide by 2
+      "addb -1(%%edi,%%ebx,),%%al  \n\t" // add Avg(x); -1 to offset inc ebx
+//pre "cmpl bpp, %%ebx             \n\t" // (bpp is preloaded into ecx)
+      "cmpl %%ecx, %%ebx           \n\t"
+      "movb %%al,-1(%%edi,%%ebx,)  \n\t" // write Raw(x); -1 to offset inc ebx
+      "jb avg_rlp                  \n\t" // mov does not affect flags
+
+      // get # of bytes to alignment
+      "movl %%edi, _dif            \n\t" // take start of row
+      "addl %%ebx, _dif            \n\t" // add bpp
+      "addl $0xf, _dif             \n\t" // add 7+8 to incr past alignment bdry
+      "andl $0xfffffff8, _dif      \n\t" // mask to alignment boundary
+      "subl %%edi, _dif            \n\t" // subtract from start => value ebx at
+      "jz avg_go                   \n\t" //  alignment
+
+      // fix alignment
+      // Compute the Raw value for the bytes up to the alignment boundary
+      //    Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
+      "xorl %%ecx, %%ecx           \n\t"
+
+   "avg_lp1:                       \n\t"
+      "xorl %%eax, %%eax           \n\t"
+      "movb (%%esi,%%ebx,), %%cl   \n\t" // load cl with Prior(x)
+      "movb (%%edx,%%ebx,), %%al   \n\t" // load al with Raw(x-bpp)
+      "addw %%cx, %%ax             \n\t"
+      "incl %%ebx                  \n\t"
+      "shrw %%ax                   \n\t" // divide by 2
+      "addb -1(%%edi,%%ebx,), %%al \n\t" // add Avg(x); -1 to offset inc ebx
+      "cmpl _dif, %%ebx            \n\t" // check if at alignment boundary
+      "movb %%al, -1(%%edi,%%ebx,) \n\t" // write Raw(x); -1 to offset inc ebx
+      "jb avg_lp1                  \n\t" // repeat until at alignment boundary
+
+   "avg_go:                        \n\t"
+      "movl _FullLength, %%eax     \n\t"
+      "movl %%eax, %%ecx           \n\t"
+      "subl %%ebx, %%eax           \n\t" // subtract alignment fix
+      "andl $0x00000007, %%eax     \n\t" // calc bytes over mult of 8
+      "subl %%eax, %%ecx           \n\t" // drop over bytes from original length
+      "movl %%ecx, _MMXLength      \n\t"
+#ifdef __PIC__
+      "popl %%ebx                  \n\t" // restore index to Global Offset Table
+#endif
+
+      : "=c" (dummy_value_c),            // output regs (dummy)
+        "=S" (dummy_value_S),
+        "=D" (dummy_value_D)
+
+      : "0" (bpp),       // ecx          // input regs
+        "1" (prev_row),  // esi
+        "2" (row)        // edi
+
+      : "%eax", "%edx"                   // clobber list
+#ifndef __PIC__
+      , "%ebx"
+#endif
+      // GRR: INCLUDE "memory" as clobbered? (_dif, _MMXLength)
+      // (seems to work fine without...)
+   );
+
+   // now do the math for the rest of the row
+   switch (bpp)
+   {
+      case 3:
+      {
+         _ActiveMask.use  = 0x0000000000ffffffLL;
+         _ShiftBpp.use = 24;    // == 3 * 8
+         _ShiftRem.use = 40;    // == 64 - 24
+
+         __asm__ __volatile__ (
+            // re-init address pointers and offset
+            "movq _ActiveMask, %%mm7      \n\t"
+            "movl _dif, %%ecx             \n\t" // ecx:  x = offset to
+            "movq _LBCarryMask, %%mm5     \n\t" //  alignment boundary
+// preload  "movl row, %%edi              \n\t" // edi:  Avg(x)
+            "movq _HBClearMask, %%mm4     \n\t"
+// preload  "movl prev_row, %%esi         \n\t" // esi:  Prior(x)
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%%edi,%%ecx,), %%mm2 \n\t" // load previous aligned 8 bytes
+                                                // (correct pos. in loop below)
+         "avg_3lp:                        \n\t"
+            "movq (%%edi,%%ecx,), %%mm0   \n\t" // load mm0 with Avg(x)
+            "movq %%mm5, %%mm3            \n\t"
+            "psrlq _ShiftRem, %%mm2       \n\t" // correct position Raw(x-bpp)
+                                                // data
+            "movq (%%esi,%%ecx,), %%mm1   \n\t" // load mm1 with Prior(x)
+            "movq %%mm7, %%mm6            \n\t"
+            "pand %%mm1, %%mm3            \n\t" // get lsb for each prev_row byte
+            "psrlq $1, %%mm1              \n\t" // divide prev_row bytes by 2
+            "pand  %%mm4, %%mm1           \n\t" // clear invalid bit 7 of each
+                                                // byte
+            "paddb %%mm1, %%mm0           \n\t" // add (Prev_row/2) to Avg for
+                                                // each byte
+            // add 1st active group (Raw(x-bpp)/2) to average with LBCarry
+            "movq %%mm3, %%mm1            \n\t" // now use mm1 for getting
+                                                // LBCarrys
+            "pand %%mm2, %%mm1            \n\t" // get LBCarrys for each byte
+                                                // where both
+                               // lsb's were == 1 (only valid for active group)
+            "psrlq $1, %%mm2              \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2           \n\t" // clear invalid bit 7 of each
+                                                // byte
+            "paddb %%mm1, %%mm2           \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                                // for each byte
+            "pand %%mm6, %%mm2            \n\t" // leave only Active Group 1
+                                                // bytes to add to Avg
+            "paddb %%mm2, %%mm0           \n\t" // add (Raw/2) + LBCarrys to
+                                                // Avg for each Active
+                               //  byte
+            // add 2nd active group (Raw(x-bpp)/2) to average with _LBCarry
+            "psllq _ShiftBpp, %%mm6       \n\t" // shift the mm6 mask to cover
+                                                // bytes 3-5
+            "movq %%mm0, %%mm2            \n\t" // mov updated Raws to mm2
+            "psllq _ShiftBpp, %%mm2       \n\t" // shift data to pos. correctly
+            "movq %%mm3, %%mm1            \n\t" // now use mm1 for getting
+                                                // LBCarrys
+            "pand %%mm2, %%mm1            \n\t" // get LBCarrys for each byte
+                                                // where both
+                               // lsb's were == 1 (only valid for active group)
+            "psrlq $1, %%mm2              \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2           \n\t" // clear invalid bit 7 of each
+                                                // byte
+            "paddb %%mm1, %%mm2           \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                                // for each byte
+            "pand %%mm6, %%mm2            \n\t" // leave only Active Group 2
+                                                // bytes to add to Avg
+            "paddb %%mm2, %%mm0           \n\t" // add (Raw/2) + LBCarrys to
+                                                // Avg for each Active
+                               //  byte
+
+            // add 3rd active group (Raw(x-bpp)/2) to average with _LBCarry
+            "psllq _ShiftBpp, %%mm6       \n\t" // shift mm6 mask to cover last
+                                                // two
+                                 // bytes
+            "movq %%mm0, %%mm2            \n\t" // mov updated Raws to mm2
+            "psllq _ShiftBpp, %%mm2       \n\t" // shift data to pos. correctly
+                              // Data only needs to be shifted once here to
+                              // get the correct x-bpp offset.
+            "movq %%mm3, %%mm1            \n\t" // now use mm1 for getting
+                                                // LBCarrys
+            "pand %%mm2, %%mm1            \n\t" // get LBCarrys for each byte
+                                                // where both
+                              // lsb's were == 1 (only valid for active group)
+            "psrlq $1, %%mm2              \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2           \n\t" // clear invalid bit 7 of each
+                                                // byte
+            "paddb %%mm1, %%mm2           \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                                // for each byte
+            "pand %%mm6, %%mm2            \n\t" // leave only Active Group 2
+                                                // bytes to add to Avg
+            "addl $8, %%ecx               \n\t"
+            "paddb %%mm2, %%mm0           \n\t" // add (Raw/2) + LBCarrys to
+                                                // Avg for each Active
+                                                // byte
+            // now ready to write back to memory
+            "movq %%mm0, -8(%%edi,%%ecx,) \n\t"
+            // move updated Raw(x) to use as Raw(x-bpp) for next loop
+            "cmpl _MMXLength, %%ecx       \n\t"
+            "movq %%mm0, %%mm2            \n\t" // mov updated Raw(x) to mm2
+            "jb avg_3lp                   \n\t"
+
+            : "=S" (dummy_value_S),             // output regs (dummy)
+              "=D" (dummy_value_D)
+
+            : "0" (prev_row),  // esi           // input regs
+              "1" (row)        // edi
+
+            : "%ecx"                            // clobber list
+#if 0  /* %mm0, ..., %mm7 not supported by gcc 2.7.2.3 or egcs 1.1 */
+            , "%mm0", "%mm1", "%mm2", "%mm3"
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 3 bpp
+
+      case 6:
+      case 4:
+      //case 7:   // who wrote this?  PNG doesn't support 5 or 7 bytes/pixel
+      //case 5:   // GRR BOGUS
+      {
+         _ActiveMask.use  = 0xffffffffffffffffLL; // use shift below to clear
+                                                  // appropriate inactive bytes
+         _ShiftBpp.use = bpp << 3;
+         _ShiftRem.use = 64 - _ShiftBpp.use;
+
+         __asm__ __volatile__ (
+            "movq _HBClearMask, %%mm4    \n\t"
+
+            // re-init address pointers and offset
+            "movl _dif, %%ecx            \n\t" // ecx:  x = offset to
+                                               // alignment boundary
+
+            // load _ActiveMask and clear all bytes except for 1st active group
+            "movq _ActiveMask, %%mm7     \n\t"
+// preload  "movl row, %%edi             \n\t" // edi:  Avg(x)
+            "psrlq _ShiftRem, %%mm7      \n\t"
+// preload  "movl prev_row, %%esi        \n\t" // esi:  Prior(x)
+            "movq %%mm7, %%mm6           \n\t"
+            "movq _LBCarryMask, %%mm5    \n\t"
+            "psllq _ShiftBpp, %%mm6      \n\t" // create mask for 2nd active
+                                               // group
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%%edi,%%ecx,), %%mm2 \n\t" // load previous aligned 8 bytes
+                                          // (we correct pos. in loop below)
+         "avg_4lp:                       \n\t"
+            "movq (%%edi,%%ecx,), %%mm0  \n\t"
+            "psrlq _ShiftRem, %%mm2      \n\t" // shift data to pos. correctly
+            "movq (%%esi,%%ecx,), %%mm1  \n\t"
+            // add (Prev_row/2) to average
+            "movq %%mm5, %%mm3           \n\t"
+            "pand %%mm1, %%mm3           \n\t" // get lsb for each prev_row byte
+            "psrlq $1, %%mm1             \n\t" // divide prev_row bytes by 2
+            "pand  %%mm4, %%mm1          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm0          \n\t" // add (Prev_row/2) to Avg for
+                                               // each byte
+            // add 1st active group (Raw(x-bpp)/2) to average with _LBCarry
+            "movq %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                               // LBCarrys
+            "pand %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                               // where both
+                              // lsb's were == 1 (only valid for active group)
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm2          \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                               // for each byte
+            "pand %%mm7, %%mm2           \n\t" // leave only Active Group 1
+                                               // bytes to add to Avg
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) + LBCarrys to Avg
+                                               // for each Active
+                              // byte
+            // add 2nd active group (Raw(x-bpp)/2) to average with _LBCarry
+            "movq %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "psllq _ShiftBpp, %%mm2      \n\t" // shift data to pos. correctly
+            "addl $8, %%ecx              \n\t"
+            "movq %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                               // LBCarrys
+            "pand %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                               // where both
+                              // lsb's were == 1 (only valid for active group)
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm2          \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                               // for each byte
+            "pand %%mm6, %%mm2           \n\t" // leave only Active Group 2
+                                               // bytes to add to Avg
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) + LBCarrys to
+                                               // Avg for each Active
+                              // byte
+            "cmpl _MMXLength, %%ecx      \n\t"
+            // now ready to write back to memory
+            "movq %%mm0, -8(%%edi,%%ecx,) \n\t"
+            // prep Raw(x-bpp) for next loop
+            "movq %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "jb avg_4lp                  \n\t"
+
+            : "=S" (dummy_value_S),            // output regs (dummy)
+              "=D" (dummy_value_D)
+
+            : "0" (prev_row),  // esi          // input regs
+              "1" (row)        // edi
+
+            : "%ecx"                           // clobber list
+#if 0  /* %mm0, ..., %mm7 not supported by gcc 2.7.2.3 or egcs 1.1 */
+            , "%mm0", "%mm1", "%mm2", "%mm3"
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 4,6 bpp
+
+      case 2:
+      {
+         _ActiveMask.use  = 0x000000000000ffffLL;
+         _ShiftBpp.use = 16;   // == 2 * 8
+         _ShiftRem.use = 48;   // == 64 - 16
+
+         __asm__ __volatile__ (
+            // load _ActiveMask
+            "movq _ActiveMask, %%mm7     \n\t"
+            // re-init address pointers and offset
+            "movl _dif, %%ecx            \n\t" // ecx:  x = offset to alignment
+                                               // boundary
+            "movq _LBCarryMask, %%mm5    \n\t"
+// preload  "movl row, %%edi             \n\t" // edi:  Avg(x)
+            "movq _HBClearMask, %%mm4    \n\t"
+// preload  "movl prev_row, %%esi        \n\t" // esi:  Prior(x)
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%%edi,%%ecx,), %%mm2 \n\t" // load previous aligned 8 bytes
+                              // (we correct pos. in loop below)
+         "avg_2lp:                       \n\t"
+            "movq (%%edi,%%ecx,), %%mm0  \n\t"
+            "psrlq _ShiftRem, %%mm2      \n\t" // shift data to pos. correctly
+            "movq (%%esi,%%ecx,), %%mm1  \n\t" //  (GRR BUGFIX:  was psllq)
+            // add (Prev_row/2) to average
+            "movq %%mm5, %%mm3           \n\t"
+            "pand %%mm1, %%mm3           \n\t" // get lsb for each prev_row byte
+            "psrlq $1, %%mm1             \n\t" // divide prev_row bytes by 2
+            "pand  %%mm4, %%mm1          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "movq %%mm7, %%mm6           \n\t"
+            "paddb %%mm1, %%mm0          \n\t" // add (Prev_row/2) to Avg for
+                                               // each byte
+
+            // add 1st active group (Raw(x-bpp)/2) to average with _LBCarry
+            "movq %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                               // LBCarrys
+            "pand %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                               // where both
+                                               // lsb's were == 1 (only valid
+                                               // for active group)
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm2          \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                               // for each byte
+            "pand %%mm6, %%mm2           \n\t" // leave only Active Group 1
+                                               // bytes to add to Avg
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) + LBCarrys to Avg
+                                               // for each Active byte
+
+            // add 2nd active group (Raw(x-bpp)/2) to average with _LBCarry
+            "psllq _ShiftBpp, %%mm6      \n\t" // shift the mm6 mask to cover
+                                               // bytes 2 & 3
+            "movq %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "psllq _ShiftBpp, %%mm2      \n\t" // shift data to pos. correctly
+            "movq %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                               // LBCarrys
+            "pand %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                               // where both
+                                               // lsb's were == 1 (only valid
+                                               // for active group)
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm2          \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                               // for each byte
+            "pand %%mm6, %%mm2           \n\t" // leave only Active Group 2
+                                               // bytes to add to Avg
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) + LBCarrys to
+                                               // Avg for each Active byte
+
+            // add 3rd active group (Raw(x-bpp)/2) to average with _LBCarry
+            "psllq _ShiftBpp, %%mm6      \n\t" // shift the mm6 mask to cover
+                                               // bytes 4 & 5
+            "movq %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "psllq _ShiftBpp, %%mm2      \n\t" // shift data to pos. correctly
+            "movq %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                               // LBCarrys
+            "pand %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                               // where both lsb's were == 1
+                                               // (only valid for active group)
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm2          \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                               // for each byte
+            "pand %%mm6, %%mm2           \n\t" // leave only Active Group 2
+                                               // bytes to add to Avg
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) + LBCarrys to
+                                               // Avg for each Active byte
+
+            // add 4th active group (Raw(x-bpp)/2) to average with _LBCarry
+            "psllq _ShiftBpp, %%mm6      \n\t" // shift the mm6 mask to cover
+                                               // bytes 6 & 7
+            "movq %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "psllq _ShiftBpp, %%mm2      \n\t" // shift data to pos. correctly
+            "addl $8, %%ecx              \n\t"
+            "movq %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                               // LBCarrys
+            "pand %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                               // where both
+                                               // lsb's were == 1 (only valid
+                                               // for active group)
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm2          \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                               // for each byte
+            "pand %%mm6, %%mm2           \n\t" // leave only Active Group 2
+                                               // bytes to add to Avg
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) + LBCarrys to
+                                               // Avg for each Active byte
+
+            "cmpl _MMXLength, %%ecx      \n\t"
+            // now ready to write back to memory
+            "movq %%mm0, -8(%%edi,%%ecx,) \n\t"
+            // prep Raw(x-bpp) for next loop
+            "movq %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "jb avg_2lp                  \n\t"
+
+            : "=S" (dummy_value_S),            // output regs (dummy)
+              "=D" (dummy_value_D)
+
+            : "0" (prev_row),  // esi          // input regs
+              "1" (row)        // edi
+
+            : "%ecx"                           // clobber list
+#if 0  /* %mm0, ..., %mm7 not supported by gcc 2.7.2.3 or egcs 1.1 */
+            , "%mm0", "%mm1", "%mm2", "%mm3"
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 2 bpp
+
+      case 1:
+      {
+         __asm__ __volatile__ (
+            // re-init address pointers and offset
+#ifdef __PIC__
+            "pushl %%ebx                 \n\t" // save Global Offset Table index
+#endif
+            "movl _dif, %%ebx            \n\t" // ebx:  x = offset to alignment
+                                               // boundary
+// preload  "movl row, %%edi             \n\t" // edi:  Avg(x)
+            "cmpl _FullLength, %%ebx     \n\t" // test if offset at end of array
+            "jnb avg_1end                \n\t"
+            // do Paeth decode for remaining bytes
+// preload  "movl prev_row, %%esi        \n\t" // esi:  Prior(x)
+            "movl %%edi, %%edx           \n\t"
+// preload  "subl bpp, %%edx             \n\t" // (bpp is preloaded into ecx)
+            "subl %%ecx, %%edx           \n\t" // edx:  Raw(x-bpp)
+            "xorl %%ecx, %%ecx           \n\t" // zero ecx before using cl & cx
+                                               //  in loop below
+         "avg_1lp:                       \n\t"
+            // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
+            "xorl %%eax, %%eax           \n\t"
+            "movb (%%esi,%%ebx,), %%cl   \n\t" // load cl with Prior(x)
+            "movb (%%edx,%%ebx,), %%al   \n\t" // load al with Raw(x-bpp)
+            "addw %%cx, %%ax             \n\t"
+            "incl %%ebx                  \n\t"
+            "shrw %%ax                   \n\t" // divide by 2
+            "addb -1(%%edi,%%ebx,), %%al \n\t" // add Avg(x); -1 to offset
+                                               // inc ebx
+            "cmpl _FullLength, %%ebx     \n\t" // check if at end of array
+            "movb %%al, -1(%%edi,%%ebx,) \n\t" // write back Raw(x);
+                         // mov does not affect flags; -1 to offset inc ebx
+            "jb avg_1lp                  \n\t"
+
+         "avg_1end:                      \n\t"
+#ifdef __PIC__
+            "popl %%ebx                  \n\t" // Global Offset Table index
+#endif
+
+            : "=c" (dummy_value_c),            // output regs (dummy)
+              "=S" (dummy_value_S),
+              "=D" (dummy_value_D)
+
+            : "0" (bpp),       // ecx          // input regs
+              "1" (prev_row),  // esi
+              "2" (row)        // edi
+
+            : "%eax", "%edx"                   // clobber list
+#ifndef __PIC__
+            , "%ebx"
+#endif
+         );
+      }
+      return;  // end 1 bpp
+
+      case 8:
+      {
+         __asm__ __volatile__ (
+            // re-init address pointers and offset
+            "movl _dif, %%ecx            \n\t" // ecx:  x == offset to alignment
+            "movq _LBCarryMask, %%mm5    \n\t" //            boundary
+// preload  "movl row, %%edi             \n\t" // edi:  Avg(x)
+            "movq _HBClearMask, %%mm4    \n\t"
+// preload  "movl prev_row, %%esi        \n\t" // esi:  Prior(x)
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%%edi,%%ecx,), %%mm2 \n\t" // load previous aligned 8 bytes
+                                      // (NO NEED to correct pos. in loop below)
+
+         "avg_8lp:                       \n\t"
+            "movq (%%edi,%%ecx,), %%mm0  \n\t"
+            "movq %%mm5, %%mm3           \n\t"
+            "movq (%%esi,%%ecx,), %%mm1  \n\t"
+            "addl $8, %%ecx              \n\t"
+            "pand %%mm1, %%mm3           \n\t" // get lsb for each prev_row byte
+            "psrlq $1, %%mm1             \n\t" // divide prev_row bytes by 2
+            "pand %%mm2, %%mm3           \n\t" // get LBCarrys for each byte
+                                               //  where both lsb's were == 1
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm1          \n\t" // clear invalid bit 7, each byte
+            "paddb %%mm3, %%mm0          \n\t" // add LBCarrys to Avg, each byte
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7, each byte
+            "paddb %%mm1, %%mm0          \n\t" // add (Prev_row/2) to Avg, each
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) to Avg for each
+            "cmpl _MMXLength, %%ecx      \n\t"
+            "movq %%mm0, -8(%%edi,%%ecx,) \n\t"
+            "movq %%mm0, %%mm2           \n\t" // reuse as Raw(x-bpp)
+            "jb avg_8lp                  \n\t"
+
+            : "=S" (dummy_value_S),            // output regs (dummy)
+              "=D" (dummy_value_D)
+
+            : "0" (prev_row),  // esi          // input regs
+              "1" (row)        // edi
+
+            : "%ecx"                           // clobber list
+#if 0  /* %mm0, ..., %mm5 not supported by gcc 2.7.2.3 or egcs 1.1 */
+            , "%mm0", "%mm1", "%mm2"
+            , "%mm3", "%mm4", "%mm5"
+#endif
+         );
+      }
+      break;  // end 8 bpp
+
+      default:                  // bpp greater than 8 (!= 1,2,3,4,[5],6,[7],8)
+      {
+
+#ifdef PNG_DEBUG
+         // GRR:  PRINT ERROR HERE:  SHOULD NEVER BE REACHED
+        png_debug(1,
+        "Internal logic error in pnggccrd (png_read_filter_row_mmx_avg())\n");
+#endif
+
+#if 0
+        __asm__ __volatile__ (
+            "movq _LBCarryMask, %%mm5    \n\t"
+            // re-init address pointers and offset
+            "movl _dif, %%ebx            \n\t" // ebx:  x = offset to
+                                               // alignment boundary
+            "movl row, %%edi             \n\t" // edi:  Avg(x)
+            "movq _HBClearMask, %%mm4    \n\t"
+            "movl %%edi, %%edx           \n\t"
+            "movl prev_row, %%esi        \n\t" // esi:  Prior(x)
+            "subl bpp, %%edx             \n\t" // edx:  Raw(x-bpp)
+         "avg_Alp:                       \n\t"
+            "movq (%%edi,%%ebx,), %%mm0  \n\t"
+            "movq %%mm5, %%mm3           \n\t"
+            "movq (%%esi,%%ebx,), %%mm1  \n\t"
+            "pand %%mm1, %%mm3           \n\t" // get lsb for each prev_row byte
+            "movq (%%edx,%%ebx,), %%mm2  \n\t"
+            "psrlq $1, %%mm1             \n\t" // divide prev_row bytes by 2
+            "pand %%mm2, %%mm3           \n\t" // get LBCarrys for each byte
+                                               // where both lsb's were == 1
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm1          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm3, %%mm0          \n\t" // add LBCarrys to Avg for each
+                                               // byte
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm0          \n\t" // add (Prev_row/2) to Avg for
+                                               // each byte
+            "addl $8, %%ebx              \n\t"
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) to Avg for each
+                                               // byte
+            "cmpl _MMXLength, %%ebx      \n\t"
+            "movq %%mm0, -8(%%edi,%%ebx,) \n\t"
+            "jb avg_Alp                  \n\t"
+
+            : // FIXASM: output regs/vars go here, e.g.:  "=m" (memory_var)
+
+            : // FIXASM: input regs, e.g.:  "c" (count), "S" (src), "D" (dest)
+
+            : "%ebx", "%edx", "%edi", "%esi" // CHECKASM: clobber list
+         );
+#endif /* 0 - NEVER REACHED */
+      }
+      break;
+
+   } // end switch (bpp)
+
+   __asm__ __volatile__ (
+      // MMX acceleration complete; now do clean-up
+      // check if any remaining bytes left to decode
+#ifdef __PIC__
+      "pushl %%ebx                 \n\t" // save index to Global Offset Table
+#endif
+      "movl _MMXLength, %%ebx      \n\t" // ebx:  x == offset bytes after MMX
+//pre "movl row, %%edi             \n\t" // edi:  Avg(x)
+      "cmpl _FullLength, %%ebx     \n\t" // test if offset at end of array
+      "jnb avg_end                 \n\t"
+
+      // do Avg decode for remaining bytes
+//pre "movl prev_row, %%esi        \n\t" // esi:  Prior(x)
+      "movl %%edi, %%edx           \n\t"
+//pre "subl bpp, %%edx             \n\t" // (bpp is preloaded into ecx)
+      "subl %%ecx, %%edx           \n\t" // edx:  Raw(x-bpp)
+      "xorl %%ecx, %%ecx           \n\t" // zero ecx before using cl & cx below
+
+   "avg_lp2:                       \n\t"
+      // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
+      "xorl %%eax, %%eax           \n\t"
+      "movb (%%esi,%%ebx,), %%cl   \n\t" // load cl with Prior(x)
+      "movb (%%edx,%%ebx,), %%al   \n\t" // load al with Raw(x-bpp)
+      "addw %%cx, %%ax             \n\t"
+      "incl %%ebx                  \n\t"
+      "shrw %%ax                   \n\t" // divide by 2
+      "addb -1(%%edi,%%ebx,), %%al \n\t" // add Avg(x); -1 to offset inc ebx
+      "cmpl _FullLength, %%ebx     \n\t" // check if at end of array
+      "movb %%al, -1(%%edi,%%ebx,) \n\t" // write back Raw(x) [mov does not
+      "jb avg_lp2                  \n\t" //  affect flags; -1 to offset inc ebx]
+
+   "avg_end:                       \n\t"
+      "EMMS                        \n\t" // end MMX; prep for poss. FP instrs.
+#ifdef __PIC__
+      "popl %%ebx                  \n\t" // restore index to Global Offset Table
+#endif
+
+      : "=c" (dummy_value_c),            // output regs (dummy)
+        "=S" (dummy_value_S),
+        "=D" (dummy_value_D)
+
+      : "0" (bpp),       // ecx          // input regs
+        "1" (prev_row),  // esi
+        "2" (row)        // edi
+
+      : "%eax", "%edx"                   // clobber list
+#ifndef __PIC__
+      , "%ebx"
+#endif
+   );
+
+} /* end png_read_filter_row_mmx_avg() */
+#endif
+
+
+
+#ifdef PNG_THREAD_UNSAFE_OK
+//===========================================================================//
+//                                                                           //
+//         P N G _ R E A D _ F I L T E R _ R O W _ M M X _ P A E T H         //
+//                                                                           //
+//===========================================================================//
+
+// Optimized code for PNG Paeth filter decoder
+
+static void /* PRIVATE */
+png_read_filter_row_mmx_paeth(png_row_infop row_info, png_bytep row,
+                              png_bytep prev_row)
+{
+   int bpp;
+   int dummy_value_c;   // fix 'forbidden register 2 (cx) was spilled' error
+   int dummy_value_S;
+   int dummy_value_D;
+
+   bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel
+   _FullLength  = row_info->rowbytes; // # of bytes to filter
+
+   __asm__ __volatile__ (
+#ifdef __PIC__
+      "pushl %%ebx                 \n\t" // save index to Global Offset Table
+#endif
+      "xorl %%ebx, %%ebx           \n\t" // ebx:  x offset
+//pre "movl row, %%edi             \n\t"
+      "xorl %%edx, %%edx           \n\t" // edx:  x-bpp offset
+//pre "movl prev_row, %%esi        \n\t"
+      "xorl %%eax, %%eax           \n\t"
+
+      // Compute the Raw value for the first bpp bytes
+      // Note: the formula works out to be always
+      //   Paeth(x) = Raw(x) + Prior(x)      where x < bpp
+   "paeth_rlp:                     \n\t"
+      "movb (%%edi,%%ebx,), %%al   \n\t"
+      "addb (%%esi,%%ebx,), %%al   \n\t"
+      "incl %%ebx                  \n\t"
+//pre "cmpl bpp, %%ebx             \n\t" (bpp is preloaded into ecx)
+      "cmpl %%ecx, %%ebx           \n\t"
+      "movb %%al, -1(%%edi,%%ebx,) \n\t"
+      "jb paeth_rlp                \n\t"
+      // get # of bytes to alignment
+      "movl %%edi, _dif            \n\t" // take start of row
+      "addl %%ebx, _dif            \n\t" // add bpp
+      "xorl %%ecx, %%ecx           \n\t"
+      "addl $0xf, _dif             \n\t" // add 7 + 8 to incr past alignment
+                                         // boundary
+      "andl $0xfffffff8, _dif      \n\t" // mask to alignment boundary
+      "subl %%edi, _dif            \n\t" // subtract from start ==> value ebx
+                                         // at alignment
+      "jz paeth_go                 \n\t"
+      // fix alignment
+
+   "paeth_lp1:                     \n\t"
+      "xorl %%eax, %%eax           \n\t"
+      // pav = p - a = (a + b - c) - a = b - c
+      "movb (%%esi,%%ebx,), %%al   \n\t" // load Prior(x) into al
+      "movb (%%esi,%%edx,), %%cl   \n\t" // load Prior(x-bpp) into cl
+      "subl %%ecx, %%eax           \n\t" // subtract Prior(x-bpp)
+      "movl %%eax, _patemp         \n\t" // Save pav for later use
+      "xorl %%eax, %%eax           \n\t"
+      // pbv = p - b = (a + b - c) - b = a - c
+      "movb (%%edi,%%edx,), %%al   \n\t" // load Raw(x-bpp) into al
+      "subl %%ecx, %%eax           \n\t" // subtract Prior(x-bpp)
+      "movl %%eax, %%ecx           \n\t"
+      // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+      "addl _patemp, %%eax         \n\t" // pcv = pav + pbv
+      // pc = abs(pcv)
+      "testl $0x80000000, %%eax    \n\t"
+      "jz paeth_pca                \n\t"
+      "negl %%eax                  \n\t" // reverse sign of neg values
+
+   "paeth_pca:                     \n\t"
+      "movl %%eax, _pctemp         \n\t" // save pc for later use
+      // pb = abs(pbv)
+      "testl $0x80000000, %%ecx    \n\t"
+      "jz paeth_pba                \n\t"
+      "negl %%ecx                  \n\t" // reverse sign of neg values
+
+   "paeth_pba:                     \n\t"
+      "movl %%ecx, _pbtemp         \n\t" // save pb for later use
+      // pa = abs(pav)
+      "movl _patemp, %%eax         \n\t"
+      "testl $0x80000000, %%eax    \n\t"
+      "jz paeth_paa                \n\t"
+      "negl %%eax                  \n\t" // reverse sign of neg values
+
+   "paeth_paa:                     \n\t"
+      "movl %%eax, _patemp         \n\t" // save pa for later use
+      // test if pa <= pb
+      "cmpl %%ecx, %%eax           \n\t"
+      "jna paeth_abb               \n\t"
+      // pa > pb; now test if pb <= pc
+      "cmpl _pctemp, %%ecx         \n\t"
+      "jna paeth_bbc               \n\t"
+      // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+      "movb (%%esi,%%edx,), %%cl   \n\t" // load Prior(x-bpp) into cl
+      "jmp paeth_paeth             \n\t"
+
+   "paeth_bbc:                     \n\t"
+      // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
+      "movb (%%esi,%%ebx,), %%cl   \n\t" // load Prior(x) into cl
+      "jmp paeth_paeth             \n\t"
+
+   "paeth_abb:                     \n\t"
+      // pa <= pb; now test if pa <= pc
+      "cmpl _pctemp, %%eax         \n\t"
+      "jna paeth_abc               \n\t"
+      // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+      "movb (%%esi,%%edx,), %%cl   \n\t" // load Prior(x-bpp) into cl
+      "jmp paeth_paeth             \n\t"
+
+   "paeth_abc:                     \n\t"
+      // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
+      "movb (%%edi,%%edx,), %%cl   \n\t" // load Raw(x-bpp) into cl
+
+   "paeth_paeth:                   \n\t"
+      "incl %%ebx                  \n\t"
+      "incl %%edx                  \n\t"
+      // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
+      "addb %%cl, -1(%%edi,%%ebx,) \n\t"
+      "cmpl _dif, %%ebx            \n\t"
+      "jb paeth_lp1                \n\t"
+
+   "paeth_go:                      \n\t"
+      "movl _FullLength, %%ecx     \n\t"
+      "movl %%ecx, %%eax           \n\t"
+      "subl %%ebx, %%eax           \n\t" // subtract alignment fix
+      "andl $0x00000007, %%eax     \n\t" // calc bytes over mult of 8
+      "subl %%eax, %%ecx           \n\t" // drop over bytes from original length
+      "movl %%ecx, _MMXLength      \n\t"
+#ifdef __PIC__
+      "popl %%ebx                  \n\t" // restore index to Global Offset Table
+#endif
+
+      : "=c" (dummy_value_c),            // output regs (dummy)
+        "=S" (dummy_value_S),
+        "=D" (dummy_value_D)
+
+      : "0" (bpp),       // ecx          // input regs
+        "1" (prev_row),  // esi
+        "2" (row)        // edi
+
+      : "%eax", "%edx"                   // clobber list
+#ifndef __PIC__
+      , "%ebx"
+#endif
+   );
+
+   // now do the math for the rest of the row
+   switch (bpp)
+   {
+      case 3:
+      {
+         _ActiveMask.use = 0x0000000000ffffffLL;
+         _ActiveMaskEnd.use = 0xffff000000000000LL;
+         _ShiftBpp.use = 24;    // == bpp(3) * 8
+         _ShiftRem.use = 40;    // == 64 - 24
+
+         __asm__ __volatile__ (
+            "movl _dif, %%ecx            \n\t"
+// preload  "movl row, %%edi             \n\t"
+// preload  "movl prev_row, %%esi        \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%%edi,%%ecx,), %%mm1 \n\t"
+         "paeth_3lp:                     \n\t"
+            "psrlq _ShiftRem, %%mm1      \n\t" // shift last 3 bytes to 1st
+                                               // 3 bytes
+            "movq (%%esi,%%ecx,), %%mm2  \n\t" // load b=Prior(x)
+            "punpcklbw %%mm0, %%mm1      \n\t" // unpack High bytes of a
+            "movq -8(%%esi,%%ecx,), %%mm3 \n\t" // prep c=Prior(x-bpp) bytes
+            "punpcklbw %%mm0, %%mm2      \n\t" // unpack High bytes of b
+            "psrlq _ShiftRem, %%mm3      \n\t" // shift last 3 bytes to 1st
+                                               // 3 bytes
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            "punpcklbw %%mm0, %%mm3      \n\t" // unpack High bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            "pxor %%mm7, %%mm7           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "paddw %%mm5, %%mm6          \n\t"
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "psubw %%mm0, %%mm4          \n\t"
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pxor %%mm1, %%mm1           \n\t"
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "packuswb %%mm1, %%mm7       \n\t"
+            "movq (%%esi,%%ecx,), %%mm3  \n\t" // load c=Prior(x-bpp)
+            "pand _ActiveMask, %%mm7     \n\t"
+            "movq %%mm3, %%mm2           \n\t" // load b=Prior(x) step 1
+            "paddb (%%edi,%%ecx,), %%mm7 \n\t" // add Paeth predictor with Raw(x)
+            "punpcklbw %%mm0, %%mm3      \n\t" // unpack High bytes of c
+            "movq %%mm7, (%%edi,%%ecx,)  \n\t" // write back updated value
+            "movq %%mm7, %%mm1           \n\t" // now mm1 will be used as
+                                               // Raw(x-bpp)
+            // now do Paeth for 2nd set of bytes (3-5)
+            "psrlq _ShiftBpp, %%mm2      \n\t" // load b=Prior(x) step 2
+            "punpcklbw %%mm0, %%mm1      \n\t" // unpack High bytes of a
+            "pxor %%mm7, %%mm7           \n\t"
+            "punpcklbw %%mm0, %%mm2      \n\t" // unpack High bytes of b
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) =
+            //       pav + pbv = pbv + pav
+            "movq %%mm5, %%mm6           \n\t"
+            "paddw %%mm4, %%mm6          \n\t"
+
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm5, %%mm0        \n\t" // create mask pbv bytes < 0
+            "pcmpgtw %%mm4, %%mm7        \n\t" // create mask pav bytes < 0
+            "pand %%mm5, %%mm0           \n\t" // only pbv bytes < 0 in mm0
+            "pand %%mm4, %%mm7           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm0, %%mm5          \n\t"
+            "psubw %%mm7, %%mm4          \n\t"
+            "psubw %%mm0, %%mm5          \n\t"
+            "psubw %%mm7, %%mm4          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "movq (%%esi,%%ecx,), %%mm2  \n\t" // load b=Prior(x)
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "pxor %%mm1, %%mm1           \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "packuswb %%mm1, %%mm7       \n\t"
+            "movq %%mm2, %%mm3           \n\t" // load c=Prior(x-bpp) step 1
+            "pand _ActiveMask, %%mm7     \n\t"
+            "punpckhbw %%mm0, %%mm2      \n\t" // unpack High bytes of b
+            "psllq _ShiftBpp, %%mm7      \n\t" // shift bytes to 2nd group of
+                                               // 3 bytes
+             // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            "paddb (%%edi,%%ecx,), %%mm7 \n\t" // add Paeth predictor with Raw(x)
+            "psllq _ShiftBpp, %%mm3      \n\t" // load c=Prior(x-bpp) step 2
+            "movq %%mm7, (%%edi,%%ecx,)  \n\t" // write back updated value
+            "movq %%mm7, %%mm1           \n\t"
+            "punpckhbw %%mm0, %%mm3      \n\t" // unpack High bytes of c
+            "psllq _ShiftBpp, %%mm1      \n\t" // shift bytes
+                                    // now mm1 will be used as Raw(x-bpp)
+            // now do Paeth for 3rd, and final, set of bytes (6-7)
+            "pxor %%mm7, %%mm7           \n\t"
+            "punpckhbw %%mm0, %%mm1      \n\t" // unpack High bytes of a
+            "psubw %%mm3, %%mm4          \n\t"
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "paddw %%mm5, %%mm6          \n\t"
+
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm1, %%mm1           \n\t"
+            "packuswb %%mm7, %%mm1       \n\t"
+            // step ecx to next set of 8 bytes and repeat loop til done
+            "addl $8, %%ecx              \n\t"
+            "pand _ActiveMaskEnd, %%mm1  \n\t"
+            "paddb -8(%%edi,%%ecx,), %%mm1 \n\t" // add Paeth predictor with
+                                                 // Raw(x)
+
+            "cmpl _MMXLength, %%ecx      \n\t"
+            "pxor %%mm0, %%mm0           \n\t" // pxor does not affect flags
+            "movq %%mm1, -8(%%edi,%%ecx,) \n\t" // write back updated value
+                                 // mm1 will be used as Raw(x-bpp) next loop
+                           // mm3 ready to be used as Prior(x-bpp) next loop
+            "jb paeth_3lp                \n\t"
+
+            : "=S" (dummy_value_S),             // output regs (dummy)
+              "=D" (dummy_value_D)
+
+            : "0" (prev_row),  // esi           // input regs
+              "1" (row)        // edi
+
+            : "%ecx"                            // clobber list
+#if 0  /* %mm0, ..., %mm7 not supported by gcc 2.7.2.3 or egcs 1.1 */
+            , "%mm0", "%mm1", "%mm2", "%mm3"
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 3 bpp
+
+      case 6:
+      //case 7:   // GRR BOGUS
+      //case 5:   // GRR BOGUS
+      {
+         _ActiveMask.use  = 0x00000000ffffffffLL;
+         _ActiveMask2.use = 0xffffffff00000000LL;
+         _ShiftBpp.use = bpp << 3;    // == bpp * 8
+         _ShiftRem.use = 64 - _ShiftBpp.use;
+
+         __asm__ __volatile__ (
+            "movl _dif, %%ecx            \n\t"
+// preload  "movl row, %%edi             \n\t"
+// preload  "movl prev_row, %%esi        \n\t"
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%%edi,%%ecx,), %%mm1 \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+
+         "paeth_6lp:                     \n\t"
+            // must shift to position Raw(x-bpp) data
+            "psrlq _ShiftRem, %%mm1      \n\t"
+            // do first set of 4 bytes
+            "movq -8(%%esi,%%ecx,), %%mm3 \n\t" // read c=Prior(x-bpp) bytes
+            "punpcklbw %%mm0, %%mm1      \n\t" // unpack Low bytes of a
+            "movq (%%esi,%%ecx,), %%mm2  \n\t" // load b=Prior(x)
+            "punpcklbw %%mm0, %%mm2      \n\t" // unpack Low bytes of b
+            // must shift to position Prior(x-bpp) data
+            "psrlq _ShiftRem, %%mm3      \n\t"
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            "punpcklbw %%mm0, %%mm3      \n\t" // unpack Low bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            "pxor %%mm7, %%mm7           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "paddw %%mm5, %%mm6          \n\t"
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "psubw %%mm0, %%mm4          \n\t"
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pxor %%mm1, %%mm1           \n\t"
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "packuswb %%mm1, %%mm7       \n\t"
+            "movq -8(%%esi,%%ecx,), %%mm3 \n\t" // load c=Prior(x-bpp)
+            "pand _ActiveMask, %%mm7     \n\t"
+            "psrlq _ShiftRem, %%mm3      \n\t"
+            "movq (%%esi,%%ecx,), %%mm2  \n\t" // load b=Prior(x) step 1
+            "paddb (%%edi,%%ecx,), %%mm7 \n\t" // add Paeth predictor and Raw(x)
+            "movq %%mm2, %%mm6           \n\t"
+            "movq %%mm7, (%%edi,%%ecx,)  \n\t" // write back updated value
+            "movq -8(%%edi,%%ecx,), %%mm1 \n\t"
+            "psllq _ShiftBpp, %%mm6      \n\t"
+            "movq %%mm7, %%mm5           \n\t"
+            "psrlq _ShiftRem, %%mm1      \n\t"
+            "por %%mm6, %%mm3            \n\t"
+            "psllq _ShiftBpp, %%mm5      \n\t"
+            "punpckhbw %%mm0, %%mm3      \n\t" // unpack High bytes of c
+            "por %%mm5, %%mm1            \n\t"
+            // do second set of 4 bytes
+            "punpckhbw %%mm0, %%mm2      \n\t" // unpack High bytes of b
+            "punpckhbw %%mm0, %%mm1      \n\t" // unpack High bytes of a
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            "pxor %%mm7, %%mm7           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "paddw %%mm5, %%mm6          \n\t"
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "psubw %%mm0, %%mm4          \n\t"
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pxor %%mm1, %%mm1           \n\t"
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "pxor %%mm1, %%mm1           \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            // step ecx to next set of 8 bytes and repeat loop til done
+            "addl $8, %%ecx              \n\t"
+            "packuswb %%mm7, %%mm1       \n\t"
+            "paddb -8(%%edi,%%ecx,), %%mm1 \n\t" // add Paeth predictor with Raw(x)
+            "cmpl _MMXLength, %%ecx      \n\t"
+            "movq %%mm1, -8(%%edi,%%ecx,) \n\t" // write back updated value
+                                // mm1 will be used as Raw(x-bpp) next loop
+            "jb paeth_6lp                \n\t"
+
+            : "=S" (dummy_value_S),             // output regs (dummy)
+              "=D" (dummy_value_D)
+
+            : "0" (prev_row),  // esi           // input regs
+              "1" (row)        // edi
+
+            : "%ecx"                            // clobber list
+#if 0  /* %mm0, ..., %mm7 not supported by gcc 2.7.2.3 or egcs 1.1 */
+            , "%mm0", "%mm1", "%mm2", "%mm3"
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 6 bpp
+
+      case 4:
+      {
+         _ActiveMask.use  = 0x00000000ffffffffLL;
+
+         __asm__ __volatile__ (
+            "movl _dif, %%ecx            \n\t"
+// preload  "movl row, %%edi             \n\t"
+// preload  "movl prev_row, %%esi        \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%%edi,%%ecx,), %%mm1 \n\t" // only time should need to read
+                                     //  a=Raw(x-bpp) bytes
+         "paeth_4lp:                     \n\t"
+            // do first set of 4 bytes
+            "movq -8(%%esi,%%ecx,), %%mm3 \n\t" // read c=Prior(x-bpp) bytes
+            "punpckhbw %%mm0, %%mm1      \n\t" // unpack Low bytes of a
+            "movq (%%esi,%%ecx,), %%mm2  \n\t" // load b=Prior(x)
+            "punpcklbw %%mm0, %%mm2      \n\t" // unpack High bytes of b
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            "punpckhbw %%mm0, %%mm3      \n\t" // unpack High bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            "pxor %%mm7, %%mm7           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "paddw %%mm5, %%mm6          \n\t"
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "psubw %%mm0, %%mm4          \n\t"
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pxor %%mm1, %%mm1           \n\t"
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "packuswb %%mm1, %%mm7       \n\t"
+            "movq (%%esi,%%ecx,), %%mm3  \n\t" // load c=Prior(x-bpp)
+            "pand _ActiveMask, %%mm7     \n\t"
+            "movq %%mm3, %%mm2           \n\t" // load b=Prior(x) step 1
+            "paddb (%%edi,%%ecx,), %%mm7 \n\t" // add Paeth predictor with Raw(x)
+            "punpcklbw %%mm0, %%mm3      \n\t" // unpack High bytes of c
+            "movq %%mm7, (%%edi,%%ecx,)  \n\t" // write back updated value
+            "movq %%mm7, %%mm1           \n\t" // now mm1 will be used as Raw(x-bpp)
+            // do second set of 4 bytes
+            "punpckhbw %%mm0, %%mm2      \n\t" // unpack Low bytes of b
+            "punpcklbw %%mm0, %%mm1      \n\t" // unpack Low bytes of a
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            "pxor %%mm7, %%mm7           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "paddw %%mm5, %%mm6          \n\t"
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "psubw %%mm0, %%mm4          \n\t"
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pxor %%mm1, %%mm1           \n\t"
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "pxor %%mm1, %%mm1           \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            // step ecx to next set of 8 bytes and repeat loop til done
+            "addl $8, %%ecx              \n\t"
+            "packuswb %%mm7, %%mm1       \n\t"
+            "paddb -8(%%edi,%%ecx,), %%mm1 \n\t" // add predictor with Raw(x)
+            "cmpl _MMXLength, %%ecx      \n\t"
+            "movq %%mm1, -8(%%edi,%%ecx,) \n\t" // write back updated value
+                                // mm1 will be used as Raw(x-bpp) next loop
+            "jb paeth_4lp                \n\t"
+
+            : "=S" (dummy_value_S),             // output regs (dummy)
+              "=D" (dummy_value_D)
+
+            : "0" (prev_row),  // esi           // input regs
+              "1" (row)        // edi
+
+            : "%ecx"                            // clobber list
+#if 0  /* %mm0, ..., %mm7 not supported by gcc 2.7.2.3 or egcs 1.1 */
+            , "%mm0", "%mm1", "%mm2", "%mm3"
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 4 bpp
+
+      case 8:                          // bpp == 8
+      {
+         _ActiveMask.use  = 0x00000000ffffffffLL;
+
+         __asm__ __volatile__ (
+            "movl _dif, %%ecx            \n\t"
+// preload  "movl row, %%edi             \n\t"
+// preload  "movl prev_row, %%esi        \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%%edi,%%ecx,), %%mm1 \n\t" // only time should need to read
+                                       //  a=Raw(x-bpp) bytes
+         "paeth_8lp:                     \n\t"
+            // do first set of 4 bytes
+            "movq -8(%%esi,%%ecx,), %%mm3 \n\t" // read c=Prior(x-bpp) bytes
+            "punpcklbw %%mm0, %%mm1      \n\t" // unpack Low bytes of a
+            "movq (%%esi,%%ecx,), %%mm2  \n\t" // load b=Prior(x)
+            "punpcklbw %%mm0, %%mm2      \n\t" // unpack Low bytes of b
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            "punpcklbw %%mm0, %%mm3      \n\t" // unpack Low bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            "pxor %%mm7, %%mm7           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "paddw %%mm5, %%mm6          \n\t"
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "psubw %%mm0, %%mm4          \n\t"
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pxor %%mm1, %%mm1           \n\t"
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "packuswb %%mm1, %%mm7       \n\t"
+            "movq -8(%%esi,%%ecx,), %%mm3 \n\t" // read c=Prior(x-bpp) bytes
+            "pand _ActiveMask, %%mm7     \n\t"
+            "movq (%%esi,%%ecx,), %%mm2  \n\t" // load b=Prior(x)
+            "paddb (%%edi,%%ecx,), %%mm7 \n\t" // add Paeth predictor with Raw(x)
+            "punpckhbw %%mm0, %%mm3      \n\t" // unpack High bytes of c
+            "movq %%mm7, (%%edi,%%ecx,)  \n\t" // write back updated value
+            "movq -8(%%edi,%%ecx,), %%mm1 \n\t" // read a=Raw(x-bpp) bytes
+
+            // do second set of 4 bytes
+            "punpckhbw %%mm0, %%mm2      \n\t" // unpack High bytes of b
+            "punpckhbw %%mm0, %%mm1      \n\t" // unpack High bytes of a
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            "pxor %%mm7, %%mm7           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "paddw %%mm5, %%mm6          \n\t"
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "psubw %%mm0, %%mm4          \n\t"
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pxor %%mm1, %%mm1           \n\t"
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "pxor %%mm1, %%mm1           \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            // step ecx to next set of 8 bytes and repeat loop til done
+            "addl $8, %%ecx              \n\t"
+            "packuswb %%mm7, %%mm1       \n\t"
+            "paddb -8(%%edi,%%ecx,), %%mm1 \n\t" // add Paeth predictor with Raw(x)
+            "cmpl _MMXLength, %%ecx      \n\t"
+            "movq %%mm1, -8(%%edi,%%ecx,) \n\t" // write back updated value
+                            // mm1 will be used as Raw(x-bpp) next loop
+            "jb paeth_8lp                \n\t"
+
+            : "=S" (dummy_value_S),             // output regs (dummy)
+              "=D" (dummy_value_D)
+
+            : "0" (prev_row),  // esi           // input regs
+              "1" (row)        // edi
+
+            : "%ecx"                            // clobber list
+#if 0  /* %mm0, ..., %mm7 not supported by gcc 2.7.2.3 or egcs 1.1 */
+            , "%mm0", "%mm1", "%mm2", "%mm3"
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 8 bpp
+
+      case 1:                // bpp = 1
+      case 2:                // bpp = 2
+      default:               // bpp > 8
+      {
+         __asm__ __volatile__ (
+#ifdef __PIC__
+            "pushl %%ebx                 \n\t" // save Global Offset Table index
+#endif
+            "movl _dif, %%ebx            \n\t"
+            "cmpl _FullLength, %%ebx     \n\t"
+            "jnb paeth_dend              \n\t"
+
+// preload  "movl row, %%edi             \n\t"
+// preload  "movl prev_row, %%esi        \n\t"
+            // do Paeth decode for remaining bytes
+            "movl %%ebx, %%edx           \n\t"
+// preload  "subl bpp, %%edx             \n\t" // (bpp is preloaded into ecx)
+            "subl %%ecx, %%edx           \n\t" // edx = ebx - bpp
+            "xorl %%ecx, %%ecx           \n\t" // zero ecx before using cl & cx
+
+         "paeth_dlp:                     \n\t"
+            "xorl %%eax, %%eax           \n\t"
+            // pav = p - a = (a + b - c) - a = b - c
+            "movb (%%esi,%%ebx,), %%al   \n\t" // load Prior(x) into al
+            "movb (%%esi,%%edx,), %%cl   \n\t" // load Prior(x-bpp) into cl
+            "subl %%ecx, %%eax           \n\t" // subtract Prior(x-bpp)
+            "movl %%eax, _patemp         \n\t" // Save pav for later use
+            "xorl %%eax, %%eax           \n\t"
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movb (%%edi,%%edx,), %%al   \n\t" // load Raw(x-bpp) into al
+            "subl %%ecx, %%eax           \n\t" // subtract Prior(x-bpp)
+            "movl %%eax, %%ecx           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "addl _patemp, %%eax         \n\t" // pcv = pav + pbv
+            // pc = abs(pcv)
+            "testl $0x80000000, %%eax    \n\t"
+            "jz paeth_dpca               \n\t"
+            "negl %%eax                  \n\t" // reverse sign of neg values
+
+         "paeth_dpca:                    \n\t"
+            "movl %%eax, _pctemp         \n\t" // save pc for later use
+            // pb = abs(pbv)
+            "testl $0x80000000, %%ecx    \n\t"
+            "jz paeth_dpba               \n\t"
+            "negl %%ecx                  \n\t" // reverse sign of neg values
+
+         "paeth_dpba:                    \n\t"
+            "movl %%ecx, _pbtemp         \n\t" // save pb for later use
+            // pa = abs(pav)
+            "movl _patemp, %%eax         \n\t"
+            "testl $0x80000000, %%eax    \n\t"
+            "jz paeth_dpaa               \n\t"
+            "negl %%eax                  \n\t" // reverse sign of neg values
+
+         "paeth_dpaa:                    \n\t"
+            "movl %%eax, _patemp         \n\t" // save pa for later use
+            // test if pa <= pb
+            "cmpl %%ecx, %%eax           \n\t"
+            "jna paeth_dabb              \n\t"
+            // pa > pb; now test if pb <= pc
+            "cmpl _pctemp, %%ecx         \n\t"
+            "jna paeth_dbbc              \n\t"
+            // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+            "movb (%%esi,%%edx,), %%cl   \n\t" // load Prior(x-bpp) into cl
+            "jmp paeth_dpaeth            \n\t"
+
+         "paeth_dbbc:                    \n\t"
+            // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
+            "movb (%%esi,%%ebx,), %%cl   \n\t" // load Prior(x) into cl
+            "jmp paeth_dpaeth            \n\t"
+
+         "paeth_dabb:                    \n\t"
+            // pa <= pb; now test if pa <= pc
+            "cmpl _pctemp, %%eax         \n\t"
+            "jna paeth_dabc              \n\t"
+            // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+            "movb (%%esi,%%edx,), %%cl   \n\t" // load Prior(x-bpp) into cl
+            "jmp paeth_dpaeth            \n\t"
+
+         "paeth_dabc:                    \n\t"
+            // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
+            "movb (%%edi,%%edx,), %%cl   \n\t" // load Raw(x-bpp) into cl
+
+         "paeth_dpaeth:                  \n\t"
+            "incl %%ebx                  \n\t"
+            "incl %%edx                  \n\t"
+            // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
+            "addb %%cl, -1(%%edi,%%ebx,) \n\t"
+            "cmpl _FullLength, %%ebx     \n\t"
+            "jb paeth_dlp                \n\t"
+
+         "paeth_dend:                    \n\t"
+#ifdef __PIC__
+            "popl %%ebx                  \n\t" // index to Global Offset Table
+#endif
+
+            : "=c" (dummy_value_c),            // output regs (dummy)
+              "=S" (dummy_value_S),
+              "=D" (dummy_value_D)
+
+            : "0" (bpp),       // ecx          // input regs
+              "1" (prev_row),  // esi
+              "2" (row)        // edi
+
+            : "%eax", "%edx"                   // clobber list
+#ifndef __PIC__
+            , "%ebx"
+#endif
+         );
+      }
+      return;                   // No need to go further with this one
+
+   } // end switch (bpp)
+
+   __asm__ __volatile__ (
+      // MMX acceleration complete; now do clean-up
+      // check if any remaining bytes left to decode
+#ifdef __PIC__
+      "pushl %%ebx                 \n\t" // save index to Global Offset Table
+#endif
+      "movl _MMXLength, %%ebx      \n\t"
+      "cmpl _FullLength, %%ebx     \n\t"
+      "jnb paeth_end               \n\t"
+//pre "movl row, %%edi             \n\t"
+//pre "movl prev_row, %%esi        \n\t"
+      // do Paeth decode for remaining bytes
+      "movl %%ebx, %%edx           \n\t"
+//pre "subl bpp, %%edx             \n\t" // (bpp is preloaded into ecx)
+      "subl %%ecx, %%edx           \n\t" // edx = ebx - bpp
+      "xorl %%ecx, %%ecx           \n\t" // zero ecx before using cl & cx below
+
+   "paeth_lp2:                     \n\t"
+      "xorl %%eax, %%eax           \n\t"
+      // pav = p - a = (a + b - c) - a = b - c
+      "movb (%%esi,%%ebx,), %%al   \n\t" // load Prior(x) into al
+      "movb (%%esi,%%edx,), %%cl   \n\t" // load Prior(x-bpp) into cl
+      "subl %%ecx, %%eax           \n\t" // subtract Prior(x-bpp)
+      "movl %%eax, _patemp         \n\t" // Save pav for later use
+      "xorl %%eax, %%eax           \n\t"
+      // pbv = p - b = (a + b - c) - b = a - c
+      "movb (%%edi,%%edx,), %%al   \n\t" // load Raw(x-bpp) into al
+      "subl %%ecx, %%eax           \n\t" // subtract Prior(x-bpp)
+      "movl %%eax, %%ecx           \n\t"
+      // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+      "addl _patemp, %%eax         \n\t" // pcv = pav + pbv
+      // pc = abs(pcv)
+      "testl $0x80000000, %%eax    \n\t"
+      "jz paeth_pca2               \n\t"
+      "negl %%eax                  \n\t" // reverse sign of neg values
+
+   "paeth_pca2:                    \n\t"
+      "movl %%eax, _pctemp         \n\t" // save pc for later use
+      // pb = abs(pbv)
+      "testl $0x80000000, %%ecx    \n\t"
+      "jz paeth_pba2               \n\t"
+      "negl %%ecx                  \n\t" // reverse sign of neg values
+
+   "paeth_pba2:                    \n\t"
+      "movl %%ecx, _pbtemp         \n\t" // save pb for later use
+      // pa = abs(pav)
+      "movl _patemp, %%eax         \n\t"
+      "testl $0x80000000, %%eax    \n\t"
+      "jz paeth_paa2               \n\t"
+      "negl %%eax                  \n\t" // reverse sign of neg values
+
+   "paeth_paa2:                    \n\t"
+      "movl %%eax, _patemp         \n\t" // save pa for later use
+      // test if pa <= pb
+      "cmpl %%ecx, %%eax           \n\t"
+      "jna paeth_abb2              \n\t"
+      // pa > pb; now test if pb <= pc
+      "cmpl _pctemp, %%ecx         \n\t"
+      "jna paeth_bbc2              \n\t"
+      // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+      "movb (%%esi,%%edx,), %%cl   \n\t" // load Prior(x-bpp) into cl
+      "jmp paeth_paeth2            \n\t"
+
+   "paeth_bbc2:                    \n\t"
+      // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
+      "movb (%%esi,%%ebx,), %%cl   \n\t" // load Prior(x) into cl
+      "jmp paeth_paeth2            \n\t"
+
+   "paeth_abb2:                    \n\t"
+      // pa <= pb; now test if pa <= pc
+      "cmpl _pctemp, %%eax         \n\t"
+      "jna paeth_abc2              \n\t"
+      // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+      "movb (%%esi,%%edx,), %%cl   \n\t" // load Prior(x-bpp) into cl
+      "jmp paeth_paeth2            \n\t"
+
+   "paeth_abc2:                    \n\t"
+      // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
+      "movb (%%edi,%%edx,), %%cl   \n\t" // load Raw(x-bpp) into cl
+
+   "paeth_paeth2:                  \n\t"
+      "incl %%ebx                  \n\t"
+      "incl %%edx                  \n\t"
+      // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
+      "addb %%cl, -1(%%edi,%%ebx,) \n\t"
+      "cmpl _FullLength, %%ebx     \n\t"
+      "jb paeth_lp2                \n\t"
+
+   "paeth_end:                     \n\t"
+      "EMMS                        \n\t" // end MMX; prep for poss. FP instrs.
+#ifdef __PIC__
+      "popl %%ebx                  \n\t" // restore index to Global Offset Table
+#endif
+
+      : "=c" (dummy_value_c),            // output regs (dummy)
+        "=S" (dummy_value_S),
+        "=D" (dummy_value_D)
+
+      : "0" (bpp),       // ecx          // input regs
+        "1" (prev_row),  // esi
+        "2" (row)        // edi
+
+      : "%eax", "%edx"                   // clobber list (no input regs!)
+#ifndef __PIC__
+      , "%ebx"
+#endif
+   );
+
+} /* end png_read_filter_row_mmx_paeth() */
+#endif
+
+
+
+
+#ifdef PNG_THREAD_UNSAFE_OK
+//===========================================================================//
+//                                                                           //
+//           P N G _ R E A D _ F I L T E R _ R O W _ M M X _ S U B           //
+//                                                                           //
+//===========================================================================//
+
+// Optimized code for PNG Sub filter decoder
+
+static void /* PRIVATE */
+png_read_filter_row_mmx_sub(png_row_infop row_info, png_bytep row)
+{
+   int bpp;
+   int dummy_value_a;
+   int dummy_value_D;
+
+   bpp = (row_info->pixel_depth + 7) >> 3;   // calc number of bytes per pixel
+   _FullLength = row_info->rowbytes - bpp;   // number of bytes to filter
+
+   __asm__ __volatile__ (
+//pre "movl row, %%edi             \n\t"
+      "movl %%edi, %%esi           \n\t" // lp = row
+//pre "movl bpp, %%eax             \n\t"
+      "addl %%eax, %%edi           \n\t" // rp = row + bpp
+//irr "xorl %%eax, %%eax           \n\t"
+      // get # of bytes to alignment
+      "movl %%edi, _dif            \n\t" // take start of row
+      "addl $0xf, _dif             \n\t" // add 7 + 8 to incr past
+                                         //  alignment boundary
+      "xorl %%ecx, %%ecx           \n\t"
+      "andl $0xfffffff8, _dif      \n\t" // mask to alignment boundary
+      "subl %%edi, _dif            \n\t" // subtract from start ==> value
+      "jz sub_go                   \n\t" //  ecx at alignment
+
+   "sub_lp1:                       \n\t" // fix alignment
+      "movb (%%esi,%%ecx,), %%al   \n\t"
+      "addb %%al, (%%edi,%%ecx,)   \n\t"
+      "incl %%ecx                  \n\t"
+      "cmpl _dif, %%ecx            \n\t"
+      "jb sub_lp1                  \n\t"
+
+   "sub_go:                        \n\t"
+      "movl _FullLength, %%eax     \n\t"
+      "movl %%eax, %%edx           \n\t"
+      "subl %%ecx, %%edx           \n\t" // subtract alignment fix
+      "andl $0x00000007, %%edx     \n\t" // calc bytes over mult of 8
+      "subl %%edx, %%eax           \n\t" // drop over bytes from length
+      "movl %%eax, _MMXLength      \n\t"
+
+      : "=a" (dummy_value_a),   // 0      // output regs (dummy)
+        "=D" (dummy_value_D)    // 1
+
+      : "0" (bpp),              // eax    // input regs
+        "1" (row)               // edi
+
+      : "%esi", "%ecx", "%edx"            // clobber list
+
+#if 0  /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */
+      , "%mm0", "%mm1", "%mm2", "%mm3"
+      , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+   );
+
+   // now do the math for the rest of the row
+   switch (bpp)
+   {
+      case 3:
+      {
+         _ActiveMask.use  = 0x0000ffffff000000LL;
+         _ShiftBpp.use = 24;       // == 3 * 8
+         _ShiftRem.use  = 40;      // == 64 - 24
+
+         __asm__ __volatile__ (
+// preload  "movl row, %%edi              \n\t"
+            "movq _ActiveMask, %%mm7       \n\t" // load _ActiveMask for 2nd
+                                                //  active byte group
+            "movl %%edi, %%esi            \n\t" // lp = row
+// preload  "movl bpp, %%eax              \n\t"
+            "addl %%eax, %%edi            \n\t" // rp = row + bpp
+            "movq %%mm7, %%mm6            \n\t"
+            "movl _dif, %%edx             \n\t"
+            "psllq _ShiftBpp, %%mm6       \n\t" // move mask in mm6 to cover
+                                                //  3rd active byte group
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%%edi,%%edx,), %%mm1 \n\t"
+
+         "sub_3lp:                        \n\t" // shift data for adding first
+            "psrlq _ShiftRem, %%mm1       \n\t" //  bpp bytes (no need for mask;
+                                                //  shift clears inactive bytes)
+            // add 1st active group
+            "movq (%%edi,%%edx,), %%mm0   \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+
+            // add 2nd active group
+            "movq %%mm0, %%mm1            \n\t" // mov updated Raws to mm1
+            "psllq _ShiftBpp, %%mm1       \n\t" // shift data to pos. correctly
+            "pand %%mm7, %%mm1            \n\t" // mask to use 2nd active group
+            "paddb %%mm1, %%mm0           \n\t"
+
+            // add 3rd active group
+            "movq %%mm0, %%mm1            \n\t" // mov updated Raws to mm1
+            "psllq _ShiftBpp, %%mm1       \n\t" // shift data to pos. correctly
+            "pand %%mm6, %%mm1            \n\t" // mask to use 3rd active group
+            "addl $8, %%edx               \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+
+            "cmpl _MMXLength, %%edx       \n\t"
+            "movq %%mm0, -8(%%edi,%%edx,) \n\t" // write updated Raws to array
+            "movq %%mm0, %%mm1            \n\t" // prep 1st add at top of loop
+            "jb sub_3lp                   \n\t"
+
+            : "=a" (dummy_value_a),   // 0      // output regs (dummy)
+              "=D" (dummy_value_D)    // 1
+
+            : "0" (bpp),              // eax    // input regs
+              "1" (row)               // edi
+
+            : "%edx", "%esi"                    // clobber list
+#if 0  /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */
+            , "%mm0", "%mm1", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;
+
+      case 1:
+      {
+         __asm__ __volatile__ (
+            "movl _dif, %%edx            \n\t"
+// preload  "movl row, %%edi             \n\t"
+            "cmpl _FullLength, %%edx     \n\t"
+            "jnb sub_1end                \n\t"
+            "movl %%edi, %%esi           \n\t" // lp = row
+            "xorl %%eax, %%eax           \n\t"
+// preload  "movl bpp, %%eax             \n\t"
+            "addl %%eax, %%edi           \n\t" // rp = row + bpp
+
+         "sub_1lp:                       \n\t"
+            "movb (%%esi,%%edx,), %%al   \n\t"
+            "addb %%al, (%%edi,%%edx,)   \n\t"
+            "incl %%edx                  \n\t"
+            "cmpl _FullLength, %%edx     \n\t"
+            "jb sub_1lp                  \n\t"
+
+         "sub_1end:                      \n\t"
+
+            : "=a" (dummy_value_a),   // 0      // output regs (dummy)
+              "=D" (dummy_value_D)    // 1
+
+            : "0" (bpp),              // eax    // input regs
+              "1" (row)               // edi
+
+            : "%edx", "%esi"                    // clobber list
+         );
+      }
+      return;
+
+      case 6:
+      case 4:
+      //case 7:   // GRR BOGUS
+      //case 5:   // GRR BOGUS
+      {
+         _ShiftBpp.use = bpp << 3;
+         _ShiftRem.use = 64 - _ShiftBpp.use;
+
+         __asm__ __volatile__ (
+// preload  "movl row, %%edi              \n\t"
+            "movl _dif, %%edx             \n\t"
+            "movl %%edi, %%esi            \n\t" // lp = row
+// preload  "movl bpp, %%eax              \n\t"
+            "addl %%eax, %%edi            \n\t" // rp = row + bpp
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%%edi,%%edx,), %%mm1 \n\t"
+
+         "sub_4lp:                        \n\t" // shift data for adding first
+            "psrlq _ShiftRem, %%mm1       \n\t" //  bpp bytes (no need for mask;
+                                                //  shift clears inactive bytes)
+            "movq (%%edi,%%edx,), %%mm0   \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+
+            // add 2nd active group
+            "movq %%mm0, %%mm1            \n\t" // mov updated Raws to mm1
+            "psllq _ShiftBpp, %%mm1       \n\t" // shift data to pos. correctly
+            "addl $8, %%edx               \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+
+            "cmpl _MMXLength, %%edx       \n\t"
+            "movq %%mm0, -8(%%edi,%%edx,) \n\t"
+            "movq %%mm0, %%mm1            \n\t" // prep 1st add at top of loop
+            "jb sub_4lp                   \n\t"
+
+            : "=a" (dummy_value_a),   // 0      // output regs (dummy)
+              "=D" (dummy_value_D)    // 1
+
+            : "0" (bpp),              // eax    // input regs
+              "1" (row)               // edi
+
+            : "%edx", "%esi"                    // clobber list
+#if 0  /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */
+            , "%mm0", "%mm1"
+#endif
+         );
+      }
+      break;
+
+      case 2:
+      {
+         _ActiveMask.use = 0x00000000ffff0000LL;
+         _ShiftBpp.use = 16;       // == 2 * 8
+         _ShiftRem.use = 48;       // == 64 - 16
+
+         __asm__ __volatile__ (
+            "movq _ActiveMask, %%mm7      \n\t" // load _ActiveMask for 2nd
+                                                //  active byte group
+            "movl _dif, %%edx             \n\t"
+            "movq %%mm7, %%mm6            \n\t"
+// preload  "movl row, %%edi              \n\t"
+            "psllq _ShiftBpp, %%mm6       \n\t" // move mask in mm6 to cover
+                                                //  3rd active byte group
+            "movl %%edi, %%esi            \n\t" // lp = row
+            "movq %%mm6, %%mm5            \n\t"
+// preload  "movl bpp, %%eax              \n\t"
+            "addl %%eax, %%edi            \n\t" // rp = row + bpp
+            "psllq _ShiftBpp, %%mm5       \n\t" // move mask in mm5 to cover
+                                                //  4th active byte group
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%%edi,%%edx,), %%mm1 \n\t"
+
+         "sub_2lp:                        \n\t" // shift data for adding first
+            "psrlq _ShiftRem, %%mm1       \n\t" //  bpp bytes (no need for mask;
+                                                //  shift clears inactive bytes)
+            // add 1st active group
+            "movq (%%edi,%%edx,), %%mm0   \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+
+            // add 2nd active group
+            "movq %%mm0, %%mm1            \n\t" // mov updated Raws to mm1
+            "psllq _ShiftBpp, %%mm1       \n\t" // shift data to pos. correctly
+            "pand %%mm7, %%mm1            \n\t" // mask to use 2nd active group
+            "paddb %%mm1, %%mm0           \n\t"
+
+            // add 3rd active group
+            "movq %%mm0, %%mm1            \n\t" // mov updated Raws to mm1
+            "psllq _ShiftBpp, %%mm1       \n\t" // shift data to pos. correctly
+            "pand %%mm6, %%mm1            \n\t" // mask to use 3rd active group
+            "paddb %%mm1, %%mm0           \n\t"
+
+            // add 4th active group
+            "movq %%mm0, %%mm1            \n\t" // mov updated Raws to mm1
+            "psllq _ShiftBpp, %%mm1       \n\t" // shift data to pos. correctly
+            "pand %%mm5, %%mm1            \n\t" // mask to use 4th active group
+            "addl $8, %%edx               \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+            "cmpl _MMXLength, %%edx       \n\t"
+            "movq %%mm0, -8(%%edi,%%edx,) \n\t" // write updated Raws to array
+            "movq %%mm0, %%mm1            \n\t" // prep 1st add at top of loop
+            "jb sub_2lp                   \n\t"
+
+            : "=a" (dummy_value_a),   // 0      // output regs (dummy)
+              "=D" (dummy_value_D)    // 1
+
+            : "0" (bpp),              // eax    // input regs
+              "1" (row)               // edi
+
+            : "%edx", "%esi"                    // clobber list
+#if 0  /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */
+            , "%mm0", "%mm1", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;
+
+      case 8:
+      {
+         __asm__ __volatile__ (
+// preload  "movl row, %%edi              \n\t"
+            "movl _dif, %%edx             \n\t"
+            "movl %%edi, %%esi            \n\t" // lp = row
+// preload  "movl bpp, %%eax              \n\t"
+            "addl %%eax, %%edi            \n\t" // rp = row + bpp
+            "movl _MMXLength, %%ecx       \n\t"
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%%edi,%%edx,), %%mm7 \n\t"
+            "andl $0x0000003f, %%ecx      \n\t" // calc bytes over mult of 64
+
+         "sub_8lp:                        \n\t"
+            "movq (%%edi,%%edx,), %%mm0   \n\t" // load Sub(x) for 1st 8 bytes
+            "paddb %%mm7, %%mm0           \n\t"
+            "movq 8(%%edi,%%edx,), %%mm1  \n\t" // load Sub(x) for 2nd 8 bytes
+            "movq %%mm0, (%%edi,%%edx,)   \n\t" // write Raw(x) for 1st 8 bytes
+
+            // Now mm0 will be used as Raw(x-bpp) for the 2nd group of 8 bytes.
+            // This will be repeated for each group of 8 bytes with the 8th
+            // group being used as the Raw(x-bpp) for the 1st group of the
+            // next loop.
+
+            "paddb %%mm0, %%mm1           \n\t"
+            "movq 16(%%edi,%%edx,), %%mm2 \n\t" // load Sub(x) for 3rd 8 bytes
+            "movq %%mm1, 8(%%edi,%%edx,)  \n\t" // write Raw(x) for 2nd 8 bytes
+            "paddb %%mm1, %%mm2           \n\t"
+            "movq 24(%%edi,%%edx,), %%mm3 \n\t" // load Sub(x) for 4th 8 bytes
+            "movq %%mm2, 16(%%edi,%%edx,) \n\t" // write Raw(x) for 3rd 8 bytes
+            "paddb %%mm2, %%mm3           \n\t"
+            "movq 32(%%edi,%%edx,), %%mm4 \n\t" // load Sub(x) for 5th 8 bytes
+            "movq %%mm3, 24(%%edi,%%edx,) \n\t" // write Raw(x) for 4th 8 bytes
+            "paddb %%mm3, %%mm4           \n\t"
+            "movq 40(%%edi,%%edx,), %%mm5 \n\t" // load Sub(x) for 6th 8 bytes
+            "movq %%mm4, 32(%%edi,%%edx,) \n\t" // write Raw(x) for 5th 8 bytes
+            "paddb %%mm4, %%mm5           \n\t"
+            "movq 48(%%edi,%%edx,), %%mm6 \n\t" // load Sub(x) for 7th 8 bytes
+            "movq %%mm5, 40(%%edi,%%edx,) \n\t" // write Raw(x) for 6th 8 bytes
+            "paddb %%mm5, %%mm6           \n\t"
+            "movq 56(%%edi,%%edx,), %%mm7 \n\t" // load Sub(x) for 8th 8 bytes
+            "movq %%mm6, 48(%%edi,%%edx,) \n\t" // write Raw(x) for 7th 8 bytes
+            "addl $64, %%edx              \n\t"
+            "paddb %%mm6, %%mm7           \n\t"
+            "cmpl %%ecx, %%edx            \n\t"
+            "movq %%mm7, -8(%%edi,%%edx,) \n\t" // write Raw(x) for 8th 8 bytes
+            "jb sub_8lp                   \n\t"
+
+            "cmpl _MMXLength, %%edx       \n\t"
+            "jnb sub_8lt8                 \n\t"
+
+         "sub_8lpA:                       \n\t"
+            "movq (%%edi,%%edx,), %%mm0   \n\t"
+            "addl $8, %%edx               \n\t"
+            "paddb %%mm7, %%mm0           \n\t"
+            "cmpl _MMXLength, %%edx       \n\t"
+            "movq %%mm0, -8(%%edi,%%edx,) \n\t" // -8 to offset early addl edx
+            "movq %%mm0, %%mm7            \n\t" // move calculated Raw(x) data
+                                                //  to mm1 to be new Raw(x-bpp)
+                                                //  for next loop
+            "jb sub_8lpA                  \n\t"
+
+         "sub_8lt8:                       \n\t"
+
+            : "=a" (dummy_value_a),   // 0      // output regs (dummy)
+              "=D" (dummy_value_D)    // 1
+
+            : "0" (bpp),              // eax    // input regs
+              "1" (row)               // edi
+
+            : "%ecx", "%edx", "%esi"            // clobber list
+#if 0  /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */
+            , "%mm0", "%mm1", "%mm2", "%mm3", "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;
+
+      default:                // bpp greater than 8 bytes   GRR BOGUS
+      {
+         __asm__ __volatile__ (
+            "movl _dif, %%edx             \n\t"
+// preload  "movl row, %%edi              \n\t"
+            "movl %%edi, %%esi            \n\t" // lp = row
+// preload  "movl bpp, %%eax              \n\t"
+            "addl %%eax, %%edi            \n\t" // rp = row + bpp
+
+         "sub_Alp:                        \n\t"
+            "movq (%%edi,%%edx,), %%mm0   \n\t"
+            "movq (%%esi,%%edx,), %%mm1   \n\t"
+            "addl $8, %%edx               \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+            "cmpl _MMXLength, %%edx       \n\t"
+            "movq %%mm0, -8(%%edi,%%edx,) \n\t" // mov does not affect flags;
+                                                //  -8 to offset addl edx
+            "jb sub_Alp                   \n\t"
+
+            : "=a" (dummy_value_a),   // 0      // output regs (dummy)
+              "=D" (dummy_value_D)    // 1
+
+            : "0" (bpp),              // eax    // input regs
+              "1" (row)               // edi
+
+            : "%edx", "%esi"                    // clobber list
+#if 0  /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */
+            , "%mm0", "%mm1"
+#endif
+         );
+      }
+      break;
+
+   } // end switch (bpp)
+
+   __asm__ __volatile__ (
+      "movl _MMXLength, %%edx       \n\t"
+//pre "movl row, %%edi              \n\t"
+      "cmpl _FullLength, %%edx      \n\t"
+      "jnb sub_end                  \n\t"
+
+      "movl %%edi, %%esi            \n\t" // lp = row
+//pre "movl bpp, %%eax              \n\t"
+      "addl %%eax, %%edi            \n\t" // rp = row + bpp
+      "xorl %%eax, %%eax            \n\t"
+
+   "sub_lp2:                        \n\t"
+      "movb (%%esi,%%edx,), %%al    \n\t"
+      "addb %%al, (%%edi,%%edx,)    \n\t"
+      "incl %%edx                   \n\t"
+      "cmpl _FullLength, %%edx      \n\t"
+      "jb sub_lp2                   \n\t"
+
+   "sub_end:                        \n\t"
+      "EMMS                         \n\t" // end MMX instructions
+
+      : "=a" (dummy_value_a),   // 0      // output regs (dummy)
+        "=D" (dummy_value_D)    // 1
+
+      : "0" (bpp),              // eax    // input regs
+        "1" (row)               // edi
+
+      : "%edx", "%esi"                    // clobber list
+   );
+
+} // end of png_read_filter_row_mmx_sub()
+#endif
+
+
+
+
+//===========================================================================//
+//                                                                           //
+//            P N G _ R E A D _ F I L T E R _ R O W _ M M X _ U P            //
+//                                                                           //
+//===========================================================================//
+
+// Optimized code for PNG Up filter decoder
+
+static void /* PRIVATE */
+png_read_filter_row_mmx_up(png_row_infop row_info, png_bytep row,
+                           png_bytep prev_row)
+{
+   png_uint_32 len;
+   int dummy_value_d;   // fix 'forbidden register 3 (dx) was spilled' error
+   int dummy_value_S;
+   int dummy_value_D;
+
+   len = row_info->rowbytes;              // number of bytes to filter
+
+   __asm__ __volatile__ (
+//pre "movl row, %%edi              \n\t"
+      // get # of bytes to alignment
+#ifdef __PIC__
+      "pushl %%ebx                  \n\t"
+#endif
+      "movl %%edi, %%ecx            \n\t"
+      "xorl %%ebx, %%ebx            \n\t"
+      "addl $0x7, %%ecx             \n\t"
+      "xorl %%eax, %%eax            \n\t"
+      "andl $0xfffffff8, %%ecx      \n\t"
+//pre "movl prev_row, %%esi         \n\t"
+      "subl %%edi, %%ecx            \n\t"
+      "jz up_go                     \n\t"
+
+   "up_lp1:                         \n\t" // fix alignment
+      "movb (%%edi,%%ebx,), %%al    \n\t"
+      "addb (%%esi,%%ebx,), %%al    \n\t"
+      "incl %%ebx                   \n\t"
+      "cmpl %%ecx, %%ebx            \n\t"
+      "movb %%al, -1(%%edi,%%ebx,)  \n\t" // mov does not affect flags; -1 to
+      "jb up_lp1                    \n\t" //  offset incl ebx
+
+   "up_go:                          \n\t"
+//pre "movl len, %%edx              \n\t"
+      "movl %%edx, %%ecx            \n\t"
+      "subl %%ebx, %%edx            \n\t" // subtract alignment fix
+      "andl $0x0000003f, %%edx      \n\t" // calc bytes over mult of 64
+      "subl %%edx, %%ecx            \n\t" // drop over bytes from length
+
+      // unrolled loop - use all MMX registers and interleave to reduce
+      // number of branch instructions (loops) and reduce partial stalls
+   "up_loop:                        \n\t"
+      "movq (%%esi,%%ebx,), %%mm1   \n\t"
+      "movq (%%edi,%%ebx,), %%mm0   \n\t"
+      "movq 8(%%esi,%%ebx,), %%mm3  \n\t"
+      "paddb %%mm1, %%mm0           \n\t"
+      "movq 8(%%edi,%%ebx,), %%mm2  \n\t"
+      "movq %%mm0, (%%edi,%%ebx,)   \n\t"
+      "paddb %%mm3, %%mm2           \n\t"
+      "movq 16(%%esi,%%ebx,), %%mm5 \n\t"
+      "movq %%mm2, 8(%%edi,%%ebx,)  \n\t"
+      "movq 16(%%edi,%%ebx,), %%mm4 \n\t"
+      "movq 24(%%esi,%%ebx,), %%mm7 \n\t"
+      "paddb %%mm5, %%mm4           \n\t"
+      "movq 24(%%edi,%%ebx,), %%mm6 \n\t"
+      "movq %%mm4, 16(%%edi,%%ebx,) \n\t"
+      "paddb %%mm7, %%mm6           \n\t"
+      "movq 32(%%esi,%%ebx,), %%mm1 \n\t"
+      "movq %%mm6, 24(%%edi,%%ebx,) \n\t"
+      "movq 32(%%edi,%%ebx,), %%mm0 \n\t"
+      "movq 40(%%esi,%%ebx,), %%mm3 \n\t"
+      "paddb %%mm1, %%mm0           \n\t"
+      "movq 40(%%edi,%%ebx,), %%mm2 \n\t"
+      "movq %%mm0, 32(%%edi,%%ebx,) \n\t"
+      "paddb %%mm3, %%mm2           \n\t"
+      "movq 48(%%esi,%%ebx,), %%mm5 \n\t"
+      "movq %%mm2, 40(%%edi,%%ebx,) \n\t"
+      "movq 48(%%edi,%%ebx,), %%mm4 \n\t"
+      "movq 56(%%esi,%%ebx,), %%mm7 \n\t"
+      "paddb %%mm5, %%mm4           \n\t"
+      "movq 56(%%edi,%%ebx,), %%mm6 \n\t"
+      "movq %%mm4, 48(%%edi,%%ebx,) \n\t"
+      "addl $64, %%ebx              \n\t"
+      "paddb %%mm7, %%mm6           \n\t"
+      "cmpl %%ecx, %%ebx            \n\t"
+      "movq %%mm6, -8(%%edi,%%ebx,) \n\t" // (+56)movq does not affect flags;
+      "jb up_loop                   \n\t" //  -8 to offset addl ebx
+
+      "cmpl $0, %%edx               \n\t" // test for bytes over mult of 64
+      "jz up_end                    \n\t"
+
+      "cmpl $8, %%edx               \n\t" // test for less than 8 bytes
+      "jb up_lt8                    \n\t" //  [added by lcreeve at netins.net]
+
+      "addl %%edx, %%ecx            \n\t"
+      "andl $0x00000007, %%edx      \n\t" // calc bytes over mult of 8
+      "subl %%edx, %%ecx            \n\t" // drop over bytes from length
+      "jz up_lt8                    \n\t"
+
+   "up_lpA:                         \n\t" // use MMX regs to update 8 bytes sim.
+      "movq (%%esi,%%ebx,), %%mm1   \n\t"
+      "movq (%%edi,%%ebx,), %%mm0   \n\t"
+      "addl $8, %%ebx               \n\t"
+      "paddb %%mm1, %%mm0           \n\t"
+      "cmpl %%ecx, %%ebx            \n\t"
+      "movq %%mm0, -8(%%edi,%%ebx,) \n\t" // movq does not affect flags; -8 to
+      "jb up_lpA                    \n\t" //  offset add ebx
+      "cmpl $0, %%edx               \n\t" // test for bytes over mult of 8
+      "jz up_end                    \n\t"
+
+   "up_lt8:                         \n\t"
+      "xorl %%eax, %%eax            \n\t"
+      "addl %%edx, %%ecx            \n\t" // move over byte count into counter
+
+   "up_lp2:                         \n\t" // use x86 regs for remaining bytes
+      "movb (%%edi,%%ebx,), %%al    \n\t"
+      "addb (%%esi,%%ebx,), %%al    \n\t"
+      "incl %%ebx                   \n\t"
+      "cmpl %%ecx, %%ebx            \n\t"
+      "movb %%al, -1(%%edi,%%ebx,)  \n\t" // mov does not affect flags; -1 to
+      "jb up_lp2                    \n\t" //  offset inc ebx
+
+   "up_end:                         \n\t"
+      "EMMS                         \n\t" // conversion of filtered row complete
+#ifdef __PIC__
+      "popl %%ebx                   \n\t"
+#endif
+
+      : "=d" (dummy_value_d),   // 0      // output regs (dummy)
+        "=S" (dummy_value_S),   // 1
+        "=D" (dummy_value_D)    // 2
+
+      : "0" (len),              // edx    // input regs
+        "1" (prev_row),         // esi
+        "2" (row)               // edi
+
+      : "%eax", "%ecx"            // clobber list (no input regs!)
+#ifndef __PIC__
+      , "%ebx"
+#endif
+
+#if 0  /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */
+      , "%mm0", "%mm1", "%mm2", "%mm3"
+      , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+   );
+
+} // end of png_read_filter_row_mmx_up()
+
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+
+
+
+
+/*===========================================================================*/
+/*                                                                           */
+/*                   P N G _ R E A D _ F I L T E R _ R O W                   */
+/*                                                                           */
+/*===========================================================================*/
+
+
+/* Optimized png_read_filter_row routines */
+
+void /* PRIVATE */
+png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep
+   row, png_bytep prev_row, int filter)
+{
+#ifdef PNG_DEBUG
+   char filnm[10];
+#endif
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+/* GRR:  these are superseded by png_ptr->asm_flags: */
+#define UseMMX_sub    1   // GRR:  converted 20000730
+#define UseMMX_up     1   // GRR:  converted 20000729
+#define UseMMX_avg    1   // GRR:  converted 20000828 (+ 16-bit bugfix 20000916)
+#define UseMMX_paeth  1   // GRR:  converted 20000828
+
+   if (_mmx_supported == 2) {
+       /* this should have happened in png_init_mmx_flags() already */
+#if !defined(PNG_1_0_X)
+       png_warning(png_ptr, "asm_flags may not have been initialized");
+#endif
+       png_mmx_support();
+   }
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+
+#ifdef PNG_DEBUG
+   png_debug(1, "in png_read_filter_row (pnggccrd.c)\n");
+   switch (filter)
+   {
+      case 0: sprintf(filnm, "none");
+         break;
+      case 1: sprintf(filnm, "sub-%s",
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK)
+#if !defined(PNG_1_0_X)
+        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)? "MMX" : 
+#endif
+#endif
+"x86");
+         break;
+      case 2: sprintf(filnm, "up-%s",
+#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
+#if !defined(PNG_1_0_X)
+        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP)? "MMX" :
+#endif
+#endif
+ "x86");
+         break;
+      case 3: sprintf(filnm, "avg-%s",
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK)
+#if !defined(PNG_1_0_X)
+        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)? "MMX" :
+#endif
+#endif
+ "x86");
+         break;
+      case 4: sprintf(filnm, "Paeth-%s",
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK)
+#if !defined(PNG_1_0_X)
+        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)? "MMX":
+#endif
+#endif
+"x86");
+         break;
+      default: sprintf(filnm, "unknw");
+         break;
+   }
+   png_debug2(0, "row_number=%5ld, %5s, ", png_ptr->row_number, filnm);
+   png_debug1(0, "row=0x%08lx, ", (unsigned long)row);
+   png_debug2(0, "pixdepth=%2d, bytes=%d, ", (int)row_info->pixel_depth,
+      (int)((row_info->pixel_depth + 7) >> 3));
+   png_debug1(0,"rowbytes=%8ld\n", row_info->rowbytes);
+#endif /* PNG_DEBUG */
+
+   switch (filter)
+   {
+      case PNG_FILTER_VALUE_NONE:
+         break;
+
+      case PNG_FILTER_VALUE_SUB:
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK)
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (_mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_sub(row_info, row);
+         }
+         else
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+         {
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_bytep rp = row + bpp;
+            png_bytep lp = row;
+
+            for (i = bpp; i < istop; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff);
+               rp++;
+            }
+         }  /* end !UseMMX_sub */
+         break;
+
+      case PNG_FILTER_VALUE_UP:
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (_mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_up(row_info, row, prev_row);
+         }
+          else
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+         {
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+
+            for (i = 0; i < istop; ++i)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+               rp++;
+            }
+         }  /* end !UseMMX_up */
+         break;
+
+      case PNG_FILTER_VALUE_AVG:
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK)
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (_mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_avg(row_info, row, prev_row);
+         }
+         else
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+         {
+            png_uint_32 i;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+            png_bytep lp = row;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_uint_32 istop = row_info->rowbytes - bpp;
+
+            for (i = 0; i < bpp; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) +
+                  ((int)(*pp++) >> 1)) & 0xff);
+               rp++;
+            }
+
+            for (i = 0; i < istop; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) +
+                  ((int)(*pp++ + *lp++) >> 1)) & 0xff);
+               rp++;
+            }
+         }  /* end !UseMMX_avg */
+         break;
+
+      case PNG_FILTER_VALUE_PAETH:
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK)
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (_mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_paeth(row_info, row, prev_row);
+         }
+         else
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+         {
+            png_uint_32 i;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+            png_bytep lp = row;
+            png_bytep cp = prev_row;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_uint_32 istop = row_info->rowbytes - bpp;
+
+            for (i = 0; i < bpp; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+               rp++;
+            }
+
+            for (i = 0; i < istop; i++)   /* use leftover rp,pp */
+            {
+               int a, b, c, pa, pb, pc, p;
+
+               a = *lp++;
+               b = *pp++;
+               c = *cp++;
+
+               p = b - c;
+               pc = a - c;
+
+#ifdef PNG_USE_ABS
+               pa = abs(p);
+               pb = abs(pc);
+               pc = abs(p + pc);
+#else
+               pa = p < 0 ? -p : p;
+               pb = pc < 0 ? -pc : pc;
+               pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+               /*
+                  if (pa <= pb && pa <= pc)
+                     p = a;
+                  else if (pb <= pc)
+                     p = b;
+                  else
+                     p = c;
+                */
+
+               p = (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c;
+
+               *rp = (png_byte)(((int)(*rp) + p) & 0xff);
+               rp++;
+            }
+         }  /* end !UseMMX_paeth */
+         break;
+
+      default:
+         png_warning(png_ptr, "Ignoring bad row-filter type");
+         *row=0;
+         break;
+   }
+}
+
+#endif /* PNG_HAVE_ASSEMBLER_READ_FILTER_ROW */
+
+
+/*===========================================================================*/
+/*                                                                           */
+/*                      P N G _ M M X _ S U P P O R T                        */
+/*                                                                           */
+/*===========================================================================*/
+
+/* GRR NOTES:  (1) the following code assumes 386 or better (pushfl/popfl)
+ *             (2) all instructions compile with gcc 2.7.2.3 and later
+ *             (3) the function is moved down here to prevent gcc from
+ *                  inlining it in multiple places and then barfing be-
+ *                  cause the ".NOT_SUPPORTED" label is multiply defined
+ *             [is there a way to signal that a *single* function should
+ *              not be inlined?  is there a way to modify the label for
+ *              each inlined instance, e.g., by appending _1, _2, etc.?
+ *              maybe if don't use leading "." in label name? (nope...sigh)]
+ */
+
+int PNGAPI
+png_mmx_support(void)
+{
+#if defined(PNG_MMX_CODE_SUPPORTED)
+    __asm__ __volatile__ (
+        "pushl %%ebx          \n\t"  // ebx gets clobbered by CPUID instruction
+        "pushl %%ecx          \n\t"  // so does ecx...
+        "pushl %%edx          \n\t"  // ...and edx (but ecx & edx safe on Linux)
+//      ".byte  0x66          \n\t"  // convert 16-bit pushf to 32-bit pushfd
+//      "pushf                \n\t"  // 16-bit pushf
+        "pushfl               \n\t"  // save Eflag to stack
+        "popl %%eax           \n\t"  // get Eflag from stack into eax
+        "movl %%eax, %%ecx    \n\t"  // make another copy of Eflag in ecx
+        "xorl $0x200000, %%eax \n\t" // toggle ID bit in Eflag (i.e., bit 21)
+        "pushl %%eax          \n\t"  // save modified Eflag back to stack
+//      ".byte  0x66          \n\t"  // convert 16-bit popf to 32-bit popfd
+//      "popf                 \n\t"  // 16-bit popf
+        "popfl                \n\t"  // restore modified value to Eflag reg
+        "pushfl               \n\t"  // save Eflag to stack
+        "popl %%eax           \n\t"  // get Eflag from stack
+        "pushl %%ecx          \n\t"  // save original Eflag to stack
+        "popfl                \n\t"  // restore original Eflag
+        "xorl %%ecx, %%eax    \n\t"  // compare new Eflag with original Eflag
+        "jz 0f                \n\t"  // if same, CPUID instr. is not supported
+
+        "xorl %%eax, %%eax    \n\t"  // set eax to zero
+//      ".byte  0x0f, 0xa2    \n\t"  // CPUID instruction (two-byte opcode)
+        "cpuid                \n\t"  // get the CPU identification info
+        "cmpl $1, %%eax       \n\t"  // make sure eax return non-zero value
+        "jl 0f                \n\t"  // if eax is zero, MMX is not supported
+
+        "xorl %%eax, %%eax    \n\t"  // set eax to zero and...
+        "incl %%eax           \n\t"  // ...increment eax to 1.  This pair is
+                                     // faster than the instruction "mov eax, 1"
+        "cpuid                \n\t"  // get the CPU identification info again
+        "andl $0x800000, %%edx \n\t" // mask out all bits but MMX bit (23)
+        "cmpl $0, %%edx       \n\t"  // 0 = MMX not supported
+        "jz 0f                \n\t"  // non-zero = yes, MMX IS supported
+
+        "movl $1, %%eax       \n\t"  // set return value to 1
+        "jmp  1f              \n\t"  // DONE:  have MMX support
+
+    "0:                       \n\t"  // .NOT_SUPPORTED: target label for jump instructions
+        "movl $0, %%eax       \n\t"  // set return value to 0
+    "1:                       \n\t"  // .RETURN: target label for jump instructions
+        "movl %%eax, _mmx_supported \n\t" // save in global static variable, too
+        "popl %%edx           \n\t"  // restore edx
+        "popl %%ecx           \n\t"  // restore ecx
+        "popl %%ebx           \n\t"  // restore ebx
+
+//      "ret                  \n\t"  // DONE:  no MMX support
+                                     // (fall through to standard C "ret")
+
+        :                            // output list (none)
+
+        :                            // any variables used on input (none)
+
+        : "%eax"                     // clobber list
+//      , "%ebx", "%ecx", "%edx"     // GRR:  we handle these manually
+//      , "memory"   // if write to a variable gcc thought was in a reg
+//      , "cc"       // "condition codes" (flag bits)
+    );
+#else     
+    _mmx_supported = 0;
+#endif /* PNG_MMX_CODE_SUPPORTED */
+
+    return _mmx_supported;
+}
+
+
+#endif /* PNG_USE_PNGGCCRD */
diff --git a/Utilities/GDAL/frmts/png/libpng/pngget.c b/Utilities/GDAL/frmts/png/libpng/pngget.c
new file mode 100644
index 0000000000..8eefa7779e
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngget.c
@@ -0,0 +1,934 @@
+
+/* pngget.c - retrieval of values from info struct
+ *
+ * libpng 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+png_uint_32 PNGAPI
+png_get_valid(png_structp png_ptr, png_infop info_ptr, png_uint_32 flag)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->valid & flag);
+   else
+      return(0);
+}
+
+png_uint_32 PNGAPI
+png_get_rowbytes(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->rowbytes);
+   else
+      return(0);
+}
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+png_bytepp PNGAPI
+png_get_rows(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->row_pointers);
+   else
+      return(0);
+}
+#endif
+
+#ifdef PNG_EASY_ACCESS_SUPPORTED
+/* easy access to info, added in libpng-0.99 */
+png_uint_32 PNGAPI
+png_get_image_width(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->width;
+   }
+   return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_image_height(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->height;
+   }
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_bit_depth(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->bit_depth;
+   }
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_color_type(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->color_type;
+   }
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_filter_type(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->filter_type;
+   }
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_interlace_type(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->interlace_type;
+   }
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_compression_type(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->compression_type;
+   }
+   return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_x_pixels_per_meter(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_pHYs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_pHYs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_x_pixels_per_meter");
+      if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER)
+          return (0);
+      else return (info_ptr->x_pixels_per_unit);
+   }
+#else
+   return (0);
+#endif
+   return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_y_pixels_per_meter(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_pHYs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_pHYs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_y_pixels_per_meter");
+      if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER)
+          return (0);
+      else return (info_ptr->y_pixels_per_unit);
+   }
+#else
+   return (0);
+#endif
+   return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_pixels_per_meter(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_pHYs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_pHYs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_pixels_per_meter");
+      if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER ||
+         info_ptr->x_pixels_per_unit != info_ptr->y_pixels_per_unit)
+          return (0);
+      else return (info_ptr->x_pixels_per_unit);
+   }
+#else
+   return (0);
+#endif
+   return (0);
+}
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+float PNGAPI
+png_get_pixel_aspect_ratio(png_structp png_ptr, png_infop info_ptr)
+   {
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_pHYs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_pHYs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_aspect_ratio");
+      if (info_ptr->x_pixels_per_unit == 0)
+         return ((float)0.0);
+      else
+         return ((float)((float)info_ptr->y_pixels_per_unit
+            /(float)info_ptr->x_pixels_per_unit));
+   }
+#else
+   return (0.0);
+#endif
+   return ((float)0.0);
+}
+#endif
+
+png_int_32 PNGAPI
+png_get_x_offset_microns(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_oFFs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_oFFs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns");
+      if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER)
+          return (0);
+      else return (info_ptr->x_offset);
+   }
+#else
+   return (0);
+#endif
+   return (0);
+}
+
+png_int_32 PNGAPI
+png_get_y_offset_microns(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_oFFs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_oFFs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns");
+      if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER)
+          return (0);
+      else return (info_ptr->y_offset);
+   }
+#else
+   return (0);
+#endif
+   return (0);
+}
+
+png_int_32 PNGAPI
+png_get_x_offset_pixels(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_oFFs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_oFFs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns");
+      if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL)
+          return (0);
+      else return (info_ptr->x_offset);
+   }
+#else
+   return (0);
+#endif
+   return (0);
+}
+
+png_int_32 PNGAPI
+png_get_y_offset_pixels(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_oFFs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_oFFs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns");
+      if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL)
+          return (0);
+      else return (info_ptr->y_offset);
+   }
+#else
+   return (0);
+#endif
+   return (0);
+}
+
+#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_pixels_per_inch(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((png_uint_32)((float)png_get_pixels_per_meter(png_ptr, info_ptr)
+     *.0254 +.5));
+}
+
+png_uint_32 PNGAPI
+png_get_x_pixels_per_inch(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((png_uint_32)((float)png_get_x_pixels_per_meter(png_ptr, info_ptr)
+     *.0254 +.5));
+}
+
+png_uint_32 PNGAPI
+png_get_y_pixels_per_inch(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((png_uint_32)((float)png_get_y_pixels_per_meter(png_ptr, info_ptr)
+     *.0254 +.5));
+}
+
+float PNGAPI
+png_get_x_offset_inches(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((float)png_get_x_offset_microns(png_ptr, info_ptr)
+     *.00003937);
+}
+
+float PNGAPI
+png_get_y_offset_inches(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((float)png_get_y_offset_microns(png_ptr, info_ptr)
+     *.00003937);
+}
+
+#if defined(PNG_pHYs_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_pHYs_dpi(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)
+{
+   png_uint_32 retval = 0;
+
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+   {
+      png_debug1(1, "in %s retrieval function\n", "pHYs");
+      if (res_x != NULL)
+      {
+         *res_x = info_ptr->x_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+      if (res_y != NULL)
+      {
+         *res_y = info_ptr->y_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+      if (unit_type != NULL)
+      {
+         *unit_type = (int)info_ptr->phys_unit_type;
+         retval |= PNG_INFO_pHYs;
+         if(*unit_type == 1)
+         {
+            if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50);
+            if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50);
+         }
+      }
+   }
+   return (retval);
+}
+#endif /* PNG_pHYs_SUPPORTED */
+#endif  /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */
+
+/* png_get_channels really belongs in here, too, but it's been around longer */
+
+#endif  /* PNG_EASY_ACCESS_SUPPORTED */
+
+png_byte PNGAPI
+png_get_channels(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->channels);
+   else
+      return (0);
+}
+
+png_bytep PNGAPI
+png_get_signature(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->signature);
+   else
+      return (NULL);
+}
+
+#if defined(PNG_bKGD_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_bKGD(png_structp png_ptr, png_infop info_ptr,
+   png_color_16p *background)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)
+      && background != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "bKGD");
+      *background = &(info_ptr->background);
+      return (PNG_INFO_bKGD);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_cHRM_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_cHRM(png_structp png_ptr, png_infop info_ptr,
+   double *white_x, double *white_y, double *red_x, double *red_y,
+   double *green_x, double *green_y, double *blue_x, double *blue_y)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM))
+   {
+      png_debug1(1, "in %s retrieval function\n", "cHRM");
+      if (white_x != NULL)
+         *white_x = (double)info_ptr->x_white;
+      if (white_y != NULL)
+         *white_y = (double)info_ptr->y_white;
+      if (red_x != NULL)
+         *red_x = (double)info_ptr->x_red;
+      if (red_y != NULL)
+         *red_y = (double)info_ptr->y_red;
+      if (green_x != NULL)
+         *green_x = (double)info_ptr->x_green;
+      if (green_y != NULL)
+         *green_y = (double)info_ptr->y_green;
+      if (blue_x != NULL)
+         *blue_x = (double)info_ptr->x_blue;
+      if (blue_y != NULL)
+         *blue_y = (double)info_ptr->y_blue;
+      return (PNG_INFO_cHRM);
+   }
+   return (0);
+}
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_cHRM_fixed(png_structp png_ptr, png_infop info_ptr,
+   png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x,
+   png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y,
+   png_fixed_point *blue_x, png_fixed_point *blue_y)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM))
+   {
+      png_debug1(1, "in %s retrieval function\n", "cHRM");
+      if (white_x != NULL)
+         *white_x = info_ptr->int_x_white;
+      if (white_y != NULL)
+         *white_y = info_ptr->int_y_white;
+      if (red_x != NULL)
+         *red_x = info_ptr->int_x_red;
+      if (red_y != NULL)
+         *red_y = info_ptr->int_y_red;
+      if (green_x != NULL)
+         *green_x = info_ptr->int_x_green;
+      if (green_y != NULL)
+         *green_y = info_ptr->int_y_green;
+      if (blue_x != NULL)
+         *blue_x = info_ptr->int_x_blue;
+      if (blue_y != NULL)
+         *blue_y = info_ptr->int_y_blue;
+      return (PNG_INFO_cHRM);
+   }
+   return (0);
+}
+#endif
+#endif
+
+#if defined(PNG_gAMA_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_gAMA(png_structp png_ptr, png_infop info_ptr, double *file_gamma)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)
+      && file_gamma != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "gAMA");
+      *file_gamma = (double)info_ptr->gamma;
+      return (PNG_INFO_gAMA);
+   }
+   return (0);
+}
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_gAMA_fixed(png_structp png_ptr, png_infop info_ptr,
+    png_fixed_point *int_file_gamma)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)
+      && int_file_gamma != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "gAMA");
+      *int_file_gamma = info_ptr->int_gamma;
+      return (PNG_INFO_gAMA);
+   }
+   return (0);
+}
+#endif
+#endif
+
+#if defined(PNG_sRGB_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_sRGB(png_structp png_ptr, png_infop info_ptr, int *file_srgb_intent)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)
+      && file_srgb_intent != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "sRGB");
+      *file_srgb_intent = (int)info_ptr->srgb_intent;
+      return (PNG_INFO_sRGB);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_iCCP_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_iCCP(png_structp png_ptr, png_infop info_ptr,
+             png_charpp name, int *compression_type,
+             png_charpp profile, png_uint_32 *proflen)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP)
+      && name != NULL && profile != NULL && proflen != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "iCCP");
+      *name = info_ptr->iccp_name;
+      *profile = info_ptr->iccp_profile;
+      /* compression_type is a dummy so the API won't have to change
+         if we introduce multiple compression types later. */
+      *proflen = (int)info_ptr->iccp_proflen;
+      *compression_type = (int)info_ptr->iccp_compression;
+      return (PNG_INFO_iCCP);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_sPLT_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_sPLT(png_structp png_ptr, png_infop info_ptr,
+             png_sPLT_tpp spalettes)
+{
+   if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL)
+     *spalettes = info_ptr->splt_palettes;
+   return ((png_uint_32)info_ptr->splt_palettes_num);
+}
+#endif
+
+#if defined(PNG_hIST_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p *hist)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)
+      && hist != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "hIST");
+      *hist = info_ptr->hist;
+      return (PNG_INFO_hIST);
+   }
+   return (0);
+}
+#endif
+
+png_uint_32 PNGAPI
+png_get_IHDR(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 *width, png_uint_32 *height, int *bit_depth,
+   int *color_type, int *interlace_type, int *compression_type,
+   int *filter_type)
+
+{
+   if (png_ptr != NULL && info_ptr != NULL && width != NULL && height != NULL &&
+      bit_depth != NULL && color_type != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "IHDR");
+      *width = info_ptr->width;
+      *height = info_ptr->height;
+      *bit_depth = info_ptr->bit_depth;
+      if (info_ptr->bit_depth < 1 || info_ptr->bit_depth > 16)
+        png_error(png_ptr, "Invalid bit depth");
+      *color_type = info_ptr->color_type;
+      if (info_ptr->color_type > 6)
+        png_error(png_ptr, "Invalid color type");
+      if (compression_type != NULL)
+         *compression_type = info_ptr->compression_type;
+      if (filter_type != NULL)
+         *filter_type = info_ptr->filter_type;
+      if (interlace_type != NULL)
+         *interlace_type = info_ptr->interlace_type;
+
+      /* check for potential overflow of rowbytes */
+      if (*width == 0 || *width > PNG_UINT_31_MAX)
+        png_error(png_ptr, "Invalid image width");
+      if (*height == 0 || *height > PNG_UINT_31_MAX)
+        png_error(png_ptr, "Invalid image height");
+      if (info_ptr->width > (PNG_UINT_32_MAX
+                 >> 3)      /* 8-byte RGBA pixels */
+                 - 64       /* bigrowbuf hack */
+                 - 1        /* filter byte */
+                 - 7*8      /* rounding of width to multiple of 8 pixels */
+                 - 8)       /* extra max_pixel_depth pad */
+      {
+         png_warning(png_ptr,
+            "Width too large for libpng to process image data.");
+      }
+      return (1);
+   }
+   return (0);
+}
+
+#if defined(PNG_oFFs_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_oFFs(png_structp png_ptr, png_infop info_ptr,
+   png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)
+      && offset_x != NULL && offset_y != NULL && unit_type != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "oFFs");
+      *offset_x = info_ptr->x_offset;
+      *offset_y = info_ptr->y_offset;
+      *unit_type = (int)info_ptr->offset_unit_type;
+      return (PNG_INFO_oFFs);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_pCAL_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_pCAL(png_structp png_ptr, png_infop info_ptr,
+   png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams,
+   png_charp *units, png_charpp *params)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)
+      && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL &&
+      nparams != NULL && units != NULL && params != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "pCAL");
+      *purpose = info_ptr->pcal_purpose;
+      *X0 = info_ptr->pcal_X0;
+      *X1 = info_ptr->pcal_X1;
+      *type = (int)info_ptr->pcal_type;
+      *nparams = (int)info_ptr->pcal_nparams;
+      *units = info_ptr->pcal_units;
+      *params = info_ptr->pcal_params;
+      return (PNG_INFO_pCAL);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_sCAL_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_sCAL(png_structp png_ptr, png_infop info_ptr,
+             int *unit, double *width, double *height)
+{
+    if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_sCAL))
+    {
+        *unit = info_ptr->scal_unit;
+        *width = info_ptr->scal_pixel_width;
+        *height = info_ptr->scal_pixel_height;
+        return (PNG_INFO_sCAL);
+    }
+    return(0);
+}
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_sCAL_s(png_structp png_ptr, png_infop info_ptr,
+             int *unit, png_charpp width, png_charpp height)
+{
+    if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_sCAL))
+    {
+        *unit = info_ptr->scal_unit;
+        *width = info_ptr->scal_s_width;
+        *height = info_ptr->scal_s_height;
+        return (PNG_INFO_sCAL);
+    }
+    return(0);
+}
+#endif
+#endif
+#endif
+
+#if defined(PNG_pHYs_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_pHYs(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)
+{
+   png_uint_32 retval = 0;
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+      (info_ptr->valid & PNG_INFO_pHYs))
+   {
+      png_debug1(1, "in %s retrieval function\n", "pHYs");
+      if (res_x != NULL)
+      {
+         *res_x = info_ptr->x_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+      if (res_y != NULL)
+      {
+         *res_y = info_ptr->y_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+      if (unit_type != NULL)
+      {
+         *unit_type = (int)info_ptr->phys_unit_type;
+         retval |= PNG_INFO_pHYs;
+      }
+   }
+   return (retval);
+}
+#endif
+
+png_uint_32 PNGAPI
+png_get_PLTE(png_structp png_ptr, png_infop info_ptr, png_colorp *palette,
+   int *num_palette)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE)
+       && palette != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "PLTE");
+      *palette = info_ptr->palette;
+      *num_palette = info_ptr->num_palette;
+      png_debug1(3, "num_palette = %d\n", *num_palette);
+      return (PNG_INFO_PLTE);
+   }
+   return (0);
+}
+
+#if defined(PNG_sBIT_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_sBIT(png_structp png_ptr, png_infop info_ptr, png_color_8p *sig_bit)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)
+      && sig_bit != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "sBIT");
+      *sig_bit = &(info_ptr->sig_bit);
+      return (PNG_INFO_sBIT);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_TEXT_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_text(png_structp png_ptr, png_infop info_ptr, png_textp *text_ptr,
+   int *num_text)
+{
+   if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0)
+   {
+      png_debug1(1, "in %s retrieval function\n",
+         (png_ptr->chunk_name[0] == '\0' ? "text"
+             : (png_const_charp)png_ptr->chunk_name));
+      if (text_ptr != NULL)
+         *text_ptr = info_ptr->text;
+      if (num_text != NULL)
+         *num_text = info_ptr->num_text;
+      return ((png_uint_32)info_ptr->num_text);
+   }
+   if (num_text != NULL)
+     *num_text = 0;
+   return(0);
+}
+#endif
+
+#if defined(PNG_tIME_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_tIME(png_structp png_ptr, png_infop info_ptr, png_timep *mod_time)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)
+       && mod_time != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "tIME");
+      *mod_time = &(info_ptr->mod_time);
+      return (PNG_INFO_tIME);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_tRNS(png_structp png_ptr, png_infop info_ptr,
+   png_bytep *trans, int *num_trans, png_color_16p *trans_values)
+{
+   png_uint_32 retval = 0;
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS))
+   {
+      png_debug1(1, "in %s retrieval function\n", "tRNS");
+      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+          if (trans != NULL)
+          {
+             *trans = info_ptr->trans;
+             retval |= PNG_INFO_tRNS;
+          }
+          if (trans_values != NULL)
+             *trans_values = &(info_ptr->trans_values);
+      }
+      else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */
+      {
+          if (trans_values != NULL)
+          {
+             *trans_values = &(info_ptr->trans_values);
+             retval |= PNG_INFO_tRNS;
+          }
+          if(trans != NULL)
+             *trans = NULL;
+      }
+      if(num_trans != NULL)
+      {
+         *num_trans = info_ptr->num_trans;
+         retval |= PNG_INFO_tRNS;
+      }
+   }
+   return (retval);
+}
+#endif
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_unknown_chunks(png_structp png_ptr, png_infop info_ptr,
+             png_unknown_chunkpp unknowns)
+{
+   if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL)
+     *unknowns = info_ptr->unknown_chunks;
+   return ((png_uint_32)info_ptr->unknown_chunks_num);
+}
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+png_byte PNGAPI
+png_get_rgb_to_gray_status (png_structp png_ptr)
+{
+   return (png_byte)(png_ptr? png_ptr->rgb_to_gray_status : 0);
+}
+#endif
+
+#if defined(PNG_USER_CHUNKS_SUPPORTED)
+png_voidp PNGAPI
+png_get_user_chunk_ptr(png_structp png_ptr)
+{
+   return (png_ptr? png_ptr->user_chunk_ptr : NULL);
+}
+#endif
+
+#ifdef PNG_WRITE_SUPPORTED
+png_uint_32 PNGAPI
+png_get_compression_buffer_size(png_structp png_ptr)
+{
+   return (png_uint_32)(png_ptr? png_ptr->zbuf_size : 0L);
+}
+#endif
+
+#ifndef PNG_1_0_X
+#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
+/* this function was added to libpng 1.2.0 and should exist by default */
+png_uint_32 PNGAPI
+png_get_asm_flags (png_structp png_ptr)
+{
+    return (png_uint_32)(png_ptr? png_ptr->asm_flags : 0L);
+}
+
+/* this function was added to libpng 1.2.0 and should exist by default */
+png_uint_32 PNGAPI
+png_get_asm_flagmask (int flag_select)
+{
+    png_uint_32 settable_asm_flags = 0;
+
+    if (flag_select & PNG_SELECT_READ)
+        settable_asm_flags |=
+          PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  |
+          PNG_ASM_FLAG_MMX_READ_INTERLACE    |
+          PNG_ASM_FLAG_MMX_READ_FILTER_SUB   |
+          PNG_ASM_FLAG_MMX_READ_FILTER_UP    |
+          PNG_ASM_FLAG_MMX_READ_FILTER_AVG   |
+          PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ;
+          /* no non-MMX flags yet */
+
+#if 0
+    /* GRR:  no write-flags yet, either, but someday... */
+    if (flag_select & PNG_SELECT_WRITE)
+        settable_asm_flags |=
+          PNG_ASM_FLAG_MMX_WRITE_ [whatever] ;
+#endif /* 0 */
+
+    return settable_asm_flags;  /* _theoretically_ settable capabilities only */
+}
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+    /* GRR:  could add this:   && defined(PNG_MMX_CODE_SUPPORTED) */
+/* this function was added to libpng 1.2.0 */
+png_uint_32 PNGAPI
+png_get_mmx_flagmask (int flag_select, int *compilerID)
+{
+    png_uint_32 settable_mmx_flags = 0;
+
+    if (flag_select & PNG_SELECT_READ)
+        settable_mmx_flags |=
+          PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  |
+          PNG_ASM_FLAG_MMX_READ_INTERLACE    |
+          PNG_ASM_FLAG_MMX_READ_FILTER_SUB   |
+          PNG_ASM_FLAG_MMX_READ_FILTER_UP    |
+          PNG_ASM_FLAG_MMX_READ_FILTER_AVG   |
+          PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ;
+#if 0
+    /* GRR:  no MMX write support yet, but someday... */
+    if (flag_select & PNG_SELECT_WRITE)
+        settable_mmx_flags |=
+          PNG_ASM_FLAG_MMX_WRITE_ [whatever] ;
+#endif /* 0 */
+
+    if (compilerID != NULL) {
+#ifdef PNG_USE_PNGVCRD
+        *compilerID = 1;    /* MSVC */
+#else
+#ifdef PNG_USE_PNGGCCRD
+        *compilerID = 2;    /* gcc/gas */
+#else
+        *compilerID = -1;   /* unknown (i.e., no asm/MMX code compiled) */
+#endif
+#endif
+    }
+
+    return settable_mmx_flags;  /* _theoretically_ settable capabilities only */
+}
+
+/* this function was added to libpng 1.2.0 */
+png_byte PNGAPI
+png_get_mmx_bitdepth_threshold (png_structp png_ptr)
+{
+    return (png_byte)(png_ptr? png_ptr->mmx_bitdepth_threshold : 0);
+}
+
+/* this function was added to libpng 1.2.0 */
+png_uint_32 PNGAPI
+png_get_mmx_rowbytes_threshold (png_structp png_ptr)
+{
+    return (png_uint_32)(png_ptr? png_ptr->mmx_rowbytes_threshold : 0L);
+}
+#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */
+
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+/* these functions were added to libpng 1.2.6 */
+png_uint_32 PNGAPI
+png_get_user_width_max (png_structp png_ptr)
+{
+    return (png_ptr? png_ptr->user_width_max : 0);
+}
+png_uint_32 PNGAPI
+png_get_user_height_max (png_structp png_ptr)
+{
+    return (png_ptr? png_ptr->user_height_max : 0);
+}
+#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */
+
+#endif /* ?PNG_1_0_X */
diff --git a/Utilities/GDAL/frmts/png/libpng/pngmem.c b/Utilities/GDAL/frmts/png/libpng/pngmem.c
new file mode 100644
index 0000000000..f1cb69395e
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngmem.c
@@ -0,0 +1,595 @@
+
+/* pngmem.c - stub functions for memory allocation
+ *
+ * libpng version 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This file provides a location for all memory allocation.  Users who
+ * need special memory handling are expected to supply replacement
+ * functions for png_malloc() and png_free(), and to use
+ * png_create_read_struct_2() and png_create_write_struct_2() to
+ * identify the replacement functions.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* Borland DOS special memory handler */
+#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__)
+/* if you change this, be sure to change the one in png.h also */
+
+/* Allocate memory for a png_struct.  The malloc and memset can be replaced
+   by a single call to calloc() if this is thought to improve performance. */
+png_voidp /* PRIVATE */
+png_create_struct(int type)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL));
+}
+
+/* Alternate version of png_create_struct, for use with user-defined malloc. */
+png_voidp /* PRIVATE */
+png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+   png_size_t size;
+   png_voidp struct_ptr;
+
+   if (type == PNG_STRUCT_INFO)
+     size = png_sizeof(png_info);
+   else if (type == PNG_STRUCT_PNG)
+     size = png_sizeof(png_struct);
+   else
+     return (png_get_copyright(NULL));
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if(malloc_fn != NULL)
+   {
+      png_struct dummy_struct;
+      png_structp png_ptr = &dummy_struct;
+      png_ptr->mem_ptr=mem_ptr;
+      struct_ptr = (*(malloc_fn))(png_ptr, (png_uint_32)size);
+   }
+   else
+#endif /* PNG_USER_MEM_SUPPORTED */
+      struct_ptr = (png_voidp)farmalloc(size);
+   if (struct_ptr != NULL)
+      png_memset(struct_ptr, 0, size);
+   return (struct_ptr);
+}
+
+/* Free memory allocated by a png_create_struct() call */
+void /* PRIVATE */
+png_destroy_struct(png_voidp struct_ptr)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL);
+}
+
+/* Free memory allocated by a png_create_struct() call */
+void /* PRIVATE */
+png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn,
+    png_voidp mem_ptr)
+{
+#endif
+   if (struct_ptr != NULL)
+   {
+#ifdef PNG_USER_MEM_SUPPORTED
+      if(free_fn != NULL)
+      {
+         png_struct dummy_struct;
+         png_structp png_ptr = &dummy_struct;
+         png_ptr->mem_ptr=mem_ptr;
+         (*(free_fn))(png_ptr, struct_ptr);
+         return;
+      }
+#endif /* PNG_USER_MEM_SUPPORTED */
+      farfree (struct_ptr);
+   }
+}
+
+/* Allocate memory.  For reasonable files, size should never exceed
+ * 64K.  However, zlib may allocate more then 64K if you don't tell
+ * it not to.  See zconf.h and png.h for more information. zlib does
+ * need to allocate exactly 64K, so whatever you call here must
+ * have the ability to do that.
+ *
+ * Borland seems to have a problem in DOS mode for exactly 64K.
+ * It gives you a segment with an offset of 8 (perhaps to store its
+ * memory stuff).  zlib doesn't like this at all, so we have to
+ * detect and deal with it.  This code should not be needed in
+ * Windows or OS/2 modes, and only in 16 bit mode.  This code has
+ * been updated by Alexander Lehmann for version 0.89 to waste less
+ * memory.
+ *
+ * Note that we can't use png_size_t for the "size" declaration,
+ * since on some systems a png_size_t is a 16-bit quantity, and as a
+ * result, we would be truncating potentially larger memory requests
+ * (which should cause a fatal error) and introducing major problems.
+ */
+
+png_voidp PNGAPI
+png_malloc(png_structp png_ptr, png_uint_32 size)
+{
+   png_voidp ret;
+
+   if (png_ptr == NULL || size == 0)
+      return (NULL);
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if(png_ptr->malloc_fn != NULL)
+       ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size));
+   else
+       ret = (png_malloc_default(png_ptr, size));
+   if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+       png_error(png_ptr, "Out of memory!");
+   return (ret);
+}
+
+png_voidp PNGAPI
+png_malloc_default(png_structp png_ptr, png_uint_32 size)
+{
+   png_voidp ret;
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (size > (png_uint_32)65536L)
+   {
+      png_warning(png_ptr, "Cannot Allocate > 64K");
+      ret = NULL;
+   }
+   else
+#endif
+
+   if (size != (size_t)size)
+     ret = NULL;
+   else if (size == (png_uint_32)65536L)
+   {
+      if (png_ptr->offset_table == NULL)
+      {
+         /* try to see if we need to do any of this fancy stuff */
+         ret = farmalloc(size);
+         if (ret == NULL || ((png_size_t)ret & 0xffff))
+         {
+            int num_blocks;
+            png_uint_32 total_size;
+            png_bytep table;
+            int i;
+            png_byte huge * hptr;
+
+            if (ret != NULL)
+            {
+               farfree(ret);
+               ret = NULL;
+            }
+
+            if(png_ptr->zlib_window_bits > 14)
+               num_blocks = (int)(1 << (png_ptr->zlib_window_bits - 14));
+            else
+               num_blocks = 1;
+            if (png_ptr->zlib_mem_level >= 7)
+               num_blocks += (int)(1 << (png_ptr->zlib_mem_level - 7));
+            else
+               num_blocks++;
+
+            total_size = ((png_uint_32)65536L) * (png_uint_32)num_blocks+16;
+
+            table = farmalloc(total_size);
+
+            if (table == NULL)
+            {
+#ifndef PNG_USER_MEM_SUPPORTED
+               if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+                  png_error(png_ptr, "Out Of Memory."); /* Note "O" and "M" */
+               else
+                  png_warning(png_ptr, "Out Of Memory.");
+#endif
+               return (NULL);
+            }
+
+            if ((png_size_t)table & 0xfff0)
+            {
+#ifndef PNG_USER_MEM_SUPPORTED
+               if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+                  png_error(png_ptr,
+                    "Farmalloc didn't return normalized pointer");
+               else
+                  png_warning(png_ptr,
+                    "Farmalloc didn't return normalized pointer");
+#endif
+               return (NULL);
+            }
+
+            png_ptr->offset_table = table;
+            png_ptr->offset_table_ptr = farmalloc(num_blocks *
+               png_sizeof (png_bytep));
+
+            if (png_ptr->offset_table_ptr == NULL)
+            {
+#ifndef PNG_USER_MEM_SUPPORTED
+               if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+                  png_error(png_ptr, "Out Of memory."); /* Note "O" and "M" */
+               else
+                  png_warning(png_ptr, "Out Of memory.");
+#endif
+               return (NULL);
+            }
+
+            hptr = (png_byte huge *)table;
+            if ((png_size_t)hptr & 0xf)
+            {
+               hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L);
+               hptr = hptr + 16L;  /* "hptr += 16L" fails on Turbo C++ 3.0 */
+            }
+            for (i = 0; i < num_blocks; i++)
+            {
+               png_ptr->offset_table_ptr[i] = (png_bytep)hptr;
+               hptr = hptr + (png_uint_32)65536L;  /* "+=" fails on TC++3.0 */
+            }
+
+            png_ptr->offset_table_number = num_blocks;
+            png_ptr->offset_table_count = 0;
+            png_ptr->offset_table_count_free = 0;
+         }
+      }
+
+      if (png_ptr->offset_table_count >= png_ptr->offset_table_number)
+      {
+#ifndef PNG_USER_MEM_SUPPORTED
+         if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+            png_error(png_ptr, "Out of Memory."); /* Note "o" and "M" */
+         else
+            png_warning(png_ptr, "Out of Memory.");
+#endif
+         return (NULL);
+      }
+
+      ret = png_ptr->offset_table_ptr[png_ptr->offset_table_count++];
+   }
+   else
+      ret = farmalloc(size);
+
+#ifndef PNG_USER_MEM_SUPPORTED
+   if (ret == NULL)
+   {
+      if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+         png_error(png_ptr, "Out of memory."); /* Note "o" and "m" */
+      else
+         png_warning(png_ptr, "Out of memory."); /* Note "o" and "m" */
+   }
+#endif
+
+   return (ret);
+}
+
+/* free a pointer allocated by png_malloc().  In the default
+   configuration, png_ptr is not used, but is passed in case it
+   is needed.  If ptr is NULL, return without taking any action. */
+void PNGAPI
+png_free(png_structp png_ptr, png_voidp ptr)
+{
+   if (png_ptr == NULL || ptr == NULL)
+      return;
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if (png_ptr->free_fn != NULL)
+   {
+      (*(png_ptr->free_fn))(png_ptr, ptr);
+      return;
+   }
+   else png_free_default(png_ptr, ptr);
+}
+
+void PNGAPI
+png_free_default(png_structp png_ptr, png_voidp ptr)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+   if (png_ptr->offset_table != NULL)
+   {
+      int i;
+
+      for (i = 0; i < png_ptr->offset_table_count; i++)
+      {
+         if (ptr == png_ptr->offset_table_ptr[i])
+         {
+            ptr = NULL;
+            png_ptr->offset_table_count_free++;
+            break;
+         }
+      }
+      if (png_ptr->offset_table_count_free == png_ptr->offset_table_count)
+      {
+         farfree(png_ptr->offset_table);
+         farfree(png_ptr->offset_table_ptr);
+         png_ptr->offset_table = NULL;
+         png_ptr->offset_table_ptr = NULL;
+      }
+   }
+
+   if (ptr != NULL)
+   {
+      farfree(ptr);
+   }
+}
+
+#else /* Not the Borland DOS special memory handler */
+
+/* Allocate memory for a png_struct or a png_info.  The malloc and
+   memset can be replaced by a single call to calloc() if this is thought
+   to improve performance noticably. */
+png_voidp /* PRIVATE */
+png_create_struct(int type)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL));
+}
+
+/* Allocate memory for a png_struct or a png_info.  The malloc and
+   memset can be replaced by a single call to calloc() if this is thought
+   to improve performance noticably. */
+png_voidp /* PRIVATE */
+png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+   png_size_t size;
+   png_voidp struct_ptr;
+
+   if (type == PNG_STRUCT_INFO)
+      size = png_sizeof(png_info);
+   else if (type == PNG_STRUCT_PNG)
+      size = png_sizeof(png_struct);
+   else
+      return (NULL);
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if(malloc_fn != NULL)
+   {
+      png_struct dummy_struct;
+      png_structp png_ptr = &dummy_struct;
+      png_ptr->mem_ptr=mem_ptr;
+      struct_ptr = (*(malloc_fn))(png_ptr, size);
+      if (struct_ptr != NULL)
+         png_memset(struct_ptr, 0, size);
+      return (struct_ptr);
+   }
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+#if defined(__TURBOC__) && !defined(__FLAT__)
+   struct_ptr = (png_voidp)farmalloc(size);
+#else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+   struct_ptr = (png_voidp)halloc(size,1);
+# else
+   struct_ptr = (png_voidp)malloc(size);
+# endif
+#endif
+   if (struct_ptr != NULL)
+      png_memset(struct_ptr, 0, size);
+
+   return (struct_ptr);
+}
+
+
+/* Free memory allocated by a png_create_struct() call */
+void /* PRIVATE */
+png_destroy_struct(png_voidp struct_ptr)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL);
+}
+
+/* Free memory allocated by a png_create_struct() call */
+void /* PRIVATE */
+png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn,
+    png_voidp mem_ptr)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+   if (struct_ptr != NULL)
+   {
+#ifdef PNG_USER_MEM_SUPPORTED
+      if(free_fn != NULL)
+      {
+         png_struct dummy_struct;
+         png_structp png_ptr = &dummy_struct;
+         png_ptr->mem_ptr=mem_ptr;
+         (*(free_fn))(png_ptr, struct_ptr);
+         return;
+      }
+#endif /* PNG_USER_MEM_SUPPORTED */
+#if defined(__TURBOC__) && !defined(__FLAT__)
+      farfree(struct_ptr);
+#else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+      hfree(struct_ptr);
+# else
+      free(struct_ptr);
+# endif
+#endif
+   }
+}
+
+/* Allocate memory.  For reasonable files, size should never exceed
+   64K.  However, zlib may allocate more then 64K if you don't tell
+   it not to.  See zconf.h and png.h for more information.  zlib does
+   need to allocate exactly 64K, so whatever you call here must
+   have the ability to do that. */
+
+png_voidp PNGAPI
+png_malloc(png_structp png_ptr, png_uint_32 size)
+{
+   png_voidp ret;
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if (png_ptr == NULL || size == 0)
+      return (NULL);
+
+   if(png_ptr->malloc_fn != NULL)
+       ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size));
+   else
+       ret = (png_malloc_default(png_ptr, size));
+   if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+       png_error(png_ptr, "Out of Memory!");
+   return (ret);
+}
+
+png_voidp PNGAPI
+png_malloc_default(png_structp png_ptr, png_uint_32 size)
+{
+   png_voidp ret;
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+   if (png_ptr == NULL || size == 0)
+      return (NULL);
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (size > (png_uint_32)65536L)
+   {
+#ifndef PNG_USER_MEM_SUPPORTED
+      if(png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+         png_error(png_ptr, "Cannot Allocate > 64K");
+      else
+#endif
+         return NULL;
+   }
+#endif
+
+ /* Check for overflow */
+#if defined(__TURBOC__) && !defined(__FLAT__)
+ if (size != (unsigned long)size)
+   ret = NULL;
+ else
+   ret = farmalloc(size);
+#else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+ if (size != (unsigned long)size)
+   ret = NULL;
+ else
+   ret = halloc(size, 1);
+# else
+ if (size != (size_t)size)
+   ret = NULL;
+ else
+   ret = malloc((size_t)size);
+# endif
+#endif
+
+#ifndef PNG_USER_MEM_SUPPORTED
+   if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+      png_error(png_ptr, "Out of Memory");
+#endif
+
+   return (ret);
+}
+
+/* Free a pointer allocated by png_malloc().  If ptr is NULL, return
+   without taking any action. */
+void PNGAPI
+png_free(png_structp png_ptr, png_voidp ptr)
+{
+   if (png_ptr == NULL || ptr == NULL)
+      return;
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if (png_ptr->free_fn != NULL)
+   {
+      (*(png_ptr->free_fn))(png_ptr, ptr);
+      return;
+   }
+   else png_free_default(png_ptr, ptr);
+}
+void PNGAPI
+png_free_default(png_structp png_ptr, png_voidp ptr)
+{
+   if (png_ptr == NULL || ptr == NULL)
+      return;
+
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+#if defined(__TURBOC__) && !defined(__FLAT__)
+   farfree(ptr);
+#else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+   hfree(ptr);
+# else
+   free(ptr);
+# endif
+#endif
+}
+
+#endif /* Not Borland DOS special memory handler */
+
+#if defined(PNG_1_0_X)
+#  define png_malloc_warn png_malloc
+#else
+/* This function was added at libpng version 1.2.3.  The png_malloc_warn()
+ * function will set up png_malloc() to issue a png_warning and return NULL
+ * instead of issuing a png_error, if it fails to allocate the requested
+ * memory.
+ */
+png_voidp PNGAPI
+png_malloc_warn(png_structp png_ptr, png_uint_32 size)
+{
+   png_voidp ptr;
+   png_uint_32 save_flags=png_ptr->flags;
+
+   png_ptr->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK;
+   ptr = (png_voidp)png_malloc((png_structp)png_ptr, size);
+   png_ptr->flags=save_flags;
+   return(ptr);
+}
+#endif
+
+png_voidp PNGAPI
+png_memcpy_check (png_structp png_ptr, png_voidp s1, png_voidp s2,
+   png_uint_32 length)
+{
+   png_size_t size;
+
+   size = (png_size_t)length;
+   if ((png_uint_32)size != length)
+      png_error(png_ptr,"Overflow in png_memcpy_check.");
+
+   return(png_memcpy (s1, s2, size));
+}
+
+png_voidp PNGAPI
+png_memset_check (png_structp png_ptr, png_voidp s1, int value,
+   png_uint_32 length)
+{
+   png_size_t size;
+
+   size = (png_size_t)length;
+   if ((png_uint_32)size != length)
+      png_error(png_ptr,"Overflow in png_memset_check.");
+
+   return (png_memset (s1, value, size));
+
+}
+
+#ifdef PNG_USER_MEM_SUPPORTED
+/* This function is called when the application wants to use another method
+ * of allocating and freeing memory.
+ */
+void PNGAPI
+png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr
+  malloc_fn, png_free_ptr free_fn)
+{
+   png_ptr->mem_ptr = mem_ptr;
+   png_ptr->malloc_fn = malloc_fn;
+   png_ptr->free_fn = free_fn;
+}
+
+/* This function returns a pointer to the mem_ptr associated with the user
+ * functions.  The application should free any memory associated with this
+ * pointer before png_write_destroy and png_read_destroy are called.
+ */
+png_voidp PNGAPI
+png_get_mem_ptr(png_structp png_ptr)
+{
+   return ((png_voidp)png_ptr->mem_ptr);
+}
+#endif /* PNG_USER_MEM_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/png/libpng/pngpread.c b/Utilities/GDAL/frmts/png/libpng/pngpread.c
new file mode 100644
index 0000000000..8c35faae44
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngpread.c
@@ -0,0 +1,1573 @@
+
+/* pngpread.c - read a png file in push mode
+ *
+ * libpng version 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+
+/* push model modes */
+#define PNG_READ_SIG_MODE   0
+#define PNG_READ_CHUNK_MODE 1
+#define PNG_READ_IDAT_MODE  2
+#define PNG_SKIP_MODE       3
+#define PNG_READ_tEXt_MODE  4
+#define PNG_READ_zTXt_MODE  5
+#define PNG_READ_DONE_MODE  6
+#define PNG_READ_iTXt_MODE  7
+#define PNG_ERROR_MODE      8
+
+void PNGAPI
+png_process_data(png_structp png_ptr, png_infop info_ptr,
+   png_bytep buffer, png_size_t buffer_size)
+{
+   png_push_restore_buffer(png_ptr, buffer, buffer_size);
+
+   while (png_ptr->buffer_size)
+   {
+      png_process_some_data(png_ptr, info_ptr);
+   }
+}
+
+/* What we do with the incoming data depends on what we were previously
+ * doing before we ran out of data...
+ */
+void /* PRIVATE */
+png_process_some_data(png_structp png_ptr, png_infop info_ptr)
+{
+   switch (png_ptr->process_mode)
+   {
+      case PNG_READ_SIG_MODE:
+      {
+         png_push_read_sig(png_ptr, info_ptr);
+         break;
+      }
+      case PNG_READ_CHUNK_MODE:
+      {
+         png_push_read_chunk(png_ptr, info_ptr);
+         break;
+      }
+      case PNG_READ_IDAT_MODE:
+      {
+         png_push_read_IDAT(png_ptr);
+         break;
+      }
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      case PNG_READ_tEXt_MODE:
+      {
+         png_push_read_tEXt(png_ptr, info_ptr);
+         break;
+      }
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      case PNG_READ_zTXt_MODE:
+      {
+         png_push_read_zTXt(png_ptr, info_ptr);
+         break;
+      }
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+      case PNG_READ_iTXt_MODE:
+      {
+         png_push_read_iTXt(png_ptr, info_ptr);
+         break;
+      }
+#endif
+      case PNG_SKIP_MODE:
+      {
+         png_push_crc_finish(png_ptr);
+         break;
+      }
+      default:
+      {
+         png_ptr->buffer_size = 0;
+         break;
+      }
+   }
+}
+
+/* Read any remaining signature bytes from the stream and compare them with
+ * the correct PNG signature.  It is possible that this routine is called
+ * with bytes already read from the signature, either because they have been
+ * checked by the calling application, or because of multiple calls to this
+ * routine.
+ */
+void /* PRIVATE */
+png_push_read_sig(png_structp png_ptr, png_infop info_ptr)
+{
+   png_size_t num_checked = png_ptr->sig_bytes,
+             num_to_check = 8 - num_checked;
+
+   if (png_ptr->buffer_size < num_to_check)
+   {
+      num_to_check = png_ptr->buffer_size;
+   }
+
+   png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]),
+      num_to_check);
+   png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes+num_to_check);
+
+   if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))
+   {
+      if (num_checked < 4 &&
+          png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
+         png_error(png_ptr, "Not a PNG file");
+      else
+         png_error(png_ptr, "PNG file corrupted by ASCII conversion");
+   }
+   else
+   {
+      if (png_ptr->sig_bytes >= 8)
+      {
+         png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+      }
+   }
+}
+
+void /* PRIVATE */
+png_push_read_chunk(png_structp png_ptr, png_infop info_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+      PNG_IHDR;
+      PNG_IDAT;
+      PNG_IEND;
+      PNG_PLTE;
+#if defined(PNG_READ_bKGD_SUPPORTED)
+      PNG_bKGD;
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+      PNG_cHRM;
+#endif
+#if defined(PNG_READ_gAMA_SUPPORTED)
+      PNG_gAMA;
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+      PNG_hIST;
+#endif
+#if defined(PNG_READ_iCCP_SUPPORTED)
+      PNG_iCCP;
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+      PNG_iTXt;
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+      PNG_oFFs;
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+      PNG_pCAL;
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+      PNG_pHYs;
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+      PNG_sBIT;
+#endif
+#if defined(PNG_READ_sCAL_SUPPORTED)
+      PNG_sCAL;
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      PNG_sRGB;
+#endif
+#if defined(PNG_READ_sPLT_SUPPORTED)
+      PNG_sPLT;
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      PNG_tEXt;
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+      PNG_tIME;
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+      PNG_tRNS;
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      PNG_zTXt;
+#endif
+#endif /* PNG_USE_LOCAL_ARRAYS */
+   /* First we make sure we have enough data for the 4 byte chunk name
+    * and the 4 byte chunk length before proceeding with decoding the
+    * chunk data.  To fully decode each of these chunks, we also make
+    * sure we have enough data in the buffer for the 4 byte CRC at the
+    * end of every chunk (except IDAT, which is handled separately).
+    */
+   if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER))
+   {
+      png_byte chunk_length[4];
+
+      if (png_ptr->buffer_size < 8)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_push_fill_buffer(png_ptr, chunk_length, 4);
+      png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length);
+      png_reset_crc(png_ptr);
+      png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+      png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
+   }
+
+   if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length);
+   }
+   else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length);
+
+      png_ptr->process_mode = PNG_READ_DONE_MODE;
+      png_push_have_end(png_ptr, info_ptr);
+   }
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+   else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+         png_ptr->mode |= PNG_HAVE_IDAT;
+      png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length);
+      if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+         png_ptr->mode |= PNG_HAVE_PLTE;
+      else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+      {
+         if (!(png_ptr->mode & PNG_HAVE_IHDR))
+            png_error(png_ptr, "Missing IHDR before IDAT");
+         else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+                  !(png_ptr->mode & PNG_HAVE_PLTE))
+            png_error(png_ptr, "Missing PLTE before IDAT");
+      }
+   }
+#endif
+   else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length);
+   }
+   else if (!png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4))
+   {
+      /* If we reach an IDAT chunk, this means we have read all of the
+       * header chunks, and we can start reading the image (or if this
+       * is called after the image has been read - we have an error).
+       */
+     if (!(png_ptr->mode & PNG_HAVE_IHDR))
+       png_error(png_ptr, "Missing IHDR before IDAT");
+     else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+         !(png_ptr->mode & PNG_HAVE_PLTE))
+       png_error(png_ptr, "Missing PLTE before IDAT");
+
+      if (png_ptr->mode & PNG_HAVE_IDAT)
+      {
+         if (png_ptr->push_length == 0)
+            return;
+
+         if (png_ptr->mode & PNG_AFTER_IDAT)
+            png_error(png_ptr, "Too many IDAT's found");
+      }
+
+      png_ptr->idat_size = png_ptr->push_length;
+      png_ptr->mode |= PNG_HAVE_IDAT;
+      png_ptr->process_mode = PNG_READ_IDAT_MODE;
+      png_push_have_info(png_ptr, info_ptr);
+      png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes;
+      png_ptr->zstream.next_out = png_ptr->row_buf;
+      return;
+   }
+#if defined(PNG_READ_gAMA_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_iCCP_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_sPLT_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_bKGD_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_sCAL_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_push_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_push_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_push_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+   else
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_push_handle_unknown(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+   png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
+}
+
+void /* PRIVATE */
+png_push_crc_skip(png_structp png_ptr, png_uint_32 skip)
+{
+   png_ptr->process_mode = PNG_SKIP_MODE;
+   png_ptr->skip_length = skip;
+}
+
+void /* PRIVATE */
+png_push_crc_finish(png_structp png_ptr)
+{
+   if (png_ptr->skip_length && png_ptr->save_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (png_ptr->skip_length < (png_uint_32)png_ptr->save_buffer_size)
+         save_size = (png_size_t)png_ptr->skip_length;
+      else
+         save_size = png_ptr->save_buffer_size;
+
+      png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);
+
+      png_ptr->skip_length -= save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->save_buffer_size -= save_size;
+      png_ptr->save_buffer_ptr += save_size;
+   }
+   if (png_ptr->skip_length && png_ptr->current_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (png_ptr->skip_length < (png_uint_32)png_ptr->current_buffer_size)
+         save_size = (png_size_t)png_ptr->skip_length;
+      else
+         save_size = png_ptr->current_buffer_size;
+
+      png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);
+
+      png_ptr->skip_length -= save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->current_buffer_size -= save_size;
+      png_ptr->current_buffer_ptr += save_size;
+   }
+   if (!png_ptr->skip_length)
+   {
+      if (png_ptr->buffer_size < 4)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_crc_finish(png_ptr, 0);
+      png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+   }
+}
+
+void PNGAPI
+png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length)
+{
+   png_bytep ptr;
+
+   ptr = buffer;
+   if (png_ptr->save_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (length < png_ptr->save_buffer_size)
+         save_size = length;
+      else
+         save_size = png_ptr->save_buffer_size;
+
+      png_memcpy(ptr, png_ptr->save_buffer_ptr, save_size);
+      length -= save_size;
+      ptr += save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->save_buffer_size -= save_size;
+      png_ptr->save_buffer_ptr += save_size;
+   }
+   if (length && png_ptr->current_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (length < png_ptr->current_buffer_size)
+         save_size = length;
+      else
+         save_size = png_ptr->current_buffer_size;
+
+      png_memcpy(ptr, png_ptr->current_buffer_ptr, save_size);
+      png_ptr->buffer_size -= save_size;
+      png_ptr->current_buffer_size -= save_size;
+      png_ptr->current_buffer_ptr += save_size;
+   }
+}
+
+void /* PRIVATE */
+png_push_save_buffer(png_structp png_ptr)
+{
+   if (png_ptr->save_buffer_size)
+   {
+      if (png_ptr->save_buffer_ptr != png_ptr->save_buffer)
+      {
+         png_size_t i,istop;
+         png_bytep sp;
+         png_bytep dp;
+
+         istop = png_ptr->save_buffer_size;
+         for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer;
+            i < istop; i++, sp++, dp++)
+         {
+            *dp = *sp;
+         }
+      }
+   }
+   if (png_ptr->save_buffer_size + png_ptr->current_buffer_size >
+      png_ptr->save_buffer_max)
+   {
+      png_size_t new_max;
+      png_bytep old_buffer;
+
+      if (png_ptr->save_buffer_size > PNG_SIZE_MAX - 
+         (png_ptr->current_buffer_size + 256))
+      {
+        png_error(png_ptr, "Potential overflow of save_buffer");
+      }
+      new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256;
+      old_buffer = png_ptr->save_buffer;
+      png_ptr->save_buffer = (png_bytep)png_malloc(png_ptr,
+         (png_uint_32)new_max);
+      png_memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size);
+      png_free(png_ptr, old_buffer);
+      png_ptr->save_buffer_max = new_max;
+   }
+   if (png_ptr->current_buffer_size)
+   {
+      png_memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size,
+         png_ptr->current_buffer_ptr, png_ptr->current_buffer_size);
+      png_ptr->save_buffer_size += png_ptr->current_buffer_size;
+      png_ptr->current_buffer_size = 0;
+   }
+   png_ptr->save_buffer_ptr = png_ptr->save_buffer;
+   png_ptr->buffer_size = 0;
+}
+
+void /* PRIVATE */
+png_push_restore_buffer(png_structp png_ptr, png_bytep buffer,
+   png_size_t buffer_length)
+{
+   png_ptr->current_buffer = buffer;
+   png_ptr->current_buffer_size = buffer_length;
+   png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size;
+   png_ptr->current_buffer_ptr = png_ptr->current_buffer;
+}
+
+void /* PRIVATE */
+png_push_read_IDAT(png_structp png_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_IDAT;
+#endif
+   if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER))
+   {
+      png_byte chunk_length[4];
+
+      if (png_ptr->buffer_size < 8)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_push_fill_buffer(png_ptr, chunk_length, 4);
+      png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length);
+      png_reset_crc(png_ptr);
+      png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+      png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
+
+      if (png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4))
+      {
+         png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+         if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
+            png_error(png_ptr, "Not enough compressed data");
+         return;
+      }
+
+      png_ptr->idat_size = png_ptr->push_length;
+   }
+   if (png_ptr->idat_size && png_ptr->save_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (png_ptr->idat_size < (png_uint_32)png_ptr->save_buffer_size)
+      {
+         save_size = (png_size_t)png_ptr->idat_size;
+         /* check for overflow */
+         if((png_uint_32)save_size != png_ptr->idat_size)
+            png_error(png_ptr, "save_size overflowed in pngpread");
+      }
+      else
+         save_size = png_ptr->save_buffer_size;
+
+      png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);
+      if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
+         png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size);
+      png_ptr->idat_size -= save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->save_buffer_size -= save_size;
+      png_ptr->save_buffer_ptr += save_size;
+   }
+   if (png_ptr->idat_size && png_ptr->current_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (png_ptr->idat_size < (png_uint_32)png_ptr->current_buffer_size)
+      {
+         save_size = (png_size_t)png_ptr->idat_size;
+         /* check for overflow */
+         if((png_uint_32)save_size != png_ptr->idat_size)
+            png_error(png_ptr, "save_size overflowed in pngpread");
+      }
+      else
+         save_size = png_ptr->current_buffer_size;
+
+      png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);
+      if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
+        png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size);
+
+      png_ptr->idat_size -= save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->current_buffer_size -= save_size;
+      png_ptr->current_buffer_ptr += save_size;
+   }
+   if (!png_ptr->idat_size)
+   {
+      if (png_ptr->buffer_size < 4)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_crc_finish(png_ptr, 0);
+      png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
+      png_ptr->mode |= PNG_AFTER_IDAT;
+   }
+}
+
+void /* PRIVATE */
+png_process_IDAT_data(png_structp png_ptr, png_bytep buffer,
+   png_size_t buffer_length)
+{
+   int ret;
+
+   if ((png_ptr->flags & PNG_FLAG_ZLIB_FINISHED) && buffer_length)
+      png_error(png_ptr, "Extra compression data");
+
+   png_ptr->zstream.next_in = buffer;
+   png_ptr->zstream.avail_in = (uInt)buffer_length;
+   for(;;)
+   {
+      ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+      if (ret != Z_OK)
+      {
+         if (ret == Z_STREAM_END)
+         {
+            if (png_ptr->zstream.avail_in)
+               png_error(png_ptr, "Extra compressed data");
+            if (!(png_ptr->zstream.avail_out))
+            {
+               png_push_process_row(png_ptr);
+            }
+
+            png_ptr->mode |= PNG_AFTER_IDAT;
+            png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+            break;
+         }
+         else if (ret == Z_BUF_ERROR)
+            break;
+         else
+            png_error(png_ptr, "Decompression Error");
+      }
+      if (!(png_ptr->zstream.avail_out))
+      {
+         if ((
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+             png_ptr->interlaced && png_ptr->pass > 6) ||
+             (!png_ptr->interlaced &&
+#endif
+             png_ptr->row_number == png_ptr->num_rows))
+         {
+           if (png_ptr->zstream.avail_in)
+             png_warning(png_ptr, "Too much data in IDAT chunks");
+           png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+           break;
+         }
+         png_push_process_row(png_ptr);
+         png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes;
+         png_ptr->zstream.next_out = png_ptr->row_buf;
+      }
+      else
+         break;
+   }
+}
+
+void /* PRIVATE */
+png_push_process_row(png_structp png_ptr)
+{
+   png_ptr->row_info.color_type = png_ptr->color_type;
+   png_ptr->row_info.width = png_ptr->iwidth;
+   png_ptr->row_info.channels = png_ptr->channels;
+   png_ptr->row_info.bit_depth = png_ptr->bit_depth;
+   png_ptr->row_info.pixel_depth = png_ptr->pixel_depth;
+
+   png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
+       png_ptr->row_info.width);
+
+   png_read_filter_row(png_ptr, &(png_ptr->row_info),
+      png_ptr->row_buf + 1, png_ptr->prev_row + 1,
+      (int)(png_ptr->row_buf[0]));
+
+   png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf,
+      png_ptr->rowbytes + 1);
+
+   if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA))
+      png_do_read_transformations(png_ptr);
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+   /* blow up interlaced rows to full size */
+   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+   {
+      if (png_ptr->pass < 6)
+/*       old interface (pre-1.0.9):
+         png_do_read_interlace(&(png_ptr->row_info),
+            png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations);
+ */
+         png_do_read_interlace(png_ptr);
+
+    switch (png_ptr->pass)
+    {
+         case 0:
+         {
+            int i;
+            for (i = 0; i < 8 && png_ptr->pass == 0; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr); /* updates png_ptr->pass */
+            }
+            if (png_ptr->pass == 2) /* pass 1 might be empty */
+            {
+               for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+               {
+                  png_push_have_row(png_ptr, png_bytep_NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+            if (png_ptr->pass == 4 && png_ptr->height <= 4)
+            {
+               for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+               {
+                  png_push_have_row(png_ptr, png_bytep_NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+            if (png_ptr->pass == 6 && png_ptr->height <= 4)
+            {
+                png_push_have_row(png_ptr, png_bytep_NULL);
+                png_read_push_finish_row(png_ptr);
+            }
+            break;
+         }
+         case 1:
+         {
+            int i;
+            for (i = 0; i < 8 && png_ptr->pass == 1; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            if (png_ptr->pass == 2) /* skip top 4 generated rows */
+            {
+               for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+               {
+                  png_push_have_row(png_ptr, png_bytep_NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+            break;
+         }
+         case 2:
+         {
+            int i;
+            for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+            {
+               png_push_have_row(png_ptr, png_bytep_NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+            if (png_ptr->pass == 4) /* pass 3 might be empty */
+            {
+               for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+               {
+                  png_push_have_row(png_ptr, png_bytep_NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+            break;
+         }
+         case 3:
+         {
+            int i;
+            for (i = 0; i < 4 && png_ptr->pass == 3; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            if (png_ptr->pass == 4) /* skip top two generated rows */
+            {
+               for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+               {
+                  png_push_have_row(png_ptr, png_bytep_NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+            break;
+         }
+         case 4:
+         {
+            int i;
+            for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+            {
+               png_push_have_row(png_ptr, png_bytep_NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+            if (png_ptr->pass == 6) /* pass 5 might be empty */
+            {
+               png_push_have_row(png_ptr, png_bytep_NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+            break;
+         }
+         case 5:
+         {
+            int i;
+            for (i = 0; i < 2 && png_ptr->pass == 5; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            if (png_ptr->pass == 6) /* skip top generated row */
+            {
+               png_push_have_row(png_ptr, png_bytep_NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+            break;
+         }
+         case 6:
+         {
+            png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+            png_read_push_finish_row(png_ptr);
+            if (png_ptr->pass != 6)
+               break;
+            png_push_have_row(png_ptr, png_bytep_NULL);
+            png_read_push_finish_row(png_ptr);
+         }
+      }
+   }
+   else
+#endif
+   {
+      png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+      png_read_push_finish_row(png_ptr);
+   }
+}
+
+void /* PRIVATE */
+png_read_push_finish_row(png_structp png_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* start of interlace block */
+   const int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* offset to next interlace block */
+   const int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* start of interlace block in the y direction */
+   const int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* offset to next interlace block in the y direction */
+   const int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
+
+   /* Width of interlace block.  This is not currently used - if you need
+    * it, uncomment it here and in png.h
+   const int FARDATA png_pass_width[] = {8, 4, 4, 2, 2, 1, 1};
+   */
+
+   /* Height of interlace block.  This is not currently used - if you need
+    * it, uncomment it here and in png.h
+   const int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1};
+   */
+#endif
+
+   png_ptr->row_number++;
+   if (png_ptr->row_number < png_ptr->num_rows)
+      return;
+
+   if (png_ptr->interlaced)
+   {
+      png_ptr->row_number = 0;
+      png_memset_check(png_ptr, png_ptr->prev_row, 0,
+         png_ptr->rowbytes + 1);
+      do
+      {
+         png_ptr->pass++;
+         if ((png_ptr->pass == 1 && png_ptr->width < 5) ||
+             (png_ptr->pass == 3 && png_ptr->width < 3) ||
+             (png_ptr->pass == 5 && png_ptr->width < 2))
+           png_ptr->pass++;
+
+         if (png_ptr->pass > 7)
+            png_ptr->pass--;
+         if (png_ptr->pass >= 7)
+            break;
+
+         png_ptr->iwidth = (png_ptr->width +
+            png_pass_inc[png_ptr->pass] - 1 -
+            png_pass_start[png_ptr->pass]) /
+            png_pass_inc[png_ptr->pass];
+
+         png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,
+            png_ptr->iwidth) + 1;
+
+         if (png_ptr->transformations & PNG_INTERLACE)
+            break;
+
+         png_ptr->num_rows = (png_ptr->height +
+            png_pass_yinc[png_ptr->pass] - 1 -
+            png_pass_ystart[png_ptr->pass]) /
+            png_pass_yinc[png_ptr->pass];
+
+      } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0);
+   }
+}
+
+#if defined(PNG_READ_tEXt_SUPPORTED)
+void /* PRIVATE */
+png_push_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32
+   length)
+{
+   if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND))
+      {
+         png_error(png_ptr, "Out of place tEXt");
+         /* to quiet some compiler warnings */
+         if(info_ptr == NULL) return;
+      }
+
+#ifdef PNG_MAX_MALLOC_64K
+   png_ptr->skip_length = 0;  /* This may not be necessary */
+
+   if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */
+   {
+      png_warning(png_ptr, "tEXt chunk too large to fit in memory");
+      png_ptr->skip_length = length - (png_uint_32)65535L;
+      length = (png_uint_32)65535L;
+   }
+#endif
+
+   png_ptr->current_text = (png_charp)png_malloc(png_ptr,
+         (png_uint_32)(length+1));
+   png_ptr->current_text[length] = '\0';
+   png_ptr->current_text_ptr = png_ptr->current_text;
+   png_ptr->current_text_size = (png_size_t)length;
+   png_ptr->current_text_left = (png_size_t)length;
+   png_ptr->process_mode = PNG_READ_tEXt_MODE;
+}
+
+void /* PRIVATE */
+png_push_read_tEXt(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr->buffer_size && png_ptr->current_text_left)
+   {
+      png_size_t text_size;
+
+      if (png_ptr->buffer_size < png_ptr->current_text_left)
+         text_size = png_ptr->buffer_size;
+      else
+         text_size = png_ptr->current_text_left;
+      png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size);
+      png_ptr->current_text_left -= text_size;
+      png_ptr->current_text_ptr += text_size;
+   }
+   if (!(png_ptr->current_text_left))
+   {
+      png_textp text_ptr;
+      png_charp text;
+      png_charp key;
+      int ret;
+
+      if (png_ptr->buffer_size < 4)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_push_crc_finish(png_ptr);
+
+#if defined(PNG_MAX_MALLOC_64K)
+      if (png_ptr->skip_length)
+         return;
+#endif
+
+      key = png_ptr->current_text;
+
+      for (text = key; *text; text++)
+         /* empty loop */ ;
+
+      if (text != key + png_ptr->current_text_size)
+         text++;
+
+      text_ptr = (png_textp)png_malloc(png_ptr,
+         (png_uint_32)png_sizeof(png_text));
+      text_ptr->compression = PNG_TEXT_COMPRESSION_NONE;
+      text_ptr->key = key;
+#ifdef PNG_iTXt_SUPPORTED
+      text_ptr->lang = NULL;
+      text_ptr->lang_key = NULL;
+#endif
+      text_ptr->text = text;
+
+      ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+      png_free(png_ptr, key);
+      png_free(png_ptr, text_ptr);
+      png_ptr->current_text = NULL;
+
+      if (ret)
+        png_warning(png_ptr, "Insufficient memory to store text chunk.");
+   }
+}
+#endif
+
+#if defined(PNG_READ_zTXt_SUPPORTED)
+void /* PRIVATE */
+png_push_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32
+   length)
+{
+   if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND))
+      {
+         png_error(png_ptr, "Out of place zTXt");
+         /* to quiet some compiler warnings */
+         if(info_ptr == NULL) return;
+      }
+
+#ifdef PNG_MAX_MALLOC_64K
+   /* We can't handle zTXt chunks > 64K, since we don't have enough space
+    * to be able to store the uncompressed data.  Actually, the threshold
+    * is probably around 32K, but it isn't as definite as 64K is.
+    */
+   if (length > (png_uint_32)65535L)
+   {
+      png_warning(png_ptr, "zTXt chunk too large to fit in memory");
+      png_push_crc_skip(png_ptr, length);
+      return;
+   }
+#endif
+
+   png_ptr->current_text = (png_charp)png_malloc(png_ptr,
+       (png_uint_32)(length+1));
+   png_ptr->current_text[length] = '\0';
+   png_ptr->current_text_ptr = png_ptr->current_text;
+   png_ptr->current_text_size = (png_size_t)length;
+   png_ptr->current_text_left = (png_size_t)length;
+   png_ptr->process_mode = PNG_READ_zTXt_MODE;
+}
+
+void /* PRIVATE */
+png_push_read_zTXt(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr->buffer_size && png_ptr->current_text_left)
+   {
+      png_size_t text_size;
+
+      if (png_ptr->buffer_size < (png_uint_32)png_ptr->current_text_left)
+         text_size = png_ptr->buffer_size;
+      else
+         text_size = png_ptr->current_text_left;
+      png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size);
+      png_ptr->current_text_left -= text_size;
+      png_ptr->current_text_ptr += text_size;
+   }
+   if (!(png_ptr->current_text_left))
+   {
+      png_textp text_ptr;
+      png_charp text;
+      png_charp key;
+      int ret;
+      png_size_t text_size, key_size;
+
+      if (png_ptr->buffer_size < 4)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_push_crc_finish(png_ptr);
+
+      key = png_ptr->current_text;
+
+      for (text = key; *text; text++)
+         /* empty loop */ ;
+
+      /* zTXt can't have zero text */
+      if (text == key + png_ptr->current_text_size)
+      {
+         png_ptr->current_text = NULL;
+         png_free(png_ptr, key);
+         return;
+      }
+
+      text++;
+
+      if (*text != PNG_TEXT_COMPRESSION_zTXt) /* check compression byte */
+      {
+         png_ptr->current_text = NULL;
+         png_free(png_ptr, key);
+         return;
+      }
+
+      text++;
+
+      png_ptr->zstream.next_in = (png_bytep )text;
+      png_ptr->zstream.avail_in = (uInt)(png_ptr->current_text_size -
+         (text - key));
+      png_ptr->zstream.next_out = png_ptr->zbuf;
+      png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+      key_size = text - key;
+      text_size = 0;
+      text = NULL;
+      ret = Z_STREAM_END;
+
+      while (png_ptr->zstream.avail_in)
+      {
+         ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+         if (ret != Z_OK && ret != Z_STREAM_END)
+         {
+            inflateReset(&png_ptr->zstream);
+            png_ptr->zstream.avail_in = 0;
+            png_ptr->current_text = NULL;
+            png_free(png_ptr, key);
+            png_free(png_ptr, text);
+            return;
+         }
+         if (!(png_ptr->zstream.avail_out) || ret == Z_STREAM_END)
+         {
+            if (text == NULL)
+            {
+               text = (png_charp)png_malloc(png_ptr,
+                  (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out
+                     + key_size + 1));
+               png_memcpy(text + key_size, png_ptr->zbuf,
+                  png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+               png_memcpy(text, key, key_size);
+               text_size = key_size + png_ptr->zbuf_size -
+                  png_ptr->zstream.avail_out;
+               *(text + text_size) = '\0';
+            }
+            else
+            {
+               png_charp tmp;
+
+               tmp = text;
+               text = (png_charp)png_malloc(png_ptr, text_size +
+                  (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out
+                   + 1));
+               png_memcpy(text, tmp, text_size);
+               png_free(png_ptr, tmp);
+               png_memcpy(text + text_size, png_ptr->zbuf,
+                  png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+               text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out;
+               *(text + text_size) = '\0';
+            }
+            if (ret != Z_STREAM_END)
+            {
+               png_ptr->zstream.next_out = png_ptr->zbuf;
+               png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+            }
+         }
+         else
+         {
+            break;
+         }
+
+         if (ret == Z_STREAM_END)
+            break;
+      }
+
+      inflateReset(&png_ptr->zstream);
+      png_ptr->zstream.avail_in = 0;
+
+      if (ret != Z_STREAM_END)
+      {
+         png_ptr->current_text = NULL;
+         png_free(png_ptr, key);
+         png_free(png_ptr, text);
+         return;
+      }
+
+      png_ptr->current_text = NULL;
+      png_free(png_ptr, key);
+      key = text;
+      text += key_size;
+
+      text_ptr = (png_textp)png_malloc(png_ptr,
+          (png_uint_32)png_sizeof(png_text));
+      text_ptr->compression = PNG_TEXT_COMPRESSION_zTXt;
+      text_ptr->key = key;
+#ifdef PNG_iTXt_SUPPORTED
+      text_ptr->lang = NULL;
+      text_ptr->lang_key = NULL;
+#endif
+      text_ptr->text = text;
+
+      ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+      png_free(png_ptr, key);
+      png_free(png_ptr, text_ptr);
+
+      if (ret)
+        png_warning(png_ptr, "Insufficient memory to store text chunk.");
+   }
+}
+#endif
+
+#if defined(PNG_READ_iTXt_SUPPORTED)
+void /* PRIVATE */
+png_push_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32
+   length)
+{
+   if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND))
+      {
+         png_error(png_ptr, "Out of place iTXt");
+         /* to quiet some compiler warnings */
+         if(info_ptr == NULL) return;
+      }
+
+#ifdef PNG_MAX_MALLOC_64K
+   png_ptr->skip_length = 0;  /* This may not be necessary */
+
+   if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */
+   {
+      png_warning(png_ptr, "iTXt chunk too large to fit in memory");
+      png_ptr->skip_length = length - (png_uint_32)65535L;
+      length = (png_uint_32)65535L;
+   }
+#endif
+
+   png_ptr->current_text = (png_charp)png_malloc(png_ptr,
+         (png_uint_32)(length+1));
+   png_ptr->current_text[length] = '\0';
+   png_ptr->current_text_ptr = png_ptr->current_text;
+   png_ptr->current_text_size = (png_size_t)length;
+   png_ptr->current_text_left = (png_size_t)length;
+   png_ptr->process_mode = PNG_READ_iTXt_MODE;
+}
+
+void /* PRIVATE */
+png_push_read_iTXt(png_structp png_ptr, png_infop info_ptr)
+{
+
+   if (png_ptr->buffer_size && png_ptr->current_text_left)
+   {
+      png_size_t text_size;
+
+      if (png_ptr->buffer_size < png_ptr->current_text_left)
+         text_size = png_ptr->buffer_size;
+      else
+         text_size = png_ptr->current_text_left;
+      png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size);
+      png_ptr->current_text_left -= text_size;
+      png_ptr->current_text_ptr += text_size;
+   }
+   if (!(png_ptr->current_text_left))
+   {
+      png_textp text_ptr;
+      png_charp key;
+      int comp_flag;
+      png_charp lang;
+      png_charp lang_key;
+      png_charp text;
+      int ret;
+
+      if (png_ptr->buffer_size < 4)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_push_crc_finish(png_ptr);
+
+#if defined(PNG_MAX_MALLOC_64K)
+      if (png_ptr->skip_length)
+         return;
+#endif
+
+      key = png_ptr->current_text;
+
+      for (lang = key; *lang; lang++)
+         /* empty loop */ ;
+
+      if (lang != key + png_ptr->current_text_size)
+         lang++;
+
+      comp_flag = *lang++;
+      lang++;     /* skip comp_type, always zero */
+
+      for (lang_key = lang; *lang_key; lang_key++)
+         /* empty loop */ ;
+      lang_key++;        /* skip NUL separator */
+
+      for (text = lang_key; *text; text++)
+         /* empty loop */ ;
+
+      if (text != key + png_ptr->current_text_size)
+         text++;
+
+      text_ptr = (png_textp)png_malloc(png_ptr,
+         (png_uint_32)png_sizeof(png_text));
+      text_ptr->compression = comp_flag + 2;
+      text_ptr->key = key;
+      text_ptr->lang = lang;
+      text_ptr->lang_key = lang_key;
+      text_ptr->text = text;
+      text_ptr->text_length = 0;
+      text_ptr->itxt_length = png_strlen(text);
+
+      ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+      png_ptr->current_text = NULL;
+
+      png_free(png_ptr, text_ptr);
+      if (ret)
+        png_warning(png_ptr, "Insufficient memory to store iTXt chunk.");
+   }
+}
+#endif
+
+/* This function is called when we haven't found a handler for this
+ * chunk.  If there isn't a problem with the chunk itself (ie a bad chunk
+ * name or a critical chunk), the chunk is (currently) silently ignored.
+ */
+void /* PRIVATE */
+png_push_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32
+   length)
+{
+   png_uint_32 skip=0;
+   png_check_chunk_name(png_ptr, png_ptr->chunk_name);
+
+   if (!(png_ptr->chunk_name[0] & 0x20))
+   {
+#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
+      if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) !=
+           PNG_HANDLE_CHUNK_ALWAYS
+#if defined(PNG_READ_USER_CHUNKS_SUPPORTED)
+           && png_ptr->read_user_chunk_fn == NULL
+#endif
+         )
+#endif
+         png_chunk_error(png_ptr, "unknown critical chunk");
+
+      /* to quiet compiler warnings about unused info_ptr */
+      if (info_ptr == NULL)
+         return;
+   }
+
+#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
+   if (png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS)
+   {
+       png_unknown_chunk chunk;
+
+#ifdef PNG_MAX_MALLOC_64K
+       if (length > (png_uint_32)65535L)
+       {
+           png_warning(png_ptr, "unknown chunk too large to fit in memory");
+           skip = length - (png_uint_32)65535L;
+           length = (png_uint_32)65535L;
+       }
+#endif
+
+       png_strcpy((png_charp)chunk.name, (png_charp)png_ptr->chunk_name);
+       chunk.data = (png_bytep)png_malloc(png_ptr, length);
+       png_crc_read(png_ptr, chunk.data, length);
+       chunk.size = length;
+#if defined(PNG_READ_USER_CHUNKS_SUPPORTED)
+       if(png_ptr->read_user_chunk_fn != NULL)
+       {
+          /* callback to user unknown chunk handler */
+          if ((*(png_ptr->read_user_chunk_fn)) (png_ptr, &chunk) <= 0)
+          {
+             if (!(png_ptr->chunk_name[0] & 0x20))
+                if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) !=
+                     PNG_HANDLE_CHUNK_ALWAYS)
+                   png_chunk_error(png_ptr, "unknown critical chunk");
+          }
+             png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1);
+       }
+       else
+#endif
+          png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1);
+       png_free(png_ptr, chunk.data);
+   }
+   else
+#endif
+      skip=length;
+   png_push_crc_skip(png_ptr, skip);
+}
+
+void /* PRIVATE */
+png_push_have_info(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr->info_fn != NULL)
+      (*(png_ptr->info_fn))(png_ptr, info_ptr);
+}
+
+void /* PRIVATE */
+png_push_have_end(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr->end_fn != NULL)
+      (*(png_ptr->end_fn))(png_ptr, info_ptr);
+}
+
+void /* PRIVATE */
+png_push_have_row(png_structp png_ptr, png_bytep row)
+{
+   if (png_ptr->row_fn != NULL)
+      (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number,
+         (int)png_ptr->pass);
+}
+
+void PNGAPI
+png_progressive_combine_row (png_structp png_ptr,
+   png_bytep old_row, png_bytep new_row)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   const int FARDATA png_pass_dsp_mask[7] =
+      {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff};
+#endif
+   if (new_row != NULL)    /* new_row must == png_ptr->row_buf here. */
+      png_combine_row(png_ptr, old_row, png_pass_dsp_mask[png_ptr->pass]);
+}
+
+void PNGAPI
+png_set_progressive_read_fn(png_structp png_ptr, png_voidp progressive_ptr,
+   png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn,
+   png_progressive_end_ptr end_fn)
+{
+   png_ptr->info_fn = info_fn;
+   png_ptr->row_fn = row_fn;
+   png_ptr->end_fn = end_fn;
+
+   png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer);
+}
+
+png_voidp PNGAPI
+png_get_progressive_ptr(png_structp png_ptr)
+{
+   return png_ptr->io_ptr;
+}
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/png/libpng/pngread.c b/Utilities/GDAL/frmts/png/libpng/pngread.c
new file mode 100644
index 0000000000..5924333df3
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngread.c
@@ -0,0 +1,1456 @@
+
+/* pngread.c - read a PNG file
+ *
+ * libpng 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This file contains routines that an application calls directly to
+ * read a PNG file or stream.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* Create a PNG structure for reading, and allocate any memory needed. */
+png_structp PNGAPI
+png_create_read_struct(png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn)
+{
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   return (png_create_read_struct_2(user_png_ver, error_ptr, error_fn,
+      warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL));
+}
+
+/* Alternate create PNG structure for reading, and allocate any memory needed. */
+png_structp PNGAPI
+png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+   png_malloc_ptr malloc_fn, png_free_ptr free_fn)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+   png_structp png_ptr;
+
+#ifdef PNG_SETJMP_SUPPORTED
+#ifdef USE_FAR_KEYWORD
+   jmp_buf jmpbuf;
+#endif
+#endif
+
+   int i;
+
+   png_debug(1, "in png_create_read_struct\n");
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG,
+      (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr);
+#else
+   png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
+#endif
+   if (png_ptr == NULL)
+      return (NULL);
+
+#if !defined(PNG_1_0_X)
+#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
+   png_init_mmx_flags(png_ptr);   /* 1.2.0 addition */
+#endif
+#endif /* PNG_1_0_X */
+
+   /* added at libpng-1.2.6 */
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   png_ptr->user_width_max=PNG_USER_WIDTH_MAX;
+   png_ptr->user_height_max=PNG_USER_HEIGHT_MAX;
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+#ifdef USE_FAR_KEYWORD
+   if (setjmp(jmpbuf))
+#else
+   if (setjmp(png_ptr->jmpbuf))
+#endif
+   {
+      png_free(png_ptr, png_ptr->zbuf);
+      png_ptr->zbuf=NULL;
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)png_ptr, 
+         (png_free_ptr)free_fn, (png_voidp)mem_ptr);
+#else
+      png_destroy_struct((png_voidp)png_ptr);
+#endif
+      return (NULL);
+   }
+#ifdef USE_FAR_KEYWORD
+   png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf));
+#endif
+#endif
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn);
+#endif
+
+   png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
+
+   i=0;
+   do
+   {
+     if(user_png_ver[i] != png_libpng_ver[i])
+        png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+   } while (png_libpng_ver[i++]);
+
+   if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
+   {
+     /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
+      * we must recompile any applications that use any older library version.
+      * For versions after libpng 1.0, we will be compatible, so we need
+      * only check the first digit.
+      */
+     if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
+         (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) ||
+         (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
+     {
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+        char msg[80];
+        if (user_png_ver)
+        {
+          sprintf(msg, "Application was compiled with png.h from libpng-%.20s",
+             user_png_ver);
+          png_warning(png_ptr, msg);
+        }
+        sprintf(msg, "Application  is  running with png.c from libpng-%.20s",
+           png_libpng_ver);
+        png_warning(png_ptr, msg);
+#endif
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+        png_ptr->flags=0;
+#endif
+        png_error(png_ptr,
+           "Incompatible libpng version in application and library");
+     }
+   }
+
+   /* initialize zbuf - compression buffer */
+   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+   png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
+     (png_uint_32)png_ptr->zbuf_size);
+   png_ptr->zstream.zalloc = png_zalloc;
+   png_ptr->zstream.zfree = png_zfree;
+   png_ptr->zstream.opaque = (voidpf)png_ptr;
+
+   switch (inflateInit(&png_ptr->zstream))
+   {
+     case Z_OK: /* Do nothing */ break;
+     case Z_MEM_ERROR:
+     case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory error"); break;
+     case Z_VERSION_ERROR: png_error(png_ptr, "zlib version error"); break;
+     default: png_error(png_ptr, "Unknown zlib error");
+   }
+
+   png_ptr->zstream.next_out = png_ptr->zbuf;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+   png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL);
+
+#ifdef PNG_SETJMP_SUPPORTED
+/* Applications that neglect to set up their own setjmp() and then encounter
+   a png_error() will longjmp here.  Since the jmpbuf is then meaningless we
+   abort instead of returning. */
+#ifdef USE_FAR_KEYWORD
+   if (setjmp(jmpbuf))
+      PNG_ABORT();
+   png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf));
+#else
+   if (setjmp(png_ptr->jmpbuf))
+      PNG_ABORT();
+#endif
+#endif
+   return (png_ptr);
+}
+
+/* Initialize PNG structure for reading, and allocate any memory needed.
+   This interface is deprecated in favour of the png_create_read_struct(),
+   and it will eventually disappear. */
+#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
+#undef png_read_init
+void PNGAPI
+png_read_init(png_structp png_ptr)
+{
+   /* We only come here via pre-1.0.7-compiled applications */
+   png_read_init_2(png_ptr, "1.0.6 or earlier", 0, 0);
+}
+#endif
+
+void PNGAPI
+png_read_init_2(png_structp png_ptr, png_const_charp user_png_ver,
+   png_size_t png_struct_size, png_size_t png_info_size)
+{
+   /* We only come here via pre-1.0.12-compiled applications */
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+   if(png_sizeof(png_struct) > png_struct_size || 
+      png_sizeof(png_info) > png_info_size)
+   {
+      char msg[80];
+      png_ptr->warning_fn=NULL;
+      if (user_png_ver)
+      {
+        sprintf(msg, "Application was compiled with png.h from libpng-%.20s",
+           user_png_ver);
+        png_warning(png_ptr, msg);
+      }
+      sprintf(msg, "Application  is  running with png.c from libpng-%.20s",
+         png_libpng_ver);
+      png_warning(png_ptr, msg);
+   }
+#endif
+   if(png_sizeof(png_struct) > png_struct_size)
+     {
+       png_ptr->error_fn=NULL;
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+       png_ptr->flags=0;
+#endif
+       png_error(png_ptr,
+       "The png struct allocated by the application for reading is too small.");
+     }
+   if(png_sizeof(png_info) > png_info_size)
+     {
+       png_ptr->error_fn=NULL;
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+       png_ptr->flags=0;
+#endif
+       png_error(png_ptr,
+         "The info struct allocated by application for reading is too small.");
+     }
+   png_read_init_3(&png_ptr, user_png_ver, png_struct_size);
+}
+
+void PNGAPI
+png_read_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver,
+   png_size_t png_struct_size)
+{
+#ifdef PNG_SETJMP_SUPPORTED
+   jmp_buf tmp_jmp;  /* to save current jump buffer */
+#endif
+
+   int i=0;
+
+   png_structp png_ptr=*ptr_ptr;
+
+   do
+   {
+     if(user_png_ver[i] != png_libpng_ver[i])
+     {
+#ifdef PNG_LEGACY_SUPPORTED
+       png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+#else
+       png_ptr->warning_fn=NULL;
+       png_warning(png_ptr,
+        "Application uses deprecated png_read_init() and should be recompiled.");
+       break;
+#endif
+     }
+   } while (png_libpng_ver[i++]);
+
+   png_debug(1, "in png_read_init_3\n");
+
+#ifdef PNG_SETJMP_SUPPORTED
+   /* save jump buffer and error functions */
+   png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf));
+#endif
+
+   if(png_sizeof(png_struct) > png_struct_size)
+     {
+       png_destroy_struct(png_ptr);
+       *ptr_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
+       png_ptr = *ptr_ptr;
+     }
+
+   /* reset all variables to 0 */
+   png_memset(png_ptr, 0, png_sizeof (png_struct));
+
+#ifdef PNG_SETJMP_SUPPORTED
+   /* restore jump buffer */
+   png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf));
+#endif
+
+   /* added at libpng-1.2.6 */
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   png_ptr->user_width_max=PNG_USER_WIDTH_MAX;
+   png_ptr->user_height_max=PNG_USER_HEIGHT_MAX;
+#endif
+
+   /* initialize zbuf - compression buffer */
+   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+   png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
+     (png_uint_32)png_ptr->zbuf_size);
+   png_ptr->zstream.zalloc = png_zalloc;
+   png_ptr->zstream.zfree = png_zfree;
+   png_ptr->zstream.opaque = (voidpf)png_ptr;
+
+   switch (inflateInit(&png_ptr->zstream))
+   {
+     case Z_OK: /* Do nothing */ break;
+     case Z_MEM_ERROR:
+     case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory"); break;
+     case Z_VERSION_ERROR: png_error(png_ptr, "zlib version"); break;
+     default: png_error(png_ptr, "Unknown zlib error");
+   }
+
+   png_ptr->zstream.next_out = png_ptr->zbuf;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+   png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL);
+}
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* Read the information before the actual image data.  This has been
+ * changed in v0.90 to allow reading a file that already has the magic
+ * bytes read from the stream.  You can tell libpng how many bytes have
+ * been read from the beginning of the stream (up to the maximum of 8)
+ * via png_set_sig_bytes(), and we will only check the remaining bytes
+ * here.  The application can then have access to the signature bytes we
+ * read if it is determined that this isn't a valid PNG file.
+ */
+void PNGAPI
+png_read_info(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_read_info\n");
+   /* If we haven't checked all of the PNG signature bytes, do so now. */
+   if (png_ptr->sig_bytes < 8)
+   {
+      png_size_t num_checked = png_ptr->sig_bytes,
+                 num_to_check = 8 - num_checked;
+
+      png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check);
+      png_ptr->sig_bytes = 8;
+
+      if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))
+      {
+         if (num_checked < 4 &&
+             png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
+            png_error(png_ptr, "Not a PNG file");
+         else
+            png_error(png_ptr, "PNG file corrupted by ASCII conversion");
+      }
+      if (num_checked < 3)
+         png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
+   }
+
+   for(;;)
+   {
+#ifdef PNG_USE_LOCAL_ARRAYS
+      PNG_IHDR;
+      PNG_IDAT;
+      PNG_IEND;
+      PNG_PLTE;
+#if defined(PNG_READ_bKGD_SUPPORTED)
+      PNG_bKGD;
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+      PNG_cHRM;
+#endif
+#if defined(PNG_READ_gAMA_SUPPORTED)
+      PNG_gAMA;
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+      PNG_hIST;
+#endif
+#if defined(PNG_READ_iCCP_SUPPORTED)
+      PNG_iCCP;
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+      PNG_iTXt;
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+      PNG_oFFs;
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+      PNG_pCAL;
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+      PNG_pHYs;
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+      PNG_sBIT;
+#endif
+#if defined(PNG_READ_sCAL_SUPPORTED)
+      PNG_sCAL;
+#endif
+#if defined(PNG_READ_sPLT_SUPPORTED)
+      PNG_sPLT;
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      PNG_sRGB;
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      PNG_tEXt;
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+      PNG_tIME;
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+      PNG_tRNS;
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      PNG_zTXt;
+#endif
+#endif /* PNG_USE_LOCAL_ARRAYS */
+      png_byte chunk_length[4];
+      png_uint_32 length;
+
+      png_read_data(png_ptr, chunk_length, 4);
+      length = png_get_uint_31(png_ptr,chunk_length);
+
+      png_reset_crc(png_ptr);
+      png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+
+      png_debug2(0, "Reading %s chunk, length=%lu.\n", png_ptr->chunk_name,
+         length);
+
+      /* This should be a binary subdivision search or a hash for
+       * matching the chunk name rather than a linear search.
+       */
+      if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4))
+         png_handle_IHDR(png_ptr, info_ptr, length);
+      else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4))
+         png_handle_IEND(png_ptr, info_ptr, length);
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+      else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name))
+      {
+         if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+            png_ptr->mode |= PNG_HAVE_IDAT;
+         png_handle_unknown(png_ptr, info_ptr, length);
+         if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+            png_ptr->mode |= PNG_HAVE_PLTE;
+         else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+         {
+            if (!(png_ptr->mode & PNG_HAVE_IHDR))
+               png_error(png_ptr, "Missing IHDR before IDAT");
+            else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+                     !(png_ptr->mode & PNG_HAVE_PLTE))
+               png_error(png_ptr, "Missing PLTE before IDAT");
+            break;
+         }
+      }
+#endif
+      else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+         png_handle_PLTE(png_ptr, info_ptr, length);
+      else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+      {
+         if (!(png_ptr->mode & PNG_HAVE_IHDR))
+            png_error(png_ptr, "Missing IHDR before IDAT");
+         else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+                  !(png_ptr->mode & PNG_HAVE_PLTE))
+            png_error(png_ptr, "Missing PLTE before IDAT");
+
+         png_ptr->idat_size = length;
+         png_ptr->mode |= PNG_HAVE_IDAT;
+         break;
+      }
+#if defined(PNG_READ_bKGD_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4))
+         png_handle_bKGD(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4))
+         png_handle_cHRM(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_gAMA_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4))
+         png_handle_gAMA(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4))
+         png_handle_hIST(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4))
+         png_handle_oFFs(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4))
+         png_handle_pCAL(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sCAL_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4))
+         png_handle_sCAL(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4))
+         png_handle_pHYs(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4))
+         png_handle_sBIT(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4))
+         png_handle_sRGB(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_iCCP_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4))
+         png_handle_iCCP(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sPLT_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4))
+         png_handle_sPLT(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4))
+         png_handle_tEXt(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4))
+         png_handle_tIME(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4))
+         png_handle_tRNS(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4))
+         png_handle_zTXt(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4))
+         png_handle_iTXt(png_ptr, info_ptr, length);
+#endif
+      else
+         png_handle_unknown(png_ptr, info_ptr, length);
+   }
+}
+#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */
+
+/* optional call to update the users info_ptr structure */
+void PNGAPI
+png_read_update_info(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_read_update_info\n");
+   if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+      png_read_start_row(png_ptr);
+   else
+      png_warning(png_ptr,
+      "Ignoring extra png_read_update_info() call; row buffer not reallocated");
+   png_read_transform_info(png_ptr, info_ptr);
+}
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* Initialize palette, background, etc, after transformations
+ * are set, but before any reading takes place.  This allows
+ * the user to obtain a gamma-corrected palette, for example.
+ * If the user doesn't call this, we will do it ourselves.
+ */
+void PNGAPI
+png_start_read_image(png_structp png_ptr)
+{
+   png_debug(1, "in png_start_read_image\n");
+   if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+      png_read_start_row(png_ptr);
+}
+#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+void PNGAPI
+png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_IDAT;
+   const int png_pass_dsp_mask[7] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff};
+   const int png_pass_mask[7] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff};
+#endif
+   int ret;
+   png_debug2(1, "in png_read_row (row %lu, pass %d)\n",
+      png_ptr->row_number, png_ptr->pass);
+   if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+      png_read_start_row(png_ptr);
+   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
+   {
+   /* check for transforms that have been set but were defined out */
+#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_MONO)
+      png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED)
+   if (png_ptr->transformations & PNG_FILLER)
+      png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && !defined(PNG_READ_PACKSWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACKSWAP)
+      png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACK)
+      png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED)
+   if (png_ptr->transformations & PNG_SHIFT)
+      png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED)
+   if (png_ptr->transformations & PNG_BGR)
+      png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_BYTES)
+      png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined.");
+#endif
+   }
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+   /* if interlaced and we do not need a new row, combine row and return */
+   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+   {
+      switch (png_ptr->pass)
+      {
+         case 0:
+            if (png_ptr->row_number & 0x07)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 1:
+            if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 2:
+            if ((png_ptr->row_number & 0x07) != 4)
+            {
+               if (dsp_row != NULL && (png_ptr->row_number & 4))
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 3:
+            if ((png_ptr->row_number & 3) || png_ptr->width < 3)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 4:
+            if ((png_ptr->row_number & 3) != 2)
+            {
+               if (dsp_row != NULL && (png_ptr->row_number & 2))
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 5:
+            if ((png_ptr->row_number & 1) || png_ptr->width < 2)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 6:
+            if (!(png_ptr->row_number & 1))
+            {
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+      }
+   }
+#endif
+
+   if (!(png_ptr->mode & PNG_HAVE_IDAT))
+      png_error(png_ptr, "Invalid attempt to read row data");
+
+   png_ptr->zstream.next_out = png_ptr->row_buf;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes;
+   do
+   {
+      if (!(png_ptr->zstream.avail_in))
+      {
+         while (!png_ptr->idat_size)
+         {
+            png_byte chunk_length[4];
+
+            png_crc_finish(png_ptr, 0);
+
+            png_read_data(png_ptr, chunk_length, 4);
+            png_ptr->idat_size = png_get_uint_31(png_ptr,chunk_length);
+
+            png_reset_crc(png_ptr);
+            png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+            if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+               png_error(png_ptr, "Not enough image data");
+         }
+         png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size;
+         png_ptr->zstream.next_in = png_ptr->zbuf;
+         if (png_ptr->zbuf_size > png_ptr->idat_size)
+            png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size;
+         png_crc_read(png_ptr, png_ptr->zbuf,
+            (png_size_t)png_ptr->zstream.avail_in);
+         png_ptr->idat_size -= png_ptr->zstream.avail_in;
+      }
+      ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+      if (ret == Z_STREAM_END)
+      {
+         if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in ||
+            png_ptr->idat_size)
+            png_error(png_ptr, "Extra compressed data");
+         png_ptr->mode |= PNG_AFTER_IDAT;
+         png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+         break;
+      }
+      if (ret != Z_OK)
+         png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg :
+                   "Decompression error");
+
+   } while (png_ptr->zstream.avail_out);
+
+   png_ptr->row_info.color_type = png_ptr->color_type;
+   png_ptr->row_info.width = png_ptr->iwidth;
+   png_ptr->row_info.channels = png_ptr->channels;
+   png_ptr->row_info.bit_depth = png_ptr->bit_depth;
+   png_ptr->row_info.pixel_depth = png_ptr->pixel_depth;
+   png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
+       png_ptr->row_info.width);
+
+   if(png_ptr->row_buf[0])
+   png_read_filter_row(png_ptr, &(png_ptr->row_info),
+      png_ptr->row_buf + 1, png_ptr->prev_row + 1,
+      (int)(png_ptr->row_buf[0]));
+
+   png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf,
+      png_ptr->rowbytes + 1);
+   
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+      (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
+   {
+      /* Intrapixel differencing */
+      png_do_read_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   }
+#endif
+
+
+   if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA))
+      png_do_read_transformations(png_ptr);
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+   /* blow up interlaced rows to full size */
+   if (png_ptr->interlaced &&
+      (png_ptr->transformations & PNG_INTERLACE))
+   {
+      if (png_ptr->pass < 6)
+/*       old interface (pre-1.0.9):
+         png_do_read_interlace(&(png_ptr->row_info),
+            png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations);
+ */
+         png_do_read_interlace(png_ptr);
+
+      if (dsp_row != NULL)
+         png_combine_row(png_ptr, dsp_row,
+            png_pass_dsp_mask[png_ptr->pass]);
+      if (row != NULL)
+         png_combine_row(png_ptr, row,
+            png_pass_mask[png_ptr->pass]);
+   }
+   else
+#endif
+   {
+      if (row != NULL)
+         png_combine_row(png_ptr, row, 0xff);
+      if (dsp_row != NULL)
+         png_combine_row(png_ptr, dsp_row, 0xff);
+   }
+   png_read_finish_row(png_ptr);
+
+   if (png_ptr->read_row_fn != NULL)
+      (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
+}
+#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* Read one or more rows of image data.  If the image is interlaced,
+ * and png_set_interlace_handling() has been called, the rows need to
+ * contain the contents of the rows from the previous pass.  If the
+ * image has alpha or transparency, and png_handle_alpha()[*] has been
+ * called, the rows contents must be initialized to the contents of the
+ * screen.
+ *
+ * "row" holds the actual image, and pixels are placed in it
+ * as they arrive.  If the image is displayed after each pass, it will
+ * appear to "sparkle" in.  "display_row" can be used to display a
+ * "chunky" progressive image, with finer detail added as it becomes
+ * available.  If you do not want this "chunky" display, you may pass
+ * NULL for display_row.  If you do not want the sparkle display, and
+ * you have not called png_handle_alpha(), you may pass NULL for rows.
+ * If you have called png_handle_alpha(), and the image has either an
+ * alpha channel or a transparency chunk, you must provide a buffer for
+ * rows.  In this case, you do not have to provide a display_row buffer
+ * also, but you may.  If the image is not interlaced, or if you have
+ * not called png_set_interlace_handling(), the display_row buffer will
+ * be ignored, so pass NULL to it.
+ *
+ * [*] png_handle_alpha() does not exist yet, as of libpng version 1.2.8
+ */
+
+void PNGAPI
+png_read_rows(png_structp png_ptr, png_bytepp row,
+   png_bytepp display_row, png_uint_32 num_rows)
+{
+   png_uint_32 i;
+   png_bytepp rp;
+   png_bytepp dp;
+
+   png_debug(1, "in png_read_rows\n");
+   rp = row;
+   dp = display_row;
+   if (rp != NULL && dp != NULL)
+      for (i = 0; i < num_rows; i++)
+      {
+         png_bytep rptr = *rp++;
+         png_bytep dptr = *dp++;
+
+         png_read_row(png_ptr, rptr, dptr);
+      }
+   else if(rp != NULL)
+      for (i = 0; i < num_rows; i++)
+      {
+         png_bytep rptr = *rp;
+         png_read_row(png_ptr, rptr, png_bytep_NULL);
+         rp++;
+      }
+   else if(dp != NULL)
+      for (i = 0; i < num_rows; i++)
+      {
+         png_bytep dptr = *dp;
+         png_read_row(png_ptr, png_bytep_NULL, dptr);
+         dp++;
+      }
+}
+#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* Read the entire image.  If the image has an alpha channel or a tRNS
+ * chunk, and you have called png_handle_alpha()[*], you will need to
+ * initialize the image to the current image that PNG will be overlaying.
+ * We set the num_rows again here, in case it was incorrectly set in
+ * png_read_start_row() by a call to png_read_update_info() or
+ * png_start_read_image() if png_set_interlace_handling() wasn't called
+ * prior to either of these functions like it should have been.  You can
+ * only call this function once.  If you desire to have an image for
+ * each pass of a interlaced image, use png_read_rows() instead.
+ *
+ * [*] png_handle_alpha() does not exist yet, as of libpng version 1.2.8
+ */
+void PNGAPI
+png_read_image(png_structp png_ptr, png_bytepp image)
+{
+   png_uint_32 i,image_height;
+   int pass, j;
+   png_bytepp rp;
+
+   png_debug(1, "in png_read_image\n");
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+   pass = png_set_interlace_handling(png_ptr);
+#else
+   if (png_ptr->interlaced)
+      png_error(png_ptr,
+        "Cannot read interlaced image -- interlace handler disabled.");
+   pass = 1;
+#endif
+
+
+   image_height=png_ptr->height;
+   png_ptr->num_rows = image_height; /* Make sure this is set correctly */
+
+   for (j = 0; j < pass; j++)
+   {
+      rp = image;
+      for (i = 0; i < image_height; i++)
+      {
+         png_read_row(png_ptr, *rp, png_bytep_NULL);
+         rp++;
+      }
+   }
+}
+#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* Read the end of the PNG file.  Will not read past the end of the
+ * file, will verify the end is accurate, and will read any comments
+ * or time information at the end of the file, if info is not NULL.
+ */
+void PNGAPI
+png_read_end(png_structp png_ptr, png_infop info_ptr)
+{
+   png_byte chunk_length[4];
+   png_uint_32 length;
+
+   png_debug(1, "in png_read_end\n");
+   png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */
+
+   do
+   {
+#ifdef PNG_USE_LOCAL_ARRAYS
+      PNG_IHDR;
+      PNG_IDAT;
+      PNG_IEND;
+      PNG_PLTE;
+#if defined(PNG_READ_bKGD_SUPPORTED)
+      PNG_bKGD;
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+      PNG_cHRM;
+#endif
+#if defined(PNG_READ_gAMA_SUPPORTED)
+      PNG_gAMA;
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+      PNG_hIST;
+#endif
+#if defined(PNG_READ_iCCP_SUPPORTED)
+      PNG_iCCP;
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+      PNG_iTXt;
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+      PNG_oFFs;
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+      PNG_pCAL;
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+      PNG_pHYs;
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+      PNG_sBIT;
+#endif
+#if defined(PNG_READ_sCAL_SUPPORTED)
+      PNG_sCAL;
+#endif
+#if defined(PNG_READ_sPLT_SUPPORTED)
+      PNG_sPLT;
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      PNG_sRGB;
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      PNG_tEXt;
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+      PNG_tIME;
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+      PNG_tRNS;
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      PNG_zTXt;
+#endif
+#endif /* PNG_USE_LOCAL_ARRAYS */
+
+      png_read_data(png_ptr, chunk_length, 4);
+      length = png_get_uint_31(png_ptr,chunk_length);
+
+      png_reset_crc(png_ptr);
+      png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+
+      png_debug1(0, "Reading %s chunk.\n", png_ptr->chunk_name);
+
+      if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4))
+         png_handle_IHDR(png_ptr, info_ptr, length);
+      else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4))
+         png_handle_IEND(png_ptr, info_ptr, length);
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+      else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name))
+      {
+         if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+         {
+            if (length > 0 || png_ptr->mode & PNG_AFTER_IDAT)
+               png_error(png_ptr, "Too many IDAT's found");
+         }
+         else
+            png_ptr->mode |= PNG_AFTER_IDAT;
+         png_handle_unknown(png_ptr, info_ptr, length);
+         if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+            png_ptr->mode |= PNG_HAVE_PLTE;
+      }
+#endif
+      else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+      {
+         /* Zero length IDATs are legal after the last IDAT has been
+          * read, but not after other chunks have been read.
+          */
+         if (length > 0 || png_ptr->mode & PNG_AFTER_IDAT)
+            png_error(png_ptr, "Too many IDAT's found");
+         png_crc_finish(png_ptr, length);
+      }
+      else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+         png_handle_PLTE(png_ptr, info_ptr, length);
+#if defined(PNG_READ_bKGD_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4))
+         png_handle_bKGD(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4))
+         png_handle_cHRM(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_gAMA_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4))
+         png_handle_gAMA(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4))
+         png_handle_hIST(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4))
+         png_handle_oFFs(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4))
+         png_handle_pCAL(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sCAL_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4))
+         png_handle_sCAL(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4))
+         png_handle_pHYs(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4))
+         png_handle_sBIT(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4))
+         png_handle_sRGB(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_iCCP_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4))
+         png_handle_iCCP(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sPLT_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4))
+         png_handle_sPLT(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4))
+         png_handle_tEXt(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4))
+         png_handle_tIME(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4))
+         png_handle_tRNS(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4))
+         png_handle_zTXt(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4))
+         png_handle_iTXt(png_ptr, info_ptr, length);
+#endif
+      else
+         png_handle_unknown(png_ptr, info_ptr, length);
+   } while (!(png_ptr->mode & PNG_HAVE_IEND));
+}
+#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */
+
+/* free all memory used by the read */
+void PNGAPI
+png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr,
+   png_infopp end_info_ptr_ptr)
+{
+   png_structp png_ptr = NULL;
+   png_infop info_ptr = NULL, end_info_ptr = NULL;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_free_ptr free_fn;
+   png_voidp mem_ptr;
+#endif
+
+   png_debug(1, "in png_destroy_read_struct\n");
+   if (png_ptr_ptr != NULL)
+      png_ptr = *png_ptr_ptr;
+
+   if (info_ptr_ptr != NULL)
+      info_ptr = *info_ptr_ptr;
+
+   if (end_info_ptr_ptr != NULL)
+      end_info_ptr = *end_info_ptr_ptr;
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   free_fn = png_ptr->free_fn;
+   mem_ptr = png_ptr->mem_ptr;
+#endif
+
+   png_read_destroy(png_ptr, info_ptr, end_info_ptr);
+
+   if (info_ptr != NULL)
+   {
+#if defined(PNG_TEXT_SUPPORTED)
+      png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, -1);
+#endif
+
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn,
+          (png_voidp)mem_ptr);
+#else
+      png_destroy_struct((png_voidp)info_ptr);
+#endif
+      *info_ptr_ptr = NULL;
+   }
+
+   if (end_info_ptr != NULL)
+   {
+#if defined(PNG_READ_TEXT_SUPPORTED)
+      png_free_data(png_ptr, end_info_ptr, PNG_FREE_TEXT, -1);
+#endif
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)end_info_ptr, (png_free_ptr)free_fn,
+         (png_voidp)mem_ptr);
+#else
+      png_destroy_struct((png_voidp)end_info_ptr);
+#endif
+      *end_info_ptr_ptr = NULL;
+   }
+
+   if (png_ptr != NULL)
+   {
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn,
+          (png_voidp)mem_ptr);
+#else
+      png_destroy_struct((png_voidp)png_ptr);
+#endif
+      *png_ptr_ptr = NULL;
+   }
+}
+
+/* free all memory used by the read (old method) */
+void /* PRIVATE */
+png_read_destroy(png_structp png_ptr, png_infop info_ptr, png_infop end_info_ptr)
+{
+#ifdef PNG_SETJMP_SUPPORTED
+   jmp_buf tmp_jmp;
+#endif
+   png_error_ptr error_fn;
+   png_error_ptr warning_fn;
+   png_voidp error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_free_ptr free_fn;
+#endif
+
+   png_debug(1, "in png_read_destroy\n");
+   if (info_ptr != NULL)
+      png_info_destroy(png_ptr, info_ptr);
+
+   if (end_info_ptr != NULL)
+      png_info_destroy(png_ptr, end_info_ptr);
+
+   png_free(png_ptr, png_ptr->zbuf);
+   png_free(png_ptr, png_ptr->big_row_buf);
+   png_free(png_ptr, png_ptr->prev_row);
+#if defined(PNG_READ_DITHER_SUPPORTED)
+   png_free(png_ptr, png_ptr->palette_lookup);
+   png_free(png_ptr, png_ptr->dither_index);
+#endif
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   png_free(png_ptr, png_ptr->gamma_table);
+#endif
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   png_free(png_ptr, png_ptr->gamma_from_1);
+   png_free(png_ptr, png_ptr->gamma_to_1);
+#endif
+#ifdef PNG_FREE_ME_SUPPORTED
+   if (png_ptr->free_me & PNG_FREE_PLTE)
+      png_zfree(png_ptr, png_ptr->palette);
+   png_ptr->free_me &= ~PNG_FREE_PLTE;
+#else
+   if (png_ptr->flags & PNG_FLAG_FREE_PLTE)
+      png_zfree(png_ptr, png_ptr->palette);
+   png_ptr->flags &= ~PNG_FLAG_FREE_PLTE;
+#endif
+#if defined(PNG_tRNS_SUPPORTED) || \
+    defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+#ifdef PNG_FREE_ME_SUPPORTED
+   if (png_ptr->free_me & PNG_FREE_TRNS)
+      png_free(png_ptr, png_ptr->trans);
+   png_ptr->free_me &= ~PNG_FREE_TRNS;
+#else
+   if (png_ptr->flags & PNG_FLAG_FREE_TRNS)
+      png_free(png_ptr, png_ptr->trans);
+   png_ptr->flags &= ~PNG_FLAG_FREE_TRNS;
+#endif
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+#ifdef PNG_FREE_ME_SUPPORTED
+   if (png_ptr->free_me & PNG_FREE_HIST)
+      png_free(png_ptr, png_ptr->hist);
+   png_ptr->free_me &= ~PNG_FREE_HIST;
+#else
+   if (png_ptr->flags & PNG_FLAG_FREE_HIST)
+      png_free(png_ptr, png_ptr->hist);
+   png_ptr->flags &= ~PNG_FLAG_FREE_HIST;
+#endif
+#endif
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   if (png_ptr->gamma_16_table != NULL)
+   {
+      int i;
+      int istop = (1 << (8 - png_ptr->gamma_shift));
+      for (i = 0; i < istop; i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_table[i]);
+      }
+   png_free(png_ptr, png_ptr->gamma_16_table);
+   }
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if (png_ptr->gamma_16_from_1 != NULL)
+   {
+      int i;
+      int istop = (1 << (8 - png_ptr->gamma_shift));
+      for (i = 0; i < istop; i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_from_1[i]);
+      }
+   png_free(png_ptr, png_ptr->gamma_16_from_1);
+   }
+   if (png_ptr->gamma_16_to_1 != NULL)
+   {
+      int i;
+      int istop = (1 << (8 - png_ptr->gamma_shift));
+      for (i = 0; i < istop; i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_to_1[i]);
+      }
+   png_free(png_ptr, png_ptr->gamma_16_to_1);
+   }
+#endif
+#endif
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+   png_free(png_ptr, png_ptr->time_buffer);
+#endif
+
+   inflateEnd(&png_ptr->zstream);
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+   png_free(png_ptr, png_ptr->save_buffer);
+#endif
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+#ifdef PNG_TEXT_SUPPORTED
+   png_free(png_ptr, png_ptr->current_text);
+#endif /* PNG_TEXT_SUPPORTED */
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+   /* Save the important info out of the png_struct, in case it is
+    * being used again.
+    */
+#ifdef PNG_SETJMP_SUPPORTED
+   png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf));
+#endif
+
+   error_fn = png_ptr->error_fn;
+   warning_fn = png_ptr->warning_fn;
+   error_ptr = png_ptr->error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   free_fn = png_ptr->free_fn;
+#endif
+
+   png_memset(png_ptr, 0, png_sizeof (png_struct));
+
+   png_ptr->error_fn = error_fn;
+   png_ptr->warning_fn = warning_fn;
+   png_ptr->error_ptr = error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_ptr->free_fn = free_fn;
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+   png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf));
+#endif
+
+}
+
+void PNGAPI
+png_set_read_status_fn(png_structp png_ptr, png_read_status_ptr read_row_fn)
+{
+   png_ptr->read_row_fn = read_row_fn;
+}
+
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+void PNGAPI
+png_read_png(png_structp png_ptr, png_infop info_ptr,
+                           int transforms,
+                           voidp params)
+{
+   int row;
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
+   /* invert the alpha channel from opacity to transparency
+    */
+   if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
+       png_set_invert_alpha(png_ptr);
+#endif
+
+   /* png_read_info() gives us all of the information from the
+    * PNG file before the first IDAT (image data chunk).
+    */
+   png_read_info(png_ptr, info_ptr);
+   if (info_ptr->height > PNG_UINT_32_MAX/png_sizeof(png_bytep))
+      png_error(png_ptr,"Image is too high to process with png_read_png()");
+
+   /* -------------- image transformations start here ------------------- */
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+   /* tell libpng to strip 16 bit/color files down to 8 bits per color
+    */
+   if (transforms & PNG_TRANSFORM_STRIP_16)
+       png_set_strip_16(png_ptr);
+#endif
+
+#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+   /* Strip alpha bytes from the input data without combining with
+    * the background (not recommended).
+    */
+   if (transforms & PNG_TRANSFORM_STRIP_ALPHA)
+       png_set_strip_alpha(png_ptr);
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED)
+   /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single
+    * byte into separate bytes (useful for paletted and grayscale images).
+    */
+   if (transforms & PNG_TRANSFORM_PACKING)
+       png_set_packing(png_ptr);
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+   /* Change the order of packed pixels to least significant bit first
+    * (not useful if you are using png_set_packing).
+    */
+   if (transforms & PNG_TRANSFORM_PACKSWAP)
+       png_set_packswap(png_ptr);
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+   /* Expand paletted colors into true RGB triplets
+    * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel
+    * Expand paletted or RGB images with transparency to full alpha
+    * channels so the data will be available as RGBA quartets.
+    */
+   if (transforms & PNG_TRANSFORM_EXPAND)
+       if ((png_ptr->bit_depth < 8) ||
+           (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ||
+           (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)))
+         png_set_expand(png_ptr);
+#endif
+
+   /* We don't handle background color or gamma transformation or dithering.
+    */
+
+#if defined(PNG_READ_INVERT_SUPPORTED)
+   /* invert monochrome files to have 0 as white and 1 as black
+    */
+   if (transforms & PNG_TRANSFORM_INVERT_MONO)
+       png_set_invert_mono(png_ptr);
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED)
+   /* If you want to shift the pixel values from the range [0,255] or
+    * [0,65535] to the original [0,7] or [0,31], or whatever range the
+    * colors were originally in:
+    */
+   if ((transforms & PNG_TRANSFORM_SHIFT)
+       && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT))
+   {
+      png_color_8p sig_bit;
+
+      png_get_sBIT(png_ptr, info_ptr, &sig_bit);
+      png_set_shift(png_ptr, sig_bit);
+   }
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED)
+   /* flip the RGB pixels to BGR (or RGBA to BGRA)
+    */
+   if (transforms & PNG_TRANSFORM_BGR)
+       png_set_bgr(png_ptr);
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED)
+   /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR)
+    */
+   if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
+       png_set_swap_alpha(png_ptr);
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED)
+   /* swap bytes of 16 bit files to least significant byte first
+    */
+   if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
+       png_set_swap(png_ptr);
+#endif
+
+   /* We don't handle adding filler bytes */
+
+   /* Optional call to gamma correct and add the background to the palette
+    * and update info structure.  REQUIRED if you are expecting libpng to
+    * update the palette for you (i.e., you selected such a transform above).
+    */
+   png_read_update_info(png_ptr, info_ptr);
+
+   /* -------------- image transformations end here ------------------- */
+
+#ifdef PNG_FREE_ME_SUPPORTED
+   png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0);
+#endif
+   if(info_ptr->row_pointers == NULL)
+   {
+      info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr,
+         info_ptr->height * png_sizeof(png_bytep));
+#ifdef PNG_FREE_ME_SUPPORTED
+      info_ptr->free_me |= PNG_FREE_ROWS;
+#endif
+      for (row = 0; row < (int)info_ptr->height; row++)
+      {
+         info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr,
+            png_get_rowbytes(png_ptr, info_ptr));
+      }
+   }
+
+   png_read_image(png_ptr, info_ptr->row_pointers);
+   info_ptr->valid |= PNG_INFO_IDAT;
+
+   /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
+   png_read_end(png_ptr, info_ptr);
+
+   if(transforms == 0 || params == NULL)
+      /* quiet compiler warnings */ return;
+
+}
+#endif
+#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/png/libpng/pngrio.c b/Utilities/GDAL/frmts/png/libpng/pngrio.c
new file mode 100644
index 0000000000..cae501a5ca
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngrio.c
@@ -0,0 +1,161 @@
+
+/* pngrio.c - functions for data input
+ *
+ * libpng 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This file provides a location for all input.  Users who need
+ * special handling are expected to write a function that has the same
+ * arguments as this and performs a similar function, but that possibly
+ * has a different input method.  Note that you shouldn't change this
+ * function, but rather write a replacement function and then make
+ * libpng use it at run time with png_set_read_fn(...).
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* Read the data from whatever input you are using.  The default routine
+   reads from a file pointer.  Note that this routine sometimes gets called
+   with very small lengths, so you should implement some kind of simple
+   buffering if you are using unbuffered reads.  This should never be asked
+   to read more then 64K on a 16 bit machine. */
+void /* PRIVATE */
+png_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_debug1(4,"reading %d bytes\n", (int)length);
+   if (png_ptr->read_data_fn != NULL)
+      (*(png_ptr->read_data_fn))(png_ptr, data, length);
+   else
+      png_error(png_ptr, "Call to NULL read function");
+}
+
+#if !defined(PNG_NO_STDIO)
+/* This is the function that does the actual reading of data.  If you are
+   not reading from a standard C stream, you should create a replacement
+   read_data function and use it at run time with png_set_read_fn(), rather
+   than changing the library. */
+#ifndef USE_FAR_KEYWORD
+void PNGAPI
+png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_size_t check;
+
+   /* fread() returns 0 on error, so it is OK to store this in a png_size_t
+    * instead of an int, which is what fread() actually returns.
+    */
+#if defined(_WIN32_WCE)
+   if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) )
+      check = 0;
+#else
+   check = (png_size_t)fread(data, (png_size_t)1, length,
+      (png_FILE_p)png_ptr->io_ptr);
+#endif
+
+   if (check != length)
+      png_error(png_ptr, "Read Error");
+}
+#else
+/* this is the model-independent version. Since the standard I/O library
+   can't handle far buffers in the medium and small models, we have to copy
+   the data.
+*/
+
+#define NEAR_BUF_SIZE 1024
+#define MIN(a,b) (a <= b ? a : b)
+
+static void /* PRIVATE */
+png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   int check;
+   png_byte *n_data;
+   png_FILE_p io_ptr;
+
+   /* Check if data really is near. If so, use usual code. */
+   n_data = (png_byte *)CVT_PTR_NOCHECK(data);
+   io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr);
+   if ((png_bytep)n_data == data)
+   {
+#if defined(_WIN32_WCE)
+      if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) )
+         check = 0;
+#else
+      check = fread(n_data, 1, length, io_ptr);
+#endif
+   }
+   else
+   {
+      png_byte buf[NEAR_BUF_SIZE];
+      png_size_t read, remaining, err;
+      check = 0;
+      remaining = length;
+      do
+      {
+         read = MIN(NEAR_BUF_SIZE, remaining);
+#if defined(_WIN32_WCE)
+         if ( !ReadFile((HANDLE)(io_ptr), buf, read, &err, NULL) )
+            err = 0;
+#else
+         err = fread(buf, (png_size_t)1, read, io_ptr);
+#endif
+         png_memcpy(data, buf, read); /* copy far buffer to near buffer */
+         if(err != read)
+            break;
+         else
+            check += err;
+         data += read;
+         remaining -= read;
+      }
+      while (remaining != 0);
+   }
+   if ((png_uint_32)check != (png_uint_32)length)
+      png_error(png_ptr, "read Error");
+}
+#endif
+#endif
+
+/* This function allows the application to supply a new input function
+   for libpng if standard C streams aren't being used.
+
+   This function takes as its arguments:
+   png_ptr      - pointer to a png input data structure
+   io_ptr       - pointer to user supplied structure containing info about
+                  the input functions.  May be NULL.
+   read_data_fn - pointer to a new input function that takes as its
+                  arguments a pointer to a png_struct, a pointer to
+                  a location where input data can be stored, and a 32-bit
+                  unsigned int that is the number of bytes to be read.
+                  To exit and output any fatal error messages the new write
+                  function should call png_error(png_ptr, "Error msg"). */
+void PNGAPI
+png_set_read_fn(png_structp png_ptr, png_voidp io_ptr,
+   png_rw_ptr read_data_fn)
+{
+   png_ptr->io_ptr = io_ptr;
+
+#if !defined(PNG_NO_STDIO)
+   if (read_data_fn != NULL)
+      png_ptr->read_data_fn = read_data_fn;
+   else
+      png_ptr->read_data_fn = png_default_read_data;
+#else
+   png_ptr->read_data_fn = read_data_fn;
+#endif
+
+   /* It is an error to write to a read device */
+   if (png_ptr->write_data_fn != NULL)
+   {
+      png_ptr->write_data_fn = NULL;
+      png_warning(png_ptr,
+         "It's an error to set both read_data_fn and write_data_fn in the ");
+      png_warning(png_ptr,
+         "same structure.  Resetting write_data_fn to NULL.");
+   }
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+   png_ptr->output_flush_fn = NULL;
+#endif
+}
diff --git a/Utilities/GDAL/frmts/png/libpng/pngrtran.c b/Utilities/GDAL/frmts/png/libpng/pngrtran.c
new file mode 100644
index 0000000000..e1d6e3ce39
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngrtran.c
@@ -0,0 +1,4177 @@
+
+/* pngrtran.c - transforms the data in a row for PNG readers
+ *
+ * libpng version  1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This file contains functions optionally called by an application
+ * in order to tell libpng how to handle data when reading a PNG.
+ * Transformations that are used in both reading and writing are
+ * in pngtrans.c.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+/* Set the action on getting a CRC error for an ancillary or critical chunk. */
+void PNGAPI
+png_set_crc_action(png_structp png_ptr, int crit_action, int ancil_action)
+{
+   png_debug(1, "in png_set_crc_action\n");
+   /* Tell libpng how we react to CRC errors in critical chunks */
+   switch (crit_action)
+   {
+      case PNG_CRC_NO_CHANGE:                        /* leave setting as is */
+         break;
+      case PNG_CRC_WARN_USE:                               /* warn/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE;
+         break;
+      case PNG_CRC_QUIET_USE:                             /* quiet/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE |
+                           PNG_FLAG_CRC_CRITICAL_IGNORE;
+         break;
+      case PNG_CRC_WARN_DISCARD:    /* not a valid action for critical data */
+         png_warning(png_ptr, "Can't discard critical data on CRC error.");
+      case PNG_CRC_ERROR_QUIT:                                /* error/quit */
+      case PNG_CRC_DEFAULT:
+      default:
+         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+         break;
+   }
+
+   switch (ancil_action)
+   {
+      case PNG_CRC_NO_CHANGE:                       /* leave setting as is */
+         break;
+      case PNG_CRC_WARN_USE:                              /* warn/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE;
+         break;
+      case PNG_CRC_QUIET_USE:                            /* quiet/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE |
+                           PNG_FLAG_CRC_ANCILLARY_NOWARN;
+         break;
+      case PNG_CRC_ERROR_QUIT:                               /* error/quit */
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN;
+         break;
+      case PNG_CRC_WARN_DISCARD:                      /* warn/discard data */
+      case PNG_CRC_DEFAULT:
+      default:
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         break;
+   }
+}
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \
+    defined(PNG_FLOATING_POINT_SUPPORTED)
+/* handle alpha and tRNS via a background color */
+void PNGAPI
+png_set_background(png_structp png_ptr,
+   png_color_16p background_color, int background_gamma_code,
+   int need_expand, double background_gamma)
+{
+   png_debug(1, "in png_set_background\n");
+   if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN)
+   {
+      png_warning(png_ptr, "Application must supply a known background gamma");
+      return;
+   }
+
+   png_ptr->transformations |= PNG_BACKGROUND;
+   png_memcpy(&(png_ptr->background), background_color,
+      png_sizeof(png_color_16));
+   png_ptr->background_gamma = (float)background_gamma;
+   png_ptr->background_gamma_type = (png_byte)(background_gamma_code);
+   png_ptr->transformations |= (need_expand ? PNG_BACKGROUND_EXPAND : 0);
+
+   /* Note:  if need_expand is set and color_type is either RGB or RGB_ALPHA
+    * (in which case need_expand is superfluous anyway), the background color
+    * might actually be gray yet not be flagged as such. This is not a problem
+    * for the current code, which uses PNG_BACKGROUND_IS_GRAY only to
+    * decide when to do the png_do_gray_to_rgb() transformation.
+    */
+   if ((need_expand && !(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) ||
+       (!need_expand && background_color->red == background_color->green &&
+        background_color->red == background_color->blue))
+      png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
+}
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+/* strip 16 bit depth files to 8 bit depth */
+void PNGAPI
+png_set_strip_16(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_strip_16\n");
+   png_ptr->transformations |= PNG_16_TO_8;
+}
+#endif
+
+#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+void PNGAPI
+png_set_strip_alpha(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_strip_alpha\n");
+   png_ptr->flags |= PNG_FLAG_STRIP_ALPHA;
+}
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+/* Dither file to 8 bit.  Supply a palette, the current number
+ * of elements in the palette, the maximum number of elements
+ * allowed, and a histogram if possible.  If the current number
+ * of colors is greater then the maximum number, the palette will be
+ * modified to fit in the maximum number.  "full_dither" indicates
+ * whether we need a dithering cube set up for RGB images, or if we
+ * simply are reducing the number of colors in a paletted image.
+ */
+
+typedef struct png_dsort_struct
+{
+   struct png_dsort_struct FAR * next;
+   png_byte left;
+   png_byte right;
+} png_dsort;
+typedef png_dsort FAR *       png_dsortp;
+typedef png_dsort FAR * FAR * png_dsortpp;
+
+void PNGAPI
+png_set_dither(png_structp png_ptr, png_colorp palette,
+   int num_palette, int maximum_colors, png_uint_16p histogram,
+   int full_dither)
+{
+   png_debug(1, "in png_set_dither\n");
+   png_ptr->transformations |= PNG_DITHER;
+
+   if (!full_dither)
+   {
+      int i;
+
+      png_ptr->dither_index = (png_bytep)png_malloc(png_ptr,
+         (png_uint_32)(num_palette * png_sizeof (png_byte)));
+      for (i = 0; i < num_palette; i++)
+         png_ptr->dither_index[i] = (png_byte)i;
+   }
+
+   if (num_palette > maximum_colors)
+   {
+      if (histogram != NULL)
+      {
+         /* This is easy enough, just throw out the least used colors.
+            Perhaps not the best solution, but good enough. */
+
+         int i;
+
+         /* initialize an array to sort colors */
+         png_ptr->dither_sort = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)(num_palette * png_sizeof (png_byte)));
+
+         /* initialize the dither_sort array */
+         for (i = 0; i < num_palette; i++)
+            png_ptr->dither_sort[i] = (png_byte)i;
+
+         /* Find the least used palette entries by starting a
+            bubble sort, and running it until we have sorted
+            out enough colors.  Note that we don't care about
+            sorting all the colors, just finding which are
+            least used. */
+
+         for (i = num_palette - 1; i >= maximum_colors; i--)
+         {
+            int done; /* to stop early if the list is pre-sorted */
+            int j;
+
+            done = 1;
+            for (j = 0; j < i; j++)
+            {
+               if (histogram[png_ptr->dither_sort[j]]
+                   < histogram[png_ptr->dither_sort[j + 1]])
+               {
+                  png_byte t;
+
+                  t = png_ptr->dither_sort[j];
+                  png_ptr->dither_sort[j] = png_ptr->dither_sort[j + 1];
+                  png_ptr->dither_sort[j + 1] = t;
+                  done = 0;
+               }
+            }
+            if (done)
+               break;
+         }
+
+         /* swap the palette around, and set up a table, if necessary */
+         if (full_dither)
+         {
+            int j = num_palette;
+
+            /* put all the useful colors within the max, but don't
+               move the others */
+            for (i = 0; i < maximum_colors; i++)
+            {
+               if ((int)png_ptr->dither_sort[i] >= maximum_colors)
+               {
+                  do
+                     j--;
+                  while ((int)png_ptr->dither_sort[j] >= maximum_colors);
+                  palette[i] = palette[j];
+               }
+            }
+         }
+         else
+         {
+            int j = num_palette;
+
+            /* move all the used colors inside the max limit, and
+               develop a translation table */
+            for (i = 0; i < maximum_colors; i++)
+            {
+               /* only move the colors we need to */
+               if ((int)png_ptr->dither_sort[i] >= maximum_colors)
+               {
+                  png_color tmp_color;
+
+                  do
+                     j--;
+                  while ((int)png_ptr->dither_sort[j] >= maximum_colors);
+
+                  tmp_color = palette[j];
+                  palette[j] = palette[i];
+                  palette[i] = tmp_color;
+                  /* indicate where the color went */
+                  png_ptr->dither_index[j] = (png_byte)i;
+                  png_ptr->dither_index[i] = (png_byte)j;
+               }
+            }
+
+            /* find closest color for those colors we are not using */
+            for (i = 0; i < num_palette; i++)
+            {
+               if ((int)png_ptr->dither_index[i] >= maximum_colors)
+               {
+                  int min_d, k, min_k, d_index;
+
+                  /* find the closest color to one we threw out */
+                  d_index = png_ptr->dither_index[i];
+                  min_d = PNG_COLOR_DIST(palette[d_index], palette[0]);
+                  for (k = 1, min_k = 0; k < maximum_colors; k++)
+                  {
+                     int d;
+
+                     d = PNG_COLOR_DIST(palette[d_index], palette[k]);
+
+                     if (d < min_d)
+                     {
+                        min_d = d;
+                        min_k = k;
+                     }
+                  }
+                  /* point to closest color */
+                  png_ptr->dither_index[i] = (png_byte)min_k;
+               }
+            }
+         }
+         png_free(png_ptr, png_ptr->dither_sort);
+         png_ptr->dither_sort=NULL;
+      }
+      else
+      {
+         /* This is much harder to do simply (and quickly).  Perhaps
+            we need to go through a median cut routine, but those
+            don't always behave themselves with only a few colors
+            as input.  So we will just find the closest two colors,
+            and throw out one of them (chosen somewhat randomly).
+            [We don't understand this at all, so if someone wants to
+             work on improving it, be our guest - AED, GRP]
+            */
+         int i;
+         int max_d;
+         int num_new_palette;
+         png_dsortp t;
+         png_dsortpp hash;
+
+         t=NULL;
+
+         /* initialize palette index arrays */
+         png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)(num_palette * png_sizeof (png_byte)));
+         png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)(num_palette * png_sizeof (png_byte)));
+
+         /* initialize the sort array */
+         for (i = 0; i < num_palette; i++)
+         {
+            png_ptr->index_to_palette[i] = (png_byte)i;
+            png_ptr->palette_to_index[i] = (png_byte)i;
+         }
+
+         hash = (png_dsortpp)png_malloc(png_ptr, (png_uint_32)(769 *
+            png_sizeof (png_dsortp)));
+         for (i = 0; i < 769; i++)
+            hash[i] = NULL;
+/*         png_memset(hash, 0, 769 * png_sizeof (png_dsortp)); */
+
+         num_new_palette = num_palette;
+
+         /* initial wild guess at how far apart the farthest pixel
+            pair we will be eliminating will be.  Larger
+            numbers mean more areas will be allocated, Smaller
+            numbers run the risk of not saving enough data, and
+            having to do this all over again.
+
+            I have not done extensive checking on this number.
+            */
+         max_d = 96;
+
+         while (num_new_palette > maximum_colors)
+         {
+            for (i = 0; i < num_new_palette - 1; i++)
+            {
+               int j;
+
+               for (j = i + 1; j < num_new_palette; j++)
+               {
+                  int d;
+
+                  d = PNG_COLOR_DIST(palette[i], palette[j]);
+
+                  if (d <= max_d)
+                  {
+
+                     t = (png_dsortp)png_malloc_warn(png_ptr,
+                         (png_uint_32)(png_sizeof(png_dsort)));
+                     if (t == NULL)
+                         break;
+                     t->next = hash[d];
+                     t->left = (png_byte)i;
+                     t->right = (png_byte)j;
+                     hash[d] = t;
+                  }
+               }
+               if (t == NULL)
+                  break;
+            }
+
+            if (t != NULL)
+            for (i = 0; i <= max_d; i++)
+            {
+               if (hash[i] != NULL)
+               {
+                  png_dsortp p;
+
+                  for (p = hash[i]; p; p = p->next)
+                  {
+                     if ((int)png_ptr->index_to_palette[p->left]
+                        < num_new_palette &&
+                        (int)png_ptr->index_to_palette[p->right]
+                        < num_new_palette)
+                     {
+                        int j, next_j;
+
+                        if (num_new_palette & 0x01)
+                        {
+                           j = p->left;
+                           next_j = p->right;
+                        }
+                        else
+                        {
+                           j = p->right;
+                           next_j = p->left;
+                        }
+
+                        num_new_palette--;
+                        palette[png_ptr->index_to_palette[j]]
+                          = palette[num_new_palette];
+                        if (!full_dither)
+                        {
+                           int k;
+
+                           for (k = 0; k < num_palette; k++)
+                           {
+                              if (png_ptr->dither_index[k] ==
+                                 png_ptr->index_to_palette[j])
+                                 png_ptr->dither_index[k] =
+                                    png_ptr->index_to_palette[next_j];
+                              if ((int)png_ptr->dither_index[k] ==
+                                 num_new_palette)
+                                 png_ptr->dither_index[k] =
+                                    png_ptr->index_to_palette[j];
+                           }
+                        }
+
+                        png_ptr->index_to_palette[png_ptr->palette_to_index
+                           [num_new_palette]] = png_ptr->index_to_palette[j];
+                        png_ptr->palette_to_index[png_ptr->index_to_palette[j]]
+                           = png_ptr->palette_to_index[num_new_palette];
+
+                        png_ptr->index_to_palette[j] = (png_byte)num_new_palette;
+                        png_ptr->palette_to_index[num_new_palette] = (png_byte)j;
+                     }
+                     if (num_new_palette <= maximum_colors)
+                        break;
+                  }
+                  if (num_new_palette <= maximum_colors)
+                     break;
+               }
+            }
+
+            for (i = 0; i < 769; i++)
+            {
+               if (hash[i] != NULL)
+               {
+                  png_dsortp p = hash[i];
+                  while (p)
+                  {
+                     t = p->next;
+                     png_free(png_ptr, p);
+                     p = t;
+                  }
+               }
+               hash[i] = 0;
+            }
+            max_d += 96;
+         }
+         png_free(png_ptr, hash);
+         png_free(png_ptr, png_ptr->palette_to_index);
+         png_free(png_ptr, png_ptr->index_to_palette);
+         png_ptr->palette_to_index=NULL;
+         png_ptr->index_to_palette=NULL;
+      }
+      num_palette = maximum_colors;
+   }
+   if (png_ptr->palette == NULL)
+   {
+      png_ptr->palette = palette;
+   }
+   png_ptr->num_palette = (png_uint_16)num_palette;
+
+   if (full_dither)
+   {
+      int i;
+      png_bytep distance;
+      int total_bits = PNG_DITHER_RED_BITS + PNG_DITHER_GREEN_BITS +
+         PNG_DITHER_BLUE_BITS;
+      int num_red = (1 << PNG_DITHER_RED_BITS);
+      int num_green = (1 << PNG_DITHER_GREEN_BITS);
+      int num_blue = (1 << PNG_DITHER_BLUE_BITS);
+      png_size_t num_entries = ((png_size_t)1 << total_bits);
+
+      png_ptr->palette_lookup = (png_bytep )png_malloc(png_ptr,
+         (png_uint_32)(num_entries * png_sizeof (png_byte)));
+
+      png_memset(png_ptr->palette_lookup, 0, num_entries *
+         png_sizeof (png_byte));
+
+      distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries *
+         png_sizeof(png_byte)));
+
+      png_memset(distance, 0xff, num_entries * png_sizeof(png_byte));
+
+      for (i = 0; i < num_palette; i++)
+      {
+         int ir, ig, ib;
+         int r = (palette[i].red >> (8 - PNG_DITHER_RED_BITS));
+         int g = (palette[i].green >> (8 - PNG_DITHER_GREEN_BITS));
+         int b = (palette[i].blue >> (8 - PNG_DITHER_BLUE_BITS));
+
+         for (ir = 0; ir < num_red; ir++)
+         {
+            /* int dr = abs(ir - r); */
+            int dr = ((ir > r) ? ir - r : r - ir);
+            int index_r = (ir << (PNG_DITHER_BLUE_BITS + PNG_DITHER_GREEN_BITS));
+
+            for (ig = 0; ig < num_green; ig++)
+            {
+               /* int dg = abs(ig - g); */
+               int dg = ((ig > g) ? ig - g : g - ig);
+               int dt = dr + dg;
+               int dm = ((dr > dg) ? dr : dg);
+               int index_g = index_r | (ig << PNG_DITHER_BLUE_BITS);
+
+               for (ib = 0; ib < num_blue; ib++)
+               {
+                  int d_index = index_g | ib;
+                  /* int db = abs(ib - b); */
+                  int db = ((ib > b) ? ib - b : b - ib);
+                  int dmax = ((dm > db) ? dm : db);
+                  int d = dmax + dt + db;
+
+                  if (d < (int)distance[d_index])
+                  {
+                     distance[d_index] = (png_byte)d;
+                     png_ptr->palette_lookup[d_index] = (png_byte)i;
+                  }
+               }
+            }
+         }
+      }
+
+      png_free(png_ptr, distance);
+   }
+}
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED)
+/* Transform the image from the file_gamma to the screen_gamma.  We
+ * only do transformations on images where the file_gamma and screen_gamma
+ * are not close reciprocals, otherwise it slows things down slightly, and
+ * also needlessly introduces small errors.
+ *
+ * We will turn off gamma transformation later if no semitransparent entries
+ * are present in the tRNS array for palette images.  We can't do it here
+ * because we don't necessarily have the tRNS chunk yet.
+ */
+void PNGAPI
+png_set_gamma(png_structp png_ptr, double scrn_gamma, double file_gamma)
+{
+   png_debug(1, "in png_set_gamma\n");
+   if ((fabs(scrn_gamma * file_gamma - 1.0) > PNG_GAMMA_THRESHOLD) ||
+       (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) ||
+       (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE))
+     png_ptr->transformations |= PNG_GAMMA;
+   png_ptr->gamma = (float)file_gamma;
+   png_ptr->screen_gamma = (float)scrn_gamma;
+}
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+/* Expand paletted images to RGB, expand grayscale images of
+ * less than 8-bit depth to 8-bit depth, and expand tRNS chunks
+ * to alpha channels.
+ */
+void PNGAPI
+png_set_expand(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_expand\n");
+   png_ptr->transformations |= PNG_EXPAND;
+}
+
+/* GRR 19990627:  the following three functions currently are identical
+ *  to png_set_expand().  However, it is entirely reasonable that someone
+ *  might wish to expand an indexed image to RGB but *not* expand a single,
+ *  fully transparent palette entry to a full alpha channel--perhaps instead
+ *  convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace
+ *  the transparent color with a particular RGB value, or drop tRNS entirely.
+ *  IOW, a future version of the library may make the transformations flag
+ *  a bit more fine-grained, with separate bits for each of these three
+ *  functions.
+ *
+ *  More to the point, these functions make it obvious what libpng will be
+ *  doing, whereas "expand" can (and does) mean any number of things.
+ */
+
+/* Expand paletted images to RGB. */
+void PNGAPI
+png_set_palette_to_rgb(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_expand\n");
+   png_ptr->transformations |= PNG_EXPAND;
+}
+
+/* Expand grayscale images of less than 8-bit depth to 8 bits. */
+void PNGAPI
+png_set_gray_1_2_4_to_8(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_expand\n");
+   png_ptr->transformations |= PNG_EXPAND;
+}
+
+/* Expand tRNS chunks to alpha channels. */
+void PNGAPI
+png_set_tRNS_to_alpha(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_expand\n");
+   png_ptr->transformations |= PNG_EXPAND;
+}
+#endif /* defined(PNG_READ_EXPAND_SUPPORTED) */
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+void PNGAPI
+png_set_gray_to_rgb(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_gray_to_rgb\n");
+   png_ptr->transformations |= PNG_GRAY_TO_RGB;
+}
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+#if defined(PNG_FLOATING_POINT_SUPPORTED)
+/* Convert a RGB image to a grayscale of the same width.  This allows us,
+ * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image.
+ */
+
+void PNGAPI
+png_set_rgb_to_gray(png_structp png_ptr, int error_action, double red,
+   double green)
+{
+      int red_fixed = (int)((float)red*100000.0 + 0.5);
+      int green_fixed = (int)((float)green*100000.0 + 0.5);
+      png_set_rgb_to_gray_fixed(png_ptr, error_action, red_fixed, green_fixed);
+}
+#endif
+
+void PNGAPI
+png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action,
+   png_fixed_point red, png_fixed_point green)
+{
+   png_debug(1, "in png_set_rgb_to_gray\n");
+   switch(error_action)
+   {
+      case 1: png_ptr->transformations |= PNG_RGB_TO_GRAY;
+              break;
+      case 2: png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN;
+              break;
+      case 3: png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR;
+   }
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+      png_ptr->transformations |= PNG_EXPAND;
+#else
+   {
+      png_warning(png_ptr, "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED.");
+      png_ptr->transformations &= ~PNG_RGB_TO_GRAY;
+   }
+#endif
+   {
+      png_uint_16 red_int, green_int;
+      if(red < 0 || green < 0)
+      {
+         red_int   =  6968; /* .212671 * 32768 + .5 */
+         green_int = 23434; /* .715160 * 32768 + .5 */
+      }
+      else if(red + green < 100000L)
+      {
+        red_int = (png_uint_16)(((png_uint_32)red*32768L)/100000L);
+        green_int = (png_uint_16)(((png_uint_32)green*32768L)/100000L);
+      }
+      else
+      {
+         png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients");
+         red_int   =  6968;
+         green_int = 23434;
+      }
+      png_ptr->rgb_to_gray_red_coeff   = red_int;
+      png_ptr->rgb_to_gray_green_coeff = green_int;
+      png_ptr->rgb_to_gray_blue_coeff  = (png_uint_16)(32768-red_int-green_int);
+   }
+}
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_LEGACY_SUPPORTED)
+void PNGAPI
+png_set_read_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
+   read_user_transform_fn)
+{
+   png_debug(1, "in png_set_read_user_transform_fn\n");
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
+   png_ptr->transformations |= PNG_USER_TRANSFORM;
+   png_ptr->read_user_transform_fn = read_user_transform_fn;
+#endif
+#ifdef PNG_LEGACY_SUPPORTED
+   if(read_user_transform_fn)
+      png_warning(png_ptr,
+        "This version of libpng does not support user transforms");
+#endif
+}
+#endif
+
+/* Initialize everything needed for the read.  This includes modifying
+ * the palette.
+ */
+void /* PRIVATE */
+png_init_read_transformations(png_structp png_ptr)
+{
+   png_debug(1, "in png_init_read_transformations\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if(png_ptr != NULL)
+#endif
+  {
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || defined(PNG_READ_SHIFT_SUPPORTED) \
+ || defined(PNG_READ_GAMMA_SUPPORTED)
+   int color_type = png_ptr->color_type;
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&
+       (png_ptr->transformations & PNG_EXPAND))
+   {
+      if (!(color_type & PNG_COLOR_MASK_COLOR))  /* i.e., GRAY or GRAY_ALPHA */
+      {
+         /* expand background chunk. */
+         switch (png_ptr->bit_depth)
+         {
+            case 1:
+               png_ptr->background.gray *= (png_uint_16)0xff;
+               png_ptr->background.red = png_ptr->background.green
+                 =  png_ptr->background.blue = png_ptr->background.gray;
+               break;
+            case 2:
+               png_ptr->background.gray *= (png_uint_16)0x55;
+               png_ptr->background.red = png_ptr->background.green
+                 = png_ptr->background.blue = png_ptr->background.gray;
+               break;
+            case 4:
+               png_ptr->background.gray *= (png_uint_16)0x11;
+               png_ptr->background.red = png_ptr->background.green
+                 = png_ptr->background.blue = png_ptr->background.gray;
+               break;
+            case 8:
+            case 16:
+               png_ptr->background.red = png_ptr->background.green
+                 = png_ptr->background.blue = png_ptr->background.gray;
+               break;
+         }
+      }
+      else if (color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_ptr->background.red   =
+            png_ptr->palette[png_ptr->background.index].red;
+         png_ptr->background.green =
+            png_ptr->palette[png_ptr->background.index].green;
+         png_ptr->background.blue  =
+            png_ptr->palette[png_ptr->background.index].blue;
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
+        if (png_ptr->transformations & PNG_INVERT_ALPHA)
+        {
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+           if (!(png_ptr->transformations & PNG_EXPAND))
+#endif
+           {
+           /* invert the alpha channel (in tRNS) unless the pixels are
+              going to be expanded, in which case leave it for later */
+              int i,istop;
+              istop=(int)png_ptr->num_trans;
+              for (i=0; i<istop; i++)
+                 png_ptr->trans[i] = (png_byte)(255 - png_ptr->trans[i]);
+           }
+        }
+#endif
+
+      }
+   }
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED)
+   png_ptr->background_1 = png_ptr->background;
+#endif
+#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED)
+
+   if ((color_type == PNG_COLOR_TYPE_PALETTE && png_ptr->num_trans != 0)
+       && (fabs(png_ptr->screen_gamma * png_ptr->gamma - 1.0)
+         < PNG_GAMMA_THRESHOLD))
+   {
+    int i,k;
+    k=0;
+    for (i=0; i<png_ptr->num_trans; i++)
+    {
+      if (png_ptr->trans[i] != 0 && png_ptr->trans[i] != 0xff)
+        k=1; /* partial transparency is present */
+    }
+    if (k == 0)
+      png_ptr->transformations &= (~PNG_GAMMA);
+   }
+
+   if (png_ptr->transformations & (PNG_GAMMA | PNG_RGB_TO_GRAY))
+   {
+      png_build_gamma_table(png_ptr);
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+      if (png_ptr->transformations & PNG_BACKGROUND)
+      {
+         if (color_type == PNG_COLOR_TYPE_PALETTE)
+         {
+           /* could skip if no transparency and 
+           */
+            png_color back, back_1;
+            png_colorp palette = png_ptr->palette;
+            int num_palette = png_ptr->num_palette;
+            int i;
+            if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE)
+            {
+               back.red = png_ptr->gamma_table[png_ptr->background.red];
+               back.green = png_ptr->gamma_table[png_ptr->background.green];
+               back.blue = png_ptr->gamma_table[png_ptr->background.blue];
+
+               back_1.red = png_ptr->gamma_to_1[png_ptr->background.red];
+               back_1.green = png_ptr->gamma_to_1[png_ptr->background.green];
+               back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue];
+            }
+            else
+            {
+               double g, gs;
+
+               switch (png_ptr->background_gamma_type)
+               {
+                  case PNG_BACKGROUND_GAMMA_SCREEN:
+                     g = (png_ptr->screen_gamma);
+                     gs = 1.0;
+                     break;
+                  case PNG_BACKGROUND_GAMMA_FILE:
+                     g = 1.0 / (png_ptr->gamma);
+                     gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma);
+                     break;
+                  case PNG_BACKGROUND_GAMMA_UNIQUE:
+                     g = 1.0 / (png_ptr->background_gamma);
+                     gs = 1.0 / (png_ptr->background_gamma *
+                                 png_ptr->screen_gamma);
+                     break;
+                  default:
+                     g = 1.0;    /* back_1 */
+                     gs = 1.0;   /* back */
+               }
+
+               if ( fabs(gs - 1.0) < PNG_GAMMA_THRESHOLD)
+               {
+                  back.red   = (png_byte)png_ptr->background.red;
+                  back.green = (png_byte)png_ptr->background.green;
+                  back.blue  = (png_byte)png_ptr->background.blue;
+               }
+               else
+               {
+                  back.red = (png_byte)(pow(
+                     (double)png_ptr->background.red/255, gs) * 255.0 + .5);
+                  back.green = (png_byte)(pow(
+                     (double)png_ptr->background.green/255, gs) * 255.0 + .5);
+                  back.blue = (png_byte)(pow(
+                     (double)png_ptr->background.blue/255, gs) * 255.0 + .5);
+               }
+
+               back_1.red = (png_byte)(pow(
+                  (double)png_ptr->background.red/255, g) * 255.0 + .5);
+               back_1.green = (png_byte)(pow(
+                  (double)png_ptr->background.green/255, g) * 255.0 + .5);
+               back_1.blue = (png_byte)(pow(
+                  (double)png_ptr->background.blue/255, g) * 255.0 + .5);
+            }
+            for (i = 0; i < num_palette; i++)
+            {
+               if (i < (int)png_ptr->num_trans && png_ptr->trans[i] != 0xff)
+               {
+                  if (png_ptr->trans[i] == 0)
+                  {
+                     palette[i] = back;
+                  }
+                  else /* if (png_ptr->trans[i] != 0xff) */
+                  {
+                     png_byte v, w;
+
+                     v = png_ptr->gamma_to_1[palette[i].red];
+                     png_composite(w, v, png_ptr->trans[i], back_1.red);
+                     palette[i].red = png_ptr->gamma_from_1[w];
+
+                     v = png_ptr->gamma_to_1[palette[i].green];
+                     png_composite(w, v, png_ptr->trans[i], back_1.green);
+                     palette[i].green = png_ptr->gamma_from_1[w];
+
+                     v = png_ptr->gamma_to_1[palette[i].blue];
+                     png_composite(w, v, png_ptr->trans[i], back_1.blue);
+                     palette[i].blue = png_ptr->gamma_from_1[w];
+                  }
+               }
+               else
+               {
+                  palette[i].red = png_ptr->gamma_table[palette[i].red];
+                  palette[i].green = png_ptr->gamma_table[palette[i].green];
+                  palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+               }
+            }
+         }
+         /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */
+         else
+         /* color_type != PNG_COLOR_TYPE_PALETTE */
+         {
+            double m = (double)(((png_uint_32)1 << png_ptr->bit_depth) - 1);
+            double g = 1.0;
+            double gs = 1.0;
+
+            switch (png_ptr->background_gamma_type)
+            {
+               case PNG_BACKGROUND_GAMMA_SCREEN:
+                  g = (png_ptr->screen_gamma);
+                  gs = 1.0;
+                  break;
+               case PNG_BACKGROUND_GAMMA_FILE:
+                  g = 1.0 / (png_ptr->gamma);
+                  gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma);
+                  break;
+               case PNG_BACKGROUND_GAMMA_UNIQUE:
+                  g = 1.0 / (png_ptr->background_gamma);
+                  gs = 1.0 / (png_ptr->background_gamma *
+                     png_ptr->screen_gamma);
+                  break;
+            }
+
+            png_ptr->background_1.gray = (png_uint_16)(pow(
+               (double)png_ptr->background.gray / m, g) * m + .5);
+            png_ptr->background.gray = (png_uint_16)(pow(
+               (double)png_ptr->background.gray / m, gs) * m + .5);
+
+            if ((png_ptr->background.red != png_ptr->background.green) ||
+                (png_ptr->background.red != png_ptr->background.blue) ||
+                (png_ptr->background.red != png_ptr->background.gray))
+            {
+               /* RGB or RGBA with color background */
+               png_ptr->background_1.red = (png_uint_16)(pow(
+                  (double)png_ptr->background.red / m, g) * m + .5);
+               png_ptr->background_1.green = (png_uint_16)(pow(
+                  (double)png_ptr->background.green / m, g) * m + .5);
+               png_ptr->background_1.blue = (png_uint_16)(pow(
+                  (double)png_ptr->background.blue / m, g) * m + .5);
+               png_ptr->background.red = (png_uint_16)(pow(
+                  (double)png_ptr->background.red / m, gs) * m + .5);
+               png_ptr->background.green = (png_uint_16)(pow(
+                  (double)png_ptr->background.green / m, gs) * m + .5);
+               png_ptr->background.blue = (png_uint_16)(pow(
+                  (double)png_ptr->background.blue / m, gs) * m + .5);
+            }
+            else
+            {
+               /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */
+               png_ptr->background_1.red = png_ptr->background_1.green
+                 = png_ptr->background_1.blue = png_ptr->background_1.gray;
+               png_ptr->background.red = png_ptr->background.green
+                 = png_ptr->background.blue = png_ptr->background.gray;
+            }
+         }
+      }
+      else
+      /* transformation does not include PNG_BACKGROUND */
+#endif /* PNG_READ_BACKGROUND_SUPPORTED */
+      if (color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_colorp palette = png_ptr->palette;
+         int num_palette = png_ptr->num_palette;
+         int i;
+
+         for (i = 0; i < num_palette; i++)
+         {
+            palette[i].red = png_ptr->gamma_table[palette[i].red];
+            palette[i].green = png_ptr->gamma_table[palette[i].green];
+            palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+         }
+      }
+   }
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   else
+#endif
+#endif /* PNG_READ_GAMMA_SUPPORTED && PNG_FLOATING_POINT_SUPPORTED */
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   /* No GAMMA transformation */
+   if ((png_ptr->transformations & PNG_BACKGROUND) &&
+       (color_type == PNG_COLOR_TYPE_PALETTE))
+   {
+      int i;
+      int istop = (int)png_ptr->num_trans;
+      png_color back;
+      png_colorp palette = png_ptr->palette;
+
+      back.red   = (png_byte)png_ptr->background.red;
+      back.green = (png_byte)png_ptr->background.green;
+      back.blue  = (png_byte)png_ptr->background.blue;
+
+      for (i = 0; i < istop; i++)
+      {
+         if (png_ptr->trans[i] == 0)
+         {
+            palette[i] = back;
+         }
+         else if (png_ptr->trans[i] != 0xff)
+         {
+            /* The png_composite() macro is defined in png.h */
+            png_composite(palette[i].red, palette[i].red,
+               png_ptr->trans[i], back.red);
+            png_composite(palette[i].green, palette[i].green,
+               png_ptr->trans[i], back.green);
+            png_composite(palette[i].blue, palette[i].blue,
+               png_ptr->trans[i], back.blue);
+         }
+      }
+   }
+#endif /* PNG_READ_BACKGROUND_SUPPORTED */
+
+#if defined(PNG_READ_SHIFT_SUPPORTED)
+   if ((png_ptr->transformations & PNG_SHIFT) &&
+      (color_type == PNG_COLOR_TYPE_PALETTE))
+   {
+      png_uint_16 i;
+      png_uint_16 istop = png_ptr->num_palette;
+      int sr = 8 - png_ptr->sig_bit.red;
+      int sg = 8 - png_ptr->sig_bit.green;
+      int sb = 8 - png_ptr->sig_bit.blue;
+
+      if (sr < 0 || sr > 8)
+         sr = 0;
+      if (sg < 0 || sg > 8)
+         sg = 0;
+      if (sb < 0 || sb > 8)
+         sb = 0;
+      for (i = 0; i < istop; i++)
+      {
+         png_ptr->palette[i].red >>= sr;
+         png_ptr->palette[i].green >>= sg;
+         png_ptr->palette[i].blue >>= sb;
+      }
+   }
+#endif  /* PNG_READ_SHIFT_SUPPORTED */
+ }
+#if !defined(PNG_READ_GAMMA_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) \
+ && !defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if(png_ptr)
+      return;
+#endif
+}
+
+/* Modify the info structure to reflect the transformations.  The
+ * info should be updated so a PNG file could be written with it,
+ * assuming the transformations result in valid PNG data.
+ */
+void /* PRIVATE */
+png_read_transform_info(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_read_transform_info\n");
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+   if (png_ptr->transformations & PNG_EXPAND)
+   {
+      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         if (png_ptr->num_trans)
+            info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+         else
+            info_ptr->color_type = PNG_COLOR_TYPE_RGB;
+         info_ptr->bit_depth = 8;
+         info_ptr->num_trans = 0;
+      }
+      else
+      {
+         if (png_ptr->num_trans)
+            info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
+         if (info_ptr->bit_depth < 8)
+            info_ptr->bit_depth = 8;
+         info_ptr->num_trans = 0;
+      }
+   }
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if (png_ptr->transformations & PNG_BACKGROUND)
+   {
+      info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA;
+      info_ptr->num_trans = 0;
+      info_ptr->background = png_ptr->background;
+   }
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   if (png_ptr->transformations & PNG_GAMMA)
+   {
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+      info_ptr->gamma = png_ptr->gamma;
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+      info_ptr->int_gamma = png_ptr->int_gamma;
+#endif
+   }
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+   if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16))
+      info_ptr->bit_depth = 8;
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+   if (png_ptr->transformations & PNG_DITHER)
+   {
+      if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||
+         (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) &&
+         png_ptr->palette_lookup && info_ptr->bit_depth == 8)
+      {
+         info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
+      }
+   }
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+   if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8))
+      info_ptr->bit_depth = 8;
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+   if (png_ptr->transformations & PNG_GRAY_TO_RGB)
+      info_ptr->color_type |= PNG_COLOR_MASK_COLOR;
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+   if (png_ptr->transformations & PNG_RGB_TO_GRAY)
+      info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR;
+#endif
+
+   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      info_ptr->channels = 1;
+   else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
+      info_ptr->channels = 3;
+   else
+      info_ptr->channels = 1;
+
+#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+   if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA)
+      info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA;
+#endif
+
+   if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
+      info_ptr->channels++;
+
+#if defined(PNG_READ_FILLER_SUPPORTED)
+   /* STRIP_ALPHA and FILLER allowed:  MASK_ALPHA bit stripped above */
+   if ((png_ptr->transformations & PNG_FILLER) &&
+       ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||
+       (info_ptr->color_type == PNG_COLOR_TYPE_GRAY)))
+   {
+      info_ptr->channels++;
+      /* if adding a true alpha channel not just filler */
+#if !defined(PNG_1_0_X)
+      if (png_ptr->transformations & PNG_ADD_ALPHA)
+        info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
+#endif
+   }
+#endif
+
+#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \
+defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
+   if(png_ptr->transformations & PNG_USER_TRANSFORM)
+     {
+       if(info_ptr->bit_depth < png_ptr->user_transform_depth)
+         info_ptr->bit_depth = png_ptr->user_transform_depth;
+       if(info_ptr->channels < png_ptr->user_transform_channels)
+         info_ptr->channels = png_ptr->user_transform_channels;
+     }
+#endif
+
+   info_ptr->pixel_depth = (png_byte)(info_ptr->channels *
+      info_ptr->bit_depth);
+
+   info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,info_ptr->width);
+
+#if !defined(PNG_READ_EXPAND_SUPPORTED)
+   if(png_ptr)
+      return;
+#endif
+}
+
+/* Transform the row.  The order of transformations is significant,
+ * and is very touchy.  If you add a transformation, take care to
+ * decide how it fits in with the other transformations here.
+ */
+void /* PRIVATE */
+png_do_read_transformations(png_structp png_ptr)
+{
+   png_debug(1, "in png_do_read_transformations\n");
+#if !defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (png_ptr->row_buf == NULL)
+   {
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+      char msg[50];
+
+      sprintf(msg, "NULL row buffer for row %ld, pass %d", png_ptr->row_number,
+         png_ptr->pass);
+      png_error(png_ptr, msg);
+#else
+      png_error(png_ptr, "NULL row buffer");
+#endif
+   }
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+   if (png_ptr->transformations & PNG_EXPAND)
+   {
+      if (png_ptr->row_info.color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_do_expand_palette(&(png_ptr->row_info), png_ptr->row_buf + 1,
+            png_ptr->palette, png_ptr->trans, png_ptr->num_trans);
+      }
+      else
+      {
+         if (png_ptr->num_trans)
+            png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1,
+               &(png_ptr->trans_values));
+         else
+            png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1,
+               NULL);
+      }
+   }
+#endif
+
+#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+   if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA)
+      png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         PNG_FLAG_FILLER_AFTER | (png_ptr->flags & PNG_FLAG_STRIP_ALPHA));
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+   if (png_ptr->transformations & PNG_RGB_TO_GRAY)
+   {
+      int rgb_error =
+         png_do_rgb_to_gray(png_ptr, &(png_ptr->row_info), png_ptr->row_buf + 1);
+      if(rgb_error)
+      {
+         png_ptr->rgb_to_gray_status=1;
+         if(png_ptr->transformations == PNG_RGB_TO_GRAY_WARN)
+            png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel");
+         if(png_ptr->transformations == PNG_RGB_TO_GRAY_ERR)
+            png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel");
+      }
+   }
+#endif
+
+/*
+From Andreas Dilger e-mail to png-implement, 26 March 1998:
+
+  In most cases, the "simple transparency" should be done prior to doing
+  gray-to-RGB, or you will have to test 3x as many bytes to check if a
+  pixel is transparent.  You would also need to make sure that the
+  transparency information is upgraded to RGB.
+
+  To summarize, the current flow is:
+  - Gray + simple transparency -> compare 1 or 2 gray bytes and composite
+                                  with background "in place" if transparent,
+                                  convert to RGB if necessary
+  - Gray + alpha -> composite with gray background and remove alpha bytes,
+                                  convert to RGB if necessary
+
+  To support RGB backgrounds for gray images we need:
+  - Gray + simple transparency -> convert to RGB + simple transparency, compare
+                                  3 or 6 bytes and composite with background
+                                  "in place" if transparent (3x compare/pixel
+                                  compared to doing composite with gray bkgrnd)
+  - Gray + alpha -> convert to RGB + alpha, composite with background and
+                                  remove alpha bytes (3x float operations/pixel
+                                  compared with composite on gray background)
+
+  Greg's change will do this.  The reason it wasn't done before is for
+  performance, as this increases the per-pixel operations.  If we would check
+  in advance if the background was gray or RGB, and position the gray-to-RGB
+  transform appropriately, then it would save a lot of work/time.
+ */
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+   /* if gray -> RGB, do so now only if background is non-gray; else do later
+    * for performance reasons */
+   if ((png_ptr->transformations & PNG_GRAY_TO_RGB) &&
+       !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY))
+      png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if ((png_ptr->transformations & PNG_BACKGROUND) &&
+      ((png_ptr->num_trans != 0 ) ||
+      (png_ptr->color_type & PNG_COLOR_MASK_ALPHA)))
+      png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         &(png_ptr->trans_values), &(png_ptr->background)
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+         , &(png_ptr->background_1),
+         png_ptr->gamma_table, png_ptr->gamma_from_1,
+         png_ptr->gamma_to_1, png_ptr->gamma_16_table,
+         png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1,
+         png_ptr->gamma_shift
+#endif
+);
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   if ((png_ptr->transformations & PNG_GAMMA) &&
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+      !((png_ptr->transformations & PNG_BACKGROUND) &&
+      ((png_ptr->num_trans != 0) ||
+      (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) &&
+#endif
+      (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE))
+      png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         png_ptr->gamma_table, png_ptr->gamma_16_table,
+         png_ptr->gamma_shift);
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+   if (png_ptr->transformations & PNG_16_TO_8)
+      png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+   if (png_ptr->transformations & PNG_DITHER)
+   {
+      png_do_dither((png_row_infop)&(png_ptr->row_info), png_ptr->row_buf + 1,
+         png_ptr->palette_lookup, png_ptr->dither_index);
+      if(png_ptr->row_info.rowbytes == (png_uint_32)0)
+         png_error(png_ptr, "png_do_dither returned rowbytes=0");
+   }
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_MONO)
+      png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED)
+   if (png_ptr->transformations & PNG_SHIFT)
+      png_do_unshift(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         &(png_ptr->shift));
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACK)
+      png_do_unpack(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED)
+   if (png_ptr->transformations & PNG_BGR)
+      png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACKSWAP)
+      png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+   /* if gray -> RGB, do so now only if we did not do so above */
+   if ((png_ptr->transformations & PNG_GRAY_TO_RGB) &&
+       (png_ptr->mode & PNG_BACKGROUND_IS_GRAY))
+      png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED)
+   if (png_ptr->transformations & PNG_FILLER)
+      png_do_read_filler(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         (png_uint_32)png_ptr->filler, png_ptr->flags);
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_ALPHA)
+      png_do_read_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_ALPHA)
+      png_do_read_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_BYTES)
+      png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
+   if (png_ptr->transformations & PNG_USER_TRANSFORM)
+    {
+      if(png_ptr->read_user_transform_fn != NULL)
+        (*(png_ptr->read_user_transform_fn)) /* user read transform function */
+          (png_ptr,                    /* png_ptr */
+           &(png_ptr->row_info),       /* row_info:     */
+             /*  png_uint_32 width;          width of row */
+             /*  png_uint_32 rowbytes;       number of bytes in row */
+             /*  png_byte color_type;        color type of pixels */
+             /*  png_byte bit_depth;         bit depth of samples */
+             /*  png_byte channels;          number of channels (1-4) */
+             /*  png_byte pixel_depth;       bits per pixel (depth*channels) */
+           png_ptr->row_buf + 1);      /* start of pixel data for row */
+#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
+      if(png_ptr->user_transform_depth)
+         png_ptr->row_info.bit_depth = png_ptr->user_transform_depth;
+      if(png_ptr->user_transform_channels)
+         png_ptr->row_info.channels = png_ptr->user_transform_channels;
+#endif
+      png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth *
+         png_ptr->row_info.channels);
+      png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
+         png_ptr->row_info.width);
+   }
+#endif
+
+}
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel,
+ * without changing the actual values.  Thus, if you had a row with
+ * a bit depth of 1, you would end up with bytes that only contained
+ * the numbers 0 or 1.  If you would rather they contain 0 and 255, use
+ * png_do_shift() after this.
+ */
+void /* PRIVATE */
+png_do_unpack(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_unpack\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL && row_info->bit_depth < 8)
+#else
+   if (row_info->bit_depth < 8)
+#endif
+   {
+      png_uint_32 i;
+      png_uint_32 row_width=row_info->width;
+
+      switch (row_info->bit_depth)
+      {
+         case 1:
+         {
+            png_bytep sp = row + (png_size_t)((row_width - 1) >> 3);
+            png_bytep dp = row + (png_size_t)row_width - 1;
+            png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07);
+            for (i = 0; i < row_width; i++)
+            {
+               *dp = (png_byte)((*sp >> shift) & 0x01);
+               if (shift == 7)
+               {
+                  shift = 0;
+                  sp--;
+               }
+               else
+                  shift++;
+
+               dp--;
+            }
+            break;
+         }
+         case 2:
+         {
+
+            png_bytep sp = row + (png_size_t)((row_width - 1) >> 2);
+            png_bytep dp = row + (png_size_t)row_width - 1;
+            png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
+            for (i = 0; i < row_width; i++)
+            {
+               *dp = (png_byte)((*sp >> shift) & 0x03);
+               if (shift == 6)
+               {
+                  shift = 0;
+                  sp--;
+               }
+               else
+                  shift += 2;
+
+               dp--;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp = row + (png_size_t)((row_width - 1) >> 1);
+            png_bytep dp = row + (png_size_t)row_width - 1;
+            png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);
+            for (i = 0; i < row_width; i++)
+            {
+               *dp = (png_byte)((*sp >> shift) & 0x0f);
+               if (shift == 4)
+               {
+                  shift = 0;
+                  sp--;
+               }
+               else
+                  shift = 4;
+
+               dp--;
+            }
+            break;
+         }
+      }
+      row_info->bit_depth = 8;
+      row_info->pixel_depth = (png_byte)(8 * row_info->channels);
+      row_info->rowbytes = row_width * row_info->channels;
+   }
+}
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED)
+/* Reverse the effects of png_do_shift.  This routine merely shifts the
+ * pixels back to their significant bits values.  Thus, if you have
+ * a row of bit depth 8, but only 5 are significant, this will shift
+ * the values back to 0 through 31.
+ */
+void /* PRIVATE */
+png_do_unshift(png_row_infop row_info, png_bytep row, png_color_8p sig_bits)
+{
+   png_debug(1, "in png_do_unshift\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL && sig_bits != NULL &&
+#endif
+       row_info->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      int shift[4];
+      int channels = 0;
+      int c;
+      png_uint_16 value = 0;
+      png_uint_32 row_width = row_info->width;
+
+      if (row_info->color_type & PNG_COLOR_MASK_COLOR)
+      {
+         shift[channels++] = row_info->bit_depth - sig_bits->red;
+         shift[channels++] = row_info->bit_depth - sig_bits->green;
+         shift[channels++] = row_info->bit_depth - sig_bits->blue;
+      }
+      else
+      {
+         shift[channels++] = row_info->bit_depth - sig_bits->gray;
+      }
+      if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+      {
+         shift[channels++] = row_info->bit_depth - sig_bits->alpha;
+      }
+
+      for (c = 0; c < channels; c++)
+      {
+         if (shift[c] <= 0)
+            shift[c] = 0;
+         else
+            value = 1;
+      }
+
+      if (!value)
+         return;
+
+      switch (row_info->bit_depth)
+      {
+         case 2:
+         {
+            png_bytep bp;
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+
+            for (bp = row, i = 0; i < istop; i++)
+            {
+               *bp >>= 1;
+               *bp++ &= 0x55;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_bytep bp = row;
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+            png_byte mask = (png_byte)((((int)0xf0 >> shift[0]) & (int)0xf0) |
+               (png_byte)((int)0xf >> shift[0]));
+
+            for (i = 0; i < istop; i++)
+            {
+               *bp >>= shift[0];
+               *bp++ &= mask;
+            }
+            break;
+         }
+         case 8:
+         {
+            png_bytep bp = row;
+            png_uint_32 i;
+            png_uint_32 istop = row_width * channels;
+
+            for (i = 0; i < istop; i++)
+            {
+               *bp++ >>= shift[i%channels];
+            }
+            break;
+         }
+         case 16:
+         {
+            png_bytep bp = row;
+            png_uint_32 i;
+            png_uint_32 istop = channels * row_width;
+
+            for (i = 0; i < istop; i++)
+            {
+               value = (png_uint_16)((*bp << 8) + *(bp + 1));
+               value >>= shift[i%channels];
+               *bp++ = (png_byte)(value >> 8);
+               *bp++ = (png_byte)(value & 0xff);
+            }
+            break;
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+/* chop rows of bit depth 16 down to 8 */
+void /* PRIVATE */
+png_do_chop(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_chop\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL && row_info->bit_depth == 16)
+#else
+   if (row_info->bit_depth == 16)
+#endif
+   {
+      png_bytep sp = row;
+      png_bytep dp = row;
+      png_uint_32 i;
+      png_uint_32 istop = row_info->width * row_info->channels;
+
+      for (i = 0; i<istop; i++, sp += 2, dp++)
+      {
+#if defined(PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED)
+      /* This does a more accurate scaling of the 16-bit color
+       * value, rather than a simple low-byte truncation.
+       *
+       * What the ideal calculation should be:
+       *   *dp = (((((png_uint_32)(*sp) << 8) |
+       *          (png_uint_32)(*(sp + 1))) * 255 + 127) / (png_uint_32)65535L;
+       *
+       * GRR: no, I think this is what it really should be:
+       *   *dp = (((((png_uint_32)(*sp) << 8) |
+       *           (png_uint_32)(*(sp + 1))) + 128L) / (png_uint_32)257L;
+       *
+       * GRR: here's the exact calculation with shifts:
+       *   temp = (((png_uint_32)(*sp) << 8) | (png_uint_32)(*(sp + 1))) + 128L;
+       *   *dp = (temp - (temp >> 8)) >> 8;
+       *
+       * Approximate calculation with shift/add instead of multiply/divide:
+       *   *dp = ((((png_uint_32)(*sp) << 8) |
+       *          (png_uint_32)((int)(*(sp + 1)) - *sp)) + 128) >> 8;
+       *
+       * What we actually do to avoid extra shifting and conversion:
+       */
+
+         *dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0);
+#else
+       /* Simply discard the low order byte */
+         *dp = *sp;
+#endif
+      }
+      row_info->bit_depth = 8;
+      row_info->pixel_depth = (png_byte)(8 * row_info->channels);
+      row_info->rowbytes = row_info->width * row_info->channels;
+   }
+}
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED)
+void /* PRIVATE */
+png_do_read_swap_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_read_swap_alpha\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      png_uint_32 row_width = row_info->width;
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         /* This converts from RGBA to ARGB */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save;
+            }
+         }
+         /* This converts from RRGGBBAA to AARRGGBB */
+         else
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save[2];
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save[0] = *(--sp);
+               save[1] = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save[0];
+               *(--dp) = save[1];
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         /* This converts from GA to AG */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save;
+            }
+         }
+         /* This converts from GGAA to AAGG */
+         else
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save[2];
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save[0] = *(--sp);
+               save[1] = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save[0];
+               *(--dp) = save[1];
+            }
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
+void /* PRIVATE */
+png_do_read_invert_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_read_invert_alpha\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      png_uint_32 row_width = row_info->width;
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         /* This inverts the alpha channel in RGBA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = (png_byte)(255 - *(--sp));
+
+/*             This does nothing:
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               We can replace it with:
+*/
+               sp-=3;
+               dp=sp;
+            }
+         }
+         /* This inverts the alpha channel in RRGGBBAA */
+         else
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = (png_byte)(255 - *(--sp));
+               *(--dp) = (png_byte)(255 - *(--sp));
+
+/*             This does nothing:
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               We can replace it with:
+*/
+               sp-=6;
+               dp=sp;
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         /* This inverts the alpha channel in GA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = (png_byte)(255 - *(--sp));
+               *(--dp) = *(--sp);
+            }
+         }
+         /* This inverts the alpha channel in GGAA */
+         else
+         {
+            png_bytep sp  = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = (png_byte)(255 - *(--sp));
+               *(--dp) = (png_byte)(255 - *(--sp));
+/*
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+*/
+               sp-=2;
+               dp=sp;
+            }
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED)
+/* Add filler channel if we have RGB color */
+void /* PRIVATE */
+png_do_read_filler(png_row_infop row_info, png_bytep row,
+   png_uint_32 filler, png_uint_32 flags)
+{
+   png_uint_32 i;
+   png_uint_32 row_width = row_info->width;
+
+   png_byte hi_filler = (png_byte)((filler>>8) & 0xff);
+   png_byte lo_filler = (png_byte)(filler & 0xff);
+
+   png_debug(1, "in png_do_read_filler\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL  && row_info != NULL &&
+#endif
+       row_info->color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      if(row_info->bit_depth == 8)
+      {
+         /* This changes the data from G to GX */
+         if (flags & PNG_FLAG_FILLER_AFTER)
+         {
+            png_bytep sp = row + (png_size_t)row_width;
+            png_bytep dp =  sp + (png_size_t)row_width;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = lo_filler;
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = lo_filler;
+            row_info->channels = 2;
+            row_info->pixel_depth = 16;
+            row_info->rowbytes = row_width * 2;
+         }
+      /* This changes the data from G to XG */
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width;
+            png_bytep dp = sp  + (png_size_t)row_width;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = lo_filler;
+            }
+            row_info->channels = 2;
+            row_info->pixel_depth = 16;
+            row_info->rowbytes = row_width * 2;
+         }
+      }
+      else if(row_info->bit_depth == 16)
+      {
+         /* This changes the data from GG to GGXX */
+         if (flags & PNG_FLAG_FILLER_AFTER)
+         {
+            png_bytep sp = row + (png_size_t)row_width * 2;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = hi_filler;
+               *(--dp) = lo_filler;
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = hi_filler;
+            *(--dp) = lo_filler;
+            row_info->channels = 2;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 4;
+         }
+         /* This changes the data from GG to XXGG */
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width * 2;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = hi_filler;
+               *(--dp) = lo_filler;
+            }
+            row_info->channels = 2;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 4;
+         }
+      }
+   } /* COLOR_TYPE == GRAY */
+   else if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+   {
+      if(row_info->bit_depth == 8)
+      {
+         /* This changes the data from RGB to RGBX */
+         if (flags & PNG_FLAG_FILLER_AFTER)
+         {
+            png_bytep sp = row + (png_size_t)row_width * 3;
+            png_bytep dp = sp  + (png_size_t)row_width;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = lo_filler;
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = lo_filler;
+            row_info->channels = 4;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 4;
+         }
+      /* This changes the data from RGB to XRGB */
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width * 3;
+            png_bytep dp = sp + (png_size_t)row_width;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = lo_filler;
+            }
+            row_info->channels = 4;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 4;
+         }
+      }
+      else if(row_info->bit_depth == 16)
+      {
+         /* This changes the data from RRGGBB to RRGGBBXX */
+         if (flags & PNG_FLAG_FILLER_AFTER)
+         {
+            png_bytep sp = row + (png_size_t)row_width * 6;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = hi_filler;
+               *(--dp) = lo_filler;
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = hi_filler;
+            *(--dp) = lo_filler;
+            row_info->channels = 4;
+            row_info->pixel_depth = 64;
+            row_info->rowbytes = row_width * 8;
+         }
+         /* This changes the data from RRGGBB to XXRRGGBB */
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width * 6;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = hi_filler;
+               *(--dp) = lo_filler;
+            }
+            row_info->channels = 4;
+            row_info->pixel_depth = 64;
+            row_info->rowbytes = row_width * 8;
+         }
+      }
+   } /* COLOR_TYPE == RGB */
+}
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+/* expand grayscale files to RGB, with or without alpha */
+void /* PRIVATE */
+png_do_gray_to_rgb(png_row_infop row_info, png_bytep row)
+{
+   png_uint_32 i;
+   png_uint_32 row_width = row_info->width;
+
+   png_debug(1, "in png_do_gray_to_rgb\n");
+   if (row_info->bit_depth >= 8 &&
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+      !(row_info->color_type & PNG_COLOR_MASK_COLOR))
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + (png_size_t)row_width - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *sp;
+               *(dp--) = *sp;
+               *(dp--) = *(sp--);
+            }
+         }
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width * 2 - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 4;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *(sp--);
+               *(dp--) = *(sp--);
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + (png_size_t)row_width * 2 - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *(sp--);
+               *(dp--) = *sp;
+               *(dp--) = *sp;
+               *(dp--) = *(sp--);
+            }
+         }
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width * 4 - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 4;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *(sp--);
+               *(dp--) = *(sp--);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *(sp--);
+               *(dp--) = *(sp--);
+            }
+         }
+      }
+      row_info->channels += (png_byte)2;
+      row_info->color_type |= PNG_COLOR_MASK_COLOR;
+      row_info->pixel_depth = (png_byte)(row_info->channels *
+         row_info->bit_depth);
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width);
+   }
+}
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+/* reduce RGB files to grayscale, with or without alpha
+ * using the equation given in Poynton's ColorFAQ at
+ * <http://www.inforamp.net/~poynton/>
+ * Copyright (c) 1998-01-04 Charles Poynton poynton at inforamp.net
+ *
+ *     Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
+ *
+ *  We approximate this with
+ *
+ *     Y = 0.21268 * R    + 0.7151 * G    + 0.07217 * B
+ *
+ *  which can be expressed with integers as
+ *
+ *     Y = (6969 * R + 23434 * G + 2365 * B)/32768
+ *
+ *  The calculation is to be done in a linear colorspace.
+ *
+ *  Other integer coefficents can be used via png_set_rgb_to_gray().
+ */
+int /* PRIVATE */
+png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row)
+
+{
+   png_uint_32 i;
+
+   png_uint_32 row_width = row_info->width;
+   int rgb_error = 0;
+
+   png_debug(1, "in png_do_rgb_to_gray\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+      (row_info->color_type & PNG_COLOR_MASK_COLOR))
+   {
+      png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff;
+      png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff;
+      png_uint_32 bc = png_ptr->rgb_to_gray_blue_coeff;
+
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+      {
+         if (row_info->bit_depth == 8)
+         {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+            if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+
+               for (i = 0; i < row_width; i++)
+               {
+                  png_byte red   = png_ptr->gamma_to_1[*(sp++)];
+                  png_byte green = png_ptr->gamma_to_1[*(sp++)];
+                  png_byte blue  = png_ptr->gamma_to_1[*(sp++)];
+                  if(red != green || red != blue)
+                  {
+                     rgb_error |= 1;
+                     *(dp++) = png_ptr->gamma_from_1[
+                       (rc*red+gc*green+bc*blue)>>15];
+                  }
+                  else
+                     *(dp++) = *(sp-1);
+               }
+            }
+            else
+#endif
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_byte red   = *(sp++);
+                  png_byte green = *(sp++);
+                  png_byte blue  = *(sp++);
+                  if(red != green || red != blue)
+                  {
+                     rgb_error |= 1;
+                     *(dp++) = (png_byte)((rc*red+gc*green+bc*blue)>>15);
+                  }
+                  else
+                     *(dp++) = *(sp-1);
+               }
+            }
+         }
+
+         else /* RGB bit_depth == 16 */
+         {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+            if (png_ptr->gamma_16_to_1 != NULL &&
+                png_ptr->gamma_16_from_1 != NULL)
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 red, green, blue, w;
+
+                  red   = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  blue  = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+
+                  if(red == green && red == blue)
+                     w = red;
+                  else
+                  {
+                     png_uint_16 red_1   = png_ptr->gamma_16_to_1[(red&0xff) >>
+                                  png_ptr->gamma_shift][red>>8];
+                     png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >>
+                                  png_ptr->gamma_shift][green>>8];
+                     png_uint_16 blue_1  = png_ptr->gamma_16_to_1[(blue&0xff) >>
+                                  png_ptr->gamma_shift][blue>>8];
+                     png_uint_16 gray16  = (png_uint_16)((rc*red_1 + gc*green_1
+                                  + bc*blue_1)>>15);
+                     w = png_ptr->gamma_16_from_1[(gray16&0xff) >>
+                         png_ptr->gamma_shift][gray16 >> 8];
+                     rgb_error |= 1;
+                  }
+
+                  *(dp++) = (png_byte)((w>>8) & 0xff);
+                  *(dp++) = (png_byte)(w & 0xff);
+               }
+            }
+            else
+#endif
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 red, green, blue, gray16;
+
+                  red   = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  blue  = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+
+                  if(red != green || red != blue)
+                     rgb_error |= 1;
+                  gray16  = (png_uint_16)((rc*red + gc*green + bc*blue)>>15);
+                  *(dp++) = (png_byte)((gray16>>8) & 0xff);
+                  *(dp++) = (png_byte)(gray16 & 0xff);
+               }
+            }
+         }
+      }
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         if (row_info->bit_depth == 8)
+         {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+            if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_byte red   = png_ptr->gamma_to_1[*(sp++)];
+                  png_byte green = png_ptr->gamma_to_1[*(sp++)];
+                  png_byte blue  = png_ptr->gamma_to_1[*(sp++)];
+                  if(red != green || red != blue)
+                     rgb_error |= 1;
+                  *(dp++) =  png_ptr->gamma_from_1
+                             [(rc*red + gc*green + bc*blue)>>15];
+                  *(dp++) = *(sp++);  /* alpha */
+               }
+            }
+            else
+#endif
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_byte red   = *(sp++);
+                  png_byte green = *(sp++);
+                  png_byte blue  = *(sp++);
+                  if(red != green || red != blue)
+                     rgb_error |= 1;
+                  *(dp++) =  (png_byte)((rc*red + gc*green + bc*blue)>>15);
+                  *(dp++) = *(sp++);  /* alpha */
+               }
+            }
+         }
+         else /* RGBA bit_depth == 16 */
+         {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+            if (png_ptr->gamma_16_to_1 != NULL &&
+                png_ptr->gamma_16_from_1 != NULL)
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 red, green, blue, w;
+
+                  red   = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  blue  = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+
+                  if(red == green && red == blue)
+                     w = red;
+                  else
+                  {
+                     png_uint_16 red_1   = png_ptr->gamma_16_to_1[(red&0xff) >>
+                                  png_ptr->gamma_shift][red>>8];
+                     png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >>
+                                  png_ptr->gamma_shift][green>>8];
+                     png_uint_16 blue_1  = png_ptr->gamma_16_to_1[(blue&0xff) >>
+                                  png_ptr->gamma_shift][blue>>8];
+                     png_uint_16 gray16  = (png_uint_16)((rc * red_1
+                                  + gc * green_1 + bc * blue_1)>>15);
+                     w = png_ptr->gamma_16_from_1[(gray16&0xff) >>
+                         png_ptr->gamma_shift][gray16 >> 8];
+                     rgb_error |= 1;
+                  }
+
+                  *(dp++) = (png_byte)((w>>8) & 0xff);
+                  *(dp++) = (png_byte)(w & 0xff);
+                  *(dp++) = *(sp++);  /* alpha */
+                  *(dp++) = *(sp++);
+               }
+            }
+            else
+#endif
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 red, green, blue, gray16;
+                  red   = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2;
+                  green = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2;
+                  blue  = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2;
+                  if(red != green || red != blue)
+                     rgb_error |= 1;
+                  gray16  = (png_uint_16)((rc*red + gc*green + bc*blue)>>15);
+                  *(dp++) = (png_byte)((gray16>>8) & 0xff);
+                  *(dp++) = (png_byte)(gray16 & 0xff);
+                  *(dp++) = *(sp++);  /* alpha */
+                  *(dp++) = *(sp++);
+               }
+            }
+         }
+      }
+   row_info->channels -= (png_byte)2;
+      row_info->color_type &= ~PNG_COLOR_MASK_COLOR;
+      row_info->pixel_depth = (png_byte)(row_info->channels *
+         row_info->bit_depth);
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width);
+   }
+   return rgb_error;
+}
+#endif
+
+/* Build a grayscale palette.  Palette is assumed to be 1 << bit_depth
+ * large of png_color.  This lets grayscale images be treated as
+ * paletted.  Most useful for gamma correction and simplification
+ * of code.
+ */
+void PNGAPI
+png_build_grayscale_palette(int bit_depth, png_colorp palette)
+{
+   int num_palette;
+   int color_inc;
+   int i;
+   int v;
+
+   png_debug(1, "in png_do_build_grayscale_palette\n");
+   if (palette == NULL)
+      return;
+
+   switch (bit_depth)
+   {
+      case 1:
+         num_palette = 2;
+         color_inc = 0xff;
+         break;
+      case 2:
+         num_palette = 4;
+         color_inc = 0x55;
+         break;
+      case 4:
+         num_palette = 16;
+         color_inc = 0x11;
+         break;
+      case 8:
+         num_palette = 256;
+         color_inc = 1;
+         break;
+      default:
+         num_palette = 0;
+         color_inc = 0;
+         break;
+   }
+
+   for (i = 0, v = 0; i < num_palette; i++, v += color_inc)
+   {
+      palette[i].red = (png_byte)v;
+      palette[i].green = (png_byte)v;
+      palette[i].blue = (png_byte)v;
+   }
+}
+
+/* This function is currently unused.  Do we really need it? */
+#if defined(PNG_READ_DITHER_SUPPORTED) && defined(PNG_CORRECT_PALETTE_SUPPORTED)
+void /* PRIVATE */
+png_correct_palette(png_structp png_ptr, png_colorp palette,
+   int num_palette)
+{
+   png_debug(1, "in png_correct_palette\n");
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \
+    defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED)
+   if (png_ptr->transformations & (PNG_GAMMA | PNG_BACKGROUND))
+   {
+      png_color back, back_1;
+
+      if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE)
+      {
+         back.red = png_ptr->gamma_table[png_ptr->background.red];
+         back.green = png_ptr->gamma_table[png_ptr->background.green];
+         back.blue = png_ptr->gamma_table[png_ptr->background.blue];
+
+         back_1.red = png_ptr->gamma_to_1[png_ptr->background.red];
+         back_1.green = png_ptr->gamma_to_1[png_ptr->background.green];
+         back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue];
+      }
+      else
+      {
+         double g;
+
+         g = 1.0 / (png_ptr->background_gamma * png_ptr->screen_gamma);
+
+         if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_SCREEN ||
+             fabs(g - 1.0) < PNG_GAMMA_THRESHOLD)
+         {
+            back.red = png_ptr->background.red;
+            back.green = png_ptr->background.green;
+            back.blue = png_ptr->background.blue;
+         }
+         else
+         {
+            back.red =
+               (png_byte)(pow((double)png_ptr->background.red/255, g) *
+                255.0 + 0.5);
+            back.green =
+               (png_byte)(pow((double)png_ptr->background.green/255, g) *
+                255.0 + 0.5);
+            back.blue =
+               (png_byte)(pow((double)png_ptr->background.blue/255, g) *
+                255.0 + 0.5);
+         }
+
+         g = 1.0 / png_ptr->background_gamma;
+
+         back_1.red =
+            (png_byte)(pow((double)png_ptr->background.red/255, g) *
+             255.0 + 0.5);
+         back_1.green =
+            (png_byte)(pow((double)png_ptr->background.green/255, g) *
+             255.0 + 0.5);
+         back_1.blue =
+            (png_byte)(pow((double)png_ptr->background.blue/255, g) *
+             255.0 + 0.5);
+      }
+
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_uint_32 i;
+
+         for (i = 0; i < (png_uint_32)num_palette; i++)
+         {
+            if (i < png_ptr->num_trans && png_ptr->trans[i] == 0)
+            {
+               palette[i] = back;
+            }
+            else if (i < png_ptr->num_trans && png_ptr->trans[i] != 0xff)
+            {
+               png_byte v, w;
+
+               v = png_ptr->gamma_to_1[png_ptr->palette[i].red];
+               png_composite(w, v, png_ptr->trans[i], back_1.red);
+               palette[i].red = png_ptr->gamma_from_1[w];
+
+               v = png_ptr->gamma_to_1[png_ptr->palette[i].green];
+               png_composite(w, v, png_ptr->trans[i], back_1.green);
+               palette[i].green = png_ptr->gamma_from_1[w];
+
+               v = png_ptr->gamma_to_1[png_ptr->palette[i].blue];
+               png_composite(w, v, png_ptr->trans[i], back_1.blue);
+               palette[i].blue = png_ptr->gamma_from_1[w];
+            }
+            else
+            {
+               palette[i].red = png_ptr->gamma_table[palette[i].red];
+               palette[i].green = png_ptr->gamma_table[palette[i].green];
+               palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+            }
+         }
+      }
+      else
+      {
+         int i;
+
+         for (i = 0; i < num_palette; i++)
+         {
+            if (palette[i].red == (png_byte)png_ptr->trans_values.gray)
+            {
+               palette[i] = back;
+            }
+            else
+            {
+               palette[i].red = png_ptr->gamma_table[palette[i].red];
+               palette[i].green = png_ptr->gamma_table[palette[i].green];
+               palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+            }
+         }
+      }
+   }
+   else
+#endif
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   if (png_ptr->transformations & PNG_GAMMA)
+   {
+      int i;
+
+      for (i = 0; i < num_palette; i++)
+      {
+         palette[i].red = png_ptr->gamma_table[palette[i].red];
+         palette[i].green = png_ptr->gamma_table[palette[i].green];
+         palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+      }
+   }
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   else
+#endif
+#endif
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if (png_ptr->transformations & PNG_BACKGROUND)
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_color back;
+
+         back.red   = (png_byte)png_ptr->background.red;
+         back.green = (png_byte)png_ptr->background.green;
+         back.blue  = (png_byte)png_ptr->background.blue;
+
+         for (i = 0; i < (int)png_ptr->num_trans; i++)
+         {
+            if (png_ptr->trans[i] == 0)
+            {
+               palette[i].red = back.red;
+               palette[i].green = back.green;
+               palette[i].blue = back.blue;
+            }
+            else if (png_ptr->trans[i] != 0xff)
+            {
+               png_composite(palette[i].red, png_ptr->palette[i].red,
+                  png_ptr->trans[i], back.red);
+               png_composite(palette[i].green, png_ptr->palette[i].green,
+                  png_ptr->trans[i], back.green);
+               png_composite(palette[i].blue, png_ptr->palette[i].blue,
+                  png_ptr->trans[i], back.blue);
+            }
+         }
+      }
+      else /* assume grayscale palette (what else could it be?) */
+      {
+         int i;
+
+         for (i = 0; i < num_palette; i++)
+         {
+            if (i == (png_byte)png_ptr->trans_values.gray)
+            {
+               palette[i].red = (png_byte)png_ptr->background.red;
+               palette[i].green = (png_byte)png_ptr->background.green;
+               palette[i].blue = (png_byte)png_ptr->background.blue;
+            }
+         }
+      }
+   }
+#endif
+}
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+/* Replace any alpha or transparency with the supplied background color.
+ * "background" is already in the screen gamma, while "background_1" is
+ * at a gamma of 1.0.  Paletted files have already been taken care of.
+ */
+void /* PRIVATE */
+png_do_background(png_row_infop row_info, png_bytep row,
+   png_color_16p trans_values, png_color_16p background
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   , png_color_16p background_1,
+   png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1,
+   png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1,
+   png_uint_16pp gamma_16_to_1, int gamma_shift
+#endif
+   )
+{
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+   int shift;
+
+   png_debug(1, "in png_do_background\n");
+   if (background != NULL &&
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+      (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) ||
+      (row_info->color_type != PNG_COLOR_TYPE_PALETTE && trans_values)))
+   {
+      switch (row_info->color_type)
+      {
+         case PNG_COLOR_TYPE_GRAY:
+         {
+            switch (row_info->bit_depth)
+            {
+               case 1:
+               {
+                  sp = row;
+                  shift = 7;
+                  for (i = 0; i < row_width; i++)
+                  {
+                     if ((png_uint_16)((*sp >> shift) & 0x01)
+                        == trans_values->gray)
+                     {
+                        *sp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
+                        *sp |= (png_byte)(background->gray << shift);
+                     }
+                     if (!shift)
+                     {
+                        shift = 7;
+                        sp++;
+                     }
+                     else
+                        shift--;
+                  }
+                  break;
+               }
+               case 2:
+               {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                  if (gamma_table != NULL)
+                  {
+                     sp = row;
+                     shift = 6;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0x03)
+                            == trans_values->gray)
+                        {
+                           *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                           *sp |= (png_byte)(background->gray << shift);
+                        }
+                        else
+                        {
+                           png_byte p = (png_byte)((*sp >> shift) & 0x03);
+                           png_byte g = (png_byte)((gamma_table [p | (p << 2) |
+                               (p << 4) | (p << 6)] >> 6) & 0x03);
+                           *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                           *sp |= (png_byte)(g << shift);
+                        }
+                        if (!shift)
+                        {
+                           shift = 6;
+                           sp++;
+                        }
+                        else
+                           shift -= 2;
+                     }
+                  }
+                  else
+#endif
+                  {
+                     sp = row;
+                     shift = 6;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0x03)
+                            == trans_values->gray)
+                        {
+                           *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                           *sp |= (png_byte)(background->gray << shift);
+                        }
+                        if (!shift)
+                        {
+                           shift = 6;
+                           sp++;
+                        }
+                        else
+                           shift -= 2;
+                     }
+                  }
+                  break;
+               }
+               case 4:
+               {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                  if (gamma_table != NULL)
+                  {
+                     sp = row;
+                     shift = 4;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0x0f)
+                            == trans_values->gray)
+                        {
+                           *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                           *sp |= (png_byte)(background->gray << shift);
+                        }
+                        else
+                        {
+                           png_byte p = (png_byte)((*sp >> shift) & 0x0f);
+                           png_byte g = (png_byte)((gamma_table[p |
+                             (p << 4)] >> 4) & 0x0f);
+                           *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                           *sp |= (png_byte)(g << shift);
+                        }
+                        if (!shift)
+                        {
+                           shift = 4;
+                           sp++;
+                        }
+                        else
+                           shift -= 4;
+                     }
+                  }
+                  else
+#endif
+                  {
+                     sp = row;
+                     shift = 4;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0x0f)
+                            == trans_values->gray)
+                        {
+                           *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                           *sp |= (png_byte)(background->gray << shift);
+                        }
+                        if (!shift)
+                        {
+                           shift = 4;
+                           sp++;
+                        }
+                        else
+                           shift -= 4;
+                     }
+                  }
+                  break;
+               }
+               case 8:
+               {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                  if (gamma_table != NULL)
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp++)
+                     {
+                        if (*sp == trans_values->gray)
+                        {
+                           *sp = (png_byte)background->gray;
+                        }
+                        else
+                        {
+                           *sp = gamma_table[*sp];
+                        }
+                     }
+                  }
+                  else
+#endif
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp++)
+                     {
+                        if (*sp == trans_values->gray)
+                        {
+                           *sp = (png_byte)background->gray;
+                        }
+                     }
+                  }
+                  break;
+               }
+               case 16:
+               {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                  if (gamma_16 != NULL)
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp += 2)
+                     {
+                        png_uint_16 v;
+
+                        v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+                        if (v == trans_values->gray)
+                        {
+                           /* background is already in screen gamma */
+                           *sp = (png_byte)((background->gray >> 8) & 0xff);
+                           *(sp + 1) = (png_byte)(background->gray & 0xff);
+                        }
+                        else
+                        {
+                           v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                           *sp = (png_byte)((v >> 8) & 0xff);
+                           *(sp + 1) = (png_byte)(v & 0xff);
+                        }
+                     }
+                  }
+                  else
+#endif
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp += 2)
+                     {
+                        png_uint_16 v;
+
+                        v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+                        if (v == trans_values->gray)
+                        {
+                           *sp = (png_byte)((background->gray >> 8) & 0xff);
+                           *(sp + 1) = (png_byte)(background->gray & 0xff);
+                        }
+                     }
+                  }
+                  break;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_RGB:
+         {
+            if (row_info->bit_depth == 8)
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_table != NULL)
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 3)
+                  {
+                     if (*sp == trans_values->red &&
+                        *(sp + 1) == trans_values->green &&
+                        *(sp + 2) == trans_values->blue)
+                     {
+                        *sp = (png_byte)background->red;
+                        *(sp + 1) = (png_byte)background->green;
+                        *(sp + 2) = (png_byte)background->blue;
+                     }
+                     else
+                     {
+                        *sp = gamma_table[*sp];
+                        *(sp + 1) = gamma_table[*(sp + 1)];
+                        *(sp + 2) = gamma_table[*(sp + 2)];
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 3)
+                  {
+                     if (*sp == trans_values->red &&
+                        *(sp + 1) == trans_values->green &&
+                        *(sp + 2) == trans_values->blue)
+                     {
+                        *sp = (png_byte)background->red;
+                        *(sp + 1) = (png_byte)background->green;
+                        *(sp + 2) = (png_byte)background->blue;
+                     }
+                  }
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_16 != NULL)
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 6)
+                  {
+                     png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+                     png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3));
+                     png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5));
+                     if (r == trans_values->red && g == trans_values->green &&
+                        b == trans_values->blue)
+                     {
+                        /* background is already in screen gamma */
+                        *sp = (png_byte)((background->red >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(background->red & 0xff);
+                        *(sp + 2) = (png_byte)((background->green >> 8) & 0xff);
+                        *(sp + 3) = (png_byte)(background->green & 0xff);
+                        *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+                        *(sp + 5) = (png_byte)(background->blue & 0xff);
+                     }
+                     else
+                     {
+                        png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                        *sp = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(v & 0xff);
+                        v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                        *(sp + 2) = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 3) = (png_byte)(v & 0xff);
+                        v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                        *(sp + 4) = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 5) = (png_byte)(v & 0xff);
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 6)
+                  {
+                     png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp+1));
+                     png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3));
+                     png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5));
+
+                     if (r == trans_values->red && g == trans_values->green &&
+                        b == trans_values->blue)
+                     {
+                        *sp = (png_byte)((background->red >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(background->red & 0xff);
+                        *(sp + 2) = (png_byte)((background->green >> 8) & 0xff);
+                        *(sp + 3) = (png_byte)(background->green & 0xff);
+                        *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+                        *(sp + 5) = (png_byte)(background->blue & 0xff);
+                     }
+                  }
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_GRAY_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
+                   gamma_table != NULL)
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 2, dp++)
+                  {
+                     png_uint_16 a = *(sp + 1);
+
+                     if (a == 0xff)
+                     {
+                        *dp = gamma_table[*sp];
+                     }
+                     else if (a == 0)
+                     {
+                        /* background is already in screen gamma */
+                        *dp = (png_byte)background->gray;
+                     }
+                     else
+                     {
+                        png_byte v, w;
+
+                        v = gamma_to_1[*sp];
+                        png_composite(w, v, a, background_1->gray);
+                        *dp = gamma_from_1[w];
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 2, dp++)
+                  {
+                     png_byte a = *(sp + 1);
+
+                     if (a == 0xff)
+                     {
+                        *dp = *sp;
+                     }
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                     else if (a == 0)
+                     {
+                        *dp = (png_byte)background->gray;
+                     }
+                     else
+                     {
+                        png_composite(*dp, *sp, a, background_1->gray);
+                     }
+#else
+                     *dp = (png_byte)background->gray;
+#endif
+                  }
+               }
+            }
+            else /* if (png_ptr->bit_depth == 16) */
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
+                   gamma_16_to_1 != NULL)
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 4, dp += 2)
+                  {
+                     png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3));
+
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_uint_16 v;
+
+                        v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                        *dp = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(v & 0xff);
+                     }
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                     else if (a == 0)
+#else
+                     else
+#endif
+                     {
+                        /* background is already in screen gamma */
+                        *dp = (png_byte)((background->gray >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(background->gray & 0xff);
+                     }
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                     else
+                     {
+                        png_uint_16 g, v, w;
+
+                        g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
+                        png_composite_16(v, g, a, background_1->gray);
+                        w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8];
+                        *dp = (png_byte)((w >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(w & 0xff);
+                     }
+#endif
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 4, dp += 2)
+                  {
+                     png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3));
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_memcpy(dp, sp, 2);
+                     }
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                     else if (a == 0)
+#else
+                     else
+#endif
+                     {
+                        *dp = (png_byte)((background->gray >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(background->gray & 0xff);
+                     }
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                     else
+                     {
+                        png_uint_16 g, v;
+
+                        g = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+                        png_composite_16(v, g, a, background_1->gray);
+                        *dp = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(v & 0xff);
+                     }
+#endif
+                  }
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_RGB_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
+                   gamma_table != NULL)
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 4, dp += 3)
+                  {
+                     png_byte a = *(sp + 3);
+
+                     if (a == 0xff)
+                     {
+                        *dp = gamma_table[*sp];
+                        *(dp + 1) = gamma_table[*(sp + 1)];
+                        *(dp + 2) = gamma_table[*(sp + 2)];
+                     }
+                     else if (a == 0)
+                     {
+                        /* background is already in screen gamma */
+                        *dp = (png_byte)background->red;
+                        *(dp + 1) = (png_byte)background->green;
+                        *(dp + 2) = (png_byte)background->blue;
+                     }
+                     else
+                     {
+                        png_byte v, w;
+
+                        v = gamma_to_1[*sp];
+                        png_composite(w, v, a, background_1->red);
+                        *dp = gamma_from_1[w];
+                        v = gamma_to_1[*(sp + 1)];
+                        png_composite(w, v, a, background_1->green);
+                        *(dp + 1) = gamma_from_1[w];
+                        v = gamma_to_1[*(sp + 2)];
+                        png_composite(w, v, a, background_1->blue);
+                        *(dp + 2) = gamma_from_1[w];
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 4, dp += 3)
+                  {
+                     png_byte a = *(sp + 3);
+
+                     if (a == 0xff)
+                     {
+                        *dp = *sp;
+                        *(dp + 1) = *(sp + 1);
+                        *(dp + 2) = *(sp + 2);
+                     }
+                     else if (a == 0)
+                     {
+                        *dp = (png_byte)background->red;
+                        *(dp + 1) = (png_byte)background->green;
+                        *(dp + 2) = (png_byte)background->blue;
+                     }
+                     else
+                     {
+                        png_composite(*dp, *sp, a, background->red);
+                        png_composite(*(dp + 1), *(sp + 1), a,
+                           background->green);
+                        png_composite(*(dp + 2), *(sp + 2), a,
+                           background->blue);
+                     }
+                  }
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
+                   gamma_16_to_1 != NULL)
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 8, dp += 6)
+                  {
+                     png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
+                         << 8) + (png_uint_16)(*(sp + 7)));
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_uint_16 v;
+
+                        v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                        *dp = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(v & 0xff);
+                        v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                        *(dp + 2) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(v & 0xff);
+                        v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                        *(dp + 4) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(v & 0xff);
+                     }
+                     else if (a == 0)
+                     {
+                        /* background is already in screen gamma */
+                        *dp = (png_byte)((background->red >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(background->red & 0xff);
+                        *(dp + 2) = (png_byte)((background->green >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(background->green & 0xff);
+                        *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(background->blue & 0xff);
+                     }
+                     else
+                     {
+                        png_uint_16 v, w, x;
+
+                        v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
+                        png_composite_16(w, v, a, background_1->red);
+                        x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8];
+                        *dp = (png_byte)((x >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(x & 0xff);
+                        v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                        png_composite_16(w, v, a, background_1->green);
+                        x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8];
+                        *(dp + 2) = (png_byte)((x >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(x & 0xff);
+                        v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                        png_composite_16(w, v, a, background_1->blue);
+                        x = gamma_16_from_1[(w & 0xff) >> gamma_shift][w >> 8];
+                        *(dp + 4) = (png_byte)((x >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(x & 0xff);
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 8, dp += 6)
+                  {
+                     png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
+                        << 8) + (png_uint_16)(*(sp + 7)));
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_memcpy(dp, sp, 6);
+                     }
+                     else if (a == 0)
+                     {
+                        *dp = (png_byte)((background->red >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(background->red & 0xff);
+                        *(dp + 2) = (png_byte)((background->green >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(background->green & 0xff);
+                        *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(background->blue & 0xff);
+                     }
+                     else
+                     {
+                        png_uint_16 v;
+
+                        png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+                        png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
+                            + *(sp + 3));
+                        png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
+                            + *(sp + 5));
+
+                        png_composite_16(v, r, a, background->red);
+                        *dp = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(v & 0xff);
+                        png_composite_16(v, g, a, background->green);
+                        *(dp + 2) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(v & 0xff);
+                        png_composite_16(v, b, a, background->blue);
+                        *(dp + 4) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(v & 0xff);
+                     }
+                  }
+               }
+            }
+            break;
+         }
+      }
+
+      if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+      {
+         row_info->color_type &= ~PNG_COLOR_MASK_ALPHA;
+         row_info->channels--;
+         row_info->pixel_depth = (png_byte)(row_info->channels *
+            row_info->bit_depth);
+         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width);
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+/* Gamma correct the image, avoiding the alpha channel.  Make sure
+ * you do this after you deal with the transparency issue on grayscale
+ * or RGB images. If your bit depth is 8, use gamma_table, if it
+ * is 16, use gamma_16_table and gamma_shift.  Build these with
+ * build_gamma_table().
+ */
+void /* PRIVATE */
+png_do_gamma(png_row_infop row_info, png_bytep row,
+   png_bytep gamma_table, png_uint_16pp gamma_16_table,
+   int gamma_shift)
+{
+   png_bytep sp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_gamma\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       ((row_info->bit_depth <= 8 && gamma_table != NULL) ||
+        (row_info->bit_depth == 16 && gamma_16_table != NULL)))
+   {
+      switch (row_info->color_type)
+      {
+         case PNG_COLOR_TYPE_RGB:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v;
+
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_RGB_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  sp++;
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 4;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_GRAY_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp += 2;
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 4;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_GRAY:
+         {
+            if (row_info->bit_depth == 2)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i += 4)
+               {
+                  int a = *sp & 0xc0;
+                  int b = *sp & 0x30;
+                  int c = *sp & 0x0c;
+                  int d = *sp & 0x03;
+
+                  *sp = (png_byte)(
+                        ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)])   ) & 0xc0)|
+                        ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)|
+                        ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)|
+                        ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) ));
+                  sp++;
+               }
+            }
+            if (row_info->bit_depth == 4)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i += 2)
+               {
+                  int msb = *sp & 0xf0;
+                  int lsb = *sp & 0x0f;
+
+                  *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0)
+                          | (((int)gamma_table[(lsb << 4) | lsb]) >> 4));
+                  sp++;
+               }
+            }
+            else if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+               }
+            }
+            else if (row_info->bit_depth == 16)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+               }
+            }
+            break;
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+/* Expands a palette row to an RGB or RGBA row depending
+ * upon whether you supply trans and num_trans.
+ */
+void /* PRIVATE */
+png_do_expand_palette(png_row_infop row_info, png_bytep row,
+   png_colorp palette, png_bytep trans, int num_trans)
+{
+   int shift, value;
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_expand_palette\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       row_info->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (row_info->bit_depth < 8)
+      {
+         switch (row_info->bit_depth)
+         {
+            case 1:
+            {
+               sp = row + (png_size_t)((row_width - 1) >> 3);
+               dp = row + (png_size_t)row_width - 1;
+               shift = 7 - (int)((row_width + 7) & 0x07);
+               for (i = 0; i < row_width; i++)
+               {
+                  if ((*sp >> shift) & 0x01)
+                     *dp = 1;
+                  else
+                     *dp = 0;
+                  if (shift == 7)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift++;
+
+                  dp--;
+               }
+               break;
+            }
+            case 2:
+            {
+               sp = row + (png_size_t)((row_width - 1) >> 2);
+               dp = row + (png_size_t)row_width - 1;
+               shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
+               for (i = 0; i < row_width; i++)
+               {
+                  value = (*sp >> shift) & 0x03;
+                  *dp = (png_byte)value;
+                  if (shift == 6)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift += 2;
+
+                  dp--;
+               }
+               break;
+            }
+            case 4:
+            {
+               sp = row + (png_size_t)((row_width - 1) >> 1);
+               dp = row + (png_size_t)row_width - 1;
+               shift = (int)((row_width & 0x01) << 2);
+               for (i = 0; i < row_width; i++)
+               {
+                  value = (*sp >> shift) & 0x0f;
+                  *dp = (png_byte)value;
+                  if (shift == 4)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift += 4;
+
+                  dp--;
+               }
+               break;
+            }
+         }
+         row_info->bit_depth = 8;
+         row_info->pixel_depth = 8;
+         row_info->rowbytes = row_width;
+      }
+      switch (row_info->bit_depth)
+      {
+         case 8:
+         {
+            if (trans != NULL)
+            {
+               sp = row + (png_size_t)row_width - 1;
+               dp = row + (png_size_t)(row_width << 2) - 1;
+
+               for (i = 0; i < row_width; i++)
+               {
+                  if ((int)(*sp) >= num_trans)
+                     *dp-- = 0xff;
+                  else
+                     *dp-- = trans[*sp];
+                  *dp-- = palette[*sp].blue;
+                  *dp-- = palette[*sp].green;
+                  *dp-- = palette[*sp].red;
+                  sp--;
+               }
+               row_info->bit_depth = 8;
+               row_info->pixel_depth = 32;
+               row_info->rowbytes = row_width * 4;
+               row_info->color_type = 6;
+               row_info->channels = 4;
+            }
+            else
+            {
+               sp = row + (png_size_t)row_width - 1;
+               dp = row + (png_size_t)(row_width * 3) - 1;
+
+               for (i = 0; i < row_width; i++)
+               {
+                  *dp-- = palette[*sp].blue;
+                  *dp-- = palette[*sp].green;
+                  *dp-- = palette[*sp].red;
+                  sp--;
+               }
+               row_info->bit_depth = 8;
+               row_info->pixel_depth = 24;
+               row_info->rowbytes = row_width * 3;
+               row_info->color_type = 2;
+               row_info->channels = 3;
+            }
+            break;
+         }
+      }
+   }
+}
+
+/* If the bit depth < 8, it is expanded to 8.  Also, if the
+ * transparency value is supplied, an alpha channel is built.
+ */
+void /* PRIVATE */
+png_do_expand(png_row_infop row_info, png_bytep row,
+   png_color_16p trans_value)
+{
+   int shift, value;
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_expand\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         png_uint_16 gray = (png_uint_16)(trans_value ? trans_value->gray : 0);
+
+         if (row_info->bit_depth < 8)
+         {
+            switch (row_info->bit_depth)
+            {
+               case 1:
+               {
+                  gray = (png_uint_16)(gray*0xff);
+                  sp = row + (png_size_t)((row_width - 1) >> 3);
+                  dp = row + (png_size_t)row_width - 1;
+                  shift = 7 - (int)((row_width + 7) & 0x07);
+                  for (i = 0; i < row_width; i++)
+                  {
+                     if ((*sp >> shift) & 0x01)
+                        *dp = 0xff;
+                     else
+                        *dp = 0;
+                     if (shift == 7)
+                     {
+                        shift = 0;
+                        sp--;
+                     }
+                     else
+                        shift++;
+
+                     dp--;
+                  }
+                  break;
+               }
+               case 2:
+               {
+                  gray = (png_uint_16)(gray*0x55);
+                  sp = row + (png_size_t)((row_width - 1) >> 2);
+                  dp = row + (png_size_t)row_width - 1;
+                  shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
+                  for (i = 0; i < row_width; i++)
+                  {
+                     value = (*sp >> shift) & 0x03;
+                     *dp = (png_byte)(value | (value << 2) | (value << 4) |
+                        (value << 6));
+                     if (shift == 6)
+                     {
+                        shift = 0;
+                        sp--;
+                     }
+                     else
+                        shift += 2;
+
+                     dp--;
+                  }
+                  break;
+               }
+               case 4:
+               {
+                  gray = (png_uint_16)(gray*0x11);
+                  sp = row + (png_size_t)((row_width - 1) >> 1);
+                  dp = row + (png_size_t)row_width - 1;
+                  shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);
+                  for (i = 0; i < row_width; i++)
+                  {
+                     value = (*sp >> shift) & 0x0f;
+                     *dp = (png_byte)(value | (value << 4));
+                     if (shift == 4)
+                     {
+                        shift = 0;
+                        sp--;
+                     }
+                     else
+                        shift = 4;
+
+                     dp--;
+                  }
+                  break;
+               }
+            }
+            row_info->bit_depth = 8;
+            row_info->pixel_depth = 8;
+            row_info->rowbytes = row_width;
+         }
+
+         if (trans_value != NULL)
+         {
+            if (row_info->bit_depth == 8)
+            {
+               sp = row + (png_size_t)row_width - 1;
+               dp = row + (png_size_t)(row_width << 1) - 1;
+               for (i = 0; i < row_width; i++)
+               {
+                  if (*sp == gray)
+                     *dp-- = 0;
+                  else
+                     *dp-- = 0xff;
+                  *dp-- = *sp--;
+               }
+            }
+            else if (row_info->bit_depth == 16)
+            {
+               sp = row + row_info->rowbytes - 1;
+               dp = row + (row_info->rowbytes << 1) - 1;
+               for (i = 0; i < row_width; i++)
+               {
+                  if (((png_uint_16)*(sp) |
+                     ((png_uint_16)*(sp - 1) << 8)) == gray)
+                  {
+                     *dp-- = 0;
+                     *dp-- = 0;
+                  }
+                  else
+                  {
+                     *dp-- = 0xff;
+                     *dp-- = 0xff;
+                  }
+                  *dp-- = *sp--;
+                  *dp-- = *sp--;
+               }
+            }
+            row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+            row_info->channels = 2;
+            row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1);
+            row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
+               row_width);
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            sp = row + (png_size_t)row_info->rowbytes - 1;
+            dp = row + (png_size_t)(row_width << 2) - 1;
+            for (i = 0; i < row_width; i++)
+            {
+               if (*(sp - 2) == trans_value->red &&
+                  *(sp - 1) == trans_value->green &&
+                  *(sp - 0) == trans_value->blue)
+                  *dp-- = 0;
+               else
+                  *dp-- = 0xff;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+            }
+         }
+         else if (row_info->bit_depth == 16)
+         {
+            sp = row + row_info->rowbytes - 1;
+            dp = row + (png_size_t)(row_width << 3) - 1;
+            for (i = 0; i < row_width; i++)
+            {
+               if ((((png_uint_16)*(sp - 4) |
+                  ((png_uint_16)*(sp - 5) << 8)) == trans_value->red) &&
+                  (((png_uint_16)*(sp - 2) |
+                  ((png_uint_16)*(sp - 3) << 8)) == trans_value->green) &&
+                  (((png_uint_16)*(sp - 0) |
+                  ((png_uint_16)*(sp - 1) << 8)) == trans_value->blue))
+               {
+                  *dp-- = 0;
+                  *dp-- = 0;
+               }
+               else
+               {
+                  *dp-- = 0xff;
+                  *dp-- = 0xff;
+               }
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+            }
+         }
+         row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+         row_info->channels = 4;
+         row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2);
+         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width);
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+void /* PRIVATE */
+png_do_dither(png_row_infop row_info, png_bytep row,
+    png_bytep palette_lookup, png_bytep dither_lookup)
+{
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_dither\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB &&
+         palette_lookup && row_info->bit_depth == 8)
+      {
+         int r, g, b, p;
+         sp = row;
+         dp = row;
+         for (i = 0; i < row_width; i++)
+         {
+            r = *sp++;
+            g = *sp++;
+            b = *sp++;
+
+            /* this looks real messy, but the compiler will reduce
+               it down to a reasonable formula.  For example, with
+               5 bits per color, we get:
+               p = (((r >> 3) & 0x1f) << 10) |
+                  (((g >> 3) & 0x1f) << 5) |
+                  ((b >> 3) & 0x1f);
+               */
+            p = (((r >> (8 - PNG_DITHER_RED_BITS)) &
+               ((1 << PNG_DITHER_RED_BITS) - 1)) <<
+               (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) |
+               (((g >> (8 - PNG_DITHER_GREEN_BITS)) &
+               ((1 << PNG_DITHER_GREEN_BITS) - 1)) <<
+               (PNG_DITHER_BLUE_BITS)) |
+               ((b >> (8 - PNG_DITHER_BLUE_BITS)) &
+               ((1 << PNG_DITHER_BLUE_BITS) - 1));
+
+            *dp++ = palette_lookup[p];
+         }
+         row_info->color_type = PNG_COLOR_TYPE_PALETTE;
+         row_info->channels = 1;
+         row_info->pixel_depth = row_info->bit_depth;
+         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width);
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&
+         palette_lookup != NULL && row_info->bit_depth == 8)
+      {
+         int r, g, b, p;
+         sp = row;
+         dp = row;
+         for (i = 0; i < row_width; i++)
+         {
+            r = *sp++;
+            g = *sp++;
+            b = *sp++;
+            sp++;
+
+            p = (((r >> (8 - PNG_DITHER_RED_BITS)) &
+               ((1 << PNG_DITHER_RED_BITS) - 1)) <<
+               (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) |
+               (((g >> (8 - PNG_DITHER_GREEN_BITS)) &
+               ((1 << PNG_DITHER_GREEN_BITS) - 1)) <<
+               (PNG_DITHER_BLUE_BITS)) |
+               ((b >> (8 - PNG_DITHER_BLUE_BITS)) &
+               ((1 << PNG_DITHER_BLUE_BITS) - 1));
+
+            *dp++ = palette_lookup[p];
+         }
+         row_info->color_type = PNG_COLOR_TYPE_PALETTE;
+         row_info->channels = 1;
+         row_info->pixel_depth = row_info->bit_depth;
+         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width);
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE &&
+         dither_lookup && row_info->bit_depth == 8)
+      {
+         sp = row;
+         for (i = 0; i < row_width; i++, sp++)
+         {
+            *sp = dither_lookup[*sp];
+         }
+      }
+   }
+}
+#endif
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+static int png_gamma_shift[] =
+   {0x10, 0x21, 0x42, 0x84, 0x110, 0x248, 0x550, 0xff0};
+
+/* We build the 8- or 16-bit gamma tables here.  Note that for 16-bit
+ * tables, we don't make a full table if we are reducing to 8-bit in
+ * the future.  Note also how the gamma_16 tables are segmented so that
+ * we don't need to allocate > 64K chunks for a full 16-bit table.
+ */
+void /* PRIVATE */
+png_build_gamma_table(png_structp png_ptr)
+{
+  png_debug(1, "in png_build_gamma_table\n");
+  if(png_ptr->gamma != 0.0)
+  {
+   if (png_ptr->bit_depth <= 8)
+   {
+      int i;
+      double g;
+
+      if (png_ptr->screen_gamma > .000001)
+         g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma);
+      else
+         g = 1.0;
+
+      png_ptr->gamma_table = (png_bytep)png_malloc(png_ptr,
+         (png_uint_32)256);
+
+      for (i = 0; i < 256; i++)
+      {
+         png_ptr->gamma_table[i] = (png_byte)(pow((double)i / 255.0,
+            g) * 255.0 + .5);
+      }
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+    defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+      if (png_ptr->transformations & ((PNG_BACKGROUND) | PNG_RGB_TO_GRAY))
+      {
+
+         g = 1.0 / (png_ptr->gamma);
+
+         png_ptr->gamma_to_1 = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)256);
+
+         for (i = 0; i < 256; i++)
+         {
+            png_ptr->gamma_to_1[i] = (png_byte)(pow((double)i / 255.0,
+               g) * 255.0 + .5);
+         }
+
+
+         png_ptr->gamma_from_1 = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)256);
+
+         if(png_ptr->screen_gamma > 0.000001)
+            g = 1.0 / png_ptr->screen_gamma;
+         else
+            g = png_ptr->gamma;   /* probably doing rgb_to_gray */
+
+         for (i = 0; i < 256; i++)
+         {
+            png_ptr->gamma_from_1[i] = (png_byte)(pow((double)i / 255.0,
+               g) * 255.0 + .5);
+
+         }
+      }
+#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */
+   }
+   else
+   {
+      double g;
+      int i, j, shift, num;
+      int sig_bit;
+      png_uint_32 ig;
+
+      if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+      {
+         sig_bit = (int)png_ptr->sig_bit.red;
+         if ((int)png_ptr->sig_bit.green > sig_bit)
+            sig_bit = png_ptr->sig_bit.green;
+         if ((int)png_ptr->sig_bit.blue > sig_bit)
+            sig_bit = png_ptr->sig_bit.blue;
+      }
+      else
+      {
+         sig_bit = (int)png_ptr->sig_bit.gray;
+      }
+
+      if (sig_bit > 0)
+         shift = 16 - sig_bit;
+      else
+         shift = 0;
+
+      if (png_ptr->transformations & PNG_16_TO_8)
+      {
+         if (shift < (16 - PNG_MAX_GAMMA_8))
+            shift = (16 - PNG_MAX_GAMMA_8);
+      }
+
+      if (shift > 8)
+         shift = 8;
+      if (shift < 0)
+         shift = 0;
+
+      png_ptr->gamma_shift = (png_byte)shift;
+
+      num = (1 << (8 - shift));
+
+      if (png_ptr->screen_gamma > .000001)
+         g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma);
+      else
+         g = 1.0;
+
+      png_ptr->gamma_16_table = (png_uint_16pp)png_malloc(png_ptr,
+         (png_uint_32)(num * png_sizeof (png_uint_16p)));
+
+      if (png_ptr->transformations & (PNG_16_TO_8 | PNG_BACKGROUND))
+      {
+         double fin, fout;
+         png_uint_32 last, max;
+
+         for (i = 0; i < num; i++)
+         {
+            png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr,
+               (png_uint_32)(256 * png_sizeof (png_uint_16)));
+         }
+
+         g = 1.0 / g;
+         last = 0;
+         for (i = 0; i < 256; i++)
+         {
+            fout = ((double)i + 0.5) / 256.0;
+            fin = pow(fout, g);
+            max = (png_uint_32)(fin * (double)((png_uint_32)num << 8));
+            while (last <= max)
+            {
+               png_ptr->gamma_16_table[(int)(last & (0xff >> shift))]
+                  [(int)(last >> (8 - shift))] = (png_uint_16)(
+                  (png_uint_16)i | ((png_uint_16)i << 8));
+               last++;
+            }
+         }
+         while (last < ((png_uint_32)num << 8))
+         {
+            png_ptr->gamma_16_table[(int)(last & (0xff >> shift))]
+               [(int)(last >> (8 - shift))] = (png_uint_16)65535L;
+            last++;
+         }
+      }
+      else
+      {
+         for (i = 0; i < num; i++)
+         {
+            png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr,
+               (png_uint_32)(256 * png_sizeof (png_uint_16)));
+
+            ig = (((png_uint_32)i * (png_uint_32)png_gamma_shift[shift]) >> 4);
+            for (j = 0; j < 256; j++)
+            {
+               png_ptr->gamma_16_table[i][j] =
+                  (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) /
+                     65535.0, g) * 65535.0 + .5);
+            }
+         }
+      }
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+    defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+      if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY))
+      {
+
+         g = 1.0 / (png_ptr->gamma);
+
+         png_ptr->gamma_16_to_1 = (png_uint_16pp)png_malloc(png_ptr,
+            (png_uint_32)(num * png_sizeof (png_uint_16p )));
+
+         for (i = 0; i < num; i++)
+         {
+            png_ptr->gamma_16_to_1[i] = (png_uint_16p)png_malloc(png_ptr,
+               (png_uint_32)(256 * png_sizeof (png_uint_16)));
+
+            ig = (((png_uint_32)i *
+               (png_uint_32)png_gamma_shift[shift]) >> 4);
+            for (j = 0; j < 256; j++)
+            {
+               png_ptr->gamma_16_to_1[i][j] =
+                  (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) /
+                     65535.0, g) * 65535.0 + .5);
+            }
+         }
+
+         if(png_ptr->screen_gamma > 0.000001)
+            g = 1.0 / png_ptr->screen_gamma;
+         else
+            g = png_ptr->gamma;   /* probably doing rgb_to_gray */
+
+         png_ptr->gamma_16_from_1 = (png_uint_16pp)png_malloc(png_ptr,
+            (png_uint_32)(num * png_sizeof (png_uint_16p)));
+
+         for (i = 0; i < num; i++)
+         {
+            png_ptr->gamma_16_from_1[i] = (png_uint_16p)png_malloc(png_ptr,
+               (png_uint_32)(256 * png_sizeof (png_uint_16)));
+
+            ig = (((png_uint_32)i *
+               (png_uint_32)png_gamma_shift[shift]) >> 4);
+            for (j = 0; j < 256; j++)
+            {
+               png_ptr->gamma_16_from_1[i][j] =
+                  (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) /
+                     65535.0, g) * 65535.0 + .5);
+            }
+         }
+      }
+#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */
+   }
+ }
+}
+#endif
+/* To do: install integer version of png_build_gamma_table here */
+#endif
+
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+/* undoes intrapixel differencing  */
+void /* PRIVATE */
+png_do_read_intrapixel(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_read_intrapixel\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       (row_info->color_type & PNG_COLOR_MASK_COLOR))
+   {
+      int bytes_per_pixel;
+      png_uint_32 row_width = row_info->width;
+      if (row_info->bit_depth == 8)
+      {
+         png_bytep rp;
+         png_uint_32 i;
+
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+            bytes_per_pixel = 3;
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            bytes_per_pixel = 4;
+         else
+            return;
+
+         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+         {
+            *(rp) = (png_byte)((256 + *rp + *(rp+1))&0xff);
+            *(rp+2) = (png_byte)((256 + *(rp+2) + *(rp+1))&0xff);
+         }
+      }
+      else if (row_info->bit_depth == 16)
+      {
+         png_bytep rp;
+         png_uint_32 i;
+
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+            bytes_per_pixel = 6;
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            bytes_per_pixel = 8;
+         else
+            return;
+
+         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+         {
+            png_uint_32 s0   = (*(rp  ) << 8) | *(rp+1);
+            png_uint_32 s1   = (*(rp+2) << 8) | *(rp+3);
+            png_uint_32 s2   = (*(rp+4) << 8) | *(rp+5);
+            png_uint_32 red  = (png_uint_32)((s0+s1+65536L) & 0xffffL);
+            png_uint_32 blue = (png_uint_32)((s2+s1+65536L) & 0xffffL);
+            *(rp  ) = (png_byte)((red >> 8) & 0xff);
+            *(rp+1) = (png_byte)(red & 0xff);
+            *(rp+4) = (png_byte)((blue >> 8) & 0xff);
+            *(rp+5) = (png_byte)(blue & 0xff);
+         }
+      }
+   }
+}
+#endif /* PNG_MNG_FEATURES_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/png/libpng/pngrutil.c b/Utilities/GDAL/frmts/png/libpng/pngrutil.c
new file mode 100644
index 0000000000..99e466f18e
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngrutil.c
@@ -0,0 +1,3124 @@
+/* pngrutil.c - utilities to read a PNG file
+ *
+ * libpng version 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This file contains routines that are only called from within
+ * libpng itself during the course of reading an image.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(_WIN32_WCE)
+/* strtod() function is not supported on WindowsCE */
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+__inline double strtod(const char *nptr, char **endptr)
+{
+   double result = 0;
+   int len;
+   wchar_t *str, *end;
+
+   len = MultiByteToWideChar(CP_ACP, 0, nptr, -1, NULL, 0);
+   str = (wchar_t *)malloc(len * sizeof(wchar_t));
+   if ( NULL != str )
+   {
+      MultiByteToWideChar(CP_ACP, 0, nptr, -1, str, len);
+      result = wcstod(str, &end);
+      len = WideCharToMultiByte(CP_ACP, 0, end, -1, NULL, 0, NULL, NULL);
+      *endptr = (char *)nptr + (png_strlen(nptr) - len + 1);
+      free(str);
+   }
+   return result;
+}
+#  endif
+#endif
+
+png_uint_32 /* PRIVATE */
+png_get_uint_31(png_structp png_ptr, png_bytep buf)
+{
+   png_uint_32 i = png_get_uint_32(buf);
+   if (i > PNG_UINT_31_MAX)
+     png_error(png_ptr, "PNG unsigned integer out of range.\n");
+   return (i);
+}
+#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED
+/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */
+png_uint_32 /* PRIVATE */
+png_get_uint_32(png_bytep buf)
+{
+   png_uint_32 i = ((png_uint_32)(*buf) << 24) +
+      ((png_uint_32)(*(buf + 1)) << 16) +
+      ((png_uint_32)(*(buf + 2)) << 8) +
+      (png_uint_32)(*(buf + 3));
+
+   return (i);
+}
+
+#if defined(PNG_READ_pCAL_SUPPORTED) || defined(PNG_READ_oFFs_SUPPORTED)
+/* Grab a signed 32-bit integer from a buffer in big-endian format.  The
+ * data is stored in the PNG file in two's complement format, and it is
+ * assumed that the machine format for signed integers is the same. */
+png_int_32 /* PRIVATE */
+png_get_int_32(png_bytep buf)
+{
+   png_int_32 i = ((png_int_32)(*buf) << 24) +
+      ((png_int_32)(*(buf + 1)) << 16) +
+      ((png_int_32)(*(buf + 2)) << 8) +
+      (png_int_32)(*(buf + 3));
+
+   return (i);
+}
+#endif /* PNG_READ_pCAL_SUPPORTED */
+
+/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */
+png_uint_16 /* PRIVATE */
+png_get_uint_16(png_bytep buf)
+{
+   png_uint_16 i = (png_uint_16)(((png_uint_16)(*buf) << 8) +
+      (png_uint_16)(*(buf + 1)));
+
+   return (i);
+}
+#endif /* PNG_READ_BIG_ENDIAN_SUPPORTED */
+
+/* Read data, and (optionally) run it through the CRC. */
+void /* PRIVATE */
+png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length)
+{
+   png_read_data(png_ptr, buf, length);
+   png_calculate_crc(png_ptr, buf, length);
+}
+
+/* Optionally skip data and then check the CRC.  Depending on whether we
+   are reading a ancillary or critical chunk, and how the program has set
+   things up, we may calculate the CRC on the data and print a message.
+   Returns '1' if there was a CRC error, '0' otherwise. */
+int /* PRIVATE */
+png_crc_finish(png_structp png_ptr, png_uint_32 skip)
+{
+   png_size_t i;
+   png_size_t istop = png_ptr->zbuf_size;
+
+   for (i = (png_size_t)skip; i > istop; i -= istop)
+   {
+      png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+   }
+   if (i)
+   {
+      png_crc_read(png_ptr, png_ptr->zbuf, i);
+   }
+
+   if (png_crc_error(png_ptr))
+   {
+      if (((png_ptr->chunk_name[0] & 0x20) &&                /* Ancillary */
+           !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) ||
+          (!(png_ptr->chunk_name[0] & 0x20) &&             /* Critical  */
+          (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE)))
+      {
+         png_chunk_warning(png_ptr, "CRC error");
+      }
+      else
+      {
+         png_chunk_error(png_ptr, "CRC error");
+      }
+      return (1);
+   }
+
+   return (0);
+}
+
+/* Compare the CRC stored in the PNG file with that calculated by libpng from
+   the data it has read thus far. */
+int /* PRIVATE */
+png_crc_error(png_structp png_ptr)
+{
+   png_byte crc_bytes[4];
+   png_uint_32 crc;
+   int need_crc = 1;
+
+   if (png_ptr->chunk_name[0] & 0x20)                     /* ancillary */
+   {
+      if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
+          (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
+         need_crc = 0;
+   }
+   else                                                    /* critical */
+   {
+      if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE)
+         need_crc = 0;
+   }
+
+   png_read_data(png_ptr, crc_bytes, 4);
+
+   if (need_crc)
+   {
+      crc = png_get_uint_32(crc_bytes);
+      return ((int)(crc != png_ptr->crc));
+   }
+   else
+      return (0);
+}
+
+#if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \
+    defined(PNG_READ_iCCP_SUPPORTED)
+/*
+ * Decompress trailing data in a chunk.  The assumption is that chunkdata
+ * points at an allocated area holding the contents of a chunk with a
+ * trailing compressed part.  What we get back is an allocated area
+ * holding the original prefix part and an uncompressed version of the
+ * trailing part (the malloc area passed in is freed).
+ */
+png_charp /* PRIVATE */
+png_decompress_chunk(png_structp png_ptr, int comp_type,
+                              png_charp chunkdata, png_size_t chunklength,
+                              png_size_t prefix_size, png_size_t *newlength)
+{
+   static char msg[] = "Error decoding compressed text";
+   png_charp text;
+   png_size_t text_size;
+
+   if (comp_type == PNG_COMPRESSION_TYPE_BASE)
+   {
+      int ret = Z_OK;
+      png_ptr->zstream.next_in = (png_bytep)(chunkdata + prefix_size);
+      png_ptr->zstream.avail_in = (uInt)(chunklength - prefix_size);
+      png_ptr->zstream.next_out = png_ptr->zbuf;
+      png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+      text_size = 0;
+      text = NULL;
+
+      while (png_ptr->zstream.avail_in)
+      {
+         ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+         if (ret != Z_OK && ret != Z_STREAM_END)
+         {
+            if (png_ptr->zstream.msg != NULL)
+               png_warning(png_ptr, png_ptr->zstream.msg);
+            else
+               png_warning(png_ptr, msg);
+            inflateReset(&png_ptr->zstream);
+            png_ptr->zstream.avail_in = 0;
+
+            if (text ==  NULL)
+            {
+               text_size = prefix_size + png_sizeof(msg) + 1;
+               text = (png_charp)png_malloc_warn(png_ptr, text_size);
+               if (text ==  NULL)
+                 {
+                    png_free(png_ptr,chunkdata);
+                    png_error(png_ptr,"Not enough memory to decompress chunk");
+                 }
+               png_memcpy(text, chunkdata, prefix_size);
+            }
+
+            text[text_size - 1] = 0x00;
+
+            /* Copy what we can of the error message into the text chunk */
+            text_size = (png_size_t)(chunklength - (text - chunkdata) - 1);
+            text_size = png_sizeof(msg) > text_size ? text_size :
+               png_sizeof(msg);
+            png_memcpy(text + prefix_size, msg, text_size + 1);
+            break;
+         }
+         if (!png_ptr->zstream.avail_out || ret == Z_STREAM_END)
+         {
+            if (text == NULL)
+            {
+               text_size = prefix_size +
+                   png_ptr->zbuf_size - png_ptr->zstream.avail_out;
+               text = (png_charp)png_malloc_warn(png_ptr, text_size + 1);
+               if (text ==  NULL)
+                 {
+                    png_free(png_ptr,chunkdata);
+                    png_error(png_ptr,"Not enough memory to decompress chunk.");
+                 }
+               png_memcpy(text + prefix_size, png_ptr->zbuf,
+                    text_size - prefix_size);
+               png_memcpy(text, chunkdata, prefix_size);
+               *(text + text_size) = 0x00;
+            }
+            else
+            {
+               png_charp tmp;
+
+               tmp = text;
+               text = (png_charp)png_malloc_warn(png_ptr,
+                  (png_uint_32)(text_size +
+                  png_ptr->zbuf_size - png_ptr->zstream.avail_out + 1));
+               if (text == NULL)
+               {
+                  png_free(png_ptr, tmp);
+                  png_free(png_ptr, chunkdata);
+                  png_error(png_ptr,"Not enough memory to decompress chunk..");
+               }
+               png_memcpy(text, tmp, text_size);
+               png_free(png_ptr, tmp);
+               png_memcpy(text + text_size, png_ptr->zbuf,
+                  (png_ptr->zbuf_size - png_ptr->zstream.avail_out));
+               text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out;
+               *(text + text_size) = 0x00;
+            }
+            if (ret == Z_STREAM_END)
+               break;
+            else
+            {
+               png_ptr->zstream.next_out = png_ptr->zbuf;
+               png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+            }
+         }
+      }
+      if (ret != Z_STREAM_END)
+      {
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+         char umsg[50];
+
+         if (ret == Z_BUF_ERROR)
+            sprintf(umsg,"Buffer error in compressed datastream in %s chunk",
+                png_ptr->chunk_name);
+         else if (ret == Z_DATA_ERROR)
+            sprintf(umsg,"Data error in compressed datastream in %s chunk",
+                png_ptr->chunk_name);
+         else
+            sprintf(umsg,"Incomplete compressed datastream in %s chunk",
+                png_ptr->chunk_name);
+         png_warning(png_ptr, umsg);
+#else
+         png_warning(png_ptr,
+            "Incomplete compressed datastream in chunk other than IDAT");
+#endif
+         text_size=prefix_size;
+         if (text ==  NULL)
+         {
+            text = (png_charp)png_malloc_warn(png_ptr, text_size+1);
+            if (text == NULL)
+              {
+                png_free(png_ptr, chunkdata);
+                png_error(png_ptr,"Not enough memory for text.");
+              }
+            png_memcpy(text, chunkdata, prefix_size);
+         }
+         *(text + text_size) = 0x00;
+      }
+
+      inflateReset(&png_ptr->zstream);
+      png_ptr->zstream.avail_in = 0;
+
+      png_free(png_ptr, chunkdata);
+      chunkdata = text;
+      *newlength=text_size;
+   }
+   else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */
+   {
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+      char umsg[50];
+
+      sprintf(umsg, "Unknown zTXt compression type %d", comp_type);
+      png_warning(png_ptr, umsg);
+#else
+      png_warning(png_ptr, "Unknown zTXt compression type");
+#endif
+
+      *(chunkdata + prefix_size) = 0x00;
+      *newlength=prefix_size;
+   }
+
+   return chunkdata;
+}
+#endif
+
+/* read and check the IDHR chunk */
+void /* PRIVATE */
+png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[13];
+   png_uint_32 width, height;
+   int bit_depth, color_type, compression_type, filter_type;
+   int interlace_type;
+
+   png_debug(1, "in png_handle_IHDR\n");
+
+   if (png_ptr->mode & PNG_HAVE_IHDR)
+      png_error(png_ptr, "Out of place IHDR");
+
+   /* check the length */
+   if (length != 13)
+      png_error(png_ptr, "Invalid IHDR chunk");
+
+   png_ptr->mode |= PNG_HAVE_IHDR;
+
+   png_crc_read(png_ptr, buf, 13);
+   png_crc_finish(png_ptr, 0);
+
+   width = png_get_uint_31(png_ptr, buf);
+   height = png_get_uint_31(png_ptr, buf + 4);
+   bit_depth = buf[8];
+   color_type = buf[9];
+   compression_type = buf[10];
+   filter_type = buf[11];
+   interlace_type = buf[12];
+
+   /* set internal variables */
+   png_ptr->width = width;
+   png_ptr->height = height;
+   png_ptr->bit_depth = (png_byte)bit_depth;
+   png_ptr->interlaced = (png_byte)interlace_type;
+   png_ptr->color_type = (png_byte)color_type;
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   png_ptr->filter_type = (png_byte)filter_type;
+#endif
+   png_ptr->compression_type = (png_byte)compression_type;
+
+   /* find number of channels */
+   switch (png_ptr->color_type)
+   {
+      case PNG_COLOR_TYPE_GRAY:
+      case PNG_COLOR_TYPE_PALETTE:
+         png_ptr->channels = 1;
+         break;
+      case PNG_COLOR_TYPE_RGB:
+         png_ptr->channels = 3;
+         break;
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+         png_ptr->channels = 2;
+         break;
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+         png_ptr->channels = 4;
+         break;
+   }
+
+   /* set up other useful info */
+   png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth *
+   png_ptr->channels);
+   png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->width);
+   png_debug1(3,"bit_depth = %d\n", png_ptr->bit_depth);
+   png_debug1(3,"channels = %d\n", png_ptr->channels);
+   png_debug1(3,"rowbytes = %lu\n", png_ptr->rowbytes);
+   png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
+      color_type, interlace_type, compression_type, filter_type);
+}
+
+/* read and check the palette */
+void /* PRIVATE */
+png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_color palette[PNG_MAX_PALETTE_LENGTH];
+   int num, i;
+#ifndef PNG_NO_POINTER_INDEXING
+   png_colorp pal_ptr;
+#endif
+
+   png_debug(1, "in png_handle_PLTE\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before PLTE");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid PLTE after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      png_error(png_ptr, "Duplicate PLTE chunk");
+
+   png_ptr->mode |= PNG_HAVE_PLTE;
+
+   if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR))
+   {
+      png_warning(png_ptr,
+        "Ignoring PLTE chunk in grayscale PNG");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+#if !defined(PNG_READ_OPT_PLTE_SUPPORTED)
+   if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+#endif
+
+   if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3)
+   {
+      if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+      {
+         png_warning(png_ptr, "Invalid palette chunk");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+      else
+      {
+         png_error(png_ptr, "Invalid palette chunk");
+      }
+   }
+
+   num = (int)length / 3;
+
+#ifndef PNG_NO_POINTER_INDEXING
+   for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++)
+   {
+      png_byte buf[3];
+
+      png_crc_read(png_ptr, buf, 3);
+      pal_ptr->red = buf[0];
+      pal_ptr->green = buf[1];
+      pal_ptr->blue = buf[2];
+   }
+#else
+   for (i = 0; i < num; i++)
+   {
+      png_byte buf[3];
+
+      png_crc_read(png_ptr, buf, 3);
+      /* don't depend upon png_color being any order */
+      palette[i].red = buf[0];
+      palette[i].green = buf[1];
+      palette[i].blue = buf[2];
+   }
+#endif
+
+   /* If we actually NEED the PLTE chunk (ie for a paletted image), we do
+      whatever the normal CRC configuration tells us.  However, if we
+      have an RGB image, the PLTE can be considered ancillary, so
+      we will act as though it is. */
+#if !defined(PNG_READ_OPT_PLTE_SUPPORTED)
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+#endif
+   {
+      png_crc_finish(png_ptr, 0);
+   }
+#if !defined(PNG_READ_OPT_PLTE_SUPPORTED)
+   else if (png_crc_error(png_ptr))  /* Only if we have a CRC error */
+   {
+      /* If we don't want to use the data from an ancillary chunk,
+         we have two options: an error abort, or a warning and we
+         ignore the data in this chunk (which should be OK, since
+         it's considered ancillary for a RGB or RGBA image). */
+      if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE))
+      {
+         if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)
+         {
+            png_chunk_error(png_ptr, "CRC error");
+         }
+         else
+         {
+            png_chunk_warning(png_ptr, "CRC error");
+            return;
+         }
+      }
+      /* Otherwise, we (optionally) emit a warning and use the chunk. */
+      else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN))
+      {
+         png_chunk_warning(png_ptr, "CRC error");
+      }
+   }
+#endif
+
+   png_set_PLTE(png_ptr, info_ptr, palette, num);
+
+#if defined(PNG_READ_tRNS_SUPPORTED)
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS))
+      {
+         if (png_ptr->num_trans > (png_uint_16)num)
+         {
+            png_warning(png_ptr, "Truncating incorrect tRNS chunk length");
+            png_ptr->num_trans = (png_uint_16)num;
+         }
+         if (info_ptr->num_trans > (png_uint_16)num)
+         {
+            png_warning(png_ptr, "Truncating incorrect info tRNS chunk length");
+            info_ptr->num_trans = (png_uint_16)num;
+         }
+      }
+   }
+#endif
+
+}
+
+void /* PRIVATE */
+png_handle_IEND(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_debug(1, "in png_handle_IEND\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT))
+   {
+      png_error(png_ptr, "No image in file");
+   }
+
+   png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND);
+
+   if (length != 0)
+   {
+      png_warning(png_ptr, "Incorrect IEND chunk length");
+   }
+   png_crc_finish(png_ptr, length);
+
+   if (&info_ptr == NULL) /* quiet compiler warnings about unused info_ptr */
+      return;
+}
+
+#if defined(PNG_READ_gAMA_SUPPORTED)
+void /* PRIVATE */
+png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_fixed_point igamma;
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   float file_gamma;
+#endif
+   png_byte buf[4];
+
+   png_debug(1, "in png_handle_gAMA\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before gAMA");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid gAMA after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      /* Should be an error, but we can cope with it */
+      png_warning(png_ptr, "Out of place gAMA chunk");
+
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      && !(info_ptr->valid & PNG_INFO_sRGB)
+#endif
+      )
+   {
+      png_warning(png_ptr, "Duplicate gAMA chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 4)
+   {
+      png_warning(png_ptr, "Incorrect gAMA chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 4);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   igamma = (png_fixed_point)png_get_uint_32(buf);
+   /* check for zero gamma */
+   if (igamma == 0)
+      {
+         png_warning(png_ptr,
+           "Ignoring gAMA chunk with gamma=0");
+         return;
+      }
+
+#if defined(PNG_READ_sRGB_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_sRGB)
+      if (PNG_OUT_OF_RANGE(igamma, 45500L, 500))
+      {
+         png_warning(png_ptr,
+           "Ignoring incorrect gAMA value when sRGB is also present");
+#ifndef PNG_NO_CONSOLE_IO
+         fprintf(stderr, "gamma = (%d/100000)\n", (int)igamma);
+#endif
+         return;
+      }
+#endif /* PNG_READ_sRGB_SUPPORTED */
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   file_gamma = (float)igamma / (float)100000.0;
+#  ifdef PNG_READ_GAMMA_SUPPORTED
+     png_ptr->gamma = file_gamma;
+#  endif
+     png_set_gAMA(png_ptr, info_ptr, file_gamma);
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   png_set_gAMA_fixed(png_ptr, info_ptr, igamma);
+#endif
+}
+#endif
+
+#if defined(PNG_READ_sBIT_SUPPORTED)
+void /* PRIVATE */
+png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_size_t truelen;
+   png_byte buf[4];
+
+   png_debug(1, "in png_handle_sBIT\n");
+
+   buf[0] = buf[1] = buf[2] = buf[3] = 0;
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before sBIT");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid sBIT after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+   {
+      /* Should be an error, but we can cope with it */
+      png_warning(png_ptr, "Out of place sBIT chunk");
+   }
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT))
+   {
+      png_warning(png_ptr, "Duplicate sBIT chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      truelen = 3;
+   else
+      truelen = (png_size_t)png_ptr->channels;
+
+   if (length != truelen || length > 4)
+   {
+      png_warning(png_ptr, "Incorrect sBIT chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, truelen);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+   {
+      png_ptr->sig_bit.red = buf[0];
+      png_ptr->sig_bit.green = buf[1];
+      png_ptr->sig_bit.blue = buf[2];
+      png_ptr->sig_bit.alpha = buf[3];
+   }
+   else
+   {
+      png_ptr->sig_bit.gray = buf[0];
+      png_ptr->sig_bit.red = buf[0];
+      png_ptr->sig_bit.green = buf[0];
+      png_ptr->sig_bit.blue = buf[0];
+      png_ptr->sig_bit.alpha = buf[1];
+   }
+   png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit));
+}
+#endif
+
+#if defined(PNG_READ_cHRM_SUPPORTED)
+void /* PRIVATE */
+png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[4];
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y;
+#endif
+   png_fixed_point int_x_white, int_y_white, int_x_red, int_y_red, int_x_green,
+      int_y_green, int_x_blue, int_y_blue;
+
+   png_uint_32 uint_x, uint_y;
+
+   png_debug(1, "in png_handle_cHRM\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before cHRM");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid cHRM after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      /* Should be an error, but we can cope with it */
+      png_warning(png_ptr, "Missing PLTE before cHRM");
+
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      && !(info_ptr->valid & PNG_INFO_sRGB)
+#endif
+      )
+   {
+      png_warning(png_ptr, "Duplicate cHRM chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 32)
+   {
+      png_warning(png_ptr, "Incorrect cHRM chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_x = png_get_uint_32(buf);
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_y = png_get_uint_32(buf);
+
+   if (uint_x > 80000L || uint_y > 80000L ||
+      uint_x + uint_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid cHRM white point");
+      png_crc_finish(png_ptr, 24);
+      return;
+   }
+   int_x_white = (png_fixed_point)uint_x;
+   int_y_white = (png_fixed_point)uint_y;
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_x = png_get_uint_32(buf);
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_y = png_get_uint_32(buf);
+
+   if (uint_x > 80000L || uint_y > 80000L ||
+      uint_x + uint_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid cHRM red point");
+      png_crc_finish(png_ptr, 16);
+      return;
+   }
+   int_x_red = (png_fixed_point)uint_x;
+   int_y_red = (png_fixed_point)uint_y;
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_x = png_get_uint_32(buf);
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_y = png_get_uint_32(buf);
+
+   if (uint_x > 80000L || uint_y > 80000L ||
+      uint_x + uint_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid cHRM green point");
+      png_crc_finish(png_ptr, 8);
+      return;
+   }
+   int_x_green = (png_fixed_point)uint_x;
+   int_y_green = (png_fixed_point)uint_y;
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_x = png_get_uint_32(buf);
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_y = png_get_uint_32(buf);
+
+   if (uint_x > 80000L || uint_y > 80000L ||
+      uint_x + uint_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid cHRM blue point");
+      png_crc_finish(png_ptr, 0);
+      return;
+   }
+   int_x_blue = (png_fixed_point)uint_x;
+   int_y_blue = (png_fixed_point)uint_y;
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   white_x = (float)int_x_white / (float)100000.0;
+   white_y = (float)int_y_white / (float)100000.0;
+   red_x   = (float)int_x_red   / (float)100000.0;
+   red_y   = (float)int_y_red   / (float)100000.0;
+   green_x = (float)int_x_green / (float)100000.0;
+   green_y = (float)int_y_green / (float)100000.0;
+   blue_x  = (float)int_x_blue  / (float)100000.0;
+   blue_y  = (float)int_y_blue  / (float)100000.0;
+#endif
+
+#if defined(PNG_READ_sRGB_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_sRGB)
+      {
+      if (PNG_OUT_OF_RANGE(int_x_white, 31270,  1000) ||
+          PNG_OUT_OF_RANGE(int_y_white, 32900,  1000) ||
+          PNG_OUT_OF_RANGE(int_x_red,   64000L, 1000) ||
+          PNG_OUT_OF_RANGE(int_y_red,   33000,  1000) ||
+          PNG_OUT_OF_RANGE(int_x_green, 30000,  1000) ||
+          PNG_OUT_OF_RANGE(int_y_green, 60000L, 1000) ||
+          PNG_OUT_OF_RANGE(int_x_blue,  15000,  1000) ||
+          PNG_OUT_OF_RANGE(int_y_blue,   6000,  1000))
+         {
+
+            png_warning(png_ptr,
+              "Ignoring incorrect cHRM value when sRGB is also present");
+#ifndef PNG_NO_CONSOLE_IO
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+            fprintf(stderr,"wx=%f, wy=%f, rx=%f, ry=%f\n",
+               white_x, white_y, red_x, red_y);
+            fprintf(stderr,"gx=%f, gy=%f, bx=%f, by=%f\n",
+               green_x, green_y, blue_x, blue_y);
+#else
+            fprintf(stderr,"wx=%ld, wy=%ld, rx=%ld, ry=%ld\n",
+               int_x_white, int_y_white, int_x_red, int_y_red);
+            fprintf(stderr,"gx=%ld, gy=%ld, bx=%ld, by=%ld\n",
+               int_x_green, int_y_green, int_x_blue, int_y_blue);
+#endif
+#endif /* PNG_NO_CONSOLE_IO */
+         }
+         png_crc_finish(png_ptr, 0);
+         return;
+      }
+#endif /* PNG_READ_sRGB_SUPPORTED */
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   png_set_cHRM(png_ptr, info_ptr,
+      white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y);
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   png_set_cHRM_fixed(png_ptr, info_ptr,
+      int_x_white, int_y_white, int_x_red, int_y_red, int_x_green,
+      int_y_green, int_x_blue, int_y_blue);
+#endif
+   if (png_crc_finish(png_ptr, 0))
+      return;
+}
+#endif
+
+#if defined(PNG_READ_sRGB_SUPPORTED)
+void /* PRIVATE */
+png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   int intent;
+   png_byte buf[1];
+
+   png_debug(1, "in png_handle_sRGB\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before sRGB");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid sRGB after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      /* Should be an error, but we can cope with it */
+      png_warning(png_ptr, "Out of place sRGB chunk");
+
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB))
+   {
+      png_warning(png_ptr, "Duplicate sRGB chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 1)
+   {
+      png_warning(png_ptr, "Incorrect sRGB chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 1);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   intent = buf[0];
+   /* check for bad intent */
+   if (intent >= PNG_sRGB_INTENT_LAST)
+   {
+      png_warning(png_ptr, "Unknown sRGB intent");
+      return;
+   }
+
+#if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED)
+   if ((info_ptr->valid & PNG_INFO_gAMA))
+   {
+   png_fixed_point igamma;
+#ifdef PNG_FIXED_POINT_SUPPORTED
+      igamma=info_ptr->int_gamma;
+#else
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+      igamma=(png_fixed_point)(info_ptr->gamma * 100000.);
+#  endif
+#endif
+      if (PNG_OUT_OF_RANGE(igamma, 45500L, 500))
+      {
+         png_warning(png_ptr,
+           "Ignoring incorrect gAMA value when sRGB is also present");
+#ifndef PNG_NO_CONSOLE_IO
+#  ifdef PNG_FIXED_POINT_SUPPORTED
+         fprintf(stderr,"incorrect gamma=(%d/100000)\n",(int)png_ptr->int_gamma);
+#  else
+#    ifdef PNG_FLOATING_POINT_SUPPORTED
+         fprintf(stderr,"incorrect gamma=%f\n",png_ptr->gamma);
+#    endif
+#  endif
+#endif
+      }
+   }
+#endif /* PNG_READ_gAMA_SUPPORTED */
+
+#ifdef PNG_READ_cHRM_SUPPORTED
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   if (info_ptr->valid & PNG_INFO_cHRM)
+      if (PNG_OUT_OF_RANGE(info_ptr->int_x_white, 31270,  1000) ||
+          PNG_OUT_OF_RANGE(info_ptr->int_y_white, 32900,  1000) ||
+          PNG_OUT_OF_RANGE(info_ptr->int_x_red,   64000L, 1000) ||
+          PNG_OUT_OF_RANGE(info_ptr->int_y_red,   33000,  1000) ||
+          PNG_OUT_OF_RANGE(info_ptr->int_x_green, 30000,  1000) ||
+          PNG_OUT_OF_RANGE(info_ptr->int_y_green, 60000L, 1000) ||
+          PNG_OUT_OF_RANGE(info_ptr->int_x_blue,  15000,  1000) ||
+          PNG_OUT_OF_RANGE(info_ptr->int_y_blue,   6000,  1000))
+         {
+            png_warning(png_ptr,
+              "Ignoring incorrect cHRM value when sRGB is also present");
+         }
+#endif /* PNG_FIXED_POINT_SUPPORTED */
+#endif /* PNG_READ_cHRM_SUPPORTED */
+
+   png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent);
+}
+#endif /* PNG_READ_sRGB_SUPPORTED */
+
+#if defined(PNG_READ_iCCP_SUPPORTED)
+void /* PRIVATE */
+png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+/* Note: this does not properly handle chunks that are > 64K under DOS */
+{
+   png_charp chunkdata;
+   png_byte compression_type;
+   png_bytep pC;
+   png_charp profile;
+   png_uint_32 skip = 0;
+   png_uint_32 profile_size, profile_length;
+   png_size_t slength, prefix_length, data_length;
+
+   png_debug(1, "in png_handle_iCCP\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before iCCP");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid iCCP after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      /* Should be an error, but we can cope with it */
+      png_warning(png_ptr, "Out of place iCCP chunk");
+
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP))
+   {
+      png_warning(png_ptr, "Duplicate iCCP chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (length > (png_uint_32)65535L)
+   {
+      png_warning(png_ptr, "iCCP chunk too large to fit in memory");
+      skip = length - (png_uint_32)65535L;
+      length = (png_uint_32)65535L;
+   }
+#endif
+
+   chunkdata = (png_charp)png_malloc(png_ptr, length + 1);
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)chunkdata, slength);
+
+   if (png_crc_finish(png_ptr, skip))
+   {
+      png_free(png_ptr, chunkdata);
+      return;
+   }
+
+   chunkdata[slength] = 0x00;
+
+   for (profile = chunkdata; *profile; profile++)
+      /* empty loop to find end of name */ ;
+
+   ++profile;
+
+   /* there should be at least one zero (the compression type byte)
+      following the separator, and we should be on it  */
+   if ( profile >= chunkdata + slength)
+   {
+      png_free(png_ptr, chunkdata);
+      png_warning(png_ptr, "Malformed iCCP chunk");
+      return;
+   }
+
+   /* compression_type should always be zero */
+   compression_type = *profile++;
+   if (compression_type)
+   {
+      png_warning(png_ptr, "Ignoring nonzero compression type in iCCP chunk");
+      compression_type=0x00;  /* Reset it to zero (libpng-1.0.6 through 1.0.8
+                                 wrote nonzero) */
+   }
+
+   prefix_length = profile - chunkdata;
+   chunkdata = png_decompress_chunk(png_ptr, compression_type, chunkdata,
+                                    slength, prefix_length, &data_length);
+
+   profile_length = data_length - prefix_length;
+
+   if ( prefix_length > data_length || profile_length < 4)
+   {
+      png_free(png_ptr, chunkdata);
+      png_warning(png_ptr, "Profile size field missing from iCCP chunk");
+      return;
+   }
+
+   /* Check the profile_size recorded in the first 32 bits of the ICC profile */
+   pC = (png_bytep)(chunkdata+prefix_length);
+   profile_size = ((*(pC  ))<<24) |
+                  ((*(pC+1))<<16) |
+                  ((*(pC+2))<< 8) |
+                  ((*(pC+3))    );
+
+   if(profile_size < profile_length)
+      profile_length = profile_size;
+
+   if(profile_size > profile_length)
+   {
+      png_free(png_ptr, chunkdata);
+      png_warning(png_ptr, "Ignoring truncated iCCP profile.\n");
+      return;
+   }
+
+   png_set_iCCP(png_ptr, info_ptr, chunkdata, compression_type,
+                chunkdata + prefix_length, profile_length);
+   png_free(png_ptr, chunkdata);
+}
+#endif /* PNG_READ_iCCP_SUPPORTED */
+
+#if defined(PNG_READ_sPLT_SUPPORTED)
+void /* PRIVATE */
+png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+/* Note: this does not properly handle chunks that are > 64K under DOS */
+{
+   png_bytep chunkdata;
+   png_bytep entry_start;
+   png_sPLT_t new_palette;
+#ifdef PNG_NO_POINTER_INDEXING
+   png_sPLT_entryp pp;
+#endif
+   int data_length, entry_size, i;
+   png_uint_32 skip = 0;
+   png_size_t slength;
+
+   png_debug(1, "in png_handle_sPLT\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before sPLT");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid sPLT after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (length > (png_uint_32)65535L)
+   {
+      png_warning(png_ptr, "sPLT chunk too large to fit in memory");
+      skip = length - (png_uint_32)65535L;
+      length = (png_uint_32)65535L;
+   }
+#endif
+
+   chunkdata = (png_bytep)png_malloc(png_ptr, length + 1);
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)chunkdata, slength);
+
+   if (png_crc_finish(png_ptr, skip))
+   {
+      png_free(png_ptr, chunkdata);
+      return;
+   }
+
+   chunkdata[slength] = 0x00;
+
+   for (entry_start = chunkdata; *entry_start; entry_start++)
+      /* empty loop to find end of name */ ;
+   ++entry_start;
+
+   /* a sample depth should follow the separator, and we should be on it  */
+   if (entry_start > chunkdata + slength)
+   {
+      png_free(png_ptr, chunkdata);
+      png_warning(png_ptr, "malformed sPLT chunk");
+      return;
+   }
+
+   new_palette.depth = *entry_start++;
+   entry_size = (new_palette.depth == 8 ? 6 : 10);
+   data_length = (slength - (entry_start - chunkdata));
+
+   /* integrity-check the data length */
+   if (data_length % entry_size)
+   {
+      png_free(png_ptr, chunkdata);
+      png_warning(png_ptr, "sPLT chunk has bad length");
+      return;
+   }
+
+   new_palette.nentries = (png_uint_32) (data_length / entry_size);
+   if ((png_uint_32) new_palette.nentries > (png_uint_32) (PNG_SIZE_MAX /
+       png_sizeof(png_sPLT_entry)))
+   {
+       png_warning(png_ptr, "sPLT chunk too long");
+       return;
+   }
+   new_palette.entries = (png_sPLT_entryp)png_malloc_warn(
+       png_ptr, new_palette.nentries * png_sizeof(png_sPLT_entry));
+   if (new_palette.entries == NULL)
+   {
+       png_warning(png_ptr, "sPLT chunk requires too much memory");
+       return;
+   }
+
+#ifndef PNG_NO_POINTER_INDEXING
+   for (i = 0; i < new_palette.nentries; i++)
+   {
+      png_sPLT_entryp pp = new_palette.entries + i;
+
+      if (new_palette.depth == 8)
+      {
+          pp->red = *entry_start++;
+          pp->green = *entry_start++;
+          pp->blue = *entry_start++;
+          pp->alpha = *entry_start++;
+      }
+      else
+      {
+          pp->red   = png_get_uint_16(entry_start); entry_start += 2;
+          pp->green = png_get_uint_16(entry_start); entry_start += 2;
+          pp->blue  = png_get_uint_16(entry_start); entry_start += 2;
+          pp->alpha = png_get_uint_16(entry_start); entry_start += 2;
+      }
+      pp->frequency = png_get_uint_16(entry_start); entry_start += 2;
+   }
+#else
+   pp = new_palette.entries;
+   for (i = 0; i < new_palette.nentries; i++)
+   {
+
+      if (new_palette.depth == 8)
+      {
+          pp[i].red   = *entry_start++;
+          pp[i].green = *entry_start++;
+          pp[i].blue  = *entry_start++;
+          pp[i].alpha = *entry_start++;
+      }
+      else
+      {
+          pp[i].red   = png_get_uint_16(entry_start); entry_start += 2;
+          pp[i].green = png_get_uint_16(entry_start); entry_start += 2;
+          pp[i].blue  = png_get_uint_16(entry_start); entry_start += 2;
+          pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2;
+      }
+      pp->frequency = png_get_uint_16(entry_start); entry_start += 2;
+   }
+#endif
+
+   /* discard all chunk data except the name and stash that */
+   new_palette.name = (png_charp)chunkdata;
+
+   png_set_sPLT(png_ptr, info_ptr, &new_palette, 1);
+
+   png_free(png_ptr, chunkdata);
+   png_free(png_ptr, new_palette.entries);
+}
+#endif /* PNG_READ_sPLT_SUPPORTED */
+
+#if defined(PNG_READ_tRNS_SUPPORTED)
+void /* PRIVATE */
+png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte readbuf[PNG_MAX_PALETTE_LENGTH];
+
+   png_debug(1, "in png_handle_tRNS\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before tRNS");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid tRNS after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS))
+   {
+      png_warning(png_ptr, "Duplicate tRNS chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      png_byte buf[2];
+
+      if (length != 2)
+      {
+         png_warning(png_ptr, "Incorrect tRNS chunk length");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+
+      png_crc_read(png_ptr, buf, 2);
+      png_ptr->num_trans = 1;
+      png_ptr->trans_values.gray = png_get_uint_16(buf);
+   }
+   else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+   {
+      png_byte buf[6];
+
+      if (length != 6)
+      {
+         png_warning(png_ptr, "Incorrect tRNS chunk length");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+      png_crc_read(png_ptr, buf, (png_size_t)length);
+      png_ptr->num_trans = 1;
+      png_ptr->trans_values.red = png_get_uint_16(buf);
+      png_ptr->trans_values.green = png_get_uint_16(buf + 2);
+      png_ptr->trans_values.blue = png_get_uint_16(buf + 4);
+   }
+   else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (!(png_ptr->mode & PNG_HAVE_PLTE))
+      {
+         /* Should be an error, but we can cope with it. */
+         png_warning(png_ptr, "Missing PLTE before tRNS");
+      }
+      if (length > (png_uint_32)png_ptr->num_palette ||
+          length > PNG_MAX_PALETTE_LENGTH)
+      {
+         png_warning(png_ptr, "Incorrect tRNS chunk length");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+      if (length == 0)
+      {
+         png_warning(png_ptr, "Zero length tRNS chunk");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+      png_crc_read(png_ptr, readbuf, (png_size_t)length);
+      png_ptr->num_trans = (png_uint_16)length;
+   }
+   else
+   {
+      png_warning(png_ptr, "tRNS chunk not allowed with alpha channel");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans,
+      &(png_ptr->trans_values));
+}
+#endif
+
+#if defined(PNG_READ_bKGD_SUPPORTED)
+void /* PRIVATE */
+png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_size_t truelen;
+   png_byte buf[6];
+
+   png_debug(1, "in png_handle_bKGD\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before bKGD");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid bKGD after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+            !(png_ptr->mode & PNG_HAVE_PLTE))
+   {
+      png_warning(png_ptr, "Missing PLTE before bKGD");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD))
+   {
+      png_warning(png_ptr, "Duplicate bKGD chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      truelen = 1;
+   else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+      truelen = 6;
+   else
+      truelen = 2;
+
+   if (length != truelen)
+   {
+      png_warning(png_ptr, "Incorrect bKGD chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, truelen);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   /* We convert the index value into RGB components so that we can allow
+    * arbitrary RGB values for background when we have transparency, and
+    * so it is easy to determine the RGB values of the background color
+    * from the info_ptr struct. */
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      png_ptr->background.index = buf[0];
+      if(info_ptr->num_palette)
+      {
+          if(buf[0] > info_ptr->num_palette)
+          {
+             png_warning(png_ptr, "Incorrect bKGD chunk index value");
+             return;
+          }
+          png_ptr->background.red =
+             (png_uint_16)png_ptr->palette[buf[0]].red;
+          png_ptr->background.green =
+             (png_uint_16)png_ptr->palette[buf[0]].green;
+          png_ptr->background.blue =
+             (png_uint_16)png_ptr->palette[buf[0]].blue;
+      }
+   }
+   else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */
+   {
+      png_ptr->background.red =
+      png_ptr->background.green =
+      png_ptr->background.blue =
+      png_ptr->background.gray = png_get_uint_16(buf);
+   }
+   else
+   {
+      png_ptr->background.red = png_get_uint_16(buf);
+      png_ptr->background.green = png_get_uint_16(buf + 2);
+      png_ptr->background.blue = png_get_uint_16(buf + 4);
+   }
+
+   png_set_bKGD(png_ptr, info_ptr, &(png_ptr->background));
+}
+#endif
+
+#if defined(PNG_READ_hIST_SUPPORTED)
+void /* PRIVATE */
+png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   unsigned int num, i;
+   png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH];
+
+   png_debug(1, "in png_handle_hIST\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before hIST");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid hIST after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (!(png_ptr->mode & PNG_HAVE_PLTE))
+   {
+      png_warning(png_ptr, "Missing PLTE before hIST");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST))
+   {
+      png_warning(png_ptr, "Duplicate hIST chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   num = length / 2 ;
+   if (num != (unsigned int) png_ptr->num_palette || num >
+      (unsigned int) PNG_MAX_PALETTE_LENGTH)
+   {
+      png_warning(png_ptr, "Incorrect hIST chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   for (i = 0; i < num; i++)
+   {
+      png_byte buf[2];
+
+      png_crc_read(png_ptr, buf, 2);
+      readbuf[i] = png_get_uint_16(buf);
+   }
+
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   png_set_hIST(png_ptr, info_ptr, readbuf);
+}
+#endif
+
+#if defined(PNG_READ_pHYs_SUPPORTED)
+void /* PRIVATE */
+png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[9];
+   png_uint_32 res_x, res_y;
+   int unit_type;
+
+   png_debug(1, "in png_handle_pHYs\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before pHYs");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid pHYs after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+   {
+      png_warning(png_ptr, "Duplicate pHYs chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 9)
+   {
+      png_warning(png_ptr, "Incorrect pHYs chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 9);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   res_x = png_get_uint_32(buf);
+   res_y = png_get_uint_32(buf + 4);
+   unit_type = buf[8];
+   png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type);
+}
+#endif
+
+#if defined(PNG_READ_oFFs_SUPPORTED)
+void /* PRIVATE */
+png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[9];
+   png_int_32 offset_x, offset_y;
+   int unit_type;
+
+   png_debug(1, "in png_handle_oFFs\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before oFFs");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid oFFs after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+   {
+      png_warning(png_ptr, "Duplicate oFFs chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 9)
+   {
+      png_warning(png_ptr, "Incorrect oFFs chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 9);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   offset_x = png_get_int_32(buf);
+   offset_y = png_get_int_32(buf + 4);
+   unit_type = buf[8];
+   png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type);
+}
+#endif
+
+#if defined(PNG_READ_pCAL_SUPPORTED)
+/* read the pCAL chunk (described in the PNG Extensions document) */
+void /* PRIVATE */
+png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_charp purpose;
+   png_int_32 X0, X1;
+   png_byte type, nparams;
+   png_charp buf, units, endptr;
+   png_charpp params;
+   png_size_t slength;
+   int i;
+
+   png_debug(1, "in png_handle_pCAL\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before pCAL");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid pCAL after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL))
+   {
+      png_warning(png_ptr, "Duplicate pCAL chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_debug1(2, "Allocating and reading pCAL chunk data (%lu bytes)\n",
+      length + 1);
+   purpose = (png_charp)png_malloc_warn(png_ptr, length + 1);
+   if (purpose == NULL)
+     {
+       png_warning(png_ptr, "No memory for pCAL purpose.");
+       return;
+     }
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)purpose, slength);
+
+   if (png_crc_finish(png_ptr, 0))
+   {
+      png_free(png_ptr, purpose);
+      return;
+   }
+
+   purpose[slength] = 0x00; /* null terminate the last string */
+
+   png_debug(3, "Finding end of pCAL purpose string\n");
+   for (buf = purpose; *buf; buf++)
+      /* empty loop */ ;
+
+   endptr = purpose + slength;
+
+   /* We need to have at least 12 bytes after the purpose string
+      in order to get the parameter information. */
+   if (endptr <= buf + 12)
+   {
+      png_warning(png_ptr, "Invalid pCAL data");
+      png_free(png_ptr, purpose);
+      return;
+   }
+
+   png_debug(3, "Reading pCAL X0, X1, type, nparams, and units\n");
+   X0 = png_get_int_32((png_bytep)buf+1);
+   X1 = png_get_int_32((png_bytep)buf+5);
+   type = buf[9];
+   nparams = buf[10];
+   units = buf + 11;
+
+   png_debug(3, "Checking pCAL equation type and number of parameters\n");
+   /* Check that we have the right number of parameters for known
+      equation types. */
+   if ((type == PNG_EQUATION_LINEAR && nparams != 2) ||
+       (type == PNG_EQUATION_BASE_E && nparams != 3) ||
+       (type == PNG_EQUATION_ARBITRARY && nparams != 3) ||
+       (type == PNG_EQUATION_HYPERBOLIC && nparams != 4))
+   {
+      png_warning(png_ptr, "Invalid pCAL parameters for equation type");
+      png_free(png_ptr, purpose);
+      return;
+   }
+   else if (type >= PNG_EQUATION_LAST)
+   {
+      png_warning(png_ptr, "Unrecognized equation type for pCAL chunk");
+   }
+
+   for (buf = units; *buf; buf++)
+      /* Empty loop to move past the units string. */ ;
+
+   png_debug(3, "Allocating pCAL parameters array\n");
+   params = (png_charpp)png_malloc_warn(png_ptr, (png_uint_32)(nparams
+      *png_sizeof(png_charp))) ;
+   if (params == NULL)
+     {
+       png_free(png_ptr, purpose);
+       png_warning(png_ptr, "No memory for pCAL params.");
+       return;
+     }
+
+   /* Get pointers to the start of each parameter string. */
+   for (i = 0; i < (int)nparams; i++)
+   {
+      buf++; /* Skip the null string terminator from previous parameter. */
+
+      png_debug1(3, "Reading pCAL parameter %d\n", i);
+      for (params[i] = buf; *buf != 0x00 && buf <= endptr; buf++)
+         /* Empty loop to move past each parameter string */ ;
+
+      /* Make sure we haven't run out of data yet */
+      if (buf > endptr)
+      {
+         png_warning(png_ptr, "Invalid pCAL data");
+         png_free(png_ptr, purpose);
+         png_free(png_ptr, params);
+         return;
+      }
+   }
+
+   png_set_pCAL(png_ptr, info_ptr, purpose, X0, X1, type, nparams,
+      units, params);
+
+   png_free(png_ptr, purpose);
+   png_free(png_ptr, params);
+}
+#endif
+
+#if defined(PNG_READ_sCAL_SUPPORTED)
+/* read the sCAL chunk */
+void /* PRIVATE */
+png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_charp buffer, ep;
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   double width, height;
+   png_charp vp;
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   png_charp swidth, sheight;
+#endif
+#endif
+   png_size_t slength;
+
+   png_debug(1, "in png_handle_sCAL\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before sCAL");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid sCAL after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL))
+   {
+      png_warning(png_ptr, "Duplicate sCAL chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_debug1(2, "Allocating and reading sCAL chunk data (%lu bytes)\n",
+      length + 1);
+   buffer = (png_charp)png_malloc_warn(png_ptr, length + 1);
+   if (buffer == NULL)
+     {
+       png_warning(png_ptr, "Out of memory while processing sCAL chunk");
+       return;
+     }
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)buffer, slength);
+
+   if (png_crc_finish(png_ptr, 0))
+   {
+      png_free(png_ptr, buffer);
+      return;
+   }
+
+   buffer[slength] = 0x00; /* null terminate the last string */
+
+   ep = buffer + 1;        /* skip unit byte */
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   width = strtod(ep, &vp);
+   if (*vp)
+   {
+       png_warning(png_ptr, "malformed width string in sCAL chunk");
+       return;
+   }
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   swidth = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1);
+   if (swidth == NULL)
+     {
+       png_warning(png_ptr, "Out of memory while processing sCAL chunk width");
+       return;
+     }
+   png_memcpy(swidth, ep, (png_size_t)png_strlen(ep));
+#endif
+#endif
+
+   for (ep = buffer; *ep; ep++)
+      /* empty loop */ ;
+   ep++;
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   height = strtod(ep, &vp);
+   if (*vp)
+   {
+       png_warning(png_ptr, "malformed height string in sCAL chunk");
+       return;
+   }
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   sheight = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1);
+   if (swidth == NULL)
+     {
+       png_warning(png_ptr, "Out of memory while processing sCAL chunk height");
+       return;
+     }
+   png_memcpy(sheight, ep, (png_size_t)png_strlen(ep));
+#endif
+#endif
+
+   if (buffer + slength < ep
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+      || width <= 0. || height <= 0.
+#endif
+      )
+   {
+      png_warning(png_ptr, "Invalid sCAL data");
+      png_free(png_ptr, buffer);
+#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED)
+      png_free(png_ptr, swidth);
+      png_free(png_ptr, sheight);
+#endif
+      return;
+   }
+
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   png_set_sCAL(png_ptr, info_ptr, buffer[0], width, height);
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   png_set_sCAL_s(png_ptr, info_ptr, buffer[0], swidth, sheight);
+#endif
+#endif
+
+   png_free(png_ptr, buffer);
+#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED)
+   png_free(png_ptr, swidth);
+   png_free(png_ptr, sheight);
+#endif
+}
+#endif
+
+#if defined(PNG_READ_tIME_SUPPORTED)
+void /* PRIVATE */
+png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[7];
+   png_time mod_time;
+
+   png_debug(1, "in png_handle_tIME\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Out of place tIME chunk");
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME))
+   {
+      png_warning(png_ptr, "Duplicate tIME chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_ptr->mode & PNG_HAVE_IDAT)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+   if (length != 7)
+   {
+      png_warning(png_ptr, "Incorrect tIME chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 7);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   mod_time.second = buf[6];
+   mod_time.minute = buf[5];
+   mod_time.hour = buf[4];
+   mod_time.day = buf[3];
+   mod_time.month = buf[2];
+   mod_time.year = png_get_uint_16(buf);
+
+   png_set_tIME(png_ptr, info_ptr, &mod_time);
+}
+#endif
+
+#if defined(PNG_READ_tEXt_SUPPORTED)
+/* Note: this does not properly handle chunks that are > 64K under DOS */
+void /* PRIVATE */
+png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_textp text_ptr;
+   png_charp key;
+   png_charp text;
+   png_uint_32 skip = 0;
+   png_size_t slength;
+   int ret;
+
+   png_debug(1, "in png_handle_tEXt\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before tEXt");
+
+   if (png_ptr->mode & PNG_HAVE_IDAT)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (length > (png_uint_32)65535L)
+   {
+      png_warning(png_ptr, "tEXt chunk too large to fit in memory");
+      skip = length - (png_uint_32)65535L;
+      length = (png_uint_32)65535L;
+   }
+#endif
+
+   key = (png_charp)png_malloc_warn(png_ptr, length + 1);
+   if (key == NULL)
+   {
+     png_warning(png_ptr, "No memory to process text chunk.");
+     return;
+   }
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)key, slength);
+
+   if (png_crc_finish(png_ptr, skip))
+   {
+      png_free(png_ptr, key);
+      return;
+   }
+
+   key[slength] = 0x00;
+
+   for (text = key; *text; text++)
+      /* empty loop to find end of key */ ;
+
+   if (text != key + slength)
+      text++;
+
+   text_ptr = (png_textp)png_malloc_warn(png_ptr,
+      (png_uint_32)png_sizeof(png_text));
+   if (text_ptr == NULL)
+   {
+     png_warning(png_ptr, "Not enough memory to process text chunk.");
+     png_free(png_ptr, key);
+     return;
+   }
+   text_ptr->compression = PNG_TEXT_COMPRESSION_NONE;
+   text_ptr->key = key;
+#ifdef PNG_iTXt_SUPPORTED
+   text_ptr->lang = NULL;
+   text_ptr->lang_key = NULL;
+   text_ptr->itxt_length = 0;
+#endif
+   text_ptr->text = text;
+   text_ptr->text_length = png_strlen(text);
+
+   ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+   png_free(png_ptr, key);
+   png_free(png_ptr, text_ptr);
+   if (ret)
+     png_warning(png_ptr, "Insufficient memory to process text chunk.");
+}
+#endif
+
+#if defined(PNG_READ_zTXt_SUPPORTED)
+/* note: this does not correctly handle chunks that are > 64K under DOS */
+void /* PRIVATE */
+png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_textp text_ptr;
+   png_charp chunkdata;
+   png_charp text;
+   int comp_type;
+   int ret;
+   png_size_t slength, prefix_len, data_len;
+
+   png_debug(1, "in png_handle_zTXt\n");
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before zTXt");
+
+   if (png_ptr->mode & PNG_HAVE_IDAT)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+#ifdef PNG_MAX_MALLOC_64K
+   /* We will no doubt have problems with chunks even half this size, but
+      there is no hard and fast rule to tell us where to stop. */
+   if (length > (png_uint_32)65535L)
+   {
+     png_warning(png_ptr,"zTXt chunk too large to fit in memory");
+     png_crc_finish(png_ptr, length);
+     return;
+   }
+#endif
+
+   chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
+   if (chunkdata == NULL)
+   {
+     png_warning(png_ptr,"Out of memory processing zTXt chunk.");
+     return;
+   }
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)chunkdata, slength);
+   if (png_crc_finish(png_ptr, 0))
+   {
+      png_free(png_ptr, chunkdata);
+      return;
+   }
+
+   chunkdata[slength] = 0x00;
+
+   for (text = chunkdata; *text; text++)
+      /* empty loop */ ;
+
+   /* zTXt must have some text after the chunkdataword */
+   if (text == chunkdata + slength)
+   {
+      comp_type = PNG_TEXT_COMPRESSION_NONE;
+      png_warning(png_ptr, "Zero length zTXt chunk");
+   }
+   else
+   {
+       comp_type = *(++text);
+       if (comp_type != PNG_TEXT_COMPRESSION_zTXt)
+       {
+          png_warning(png_ptr, "Unknown compression type in zTXt chunk");
+          comp_type = PNG_TEXT_COMPRESSION_zTXt;
+       }
+       text++;        /* skip the compression_method byte */
+   }
+   prefix_len = text - chunkdata;
+
+   chunkdata = (png_charp)png_decompress_chunk(png_ptr, comp_type, chunkdata,
+                                    (png_size_t)length, prefix_len, &data_len);
+
+   text_ptr = (png_textp)png_malloc_warn(png_ptr,
+     (png_uint_32)png_sizeof(png_text));
+   if (text_ptr == NULL)
+   {
+     png_warning(png_ptr,"Not enough memory to process zTXt chunk.");
+     png_free(png_ptr, chunkdata);
+     return;
+   }
+   text_ptr->compression = comp_type;
+   text_ptr->key = chunkdata;
+#ifdef PNG_iTXt_SUPPORTED
+   text_ptr->lang = NULL;
+   text_ptr->lang_key = NULL;
+   text_ptr->itxt_length = 0;
+#endif
+   text_ptr->text = chunkdata + prefix_len;
+   text_ptr->text_length = data_len;
+
+   ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+   png_free(png_ptr, text_ptr);
+   png_free(png_ptr, chunkdata);
+   if (ret)
+     png_error(png_ptr, "Insufficient memory to store zTXt chunk.");
+}
+#endif
+
+#if defined(PNG_READ_iTXt_SUPPORTED)
+/* note: this does not correctly handle chunks that are > 64K under DOS */
+void /* PRIVATE */
+png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_textp text_ptr;
+   png_charp chunkdata;
+   png_charp key, lang, text, lang_key;
+   int comp_flag;
+   int comp_type = 0;
+   int ret;
+   png_size_t slength, prefix_len, data_len;
+
+   png_debug(1, "in png_handle_iTXt\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before iTXt");
+
+   if (png_ptr->mode & PNG_HAVE_IDAT)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+#ifdef PNG_MAX_MALLOC_64K
+   /* We will no doubt have problems with chunks even half this size, but
+      there is no hard and fast rule to tell us where to stop. */
+   if (length > (png_uint_32)65535L)
+   {
+     png_warning(png_ptr,"iTXt chunk too large to fit in memory");
+     png_crc_finish(png_ptr, length);
+     return;
+   }
+#endif
+
+   chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
+   if (chunkdata == NULL)
+   {
+     png_warning(png_ptr, "No memory to process iTXt chunk.");
+     return;
+   }
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)chunkdata, slength);
+   if (png_crc_finish(png_ptr, 0))
+   {
+      png_free(png_ptr, chunkdata);
+      return;
+   }
+
+   chunkdata[slength] = 0x00;
+
+   for (lang = chunkdata; *lang; lang++)
+      /* empty loop */ ;
+   lang++;        /* skip NUL separator */
+
+   /* iTXt must have a language tag (possibly empty), two compression bytes,
+      translated keyword (possibly empty), and possibly some text after the
+      keyword */
+
+   if (lang >= chunkdata + slength)
+   {
+      comp_flag = PNG_TEXT_COMPRESSION_NONE;
+      png_warning(png_ptr, "Zero length iTXt chunk");
+   }
+   else
+   {
+       comp_flag = *lang++;
+       comp_type = *lang++;
+   }
+
+   for (lang_key = lang; *lang_key; lang_key++)
+      /* empty loop */ ;
+   lang_key++;        /* skip NUL separator */
+
+   for (text = lang_key; *text; text++)
+      /* empty loop */ ;
+   text++;        /* skip NUL separator */
+
+   prefix_len = text - chunkdata;
+
+   key=chunkdata;
+   if (comp_flag)
+       chunkdata = png_decompress_chunk(png_ptr, comp_type, chunkdata,
+          (size_t)length, prefix_len, &data_len);
+   else
+       data_len=png_strlen(chunkdata + prefix_len);
+   text_ptr = (png_textp)png_malloc_warn(png_ptr,
+      (png_uint_32)png_sizeof(png_text));
+   if (text_ptr == NULL)
+   {
+     png_warning(png_ptr,"Not enough memory to process iTXt chunk.");
+     png_free(png_ptr, chunkdata);
+     return;
+   }
+   text_ptr->compression = (int)comp_flag + 1;
+   text_ptr->lang_key = chunkdata+(lang_key-key);
+   text_ptr->lang = chunkdata+(lang-key);
+   text_ptr->itxt_length = data_len;
+   text_ptr->text_length = 0;
+   text_ptr->key = chunkdata;
+   text_ptr->text = chunkdata + prefix_len;
+
+   ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+   png_free(png_ptr, text_ptr);
+   png_free(png_ptr, chunkdata);
+   if (ret)
+     png_error(png_ptr, "Insufficient memory to store iTXt chunk.");
+}
+#endif
+
+/* This function is called when we haven't found a handler for a
+   chunk.  If there isn't a problem with the chunk itself (ie bad
+   chunk name, CRC, or a critical chunk), the chunk is silently ignored
+   -- unless the PNG_FLAG_UNKNOWN_CHUNKS_SUPPORTED flag is on in which
+   case it will be saved away to be written out later. */
+void /* PRIVATE */
+png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_uint_32 skip = 0;
+
+   png_debug(1, "in png_handle_unknown\n");
+
+   if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+#ifdef PNG_USE_LOCAL_ARRAYS
+      PNG_IDAT;
+#endif
+      if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4))  /* not an IDAT */
+         png_ptr->mode |= PNG_AFTER_IDAT;
+   }
+
+   png_check_chunk_name(png_ptr, png_ptr->chunk_name);
+
+   if (!(png_ptr->chunk_name[0] & 0x20))
+   {
+#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
+      if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) !=
+           PNG_HANDLE_CHUNK_ALWAYS
+#if defined(PNG_READ_USER_CHUNKS_SUPPORTED)
+           && png_ptr->read_user_chunk_fn == NULL
+#endif
+        )
+#endif
+          png_chunk_error(png_ptr, "unknown critical chunk");
+   }
+
+#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
+   if (png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS)
+   {
+       png_unknown_chunk chunk;
+
+#ifdef PNG_MAX_MALLOC_64K
+       if (length > (png_uint_32)65535L)
+       {
+           png_warning(png_ptr, "unknown chunk too large to fit in memory");
+           skip = length - (png_uint_32)65535L;
+           length = (png_uint_32)65535L;
+       }
+#endif
+       png_strcpy((png_charp)chunk.name, (png_charp)png_ptr->chunk_name);
+       chunk.data = (png_bytep)png_malloc(png_ptr, length);
+       chunk.size = (png_size_t)length;
+       png_crc_read(png_ptr, (png_bytep)chunk.data, length);
+#if defined(PNG_READ_USER_CHUNKS_SUPPORTED)
+       if(png_ptr->read_user_chunk_fn != NULL)
+       {
+          /* callback to user unknown chunk handler */
+          if ((*(png_ptr->read_user_chunk_fn)) (png_ptr, &chunk) <= 0)
+          {
+             if (!(png_ptr->chunk_name[0] & 0x20))
+                if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) !=
+                     PNG_HANDLE_CHUNK_ALWAYS)
+                 {
+                   png_free(png_ptr, chunk.data);
+                   png_chunk_error(png_ptr, "unknown critical chunk");
+                 }
+             png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1);
+          }
+       }
+       else
+#endif
+          png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1);
+       png_free(png_ptr, chunk.data);
+   }
+   else
+#endif
+      skip = length;
+
+   png_crc_finish(png_ptr, skip);
+
+#if !defined(PNG_READ_USER_CHUNKS_SUPPORTED)
+   if (&info_ptr == NULL) /* quiet compiler warnings about unused info_ptr */
+      return;
+#endif
+}
+
+/* This function is called to verify that a chunk name is valid.
+   This function can't have the "critical chunk check" incorporated
+   into it, since in the future we will need to be able to call user
+   functions to handle unknown critical chunks after we check that
+   the chunk name itself is valid. */
+
+#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
+
+void /* PRIVATE */
+png_check_chunk_name(png_structp png_ptr, png_bytep chunk_name)
+{
+   png_debug(1, "in png_check_chunk_name\n");
+   if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) ||
+       isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3]))
+   {
+      png_chunk_error(png_ptr, "invalid chunk type");
+   }
+}
+
+/* Combines the row recently read in with the existing pixels in the
+   row.  This routine takes care of alpha and transparency if requested.
+   This routine also handles the two methods of progressive display
+   of interlaced images, depending on the mask value.
+   The mask value describes which pixels are to be combined with
+   the row.  The pattern always repeats every 8 pixels, so just 8
+   bits are needed.  A one indicates the pixel is to be combined,
+   a zero indicates the pixel is to be skipped.  This is in addition
+   to any alpha or transparency value associated with the pixel.  If
+   you want all pixels to be combined, pass 0xff (255) in mask.  */
+#ifndef PNG_HAVE_ASSEMBLER_COMBINE_ROW
+void /* PRIVATE */
+png_combine_row(png_structp png_ptr, png_bytep row, int mask)
+{
+   png_debug(1,"in png_combine_row\n");
+   if (mask == 0xff)
+   {
+      png_memcpy(row, png_ptr->row_buf + 1,
+         PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->width));
+   }
+   else
+   {
+      switch (png_ptr->row_info.pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp = png_ptr->row_buf + 1;
+            png_bytep dp = row;
+            int s_inc, s_start, s_end;
+            int m = 0x80;
+            int shift;
+            png_uint_32 i;
+            png_uint_32 row_width = png_ptr->width;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+                s_start = 0;
+                s_end = 7;
+                s_inc = 1;
+            }
+            else
+#endif
+            {
+                s_start = 7;
+                s_end = 0;
+                s_inc = -1;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (m & mask)
+               {
+                  int value;
+
+                  value = (*sp >> shift) & 0x01;
+                  *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+         case 2:
+         {
+            png_bytep sp = png_ptr->row_buf + 1;
+            png_bytep dp = row;
+            int s_start, s_end, s_inc;
+            int m = 0x80;
+            int shift;
+            png_uint_32 i;
+            png_uint_32 row_width = png_ptr->width;
+            int value;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+            else
+#endif
+            {
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0x03;
+                  *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp = png_ptr->row_buf + 1;
+            png_bytep dp = row;
+            int s_start, s_end, s_inc;
+            int m = 0x80;
+            int shift;
+            png_uint_32 i;
+            png_uint_32 row_width = png_ptr->width;
+            int value;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+            else
+#endif
+            {
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            shift = s_start;
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0xf;
+                  *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+         default:
+         {
+            png_bytep sp = png_ptr->row_buf + 1;
+            png_bytep dp = row;
+            png_size_t pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+            png_uint_32 i;
+            png_uint_32 row_width = png_ptr->width;
+            png_byte m = 0x80;
+
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (m & mask)
+               {
+                  png_memcpy(dp, sp, pixel_bytes);
+               }
+
+               sp += pixel_bytes;
+               dp += pixel_bytes;
+
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+      }
+   }
+}
+#endif /* !PNG_HAVE_ASSEMBLER_COMBINE_ROW */
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+#ifndef PNG_HAVE_ASSEMBLER_READ_INTERLACE   /* else in pngvcrd.c, pnggccrd.c */
+/* OLD pre-1.0.9 interface:
+void png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass,
+   png_uint_32 transformations)
+ */
+void /* PRIVATE */
+png_do_read_interlace(png_structp png_ptr)
+{
+   png_row_infop row_info = &(png_ptr->row_info);
+   png_bytep row = png_ptr->row_buf + 1;
+   int pass = png_ptr->pass;
+   png_uint_32 transformations = png_ptr->transformations;
+#ifdef PNG_USE_LOCAL_ARRAYS
+   /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+   /* offset to next interlace block */
+   const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+#endif
+
+   png_debug(1,"in png_do_read_interlace (stock C version)\n");
+   if (row != NULL && row_info != NULL)
+   {
+      png_uint_32 final_width;
+
+      final_width = row_info->width * png_pass_inc[pass];
+
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3);
+            png_bytep dp = row + (png_size_t)((final_width - 1) >> 3);
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            int jstop = png_pass_inc[pass];
+            png_byte v;
+            png_uint_32 i;
+            int j;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+                sshift = (int)((row_info->width + 7) & 0x07);
+                dshift = (int)((final_width + 7) & 0x07);
+                s_start = 7;
+                s_end = 0;
+                s_inc = -1;
+            }
+            else
+#endif
+            {
+                sshift = 7 - (int)((row_info->width + 7) & 0x07);
+                dshift = 7 - (int)((final_width + 7) & 0x07);
+                s_start = 0;
+                s_end = 7;
+                s_inc = 1;
+            }
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               v = (png_byte)((*sp >> sshift) & 0x01);
+               for (j = 0; j < jstop; j++)
+               {
+                  *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+         case 2:
+         {
+            png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2);
+            png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2);
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            int jstop = png_pass_inc[pass];
+            png_uint_32 i;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (int)(((row_info->width + 3) & 0x03) << 1);
+               dshift = (int)(((final_width + 3) & 0x03) << 1);
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+            else
+#endif
+            {
+               sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1);
+               dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1);
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               png_byte v;
+               int j;
+
+               v = (png_byte)((*sp >> sshift) & 0x03);
+               for (j = 0; j < jstop; j++)
+               {
+                  *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1);
+            png_bytep dp = row + (png_size_t)((final_width - 1) >> 1);
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_uint_32 i;
+            int jstop = png_pass_inc[pass];
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (int)(((row_info->width + 1) & 0x01) << 2);
+               dshift = (int)(((final_width + 1) & 0x01) << 2);
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            else
+#endif
+            {
+               sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2);
+               dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2);
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               png_byte v = (png_byte)((*sp >> sshift) & 0xf);
+               int j;
+
+               for (j = 0; j < jstop; j++)
+               {
+                  *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+         default:
+         {
+            png_size_t pixel_bytes = (row_info->pixel_depth >> 3);
+            png_bytep sp = row + (png_size_t)(row_info->width - 1) * pixel_bytes;
+            png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes;
+
+            int jstop = png_pass_inc[pass];
+            png_uint_32 i;
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               png_byte v[8];
+               int j;
+
+               png_memcpy(v, sp, pixel_bytes);
+               for (j = 0; j < jstop; j++)
+               {
+                  png_memcpy(dp, v, pixel_bytes);
+                  dp -= pixel_bytes;
+               }
+               sp -= pixel_bytes;
+            }
+            break;
+         }
+      }
+      row_info->width = final_width;
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width);
+   }
+#if !defined(PNG_READ_PACKSWAP_SUPPORTED)
+   if (&transformations == NULL) /* silence compiler warning */
+      return;
+#endif
+}
+#endif /* !PNG_HAVE_ASSEMBLER_READ_INTERLACE */
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+
+#ifndef PNG_HAVE_ASSEMBLER_READ_FILTER_ROW
+void /* PRIVATE */
+png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep row,
+   png_bytep prev_row, int filter)
+{
+   png_debug(1, "in png_read_filter_row\n");
+   png_debug2(2,"row = %lu, filter = %d\n", png_ptr->row_number, filter);
+   switch (filter)
+   {
+      case PNG_FILTER_VALUE_NONE:
+         break;
+      case PNG_FILTER_VALUE_SUB:
+      {
+         png_uint_32 i;
+         png_uint_32 istop = row_info->rowbytes;
+         png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+         png_bytep rp = row + bpp;
+         png_bytep lp = row;
+
+         for (i = bpp; i < istop; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff);
+            rp++;
+         }
+         break;
+      }
+      case PNG_FILTER_VALUE_UP:
+      {
+         png_uint_32 i;
+         png_uint_32 istop = row_info->rowbytes;
+         png_bytep rp = row;
+         png_bytep pp = prev_row;
+
+         for (i = 0; i < istop; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+            rp++;
+         }
+         break;
+      }
+      case PNG_FILTER_VALUE_AVG:
+      {
+         png_uint_32 i;
+         png_bytep rp = row;
+         png_bytep pp = prev_row;
+         png_bytep lp = row;
+         png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+         png_uint_32 istop = row_info->rowbytes - bpp;
+
+         for (i = 0; i < bpp; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) +
+               ((int)(*pp++) / 2 )) & 0xff);
+            rp++;
+         }
+
+         for (i = 0; i < istop; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) +
+               (int)(*pp++ + *lp++) / 2 ) & 0xff);
+            rp++;
+         }
+         break;
+      }
+      case PNG_FILTER_VALUE_PAETH:
+      {
+         png_uint_32 i;
+         png_bytep rp = row;
+         png_bytep pp = prev_row;
+         png_bytep lp = row;
+         png_bytep cp = prev_row;
+         png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+         png_uint_32 istop=row_info->rowbytes - bpp;
+
+         for (i = 0; i < bpp; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+            rp++;
+         }
+
+         for (i = 0; i < istop; i++)   /* use leftover rp,pp */
+         {
+            int a, b, c, pa, pb, pc, p;
+
+            a = *lp++;
+            b = *pp++;
+            c = *cp++;
+
+            p = b - c;
+            pc = a - c;
+
+#ifdef PNG_USE_ABS
+            pa = abs(p);
+            pb = abs(pc);
+            pc = abs(p + pc);
+#else
+            pa = p < 0 ? -p : p;
+            pb = pc < 0 ? -pc : pc;
+            pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+            /*
+               if (pa <= pb && pa <= pc)
+                  p = a;
+               else if (pb <= pc)
+                  p = b;
+               else
+                  p = c;
+             */
+
+            p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
+
+            *rp = (png_byte)(((int)(*rp) + p) & 0xff);
+            rp++;
+         }
+         break;
+      }
+      default:
+         png_warning(png_ptr, "Ignoring bad adaptive filter type");
+         *row=0;
+         break;
+   }
+}
+#endif /* !PNG_HAVE_ASSEMBLER_READ_FILTER_ROW */
+
+void /* PRIVATE */
+png_read_finish_row(png_structp png_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* start of interlace block */
+   const int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* offset to next interlace block */
+   const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* start of interlace block in the y direction */
+   const int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* offset to next interlace block in the y direction */
+   const int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+#endif
+
+   png_debug(1, "in png_read_finish_row\n");
+   png_ptr->row_number++;
+   if (png_ptr->row_number < png_ptr->num_rows)
+      return;
+
+   if (png_ptr->interlaced)
+   {
+      png_ptr->row_number = 0;
+      png_memset_check(png_ptr, png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
+      do
+      {
+         png_ptr->pass++;
+         if (png_ptr->pass >= 7)
+            break;
+         png_ptr->iwidth = (png_ptr->width +
+            png_pass_inc[png_ptr->pass] - 1 -
+            png_pass_start[png_ptr->pass]) /
+            png_pass_inc[png_ptr->pass];
+
+         png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,
+            png_ptr->iwidth) + 1;
+
+         if (!(png_ptr->transformations & PNG_INTERLACE))
+         {
+            png_ptr->num_rows = (png_ptr->height +
+               png_pass_yinc[png_ptr->pass] - 1 -
+               png_pass_ystart[png_ptr->pass]) /
+               png_pass_yinc[png_ptr->pass];
+            if (!(png_ptr->num_rows))
+               continue;
+         }
+         else  /* if (png_ptr->transformations & PNG_INTERLACE) */
+            break;
+      } while (png_ptr->iwidth == 0);
+
+      if (png_ptr->pass < 7)
+         return;
+   }
+
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
+   {
+#ifdef PNG_USE_LOCAL_ARRAYS
+      PNG_IDAT;
+#endif
+      char extra;
+      int ret;
+
+      png_ptr->zstream.next_out = (Byte *)&extra;
+      png_ptr->zstream.avail_out = (uInt)1;
+      for(;;)
+      {
+         if (!(png_ptr->zstream.avail_in))
+         {
+            while (!png_ptr->idat_size)
+            {
+               png_byte chunk_length[4];
+
+               png_crc_finish(png_ptr, 0);
+
+               png_read_data(png_ptr, chunk_length, 4);
+               png_ptr->idat_size = png_get_uint_31(png_ptr, chunk_length);
+               png_reset_crc(png_ptr);
+               png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+               if (png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4))
+                  png_error(png_ptr, "Not enough image data");
+
+            }
+            png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size;
+            png_ptr->zstream.next_in = png_ptr->zbuf;
+            if (png_ptr->zbuf_size > png_ptr->idat_size)
+               png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size;
+            png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in);
+            png_ptr->idat_size -= png_ptr->zstream.avail_in;
+         }
+         ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+         if (ret == Z_STREAM_END)
+         {
+            if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in ||
+               png_ptr->idat_size)
+               png_warning(png_ptr, "Extra compressed data");
+            png_ptr->mode |= PNG_AFTER_IDAT;
+            png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+            break;
+         }
+         if (ret != Z_OK)
+            png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg :
+                      "Decompression Error");
+
+         if (!(png_ptr->zstream.avail_out))
+         {
+            png_warning(png_ptr, "Extra compressed data.");
+            png_ptr->mode |= PNG_AFTER_IDAT;
+            png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+            break;
+         }
+
+      }
+      png_ptr->zstream.avail_out = 0;
+   }
+
+   if (png_ptr->idat_size || png_ptr->zstream.avail_in)
+      png_warning(png_ptr, "Extra compression data");
+
+   inflateReset(&png_ptr->zstream);
+
+   png_ptr->mode |= PNG_AFTER_IDAT;
+}
+
+void /* PRIVATE */
+png_read_start_row(png_structp png_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* start of interlace block */
+   const int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* offset to next interlace block */
+   const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* start of interlace block in the y direction */
+   const int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* offset to next interlace block in the y direction */
+   const int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+#endif
+
+   int max_pixel_depth;
+   png_uint_32 row_bytes;
+
+   png_debug(1, "in png_read_start_row\n");
+   png_ptr->zstream.avail_in = 0;
+   png_init_read_transformations(png_ptr);
+   if (png_ptr->interlaced)
+   {
+      if (!(png_ptr->transformations & PNG_INTERLACE))
+         png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
+            png_pass_ystart[0]) / png_pass_yinc[0];
+      else
+         png_ptr->num_rows = png_ptr->height;
+
+      png_ptr->iwidth = (png_ptr->width +
+         png_pass_inc[png_ptr->pass] - 1 -
+         png_pass_start[png_ptr->pass]) /
+         png_pass_inc[png_ptr->pass];
+
+         row_bytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->iwidth) + 1;
+
+         png_ptr->irowbytes = (png_size_t)row_bytes;
+         if((png_uint_32)png_ptr->irowbytes != row_bytes)
+            png_error(png_ptr, "Rowbytes overflow in png_read_start_row");
+   }
+   else
+   {
+      png_ptr->num_rows = png_ptr->height;
+      png_ptr->iwidth = png_ptr->width;
+      png_ptr->irowbytes = png_ptr->rowbytes + 1;
+   }
+   max_pixel_depth = png_ptr->pixel_depth;
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+   if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8)
+      max_pixel_depth = 8;
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+   if (png_ptr->transformations & PNG_EXPAND)
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         if (png_ptr->num_trans)
+            max_pixel_depth = 32;
+         else
+            max_pixel_depth = 24;
+      }
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         if (max_pixel_depth < 8)
+            max_pixel_depth = 8;
+         if (png_ptr->num_trans)
+            max_pixel_depth *= 2;
+      }
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+      {
+         if (png_ptr->num_trans)
+         {
+            max_pixel_depth *= 4;
+            max_pixel_depth /= 3;
+         }
+      }
+   }
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED)
+   if (png_ptr->transformations & (PNG_FILLER))
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+         max_pixel_depth = 32;
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         if (max_pixel_depth <= 8)
+            max_pixel_depth = 16;
+         else
+            max_pixel_depth = 32;
+      }
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+      {
+         if (max_pixel_depth <= 32)
+            max_pixel_depth = 32;
+         else
+            max_pixel_depth = 64;
+      }
+   }
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+   if (png_ptr->transformations & PNG_GRAY_TO_RGB)
+   {
+      if (
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+        (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) ||
+#endif
+#if defined(PNG_READ_FILLER_SUPPORTED)
+        (png_ptr->transformations & (PNG_FILLER)) ||
+#endif
+        png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         if (max_pixel_depth <= 16)
+            max_pixel_depth = 32;
+         else
+            max_pixel_depth = 64;
+      }
+      else
+      {
+         if (max_pixel_depth <= 8)
+           {
+             if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+               max_pixel_depth = 32;
+             else
+               max_pixel_depth = 24;
+           }
+         else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            max_pixel_depth = 64;
+         else
+            max_pixel_depth = 48;
+      }
+   }
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \
+defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
+   if(png_ptr->transformations & PNG_USER_TRANSFORM)
+     {
+       int user_pixel_depth=png_ptr->user_transform_depth*
+         png_ptr->user_transform_channels;
+       if(user_pixel_depth > max_pixel_depth)
+         max_pixel_depth=user_pixel_depth;
+     }
+#endif
+
+   /* align the width on the next larger 8 pixels.  Mainly used
+      for interlacing */
+   row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7));
+   /* calculate the maximum bytes needed, adding a byte and a pixel
+      for safety's sake */
+   row_bytes = PNG_ROWBYTES(max_pixel_depth,row_bytes) +
+      1 + ((max_pixel_depth + 7) >> 3);
+#ifdef PNG_MAX_MALLOC_64K
+   if (row_bytes > (png_uint_32)65536L)
+      png_error(png_ptr, "This image requires a row greater than 64KB");
+#endif
+   png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes+64);
+   png_ptr->row_buf = png_ptr->big_row_buf+32;
+#if defined(PNG_DEBUG) && defined(PNG_USE_PNGGCCRD)
+   png_ptr->row_buf_size = row_bytes;
+#endif
+
+#ifdef PNG_MAX_MALLOC_64K
+   if ((png_uint_32)png_ptr->rowbytes + 1 > (png_uint_32)65536L)
+      png_error(png_ptr, "This image requires a row greater than 64KB");
+#endif
+   if ((png_uint_32)png_ptr->rowbytes > PNG_SIZE_MAX - 1)
+      png_error(png_ptr, "Row has too many bytes to allocate in memory.");
+   png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)(
+      png_ptr->rowbytes + 1));
+
+   png_memset_check(png_ptr, png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
+
+   png_debug1(3, "width = %lu,\n", png_ptr->width);
+   png_debug1(3, "height = %lu,\n", png_ptr->height);
+   png_debug1(3, "iwidth = %lu,\n", png_ptr->iwidth);
+   png_debug1(3, "num_rows = %lu\n", png_ptr->num_rows);
+   png_debug1(3, "rowbytes = %lu,\n", png_ptr->rowbytes);
+   png_debug1(3, "irowbytes = %lu,\n", png_ptr->irowbytes);
+
+   png_ptr->flags |= PNG_FLAG_ROW_INIT;
+}
diff --git a/Utilities/GDAL/frmts/png/libpng/pngset.c b/Utilities/GDAL/frmts/png/libpng/pngset.c
new file mode 100644
index 0000000000..5922cad0c5
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngset.c
@@ -0,0 +1,1219 @@
+
+/* pngset.c - storage of image information into info struct
+ *
+ * libpng 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * The functions here are used during reads to store data from the file
+ * into the info struct, and during writes to store application data
+ * into the info struct for writing into the file.  This abstracts the
+ * info struct and allows us to change the structure in the future.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_bKGD_SUPPORTED)
+void PNGAPI
+png_set_bKGD(png_structp png_ptr, png_infop info_ptr, png_color_16p background)
+{
+   png_debug1(1, "in %s storage function\n", "bKGD");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_memcpy(&(info_ptr->background), background, png_sizeof(png_color_16));
+   info_ptr->valid |= PNG_INFO_bKGD;
+}
+#endif
+
+#if defined(PNG_cHRM_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_cHRM(png_structp png_ptr, png_infop info_ptr,
+   double white_x, double white_y, double red_x, double red_y,
+   double green_x, double green_y, double blue_x, double blue_y)
+{
+   png_debug1(1, "in %s storage function\n", "cHRM");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (white_x < 0.0 || white_y < 0.0 ||
+         red_x < 0.0 ||   red_y < 0.0 ||
+       green_x < 0.0 || green_y < 0.0 ||
+        blue_x < 0.0 ||  blue_y < 0.0)
+   {
+      png_warning(png_ptr,
+        "Ignoring attempt to set negative chromaticity value");
+      return;
+   }
+   if (white_x > 21474.83 || white_y > 21474.83 ||
+         red_x > 21474.83 ||   red_y > 21474.83 ||
+       green_x > 21474.83 || green_y > 21474.83 ||
+        blue_x > 21474.83 ||  blue_y > 21474.83)
+   {
+      png_warning(png_ptr,
+        "Ignoring attempt to set chromaticity value exceeding 21474.83");
+      return;
+   }
+
+   info_ptr->x_white = (float)white_x;
+   info_ptr->y_white = (float)white_y;
+   info_ptr->x_red   = (float)red_x;
+   info_ptr->y_red   = (float)red_y;
+   info_ptr->x_green = (float)green_x;
+   info_ptr->y_green = (float)green_y;
+   info_ptr->x_blue  = (float)blue_x;
+   info_ptr->y_blue  = (float)blue_y;
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   info_ptr->int_x_white = (png_fixed_point)(white_x*100000.+0.5);
+   info_ptr->int_y_white = (png_fixed_point)(white_y*100000.+0.5);
+   info_ptr->int_x_red   = (png_fixed_point)(  red_x*100000.+0.5);
+   info_ptr->int_y_red   = (png_fixed_point)(  red_y*100000.+0.5);
+   info_ptr->int_x_green = (png_fixed_point)(green_x*100000.+0.5);
+   info_ptr->int_y_green = (png_fixed_point)(green_y*100000.+0.5);
+   info_ptr->int_x_blue  = (png_fixed_point)( blue_x*100000.+0.5);
+   info_ptr->int_y_blue  = (png_fixed_point)( blue_y*100000.+0.5);
+#endif
+   info_ptr->valid |= PNG_INFO_cHRM;
+}
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+void PNGAPI
+png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr,
+   png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x,
+   png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y,
+   png_fixed_point blue_x, png_fixed_point blue_y)
+{
+   png_debug1(1, "in %s storage function\n", "cHRM");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (white_x < 0 || white_y < 0 ||
+         red_x < 0 ||   red_y < 0 ||
+       green_x < 0 || green_y < 0 ||
+        blue_x < 0 ||  blue_y < 0)
+   {
+      png_warning(png_ptr,
+        "Ignoring attempt to set negative chromaticity value");
+      return;
+   }
+   if (white_x > (double) PNG_UINT_31_MAX ||
+       white_y > (double) PNG_UINT_31_MAX ||
+         red_x > (double) PNG_UINT_31_MAX ||
+         red_y > (double) PNG_UINT_31_MAX ||
+       green_x > (double) PNG_UINT_31_MAX ||
+       green_y > (double) PNG_UINT_31_MAX ||
+        blue_x > (double) PNG_UINT_31_MAX ||
+        blue_y > (double) PNG_UINT_31_MAX)
+   {
+      png_warning(png_ptr,
+        "Ignoring attempt to set chromaticity value exceeding 21474.83");
+      return;
+   }
+   info_ptr->int_x_white = white_x;
+   info_ptr->int_y_white = white_y;
+   info_ptr->int_x_red   = red_x;
+   info_ptr->int_y_red   = red_y;
+   info_ptr->int_x_green = green_x;
+   info_ptr->int_y_green = green_y;
+   info_ptr->int_x_blue  = blue_x;
+   info_ptr->int_y_blue  = blue_y;
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   info_ptr->x_white = (float)(white_x/100000.);
+   info_ptr->y_white = (float)(white_y/100000.);
+   info_ptr->x_red   = (float)(  red_x/100000.);
+   info_ptr->y_red   = (float)(  red_y/100000.);
+   info_ptr->x_green = (float)(green_x/100000.);
+   info_ptr->y_green = (float)(green_y/100000.);
+   info_ptr->x_blue  = (float)( blue_x/100000.);
+   info_ptr->y_blue  = (float)( blue_y/100000.);
+#endif
+   info_ptr->valid |= PNG_INFO_cHRM;
+}
+#endif
+#endif
+
+#if defined(PNG_gAMA_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma)
+{
+   double gamma;
+   png_debug1(1, "in %s storage function\n", "gAMA");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   /* Check for overflow */
+   if (file_gamma > 21474.83)
+   {
+      png_warning(png_ptr, "Limiting gamma to 21474.83");
+      gamma=21474.83;
+   }
+   else
+      gamma=file_gamma;
+   info_ptr->gamma = (float)gamma;
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   info_ptr->int_gamma = (int)(gamma*100000.+.5);
+#endif
+   info_ptr->valid |= PNG_INFO_gAMA;
+   if(gamma == 0.0)
+      png_warning(png_ptr, "Setting gamma=0");
+}
+#endif
+void PNGAPI
+png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point
+   int_gamma)
+{
+   png_fixed_point gamma;
+
+   png_debug1(1, "in %s storage function\n", "gAMA");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (int_gamma > (png_fixed_point) PNG_UINT_31_MAX)
+   {
+     png_warning(png_ptr, "Limiting gamma to 21474.83");
+     gamma=PNG_UINT_31_MAX;
+   }
+   else
+   {
+     if (int_gamma < 0)
+     {
+       png_warning(png_ptr, "Setting negative gamma to zero");
+       gamma=0;
+     }
+     else
+       gamma=int_gamma;
+   }
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   info_ptr->gamma = (float)(gamma/100000.);
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   info_ptr->int_gamma = gamma;
+#endif
+   info_ptr->valid |= PNG_INFO_gAMA;
+   if(gamma == 0)
+      png_warning(png_ptr, "Setting gamma=0");
+}
+#endif
+
+#if defined(PNG_hIST_SUPPORTED)
+void PNGAPI
+png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist)
+{
+   int i;
+
+   png_debug1(1, "in %s storage function\n", "hIST");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+   if (info_ptr->num_palette == 0)
+   {
+       png_warning(png_ptr,
+          "Palette size 0, hIST allocation skipped.");
+       return;
+   }
+
+#ifdef PNG_FREE_ME_SUPPORTED
+   png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0);
+#endif
+   /* Changed from info->num_palette to 256 in version 1.2.1 */
+   png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr,
+      (png_uint_32)(256 * png_sizeof (png_uint_16)));
+   if (png_ptr->hist == NULL)
+     {
+       png_warning(png_ptr, "Insufficient memory for hIST chunk data.");
+       return;
+     }
+
+   for (i = 0; i < info_ptr->num_palette; i++)
+       png_ptr->hist[i] = hist[i];
+   info_ptr->hist = png_ptr->hist;
+   info_ptr->valid |= PNG_INFO_hIST;
+
+#ifdef PNG_FREE_ME_SUPPORTED
+   info_ptr->free_me |= PNG_FREE_HIST;
+#else
+   png_ptr->flags |= PNG_FLAG_FREE_HIST;
+#endif
+}
+#endif
+
+void PNGAPI
+png_set_IHDR(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 width, png_uint_32 height, int bit_depth,
+   int color_type, int interlace_type, int compression_type,
+   int filter_type)
+{
+   png_debug1(1, "in %s storage function\n", "IHDR");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   /* check for width and height valid values */
+   if (width == 0 || height == 0)
+      png_error(png_ptr, "Image width or height is zero in IHDR");
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   if (width > png_ptr->user_width_max || height > png_ptr->user_height_max)
+      png_error(png_ptr, "image size exceeds user limits in IHDR");
+#else
+   if (width > PNG_USER_WIDTH_MAX || height > PNG_USER_HEIGHT_MAX)
+      png_error(png_ptr, "image size exceeds user limits in IHDR");
+#endif
+   if (width > PNG_UINT_31_MAX || height > PNG_UINT_31_MAX)
+      png_error(png_ptr, "Invalid image size in IHDR");
+   if ( width > (PNG_UINT_32_MAX
+                 >> 3)      /* 8-byte RGBA pixels */
+                 - 64       /* bigrowbuf hack */
+                 - 1        /* filter byte */
+                 - 7*8      /* rounding of width to multiple of 8 pixels */
+                 - 8)       /* extra max_pixel_depth pad */
+      png_warning(png_ptr, "Width is too large for libpng to process pixels");
+
+   /* check other values */
+   if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 &&
+      bit_depth != 8 && bit_depth != 16)
+      png_error(png_ptr, "Invalid bit depth in IHDR");
+
+   if (color_type < 0 || color_type == 1 ||
+      color_type == 5 || color_type > 6)
+      png_error(png_ptr, "Invalid color type in IHDR");
+
+   if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) ||
+       ((color_type == PNG_COLOR_TYPE_RGB ||
+         color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
+         color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8))
+      png_error(png_ptr, "Invalid color type/bit depth combination in IHDR");
+
+   if (interlace_type >= PNG_INTERLACE_LAST)
+      png_error(png_ptr, "Unknown interlace method in IHDR");
+
+   if (compression_type != PNG_COMPRESSION_TYPE_BASE)
+      png_error(png_ptr, "Unknown compression method in IHDR");
+
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   /* Accept filter_method 64 (intrapixel differencing) only if
+    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
+    * 2. Libpng did not read a PNG signature (this filter_method is only
+    *    used in PNG datastreams that are embedded in MNG datastreams) and
+    * 3. The application called png_permit_mng_features with a mask that
+    *    included PNG_FLAG_MNG_FILTER_64 and
+    * 4. The filter_method is 64 and
+    * 5. The color_type is RGB or RGBA
+    */
+   if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&png_ptr->mng_features_permitted)
+      png_warning(png_ptr,"MNG features are not allowed in a PNG datastream\n");
+   if(filter_type != PNG_FILTER_TYPE_BASE)
+   {
+     if(!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+        (filter_type == PNG_INTRAPIXEL_DIFFERENCING) &&
+        ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) &&
+        (color_type == PNG_COLOR_TYPE_RGB || 
+         color_type == PNG_COLOR_TYPE_RGB_ALPHA)))
+        png_error(png_ptr, "Unknown filter method in IHDR");
+     if(png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)
+        png_warning(png_ptr, "Invalid filter method in IHDR");
+   }
+#else
+   if(filter_type != PNG_FILTER_TYPE_BASE)
+      png_error(png_ptr, "Unknown filter method in IHDR");
+#endif
+
+   info_ptr->width = width;
+   info_ptr->height = height;
+   info_ptr->bit_depth = (png_byte)bit_depth;
+   info_ptr->color_type =(png_byte) color_type;
+   info_ptr->compression_type = (png_byte)compression_type;
+   info_ptr->filter_type = (png_byte)filter_type;
+   info_ptr->interlace_type = (png_byte)interlace_type;
+   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      info_ptr->channels = 1;
+   else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
+      info_ptr->channels = 3;
+   else
+      info_ptr->channels = 1;
+   if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
+      info_ptr->channels++;
+   info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth);
+
+   /* check for potential overflow */
+   if ( width > (PNG_UINT_32_MAX
+                 >> 3)      /* 8-byte RGBA pixels */
+                 - 64       /* bigrowbuf hack */
+                 - 1        /* filter byte */
+                 - 7*8      /* rounding of width to multiple of 8 pixels */
+                 - 8)       /* extra max_pixel_depth pad */
+      info_ptr->rowbytes = (png_size_t)0;
+   else
+      info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,width);
+}
+
+#if defined(PNG_oFFs_SUPPORTED)
+void PNGAPI
+png_set_oFFs(png_structp png_ptr, png_infop info_ptr,
+   png_int_32 offset_x, png_int_32 offset_y, int unit_type)
+{
+   png_debug1(1, "in %s storage function\n", "oFFs");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->x_offset = offset_x;
+   info_ptr->y_offset = offset_y;
+   info_ptr->offset_unit_type = (png_byte)unit_type;
+   info_ptr->valid |= PNG_INFO_oFFs;
+}
+#endif
+
+#if defined(PNG_pCAL_SUPPORTED)
+void PNGAPI
+png_set_pCAL(png_structp png_ptr, png_infop info_ptr,
+   png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams,
+   png_charp units, png_charpp params)
+{
+   png_uint_32 length;
+   int i;
+
+   png_debug1(1, "in %s storage function\n", "pCAL");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   length = png_strlen(purpose) + 1;
+   png_debug1(3, "allocating purpose for info (%lu bytes)\n", length);
+   info_ptr->pcal_purpose = (png_charp)png_malloc_warn(png_ptr, length);
+   if (info_ptr->pcal_purpose == NULL)
+     {
+       png_warning(png_ptr, "Insufficient memory for pCAL purpose.");
+       return;
+     }
+   png_memcpy(info_ptr->pcal_purpose, purpose, (png_size_t)length);
+
+   png_debug(3, "storing X0, X1, type, and nparams in info\n");
+   info_ptr->pcal_X0 = X0;
+   info_ptr->pcal_X1 = X1;
+   info_ptr->pcal_type = (png_byte)type;
+   info_ptr->pcal_nparams = (png_byte)nparams;
+
+   length = png_strlen(units) + 1;
+   png_debug1(3, "allocating units for info (%lu bytes)\n", length);
+   info_ptr->pcal_units = (png_charp)png_malloc_warn(png_ptr, length);
+   if (info_ptr->pcal_units == NULL)
+     {
+       png_warning(png_ptr, "Insufficient memory for pCAL units.");
+       return;
+     }
+   png_memcpy(info_ptr->pcal_units, units, (png_size_t)length);
+
+   info_ptr->pcal_params = (png_charpp)png_malloc_warn(png_ptr,
+      (png_uint_32)((nparams + 1) * png_sizeof(png_charp)));
+   if (info_ptr->pcal_params == NULL)
+     {
+       png_warning(png_ptr, "Insufficient memory for pCAL params.");
+       return;
+     }
+
+   info_ptr->pcal_params[nparams] = NULL;
+
+   for (i = 0; i < nparams; i++)
+   {
+      length = png_strlen(params[i]) + 1;
+      png_debug2(3, "allocating parameter %d for info (%lu bytes)\n", i, length);
+      info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length);
+      if (info_ptr->pcal_params[i] == NULL)
+        {
+          png_warning(png_ptr, "Insufficient memory for pCAL parameter.");
+          return;
+        }
+      png_memcpy(info_ptr->pcal_params[i], params[i], (png_size_t)length);
+   }
+
+   info_ptr->valid |= PNG_INFO_pCAL;
+#ifdef PNG_FREE_ME_SUPPORTED
+   info_ptr->free_me |= PNG_FREE_PCAL;
+#endif
+}
+#endif
+
+#if defined(PNG_READ_sCAL_SUPPORTED) || defined(PNG_WRITE_sCAL_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_sCAL(png_structp png_ptr, png_infop info_ptr,
+             int unit, double width, double height)
+{
+   png_debug1(1, "in %s storage function\n", "sCAL");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->scal_unit = (png_byte)unit;
+   info_ptr->scal_pixel_width = width;
+   info_ptr->scal_pixel_height = height;
+
+   info_ptr->valid |= PNG_INFO_sCAL;
+}
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+void PNGAPI
+png_set_sCAL_s(png_structp png_ptr, png_infop info_ptr,
+             int unit, png_charp swidth, png_charp sheight)
+{
+   png_uint_32 length;
+
+   png_debug1(1, "in %s storage function\n", "sCAL");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->scal_unit = (png_byte)unit;
+
+   length = png_strlen(swidth) + 1;
+   png_debug1(3, "allocating unit for info (%d bytes)\n", length);
+   info_ptr->scal_s_width = (png_charp)png_malloc_warn(png_ptr, length);
+   if (info_ptr->scal_s_width == NULL)
+   {
+      png_warning(png_ptr, "Memory allocation failed while processing sCAL.");
+   }
+   png_memcpy(info_ptr->scal_s_width, swidth, (png_size_t)length);
+
+   length = png_strlen(sheight) + 1;
+   png_debug1(3, "allocating unit for info (%d bytes)\n", length);
+   info_ptr->scal_s_height = (png_charp)png_malloc_warn(png_ptr, length);
+   if (info_ptr->scal_s_height == NULL)
+   {
+      png_free (png_ptr, info_ptr->scal_s_width);
+      png_warning(png_ptr, "Memory allocation failed while processing sCAL.");
+   }
+   png_memcpy(info_ptr->scal_s_height, sheight, (png_size_t)length);
+
+   info_ptr->valid |= PNG_INFO_sCAL;
+#ifdef PNG_FREE_ME_SUPPORTED
+   info_ptr->free_me |= PNG_FREE_SCAL;
+#endif
+}
+#endif
+#endif
+#endif
+
+#if defined(PNG_pHYs_SUPPORTED)
+void PNGAPI
+png_set_pHYs(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 res_x, png_uint_32 res_y, int unit_type)
+{
+   png_debug1(1, "in %s storage function\n", "pHYs");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->x_pixels_per_unit = res_x;
+   info_ptr->y_pixels_per_unit = res_y;
+   info_ptr->phys_unit_type = (png_byte)unit_type;
+   info_ptr->valid |= PNG_INFO_pHYs;
+}
+#endif
+
+void PNGAPI
+png_set_PLTE(png_structp png_ptr, png_infop info_ptr,
+   png_colorp palette, int num_palette)
+{
+
+   png_debug1(1, "in %s storage function\n", "PLTE");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   /*
+    * It may not actually be necessary to set png_ptr->palette here;
+    * we do it for backward compatibility with the way the png_handle_tRNS
+    * function used to do the allocation.
+    */
+#ifdef PNG_FREE_ME_SUPPORTED
+   png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0);
+#endif
+
+   /* Changed in libpng-1.2.1 to allocate 256 instead of num_palette entries,
+      in case of an invalid PNG file that has too-large sample values. */
+   png_ptr->palette = (png_colorp)png_malloc(png_ptr,
+      256 * png_sizeof(png_color));
+   png_memset(png_ptr->palette, 0, 256 * png_sizeof(png_color));
+   png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof (png_color));
+   info_ptr->palette = png_ptr->palette;
+   info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette;
+
+#ifdef PNG_FREE_ME_SUPPORTED
+   info_ptr->free_me |= PNG_FREE_PLTE;
+#else
+   png_ptr->flags |= PNG_FLAG_FREE_PLTE;
+#endif
+
+   info_ptr->valid |= PNG_INFO_PLTE;
+}
+
+#if defined(PNG_sBIT_SUPPORTED)
+void PNGAPI
+png_set_sBIT(png_structp png_ptr, png_infop info_ptr,
+   png_color_8p sig_bit)
+{
+   png_debug1(1, "in %s storage function\n", "sBIT");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_memcpy(&(info_ptr->sig_bit), sig_bit, png_sizeof (png_color_8));
+   info_ptr->valid |= PNG_INFO_sBIT;
+}
+#endif
+
+#if defined(PNG_sRGB_SUPPORTED)
+void PNGAPI
+png_set_sRGB(png_structp png_ptr, png_infop info_ptr, int intent)
+{
+   png_debug1(1, "in %s storage function\n", "sRGB");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->srgb_intent = (png_byte)intent;
+   info_ptr->valid |= PNG_INFO_sRGB;
+}
+
+void PNGAPI
+png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr,
+   int intent)
+{
+#if defined(PNG_gAMA_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   float file_gamma;
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   png_fixed_point int_file_gamma;
+#endif
+#endif
+#if defined(PNG_cHRM_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y;
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, int_green_x,
+      int_green_y, int_blue_x, int_blue_y;
+#endif
+#endif
+   png_debug1(1, "in %s storage function\n", "sRGB_gAMA_and_cHRM");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_set_sRGB(png_ptr, info_ptr, intent);
+
+#if defined(PNG_gAMA_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   file_gamma = (float).45455;
+   png_set_gAMA(png_ptr, info_ptr, file_gamma);
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   int_file_gamma = 45455L;
+   png_set_gAMA_fixed(png_ptr, info_ptr, int_file_gamma);
+#endif
+#endif
+
+#if defined(PNG_cHRM_SUPPORTED)
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   int_white_x = 31270L;
+   int_white_y = 32900L;
+   int_red_x   = 64000L;
+   int_red_y   = 33000L;
+   int_green_x = 30000L;
+   int_green_y = 60000L;
+   int_blue_x  = 15000L;
+   int_blue_y  =  6000L;
+
+   png_set_cHRM_fixed(png_ptr, info_ptr,
+      int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, int_green_y,
+      int_blue_x, int_blue_y);
+#endif
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   white_x = (float).3127;
+   white_y = (float).3290;
+   red_x   = (float).64;
+   red_y   = (float).33;
+   green_x = (float).30;
+   green_y = (float).60;
+   blue_x  = (float).15;
+   blue_y  = (float).06;
+
+   png_set_cHRM(png_ptr, info_ptr,
+      white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y);
+#endif
+#endif
+}
+#endif
+
+
+#if defined(PNG_iCCP_SUPPORTED)
+void PNGAPI
+png_set_iCCP(png_structp png_ptr, png_infop info_ptr,
+             png_charp name, int compression_type,
+             png_charp profile, png_uint_32 proflen)
+{
+   png_charp new_iccp_name;
+   png_charp new_iccp_profile;
+
+   png_debug1(1, "in %s storage function\n", "iCCP");
+   if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL)
+      return;
+
+   new_iccp_name = (png_charp)png_malloc_warn(png_ptr, png_strlen(name)+1);
+   if (new_iccp_name == NULL)
+   {
+      png_warning(png_ptr, "Insufficient memory to process iCCP chunk.");
+      return;
+   }
+   png_strcpy(new_iccp_name, name);
+   new_iccp_profile = (png_charp)png_malloc_warn(png_ptr, proflen);
+   if (new_iccp_profile == NULL)
+   {
+      png_free (png_ptr, new_iccp_name);
+      png_warning(png_ptr, "Insufficient memory to process iCCP profile.");
+      return;
+   }
+   png_memcpy(new_iccp_profile, profile, (png_size_t)proflen);
+
+   png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0);
+
+   info_ptr->iccp_proflen = proflen;
+   info_ptr->iccp_name = new_iccp_name;
+   info_ptr->iccp_profile = new_iccp_profile;
+   /* Compression is always zero but is here so the API and info structure
+    * does not have to change if we introduce multiple compression types */
+   info_ptr->iccp_compression = (png_byte)compression_type;
+#ifdef PNG_FREE_ME_SUPPORTED
+   info_ptr->free_me |= PNG_FREE_ICCP;
+#endif
+   info_ptr->valid |= PNG_INFO_iCCP;
+}
+#endif
+
+#if defined(PNG_TEXT_SUPPORTED)
+void PNGAPI
+png_set_text(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr,
+   int num_text)
+{
+   int ret;
+   ret=png_set_text_2(png_ptr, info_ptr, text_ptr, num_text);
+   if (ret)
+     png_error(png_ptr, "Insufficient memory to store text");
+}
+
+int /* PRIVATE */
+png_set_text_2(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr,
+   int num_text)
+{
+   int i;
+
+   png_debug1(1, "in %s storage function\n", (png_ptr->chunk_name[0] == '\0' ?
+      "text" : (png_const_charp)png_ptr->chunk_name));
+
+   if (png_ptr == NULL || info_ptr == NULL || num_text == 0)
+      return(0);
+
+   /* Make sure we have enough space in the "text" array in info_struct
+    * to hold all of the incoming text_ptr objects.
+    */
+   if (info_ptr->num_text + num_text > info_ptr->max_text)
+   {
+      if (info_ptr->text != NULL)
+      {
+         png_textp old_text;
+         int old_max;
+
+         old_max = info_ptr->max_text;
+         info_ptr->max_text = info_ptr->num_text + num_text + 8;
+         old_text = info_ptr->text;
+         info_ptr->text = (png_textp)png_malloc_warn(png_ptr,
+            (png_uint_32)(info_ptr->max_text * png_sizeof (png_text)));
+         if (info_ptr->text == NULL)
+           {
+             png_free(png_ptr, old_text);
+             return(1);
+           }
+         png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max *
+            png_sizeof(png_text)));
+         png_free(png_ptr, old_text);
+      }
+      else
+      {
+         info_ptr->max_text = num_text + 8;
+         info_ptr->num_text = 0;
+         info_ptr->text = (png_textp)png_malloc_warn(png_ptr,
+            (png_uint_32)(info_ptr->max_text * png_sizeof (png_text)));
+         if (info_ptr->text == NULL)
+           return(1);
+#ifdef PNG_FREE_ME_SUPPORTED
+         info_ptr->free_me |= PNG_FREE_TEXT;
+#endif
+      }
+      png_debug1(3, "allocated %d entries for info_ptr->text\n",
+         info_ptr->max_text);
+   }
+   for (i = 0; i < num_text; i++)
+   {
+      png_size_t text_length,key_len;
+      png_size_t lang_len,lang_key_len;
+      png_textp textp = &(info_ptr->text[info_ptr->num_text]);
+
+      if (text_ptr[i].key == NULL)
+          continue;
+
+      key_len = png_strlen(text_ptr[i].key);
+
+      if(text_ptr[i].compression <= 0)
+      {
+        lang_len = 0;
+        lang_key_len = 0;
+      }
+      else
+#ifdef PNG_iTXt_SUPPORTED
+      {
+        /* set iTXt data */
+        if (text_ptr[i].lang != NULL)
+          lang_len = png_strlen(text_ptr[i].lang);
+        else
+          lang_len = 0;
+        if (text_ptr[i].lang_key != NULL)
+          lang_key_len = png_strlen(text_ptr[i].lang_key);
+        else
+          lang_key_len = 0;
+      }
+#else
+      {
+        png_warning(png_ptr, "iTXt chunk not supported.");
+        continue;
+      }
+#endif
+
+      if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0')
+      {
+         text_length = 0;
+#ifdef PNG_iTXt_SUPPORTED
+         if(text_ptr[i].compression > 0)
+            textp->compression = PNG_ITXT_COMPRESSION_NONE;
+         else
+#endif
+            textp->compression = PNG_TEXT_COMPRESSION_NONE;
+      }
+      else
+      {
+         text_length = png_strlen(text_ptr[i].text);
+         textp->compression = text_ptr[i].compression;
+      }
+
+      textp->key = (png_charp)png_malloc_warn(png_ptr,
+         (png_uint_32)(key_len + text_length + lang_len + lang_key_len + 4));
+      if (textp->key == NULL)
+        return(1);
+      png_debug2(2, "Allocated %lu bytes at %x in png_set_text\n",
+         (png_uint_32)(key_len + lang_len + lang_key_len + text_length + 4),
+         (int)textp->key);
+
+      png_memcpy(textp->key, text_ptr[i].key,
+         (png_size_t)(key_len));
+      *(textp->key+key_len) = '\0';
+#ifdef PNG_iTXt_SUPPORTED
+      if (text_ptr[i].compression > 0)
+      {
+         textp->lang=textp->key + key_len + 1;
+         png_memcpy(textp->lang, text_ptr[i].lang, lang_len);
+         *(textp->lang+lang_len) = '\0';
+         textp->lang_key=textp->lang + lang_len + 1;
+         png_memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len);
+         *(textp->lang_key+lang_key_len) = '\0';
+         textp->text=textp->lang_key + lang_key_len + 1;
+      }
+      else
+#endif
+      {
+#ifdef PNG_iTXt_SUPPORTED
+         textp->lang=NULL;
+         textp->lang_key=NULL;
+#endif
+         textp->text=textp->key + key_len + 1;
+      }
+      if(text_length)
+         png_memcpy(textp->text, text_ptr[i].text,
+            (png_size_t)(text_length));
+      *(textp->text+text_length) = '\0';
+
+#ifdef PNG_iTXt_SUPPORTED
+      if(textp->compression > 0)
+      {
+         textp->text_length = 0;
+         textp->itxt_length = text_length;
+      }
+      else
+#endif
+      {
+         textp->text_length = text_length;
+#ifdef PNG_iTXt_SUPPORTED
+         textp->itxt_length = 0;
+#endif
+      }
+      info_ptr->text[info_ptr->num_text]= *textp;
+      info_ptr->num_text++;
+      png_debug1(3, "transferred text chunk %d\n", info_ptr->num_text);
+   }
+   return(0);
+}
+#endif
+
+#if defined(PNG_tIME_SUPPORTED)
+void PNGAPI
+png_set_tIME(png_structp png_ptr, png_infop info_ptr, png_timep mod_time)
+{
+   png_debug1(1, "in %s storage function\n", "tIME");
+   if (png_ptr == NULL || info_ptr == NULL ||
+       (png_ptr->mode & PNG_WROTE_tIME))
+      return;
+
+   png_memcpy(&(info_ptr->mod_time), mod_time, png_sizeof (png_time));
+   info_ptr->valid |= PNG_INFO_tIME;
+}
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED)
+void PNGAPI
+png_set_tRNS(png_structp png_ptr, png_infop info_ptr,
+   png_bytep trans, int num_trans, png_color_16p trans_values)
+{
+   png_debug1(1, "in %s storage function\n", "tRNS");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (trans != NULL)
+   {
+       /*
+        * It may not actually be necessary to set png_ptr->trans here;
+        * we do it for backward compatibility with the way the png_handle_tRNS
+        * function used to do the allocation.
+        */
+#ifdef PNG_FREE_ME_SUPPORTED
+       png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);
+#endif
+       /* Changed from num_trans to 256 in version 1.2.1 */
+       png_ptr->trans = info_ptr->trans = (png_bytep)png_malloc(png_ptr,
+           (png_uint_32)256);
+       png_memcpy(info_ptr->trans, trans, (png_size_t)num_trans);
+#ifdef PNG_FREE_ME_SUPPORTED
+       info_ptr->free_me |= PNG_FREE_TRNS;
+#else
+       png_ptr->flags |= PNG_FLAG_FREE_TRNS;
+#endif
+   }
+
+   if (trans_values != NULL)
+   {
+      png_memcpy(&(info_ptr->trans_values), trans_values,
+         png_sizeof(png_color_16));
+      if (num_trans == 0)
+        num_trans = 1;
+   }
+   info_ptr->num_trans = (png_uint_16)num_trans;
+   info_ptr->valid |= PNG_INFO_tRNS;
+}
+#endif
+
+#if defined(PNG_sPLT_SUPPORTED)
+void PNGAPI
+png_set_sPLT(png_structp png_ptr,
+             png_infop info_ptr, png_sPLT_tp entries, int nentries)
+{
+    png_sPLT_tp np;
+    int i;
+
+    np = (png_sPLT_tp)png_malloc_warn(png_ptr,
+        (info_ptr->splt_palettes_num + nentries) * png_sizeof(png_sPLT_t));
+    if (np == NULL)
+    {
+      png_warning(png_ptr, "No memory for sPLT palettes.");
+      return;
+    }
+
+    png_memcpy(np, info_ptr->splt_palettes,
+           info_ptr->splt_palettes_num * png_sizeof(png_sPLT_t));
+    png_free(png_ptr, info_ptr->splt_palettes);
+    info_ptr->splt_palettes=NULL;
+
+    for (i = 0; i < nentries; i++)
+    {
+        png_sPLT_tp to = np + info_ptr->splt_palettes_num + i;
+        png_sPLT_tp from = entries + i;
+
+        to->name = (png_charp)png_malloc(png_ptr,
+            png_strlen(from->name) + 1);
+        /* TODO: use png_malloc_warn */
+        png_strcpy(to->name, from->name);
+        to->entries = (png_sPLT_entryp)png_malloc(png_ptr,
+            from->nentries * png_sizeof(png_sPLT_t));
+        /* TODO: use png_malloc_warn */
+        png_memcpy(to->entries, from->entries,
+            from->nentries * png_sizeof(png_sPLT_t));
+        to->nentries = from->nentries;
+        to->depth = from->depth;
+    }
+
+    info_ptr->splt_palettes = np;
+    info_ptr->splt_palettes_num += nentries;
+    info_ptr->valid |= PNG_INFO_sPLT;
+#ifdef PNG_FREE_ME_SUPPORTED
+    info_ptr->free_me |= PNG_FREE_SPLT;
+#endif
+}
+#endif /* PNG_sPLT_SUPPORTED */
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+void PNGAPI
+png_set_unknown_chunks(png_structp png_ptr,
+   png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)
+{
+    png_unknown_chunkp np;
+    int i;
+
+    if (png_ptr == NULL || info_ptr == NULL || num_unknowns == 0)
+        return;
+
+    np = (png_unknown_chunkp)png_malloc_warn(png_ptr,
+        (info_ptr->unknown_chunks_num + num_unknowns) *
+        png_sizeof(png_unknown_chunk));
+    if (np == NULL)
+    {
+       png_warning(png_ptr, "Out of memory while processing unknown chunk.");
+       return;
+    }
+
+    png_memcpy(np, info_ptr->unknown_chunks,
+           info_ptr->unknown_chunks_num * png_sizeof(png_unknown_chunk));
+    png_free(png_ptr, info_ptr->unknown_chunks);
+    info_ptr->unknown_chunks=NULL;
+
+    for (i = 0; i < num_unknowns; i++)
+    {
+        png_unknown_chunkp to = np + info_ptr->unknown_chunks_num + i;
+        png_unknown_chunkp from = unknowns + i;
+
+        png_strncpy((png_charp)to->name, (png_charp)from->name, 5);
+        to->data = (png_bytep)png_malloc_warn(png_ptr, from->size);
+        if (to->data == NULL)
+        {
+           png_warning(png_ptr, "Out of memory processing unknown chunk.");
+        }
+        else
+        {
+           png_memcpy(to->data, from->data, from->size);
+           to->size = from->size;
+
+           /* note our location in the read or write sequence */
+           to->location = (png_byte)(png_ptr->mode & 0xff);
+        }
+    }
+
+    info_ptr->unknown_chunks = np;
+    info_ptr->unknown_chunks_num += num_unknowns;
+#ifdef PNG_FREE_ME_SUPPORTED
+    info_ptr->free_me |= PNG_FREE_UNKN;
+#endif
+}
+void PNGAPI
+png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr,
+   int chunk, int location)
+{
+   if(png_ptr != NULL && info_ptr != NULL && chunk >= 0 && chunk <
+         (int)info_ptr->unknown_chunks_num)
+      info_ptr->unknown_chunks[chunk].location = (png_byte)location;
+}
+#endif
+
+#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \
+    defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED)
+void PNGAPI
+png_permit_empty_plte (png_structp png_ptr, int empty_plte_permitted)
+{
+   /* This function is deprecated in favor of png_permit_mng_features()
+      and will be removed from libpng-2.0.0 */
+   png_debug(1, "in png_permit_empty_plte, DEPRECATED.\n");
+   if (png_ptr == NULL)
+      return;
+   png_ptr->mng_features_permitted = (png_byte)
+     ((png_ptr->mng_features_permitted & (~(PNG_FLAG_MNG_EMPTY_PLTE))) |
+     ((empty_plte_permitted & PNG_FLAG_MNG_EMPTY_PLTE)));
+}
+#endif
+
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+png_uint_32 PNGAPI
+png_permit_mng_features (png_structp png_ptr, png_uint_32 mng_features)
+{
+   png_debug(1, "in png_permit_mng_features\n");
+   if (png_ptr == NULL)
+      return (png_uint_32)0;
+   png_ptr->mng_features_permitted =
+     (png_byte)(mng_features & PNG_ALL_MNG_FEATURES);
+   return (png_uint_32)png_ptr->mng_features_permitted;
+}
+#endif
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+void PNGAPI
+png_set_keep_unknown_chunks(png_structp png_ptr, int keep, png_bytep
+   chunk_list, int num_chunks)
+{
+    png_bytep new_list, p;
+    int i, old_num_chunks;
+    if (num_chunks == 0)
+    {
+      if(keep == PNG_HANDLE_CHUNK_ALWAYS || keep == PNG_HANDLE_CHUNK_IF_SAFE)
+        png_ptr->flags |= PNG_FLAG_KEEP_UNKNOWN_CHUNKS;
+      else
+        png_ptr->flags &= ~PNG_FLAG_KEEP_UNKNOWN_CHUNKS;
+
+      if(keep == PNG_HANDLE_CHUNK_ALWAYS)
+        png_ptr->flags |= PNG_FLAG_KEEP_UNSAFE_CHUNKS;
+      else
+        png_ptr->flags &= ~PNG_FLAG_KEEP_UNSAFE_CHUNKS;
+      return;
+    }
+    if (chunk_list == NULL)
+      return;
+    old_num_chunks=png_ptr->num_chunk_list;
+    new_list=(png_bytep)png_malloc(png_ptr,
+       (png_uint_32)(5*(num_chunks+old_num_chunks)));
+    if(png_ptr->chunk_list != NULL)
+    {
+       png_memcpy(new_list, png_ptr->chunk_list,
+          (png_size_t)(5*old_num_chunks));
+       png_free(png_ptr, png_ptr->chunk_list);
+       png_ptr->chunk_list=NULL;
+    }
+    png_memcpy(new_list+5*old_num_chunks, chunk_list,
+       (png_size_t)(5*num_chunks));
+    for (p=new_list+5*old_num_chunks+4, i=0; i<num_chunks; i++, p+=5)
+       *p=(png_byte)keep;
+    png_ptr->num_chunk_list=old_num_chunks+num_chunks;
+    png_ptr->chunk_list=new_list;
+#ifdef PNG_FREE_ME_SUPPORTED
+    png_ptr->free_me |= PNG_FREE_LIST;
+#endif
+}
+#endif
+
+#if defined(PNG_READ_USER_CHUNKS_SUPPORTED)
+void PNGAPI
+png_set_read_user_chunk_fn(png_structp png_ptr, png_voidp user_chunk_ptr,
+   png_user_chunk_ptr read_user_chunk_fn)
+{
+   png_debug(1, "in png_set_read_user_chunk_fn\n");
+   png_ptr->read_user_chunk_fn = read_user_chunk_fn;
+   png_ptr->user_chunk_ptr = user_chunk_ptr;
+}
+#endif
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+void PNGAPI
+png_set_rows(png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers)
+{
+   png_debug1(1, "in %s storage function\n", "rows");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if(info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers))
+      png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0);
+   info_ptr->row_pointers = row_pointers;
+   if(row_pointers)
+      info_ptr->valid |= PNG_INFO_IDAT;
+}
+#endif
+
+#ifdef PNG_WRITE_SUPPORTED
+void PNGAPI
+png_set_compression_buffer_size(png_structp png_ptr, png_uint_32 size)
+{
+    if(png_ptr->zbuf)
+       png_free(png_ptr, png_ptr->zbuf);
+    png_ptr->zbuf_size = (png_size_t)size;
+    png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size);
+    png_ptr->zstream.next_out = png_ptr->zbuf;
+    png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+}
+#endif
+
+void PNGAPI
+png_set_invalid(png_structp png_ptr, png_infop info_ptr, int mask)
+{
+   if (png_ptr && info_ptr)
+      info_ptr->valid &= ~(mask);
+}
+
+
+#ifndef PNG_1_0_X
+#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
+/* this function was added to libpng 1.2.0 and should always exist by default */
+void PNGAPI
+png_set_asm_flags (png_structp png_ptr, png_uint_32 asm_flags)
+{
+    png_uint_32 settable_asm_flags;
+    png_uint_32 settable_mmx_flags;
+
+    settable_mmx_flags =
+#ifdef PNG_HAVE_ASSEMBLER_COMBINE_ROW
+                         PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  |
+#endif
+#ifdef PNG_HAVE_ASSEMBLER_READ_INTERLACE
+                         PNG_ASM_FLAG_MMX_READ_INTERLACE    |
+#endif
+#ifdef PNG_HAVE_ASSEMBLER_READ_FILTER_ROW
+                         PNG_ASM_FLAG_MMX_READ_FILTER_SUB   |
+                         PNG_ASM_FLAG_MMX_READ_FILTER_UP    |
+                         PNG_ASM_FLAG_MMX_READ_FILTER_AVG   |
+                         PNG_ASM_FLAG_MMX_READ_FILTER_PAETH |
+#endif
+                         0;
+
+    /* could be some non-MMX ones in the future, but not currently: */
+    settable_asm_flags = settable_mmx_flags;
+
+    if (!(png_ptr->asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_COMPILED) ||
+        !(png_ptr->asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU))
+    {
+        /* clear all MMX flags if MMX isn't supported */
+        settable_asm_flags &= ~settable_mmx_flags;
+        png_ptr->asm_flags &= ~settable_mmx_flags;
+    }
+
+    /* we're replacing the settable bits with those passed in by the user,
+     * so first zero them out of the master copy, then logical-OR in the
+     * allowed subset that was requested */
+
+    png_ptr->asm_flags &= ~settable_asm_flags;               /* zero them */
+    png_ptr->asm_flags |= (asm_flags & settable_asm_flags);  /* set them */
+}
+#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */
+
+#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
+/* this function was added to libpng 1.2.0 */
+void PNGAPI
+png_set_mmx_thresholds (png_structp png_ptr,
+                        png_byte mmx_bitdepth_threshold,
+                        png_uint_32 mmx_rowbytes_threshold)
+{
+    png_ptr->mmx_bitdepth_threshold = mmx_bitdepth_threshold;
+    png_ptr->mmx_rowbytes_threshold = mmx_rowbytes_threshold;
+}
+#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */
+
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+/* this function was added to libpng 1.2.6 */
+void PNGAPI
+png_set_user_limits (png_structp png_ptr, png_uint_32 user_width_max,
+    png_uint_32 user_height_max)
+{
+    /* Images with dimensions larger than these limits will be
+     * rejected by png_set_IHDR().  To accept any PNG datastream
+     * regardless of dimensions, set both limits to 0x7ffffffL.
+     */
+    png_ptr->user_width_max = user_width_max;
+    png_ptr->user_height_max = user_height_max;
+}
+#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */
+
+#endif /* ?PNG_1_0_X */
diff --git a/Utilities/GDAL/frmts/png/libpng/pngtrans.c b/Utilities/GDAL/frmts/png/libpng/pngtrans.c
new file mode 100644
index 0000000000..9003a2109b
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngtrans.c
@@ -0,0 +1,650 @@
+
+/* pngtrans.c - transforms the data in a row (used by both readers and writers)
+ *
+ * libpng  1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* turn on BGR-to-RGB mapping */
+void PNGAPI
+png_set_bgr(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_bgr\n");
+   png_ptr->transformations |= PNG_BGR;
+}
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* turn on 16 bit byte swapping */
+void PNGAPI
+png_set_swap(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_swap\n");
+   if (png_ptr->bit_depth == 16)
+      png_ptr->transformations |= PNG_SWAP_BYTES;
+}
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+/* turn on pixel packing */
+void PNGAPI
+png_set_packing(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_packing\n");
+   if (png_ptr->bit_depth < 8)
+   {
+      png_ptr->transformations |= PNG_PACK;
+      png_ptr->usr_bit_depth = 8;
+   }
+}
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+/* turn on packed pixel swapping */
+void PNGAPI
+png_set_packswap(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_packswap\n");
+   if (png_ptr->bit_depth < 8)
+      png_ptr->transformations |= PNG_PACKSWAP;
+}
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+void PNGAPI
+png_set_shift(png_structp png_ptr, png_color_8p true_bits)
+{
+   png_debug(1, "in png_set_shift\n");
+   png_ptr->transformations |= PNG_SHIFT;
+   png_ptr->shift = *true_bits;
+}
+#endif
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
+    defined(PNG_WRITE_INTERLACING_SUPPORTED)
+int PNGAPI
+png_set_interlace_handling(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_interlace handling\n");
+   if (png_ptr->interlaced)
+   {
+      png_ptr->transformations |= PNG_INTERLACE;
+      return (7);
+   }
+
+   return (1);
+}
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+/* Add a filler byte on read, or remove a filler or alpha byte on write.
+ * The filler type has changed in v0.95 to allow future 2-byte fillers
+ * for 48-bit input data, as well as to avoid problems with some compilers
+ * that don't like bytes as parameters.
+ */
+void PNGAPI
+png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc)
+{
+   png_debug(1, "in png_set_filler\n");
+   png_ptr->transformations |= PNG_FILLER;
+   png_ptr->filler = (png_byte)filler;
+   if (filler_loc == PNG_FILLER_AFTER)
+      png_ptr->flags |= PNG_FLAG_FILLER_AFTER;
+   else
+      png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER;
+
+   /* This should probably go in the "do_read_filler" routine.
+    * I attempted to do that in libpng-1.0.1a but that caused problems
+    * so I restored it in libpng-1.0.2a
+   */
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+   {
+      png_ptr->usr_channels = 4;
+   }
+
+   /* Also I added this in libpng-1.0.2a (what happens when we expand
+    * a less-than-8-bit grayscale to GA? */
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8)
+   {
+      png_ptr->usr_channels = 2;
+   }
+}
+
+#if !defined(PNG_1_0_X)
+/* Added to libpng-1.2.7 */
+void PNGAPI
+png_set_add_alpha(png_structp png_ptr, png_uint_32 filler, int filler_loc)
+{
+   png_debug(1, "in png_set_add_alpha\n");
+   png_set_filler(png_ptr, filler, filler_loc);
+   png_ptr->transformations |= PNG_ADD_ALPHA;
+}
+#endif
+
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+void PNGAPI
+png_set_swap_alpha(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_swap_alpha\n");
+   png_ptr->transformations |= PNG_SWAP_ALPHA;
+}
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+void PNGAPI
+png_set_invert_alpha(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_invert_alpha\n");
+   png_ptr->transformations |= PNG_INVERT_ALPHA;
+}
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+void PNGAPI
+png_set_invert_mono(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_invert_mono\n");
+   png_ptr->transformations |= PNG_INVERT_MONO;
+}
+
+/* invert monochrome grayscale data */
+void /* PRIVATE */
+png_do_invert(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_invert\n");
+  /* This test removed from libpng version 1.0.13 and 1.2.0:
+   *   if (row_info->bit_depth == 1 &&
+   */
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row == NULL || row_info == NULL)
+     return;
+#endif
+   if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      png_bytep rp = row;
+      png_uint_32 i;
+      png_uint_32 istop = row_info->rowbytes;
+
+      for (i = 0; i < istop; i++)
+      {
+         *rp = (png_byte)(~(*rp));
+         rp++;
+      }
+   }
+   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
+      row_info->bit_depth == 8)
+   {
+      png_bytep rp = row;
+      png_uint_32 i;
+      png_uint_32 istop = row_info->rowbytes;
+
+      for (i = 0; i < istop; i+=2)
+      {
+         *rp = (png_byte)(~(*rp));
+         rp+=2;
+      }
+   }
+   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
+      row_info->bit_depth == 16)
+   {
+      png_bytep rp = row;
+      png_uint_32 i;
+      png_uint_32 istop = row_info->rowbytes;
+
+      for (i = 0; i < istop; i+=4)
+      {
+         *rp = (png_byte)(~(*rp));
+         *(rp+1) = (png_byte)(~(*(rp+1)));
+         rp+=4;
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* swaps byte order on 16 bit depth images */
+void /* PRIVATE */
+png_do_swap(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_swap\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       row_info->bit_depth == 16)
+   {
+      png_bytep rp = row;
+      png_uint_32 i;
+      png_uint_32 istop= row_info->width * row_info->channels;
+
+      for (i = 0; i < istop; i++, rp += 2)
+      {
+         png_byte t = *rp;
+         *rp = *(rp + 1);
+         *(rp + 1) = t;
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+static png_byte onebppswaptable[256] = {
+   0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+   0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+   0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+   0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+   0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+   0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+   0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+   0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+   0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+   0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+   0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+   0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+   0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+   0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+   0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+   0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+   0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
+   0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+   0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
+   0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+   0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+   0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+   0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
+   0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+   0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
+   0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+   0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+   0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+   0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
+   0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+   0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
+   0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
+};
+
+static png_byte twobppswaptable[256] = {
+   0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0,
+   0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0,
+   0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4,
+   0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4,
+   0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8,
+   0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8,
+   0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC,
+   0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC,
+   0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1,
+   0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1,
+   0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5,
+   0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5,
+   0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9,
+   0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9,
+   0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD,
+   0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD,
+   0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2,
+   0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2,
+   0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6,
+   0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6,
+   0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA,
+   0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA,
+   0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE,
+   0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE,
+   0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3,
+   0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3,
+   0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7,
+   0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7,
+   0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB,
+   0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB,
+   0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF,
+   0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF
+};
+
+static png_byte fourbppswaptable[256] = {
+   0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
+   0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
+   0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71,
+   0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1,
+   0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72,
+   0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2,
+   0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73,
+   0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3,
+   0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74,
+   0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4,
+   0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75,
+   0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5,
+   0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76,
+   0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6,
+   0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77,
+   0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7,
+   0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78,
+   0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8,
+   0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79,
+   0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9,
+   0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A,
+   0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA,
+   0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B,
+   0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB,
+   0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C,
+   0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC,
+   0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D,
+   0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD,
+   0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E,
+   0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE,
+   0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F,
+   0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF
+};
+
+/* swaps pixel packing order within bytes */
+void /* PRIVATE */
+png_do_packswap(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_packswap\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       row_info->bit_depth < 8)
+   {
+      png_bytep rp, end, table;
+
+      end = row + row_info->rowbytes;
+
+      if (row_info->bit_depth == 1)
+         table = onebppswaptable;
+      else if (row_info->bit_depth == 2)
+         table = twobppswaptable;
+      else if (row_info->bit_depth == 4)
+         table = fourbppswaptable;
+      else
+         return;
+
+      for (rp = row; rp < end; rp++)
+         *rp = table[*rp];
+   }
+}
+#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */
+
+#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
+    defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+/* remove filler or alpha byte(s) */
+void /* PRIVATE */
+png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags)
+{
+   png_debug(1, "in png_do_strip_filler\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      png_bytep sp=row;
+      png_bytep dp=row;
+      png_uint_32 row_width=row_info->width;
+      png_uint_32 i;
+
+      if ((row_info->color_type == PNG_COLOR_TYPE_RGB ||
+         (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&
+         (flags & PNG_FLAG_STRIP_ALPHA))) &&
+         row_info->channels == 4)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            /* This converts from RGBX or RGBA to RGB */
+            if (flags & PNG_FLAG_FILLER_AFTER)
+            {
+               dp+=3; sp+=4;
+               for (i = 1; i < row_width; i++)
+               {
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  sp++;
+               }
+            }
+            /* This converts from XRGB or ARGB to RGB */
+            else
+            {
+               for (i = 0; i < row_width; i++)
+               {
+                  sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+               }
+            }
+            row_info->pixel_depth = 24;
+            row_info->rowbytes = row_width * 3;
+         }
+         else /* if (row_info->bit_depth == 16) */
+         {
+            if (flags & PNG_FLAG_FILLER_AFTER)
+            {
+               /* This converts from RRGGBBXX or RRGGBBAA to RRGGBB */
+               sp += 8; dp += 6;
+               for (i = 1; i < row_width; i++)
+               {
+                  /* This could be (although png_memcpy is probably slower):
+                  png_memcpy(dp, sp, 6);
+                  sp += 8;
+                  dp += 6;
+                  */
+
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  sp += 2;
+               }
+            }
+            else
+            {
+               /* This converts from XXRRGGBB or AARRGGBB to RRGGBB */
+               for (i = 0; i < row_width; i++)
+               {
+                  /* This could be (although png_memcpy is probably slower):
+                  png_memcpy(dp, sp, 6);
+                  sp += 8;
+                  dp += 6;
+                  */
+
+                  sp+=2;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+               }
+            }
+            row_info->pixel_depth = 48;
+            row_info->rowbytes = row_width * 6;
+         }
+         row_info->channels = 3;
+      }
+      else if ((row_info->color_type == PNG_COLOR_TYPE_GRAY ||
+         (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
+         (flags & PNG_FLAG_STRIP_ALPHA))) &&
+          row_info->channels == 2)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            /* This converts from GX or GA to G */
+            if (flags & PNG_FLAG_FILLER_AFTER)
+            {
+               for (i = 0; i < row_width; i++)
+               {
+                  *dp++ = *sp++;
+                  sp++;
+               }
+            }
+            /* This converts from XG or AG to G */
+            else
+            {
+               for (i = 0; i < row_width; i++)
+               {
+                  sp++;
+                  *dp++ = *sp++;
+               }
+            }
+            row_info->pixel_depth = 8;
+            row_info->rowbytes = row_width;
+         }
+         else /* if (row_info->bit_depth == 16) */
+         {
+            if (flags & PNG_FLAG_FILLER_AFTER)
+            {
+               /* This converts from GGXX or GGAA to GG */
+               sp += 4; dp += 2;
+               for (i = 1; i < row_width; i++)
+               {
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  sp += 2;
+               }
+            }
+            else
+            {
+               /* This converts from XXGG or AAGG to GG */
+               for (i = 0; i < row_width; i++)
+               {
+                  sp += 2;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+               }
+            }
+            row_info->pixel_depth = 16;
+            row_info->rowbytes = row_width * 2;
+         }
+         row_info->channels = 1;
+      }
+      if (flags & PNG_FLAG_STRIP_ALPHA)
+        row_info->color_type &= ~PNG_COLOR_MASK_ALPHA;
+   }
+}
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* swaps red and blue bytes within a pixel */
+void /* PRIVATE */
+png_do_bgr(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_bgr\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       (row_info->color_type & PNG_COLOR_MASK_COLOR))
+   {
+      png_uint_32 row_width = row_info->width;
+      if (row_info->bit_depth == 8)
+      {
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 3)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 2);
+               *(rp + 2) = save;
+            }
+         }
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 4)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 2);
+               *(rp + 2) = save;
+            }
+         }
+      }
+      else if (row_info->bit_depth == 16)
+      {
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 6)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 4);
+               *(rp + 4) = save;
+               save = *(rp + 1);
+               *(rp + 1) = *(rp + 5);
+               *(rp + 5) = save;
+            }
+         }
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 8)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 4);
+               *(rp + 4) = save;
+               save = *(rp + 1);
+               *(rp + 1) = *(rp + 5);
+               *(rp + 5) = save;
+            }
+         }
+      }
+   }
+}
+#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_LEGACY_SUPPORTED)
+void PNGAPI
+png_set_user_transform_info(png_structp png_ptr, png_voidp
+   user_transform_ptr, int user_transform_depth, int user_transform_channels)
+{
+   png_debug(1, "in png_set_user_transform_info\n");
+#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
+   png_ptr->user_transform_ptr = user_transform_ptr;
+   png_ptr->user_transform_depth = (png_byte)user_transform_depth;
+   png_ptr->user_transform_channels = (png_byte)user_transform_channels;
+#else
+   if(user_transform_ptr || user_transform_depth || user_transform_channels)
+      png_warning(png_ptr,
+        "This version of libpng does not support user transform info");
+#endif
+}
+#endif
+
+/* This function returns a pointer to the user_transform_ptr associated with
+ * the user transform functions.  The application should free any memory
+ * associated with this pointer before png_write_destroy and png_read_destroy
+ * are called.
+ */
+png_voidp PNGAPI
+png_get_user_transform_ptr(png_structp png_ptr)
+{
+#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
+   return ((png_voidp)png_ptr->user_transform_ptr);
+#else
+   if(png_ptr)
+     return (NULL);
+   return (NULL);
+#endif
+}
diff --git a/Utilities/GDAL/frmts/png/libpng/pngvcrd.c b/Utilities/GDAL/frmts/png/libpng/pngvcrd.c
new file mode 100644
index 0000000000..940a7fc0f4
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngvcrd.c
@@ -0,0 +1,3903 @@
+/* pngvcrd.c - mixed C/assembler version of utilities to read a PNG file
+ *
+ * For Intel x86 CPU and Microsoft Visual C++ compiler
+ *
+ * libpng version 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * Copyright (c) 1998, Intel Corporation
+ *
+ * Contributed by Nirav Chhatrapati, Intel Corporation, 1998
+ * Interface to libpng contributed by Gilles Vollant, 1999
+ *
+ *
+ * In png_do_read_interlace() in libpng versions 1.0.3a through 1.0.4d,
+ * a sign error in the post-MMX cleanup code for each pixel_depth resulted
+ * in bad pixels at the beginning of some rows of some images, and also
+ * (due to out-of-range memory reads and writes) caused heap corruption
+ * when compiled with MSVC 6.0.  The error was fixed in version 1.0.4e.
+ *
+ * [png_read_filter_row_mmx_avg() bpp == 2 bugfix, GRR 20000916]
+ *
+ * [runtime MMX configuration, GRR 20010102]
+ *
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_USE_PNGVCRD)
+
+static int mmx_supported=2;
+
+
+int PNGAPI
+png_mmx_support(void)
+{
+  int mmx_supported_local = 0;
+  _asm {
+    push ebx          //CPUID will trash these
+    push ecx
+    push edx
+
+    pushfd            //Save Eflag to stack
+    pop eax           //Get Eflag from stack into eax
+    mov ecx, eax      //Make another copy of Eflag in ecx
+    xor eax, 0x200000 //Toggle ID bit in Eflag [i.e. bit(21)]
+    push eax          //Save modified Eflag back to stack
+
+    popfd             //Restored modified value back to Eflag reg
+    pushfd            //Save Eflag to stack
+    pop eax           //Get Eflag from stack
+    push ecx          // save original Eflag to stack
+    popfd             // restore original Eflag
+    xor eax, ecx      //Compare the new Eflag with the original Eflag
+    jz NOT_SUPPORTED  //If the same, CPUID instruction is not supported,
+                      //skip following instructions and jump to
+                      //NOT_SUPPORTED label
+
+    xor eax, eax      //Set eax to zero
+
+    _asm _emit 0x0f   //CPUID instruction  (two bytes opcode)
+    _asm _emit 0xa2
+
+    cmp eax, 1        //make sure eax return non-zero value
+    jl NOT_SUPPORTED  //If eax is zero, mmx not supported
+
+    xor eax, eax      //set eax to zero
+    inc eax           //Now increment eax to 1.  This instruction is
+                      //faster than the instruction "mov eax, 1"
+
+    _asm _emit 0x0f   //CPUID instruction
+    _asm _emit 0xa2
+
+    and edx, 0x00800000  //mask out all bits but mmx bit(24)
+    cmp edx, 0        // 0 = mmx not supported
+    jz  NOT_SUPPORTED // non-zero = Yes, mmx IS supported
+
+    mov  mmx_supported_local, 1  //set return value to 1
+
+NOT_SUPPORTED:
+    mov  eax, mmx_supported_local  //move return value to eax
+    pop edx          //CPUID trashed these
+    pop ecx
+    pop ebx
+  }
+
+  //mmx_supported_local=0; // test code for force don't support MMX
+  //printf("MMX : %u (1=MMX supported)\n",mmx_supported_local);
+
+  mmx_supported = mmx_supported_local;
+  return mmx_supported_local;
+}
+
+/* Combines the row recently read in with the previous row.
+   This routine takes care of alpha and transparency if requested.
+   This routine also handles the two methods of progressive display
+   of interlaced images, depending on the mask value.
+   The mask value describes which pixels are to be combined with
+   the row.  The pattern always repeats every 8 pixels, so just 8
+   bits are needed.  A one indicates the pixel is to be combined; a
+   zero indicates the pixel is to be skipped.  This is in addition
+   to any alpha or transparency value associated with the pixel.  If
+   you want all pixels to be combined, pass 0xff (255) in mask.  */
+
+/* Use this routine for x86 platform - uses faster MMX routine if machine
+   supports MMX */
+
+void /* PRIVATE */
+png_combine_row(png_structp png_ptr, png_bytep row, int mask)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+#endif
+
+   png_debug(1,"in png_combine_row_asm\n");
+
+   if (mmx_supported == 2) {
+#if !defined(PNG_1_0_X)
+       /* this should have happened in png_init_mmx_flags() already */
+       png_warning(png_ptr, "asm_flags may not have been initialized");
+#endif
+       png_mmx_support();
+   }
+
+   if (mask == 0xff)
+   {
+      png_memcpy(row, png_ptr->row_buf + 1,
+       (png_size_t)PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
+       png_ptr->width));
+   }
+   /* GRR:  add "else if (mask == 0)" case?
+    *       or does png_combine_row() not even get called in that case? */
+   else
+   {
+      switch (png_ptr->row_info.pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_inc, s_start, s_end;
+            int m;
+            int shift;
+            png_uint_32 i;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+                s_start = 0;
+                s_end = 7;
+                s_inc = 1;
+            }
+            else
+#endif
+            {
+                s_start = 7;
+                s_end = 0;
+                s_inc = -1;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  int value;
+
+                  value = (*sp >> shift) & 0x1;
+                  *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+
+         case 2:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_start, s_end, s_inc;
+            int m;
+            int shift;
+            png_uint_32 i;
+            int value;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+            else
+#endif
+            {
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0x3;
+                  *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+
+         case 4:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_start, s_end, s_inc;
+            int m;
+            int shift;
+            png_uint_32 i;
+            int value;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+            else
+#endif
+            {
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0xf;
+                  *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+
+         case 8:
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            png_uint_32 len;
+            int m;
+            int diff, unmask;
+
+            __int64 mask0=0x0102040810204080;
+
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && mmx_supported */ )
+#else
+            if (mmx_supported)
+#endif
+            {
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+               m = 0x80;
+               unmask = ~mask;
+               len  = png_ptr->width &~7;  //reduce to multiple of 8
+               diff = png_ptr->width & 7;  //amount lost
+
+               _asm
+               {
+                  movd       mm7, unmask   //load bit pattern
+                  psubb      mm6,mm6       //zero mm6
+                  punpcklbw  mm7,mm7
+                  punpcklwd  mm7,mm7
+                  punpckldq  mm7,mm7       //fill register with 8 masks
+
+                  movq       mm0,mask0
+
+                  pand       mm0,mm7       //nonzero if keep byte
+                  pcmpeqb    mm0,mm6       //zeros->1s, v versa
+
+                  mov        ecx,len       //load length of line (pixels)
+                  mov        esi,srcptr    //load source
+                  mov        ebx,dstptr    //load dest
+                  cmp        ecx,0         //lcr
+                  je         mainloop8end
+
+mainloop8:
+                  movq       mm4,[esi]
+                  pand       mm4,mm0
+                  movq       mm6,mm0
+                  pandn      mm6,[ebx]
+                  por        mm4,mm6
+                  movq       [ebx],mm4
+
+                  add        esi,8         //inc by 8 bytes processed
+                  add        ebx,8
+                  sub        ecx,8         //dec by 8 pixels processed
+
+                  ja         mainloop8
+mainloop8end:
+
+                  mov        ecx,diff
+                  cmp        ecx,0
+                  jz         end8
+
+                  mov        edx,mask
+                  sal        edx,24        //make low byte the high byte
+
+secondloop8:
+                  sal        edx,1         //move high bit to CF
+                  jnc        skip8         //if CF = 0
+                  mov        al,[esi]
+                  mov        [ebx],al
+skip8:
+                  inc        esi
+                  inc        ebx
+
+                  dec        ecx
+                  jnz        secondloop8
+end8:
+                  emms
+               }
+            }
+            else /* mmx not supported - use modified C routine */
+            {
+               register unsigned int incr1, initial_val, final_val;
+               png_size_t pixel_bytes;
+               png_uint_32 i;
+               register int disp = png_pass_inc[png_ptr->pass];
+               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};
+
+               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
+                  pixel_bytes;
+               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
+               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
+               final_val = png_ptr->width*pixel_bytes;
+               incr1 = (disp)*pixel_bytes;
+               for (i = initial_val; i < final_val; i += incr1)
+               {
+                  png_memcpy(dstptr, srcptr, pixel_bytes);
+                  srcptr += incr1;
+                  dstptr += incr1;
+               }
+            } /* end of else */
+
+            break;
+         }       // end 8 bpp
+
+         case 16:
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            png_uint_32 len;
+            int unmask, diff;
+            __int64 mask1=0x0101020204040808,
+                    mask0=0x1010202040408080;
+
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && mmx_supported */ )
+#else
+            if (mmx_supported)
+#endif
+            {
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+
+               unmask = ~mask;
+               len     = (png_ptr->width)&~7;
+               diff = (png_ptr->width)&7;
+               _asm
+               {
+                  movd       mm7, unmask       //load bit pattern
+                  psubb      mm6,mm6           //zero mm6
+                  punpcklbw  mm7,mm7
+                  punpcklwd  mm7,mm7
+                  punpckldq  mm7,mm7           //fill register with 8 masks
+
+                  movq       mm0,mask0
+                  movq       mm1,mask1
+
+                  pand       mm0,mm7
+                  pand       mm1,mm7
+
+                  pcmpeqb    mm0,mm6
+                  pcmpeqb    mm1,mm6
+
+                  mov        ecx,len           //load length of line
+                  mov        esi,srcptr        //load source
+                  mov        ebx,dstptr        //load dest
+                  cmp        ecx,0             //lcr
+                  jz         mainloop16end
+
+mainloop16:
+                  movq       mm4,[esi]
+                  pand       mm4,mm0
+                  movq       mm6,mm0
+                  movq       mm7,[ebx]
+                  pandn      mm6,mm7
+                  por        mm4,mm6
+                  movq       [ebx],mm4
+
+                  movq       mm5,[esi+8]
+                  pand       mm5,mm1
+                  movq       mm7,mm1
+                  movq       mm6,[ebx+8]
+                  pandn      mm7,mm6
+                  por        mm5,mm7
+                  movq       [ebx+8],mm5
+
+                  add        esi,16            //inc by 16 bytes processed
+                  add        ebx,16
+                  sub        ecx,8             //dec by 8 pixels processed
+
+                  ja         mainloop16
+
+mainloop16end:
+                  mov        ecx,diff
+                  cmp        ecx,0
+                  jz         end16
+
+                  mov        edx,mask
+                  sal        edx,24            //make low byte the high byte
+secondloop16:
+                  sal        edx,1             //move high bit to CF
+                  jnc        skip16            //if CF = 0
+                  mov        ax,[esi]
+                  mov        [ebx],ax
+skip16:
+                  add        esi,2
+                  add        ebx,2
+
+                  dec        ecx
+                  jnz        secondloop16
+end16:
+                  emms
+               }
+            }
+            else /* mmx not supported - use modified C routine */
+            {
+               register unsigned int incr1, initial_val, final_val;
+               png_size_t pixel_bytes;
+               png_uint_32 i;
+               register int disp = png_pass_inc[png_ptr->pass];
+               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};
+
+               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
+                  pixel_bytes;
+               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
+               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
+               final_val = png_ptr->width*pixel_bytes;
+               incr1 = (disp)*pixel_bytes;
+               for (i = initial_val; i < final_val; i += incr1)
+               {
+                  png_memcpy(dstptr, srcptr, pixel_bytes);
+                  srcptr += incr1;
+                  dstptr += incr1;
+               }
+            } /* end of else */
+
+            break;
+         }       // end 16 bpp
+
+         case 24:
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            png_uint_32 len;
+            int unmask, diff;
+
+            __int64 mask2=0x0101010202020404,  //24bpp
+                    mask1=0x0408080810101020,
+                    mask0=0x2020404040808080;
+
+            srcptr = png_ptr->row_buf + 1;
+            dstptr = row;
+
+            unmask = ~mask;
+            len     = (png_ptr->width)&~7;
+            diff = (png_ptr->width)&7;
+
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && mmx_supported */ )
+#else
+            if (mmx_supported)
+#endif
+            {
+               _asm
+               {
+                  movd       mm7, unmask       //load bit pattern
+                  psubb      mm6,mm6           //zero mm6
+                  punpcklbw  mm7,mm7
+                  punpcklwd  mm7,mm7
+                  punpckldq  mm7,mm7           //fill register with 8 masks
+
+                  movq       mm0,mask0
+                  movq       mm1,mask1
+                  movq       mm2,mask2
+
+                  pand       mm0,mm7
+                  pand       mm1,mm7
+                  pand       mm2,mm7
+
+                  pcmpeqb    mm0,mm6
+                  pcmpeqb    mm1,mm6
+                  pcmpeqb    mm2,mm6
+
+                  mov        ecx,len           //load length of line
+                  mov        esi,srcptr        //load source
+                  mov        ebx,dstptr        //load dest
+                  cmp        ecx,0
+                  jz         mainloop24end
+
+mainloop24:
+                  movq       mm4,[esi]
+                  pand       mm4,mm0
+                  movq       mm6,mm0
+                  movq       mm7,[ebx]
+                  pandn      mm6,mm7
+                  por        mm4,mm6
+                  movq       [ebx],mm4
+
+
+                  movq       mm5,[esi+8]
+                  pand       mm5,mm1
+                  movq       mm7,mm1
+                  movq       mm6,[ebx+8]
+                  pandn      mm7,mm6
+                  por        mm5,mm7
+                  movq       [ebx+8],mm5
+
+                  movq       mm6,[esi+16]
+                  pand       mm6,mm2
+                  movq       mm4,mm2
+                  movq       mm7,[ebx+16]
+                  pandn      mm4,mm7
+                  por        mm6,mm4
+                  movq       [ebx+16],mm6
+
+                  add        esi,24            //inc by 24 bytes processed
+                  add        ebx,24
+                  sub        ecx,8             //dec by 8 pixels processed
+
+                  ja         mainloop24
+
+mainloop24end:
+                  mov        ecx,diff
+                  cmp        ecx,0
+                  jz         end24
+
+                  mov        edx,mask
+                  sal        edx,24            //make low byte the high byte
+secondloop24:
+                  sal        edx,1             //move high bit to CF
+                  jnc        skip24            //if CF = 0
+                  mov        ax,[esi]
+                  mov        [ebx],ax
+                  xor        eax,eax
+                  mov        al,[esi+2]
+                  mov        [ebx+2],al
+skip24:
+                  add        esi,3
+                  add        ebx,3
+
+                  dec        ecx
+                  jnz        secondloop24
+
+end24:
+                  emms
+               }
+            }
+            else /* mmx not supported - use modified C routine */
+            {
+               register unsigned int incr1, initial_val, final_val;
+               png_size_t pixel_bytes;
+               png_uint_32 i;
+               register int disp = png_pass_inc[png_ptr->pass];
+               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};
+
+               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
+                  pixel_bytes;
+               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
+               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
+               final_val = png_ptr->width*pixel_bytes;
+               incr1 = (disp)*pixel_bytes;
+               for (i = initial_val; i < final_val; i += incr1)
+               {
+                  png_memcpy(dstptr, srcptr, pixel_bytes);
+                  srcptr += incr1;
+                  dstptr += incr1;
+               }
+            } /* end of else */
+
+            break;
+         }       // end 24 bpp
+
+         case 32:
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            png_uint_32 len;
+            int unmask, diff;
+
+            __int64 mask3=0x0101010102020202,  //32bpp
+                    mask2=0x0404040408080808,
+                    mask1=0x1010101020202020,
+                    mask0=0x4040404080808080;
+
+            srcptr = png_ptr->row_buf + 1;
+            dstptr = row;
+
+            unmask = ~mask;
+            len     = (png_ptr->width)&~7;
+            diff = (png_ptr->width)&7;
+
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && mmx_supported */ )
+#else
+            if (mmx_supported)
+#endif
+            {
+               _asm
+               {
+                  movd       mm7, unmask       //load bit pattern
+                  psubb      mm6,mm6           //zero mm6
+                  punpcklbw  mm7,mm7
+                  punpcklwd  mm7,mm7
+                  punpckldq  mm7,mm7           //fill register with 8 masks
+
+                  movq       mm0,mask0
+                  movq       mm1,mask1
+                  movq       mm2,mask2
+                  movq       mm3,mask3
+
+                  pand       mm0,mm7
+                  pand       mm1,mm7
+                  pand       mm2,mm7
+                  pand       mm3,mm7
+
+                  pcmpeqb    mm0,mm6
+                  pcmpeqb    mm1,mm6
+                  pcmpeqb    mm2,mm6
+                  pcmpeqb    mm3,mm6
+
+                  mov        ecx,len           //load length of line
+                  mov        esi,srcptr        //load source
+                  mov        ebx,dstptr        //load dest
+
+                  cmp        ecx,0             //lcr
+                  jz         mainloop32end
+
+mainloop32:
+                  movq       mm4,[esi]
+                  pand       mm4,mm0
+                  movq       mm6,mm0
+                  movq       mm7,[ebx]
+                  pandn      mm6,mm7
+                  por        mm4,mm6
+                  movq       [ebx],mm4
+
+                  movq       mm5,[esi+8]
+                  pand       mm5,mm1
+                  movq       mm7,mm1
+                  movq       mm6,[ebx+8]
+                  pandn      mm7,mm6
+                  por        mm5,mm7
+                  movq       [ebx+8],mm5
+
+                  movq       mm6,[esi+16]
+                  pand       mm6,mm2
+                  movq       mm4,mm2
+                  movq       mm7,[ebx+16]
+                  pandn      mm4,mm7
+                  por        mm6,mm4
+                  movq       [ebx+16],mm6
+
+                  movq       mm7,[esi+24]
+                  pand       mm7,mm3
+                  movq       mm5,mm3
+                  movq       mm4,[ebx+24]
+                  pandn      mm5,mm4
+                  por        mm7,mm5
+                  movq       [ebx+24],mm7
+
+                  add        esi,32            //inc by 32 bytes processed
+                  add        ebx,32
+                  sub        ecx,8             //dec by 8 pixels processed
+
+                  ja         mainloop32
+
+mainloop32end:
+                  mov        ecx,diff
+                  cmp        ecx,0
+                  jz         end32
+
+                  mov        edx,mask
+                  sal        edx,24            //make low byte the high byte
+secondloop32:
+                  sal        edx,1             //move high bit to CF
+                  jnc        skip32            //if CF = 0
+                  mov        eax,[esi]
+                  mov        [ebx],eax
+skip32:
+                  add        esi,4
+                  add        ebx,4
+
+                  dec        ecx
+                  jnz        secondloop32
+
+end32:
+                  emms
+               }
+            }
+            else /* mmx _not supported - Use modified C routine */
+            {
+               register unsigned int incr1, initial_val, final_val;
+               png_size_t pixel_bytes;
+               png_uint_32 i;
+               register int disp = png_pass_inc[png_ptr->pass];
+               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};
+
+               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
+                  pixel_bytes;
+               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
+               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
+               final_val = png_ptr->width*pixel_bytes;
+               incr1 = (disp)*pixel_bytes;
+               for (i = initial_val; i < final_val; i += incr1)
+               {
+                  png_memcpy(dstptr, srcptr, pixel_bytes);
+                  srcptr += incr1;
+                  dstptr += incr1;
+               }
+            } /* end of else */
+
+            break;
+         }       // end 32 bpp
+
+         case 48:
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            png_uint_32 len;
+            int unmask, diff;
+
+            __int64 mask5=0x0101010101010202,
+                    mask4=0x0202020204040404,
+                    mask3=0x0404080808080808,
+                    mask2=0x1010101010102020,
+                    mask1=0x2020202040404040,
+                    mask0=0x4040808080808080;
+
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && mmx_supported */ )
+#else
+            if (mmx_supported)
+#endif
+            {
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+
+               unmask = ~mask;
+               len     = (png_ptr->width)&~7;
+               diff = (png_ptr->width)&7;
+               _asm
+               {
+                  movd       mm7, unmask       //load bit pattern
+                  psubb      mm6,mm6           //zero mm6
+                  punpcklbw  mm7,mm7
+                  punpcklwd  mm7,mm7
+                  punpckldq  mm7,mm7           //fill register with 8 masks
+
+                  movq       mm0,mask0
+                  movq       mm1,mask1
+                  movq       mm2,mask2
+                  movq       mm3,mask3
+                  movq       mm4,mask4
+                  movq       mm5,mask5
+
+                  pand       mm0,mm7
+                  pand       mm1,mm7
+                  pand       mm2,mm7
+                  pand       mm3,mm7
+                  pand       mm4,mm7
+                  pand       mm5,mm7
+
+                  pcmpeqb    mm0,mm6
+                  pcmpeqb    mm1,mm6
+                  pcmpeqb    mm2,mm6
+                  pcmpeqb    mm3,mm6
+                  pcmpeqb    mm4,mm6
+                  pcmpeqb    mm5,mm6
+
+                  mov        ecx,len           //load length of line
+                  mov        esi,srcptr        //load source
+                  mov        ebx,dstptr        //load dest
+
+                  cmp        ecx,0
+                  jz         mainloop48end
+
+mainloop48:
+                  movq       mm7,[esi]
+                  pand       mm7,mm0
+                  movq       mm6,mm0
+                  pandn      mm6,[ebx]
+                  por        mm7,mm6
+                  movq       [ebx],mm7
+
+                  movq       mm6,[esi+8]
+                  pand       mm6,mm1
+                  movq       mm7,mm1
+                  pandn      mm7,[ebx+8]
+                  por        mm6,mm7
+                  movq       [ebx+8],mm6
+
+                  movq       mm6,[esi+16]
+                  pand       mm6,mm2
+                  movq       mm7,mm2
+                  pandn      mm7,[ebx+16]
+                  por        mm6,mm7
+                  movq       [ebx+16],mm6
+
+                  movq       mm7,[esi+24]
+                  pand       mm7,mm3
+                  movq       mm6,mm3
+                  pandn      mm6,[ebx+24]
+                  por        mm7,mm6
+                  movq       [ebx+24],mm7
+
+                  movq       mm6,[esi+32]
+                  pand       mm6,mm4
+                  movq       mm7,mm4
+                  pandn      mm7,[ebx+32]
+                  por        mm6,mm7
+                  movq       [ebx+32],mm6
+
+                  movq       mm7,[esi+40]
+                  pand       mm7,mm5
+                  movq       mm6,mm5
+                  pandn      mm6,[ebx+40]
+                  por        mm7,mm6
+                  movq       [ebx+40],mm7
+
+                  add        esi,48            //inc by 32 bytes processed
+                  add        ebx,48
+                  sub        ecx,8             //dec by 8 pixels processed
+
+                  ja         mainloop48
+mainloop48end:
+
+                  mov        ecx,diff
+                  cmp        ecx,0
+                  jz         end48
+
+                  mov        edx,mask
+                  sal        edx,24            //make low byte the high byte
+
+secondloop48:
+                  sal        edx,1             //move high bit to CF
+                  jnc        skip48            //if CF = 0
+                  mov        eax,[esi]
+                  mov        [ebx],eax
+skip48:
+                  add        esi,4
+                  add        ebx,4
+
+                  dec        ecx
+                  jnz        secondloop48
+
+end48:
+                  emms
+               }
+            }
+            else /* mmx _not supported - Use modified C routine */
+            {
+               register unsigned int incr1, initial_val, final_val;
+               png_size_t pixel_bytes;
+               png_uint_32 i;
+               register int disp = png_pass_inc[png_ptr->pass];
+               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};
+
+               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
+                  pixel_bytes;
+               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
+               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
+               final_val = png_ptr->width*pixel_bytes;
+               incr1 = (disp)*pixel_bytes;
+               for (i = initial_val; i < final_val; i += incr1)
+               {
+                  png_memcpy(dstptr, srcptr, pixel_bytes);
+                  srcptr += incr1;
+                  dstptr += incr1;
+               }
+            } /* end of else */
+
+            break;
+         }       // end 48 bpp
+
+         default:
+         {
+            png_bytep sptr;
+            png_bytep dp;
+            png_size_t pixel_bytes;
+            int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};
+            unsigned int i;
+            register int disp = png_pass_inc[png_ptr->pass];  // get the offset
+            register unsigned int incr1, initial_val, final_val;
+
+            pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+            sptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
+               pixel_bytes;
+            dp = row + offset_table[png_ptr->pass]*pixel_bytes;
+            initial_val = offset_table[png_ptr->pass]*pixel_bytes;
+            final_val = png_ptr->width*pixel_bytes;
+            incr1 = (disp)*pixel_bytes;
+            for (i = initial_val; i < final_val; i += incr1)
+            {
+               png_memcpy(dp, sptr, pixel_bytes);
+               sptr += incr1;
+               dp += incr1;
+            }
+            break;
+         }
+      } /* end switch (png_ptr->row_info.pixel_depth) */
+   } /* end if (non-trivial mask) */
+
+} /* end png_combine_row() */
+
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+
+void /* PRIVATE */
+png_do_read_interlace(png_structp png_ptr)
+{
+   png_row_infop row_info = &(png_ptr->row_info);
+   png_bytep row = png_ptr->row_buf + 1;
+   int pass = png_ptr->pass;
+   png_uint_32 transformations = png_ptr->transformations;
+#ifdef PNG_USE_LOCAL_ARRAYS
+   const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+#endif
+
+   png_debug(1,"in png_do_read_interlace\n");
+
+   if (mmx_supported == 2) {
+#if !defined(PNG_1_0_X)
+       /* this should have happened in png_init_mmx_flags() already */
+       png_warning(png_ptr, "asm_flags may not have been initialized");
+#endif
+       png_mmx_support();
+   }
+
+   if (row != NULL && row_info != NULL)
+   {
+      png_uint_32 final_width;
+
+      final_width = row_info->width * png_pass_inc[pass];
+
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_byte v;
+            png_uint_32 i;
+            int j;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 3);
+            dp = row + (png_size_t)((final_width - 1) >> 3);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (int)((row_info->width + 7) & 7);
+               dshift = (int)((final_width + 7) & 7);
+               s_start = 7;
+               s_end = 0;
+               s_inc = -1;
+            }
+            else
+#endif
+            {
+               sshift = 7 - (int)((row_info->width + 7) & 7);
+               dshift = 7 - (int)((final_width + 7) & 7);
+               s_start = 0;
+               s_end = 7;
+               s_inc = 1;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               v = (png_byte)((*sp >> sshift) & 0x1);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         case 2:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 2);
+            dp = row + (png_size_t)((final_width - 1) >> 2);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (png_size_t)(((row_info->width + 3) & 3) << 1);
+               dshift = (png_size_t)(((final_width + 3) & 3) << 1);
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+            else
+#endif
+            {
+               sshift = (png_size_t)((3 - ((row_info->width + 3) & 3)) << 1);
+               dshift = (png_size_t)((3 - ((final_width + 3) & 3)) << 1);
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               png_byte v;
+               int j;
+
+               v = (png_byte)((*sp >> sshift) & 0x3);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         case 4:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 1);
+            dp = row + (png_size_t)((final_width - 1) >> 1);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (png_size_t)(((row_info->width + 1) & 1) << 2);
+               dshift = (png_size_t)(((final_width + 1) & 1) << 2);
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            else
+#endif
+            {
+               sshift = (png_size_t)((1 - ((row_info->width + 1) & 1)) << 2);
+               dshift = (png_size_t)((1 - ((final_width + 1) & 1)) << 2);
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               png_byte v;
+               int j;
+
+               v = (png_byte)((*sp >> sshift) & 0xf);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         default:         // This is the place where the routine is modified
+         {
+            __int64 const4 = 0x0000000000FFFFFF;
+            // __int64 const5 = 0x000000FFFFFF0000;  // unused...
+            __int64 const6 = 0x00000000000000FF;
+            png_bytep sptr, dp;
+            png_uint_32 i;
+            png_size_t pixel_bytes;
+            int width = row_info->width;
+
+            pixel_bytes = (row_info->pixel_depth >> 3);
+
+            sptr = row + (width - 1) * pixel_bytes;
+            dp = row + (final_width - 1) * pixel_bytes;
+            // New code by Nirav Chhatrapati - Intel Corporation
+            // sign fix by GRR
+            // NOTE:  there is NO MMX code for 48-bit and 64-bit images
+
+            // use MMX routine if machine supports it
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_INTERLACE)
+                /* && mmx_supported */ )
+#else
+            if (mmx_supported)
+#endif
+            {
+               if (pixel_bytes == 3)
+               {
+                  if (((pass == 0) || (pass == 1)) && width)
+                  {
+                     _asm
+                     {
+                        mov esi, sptr
+                        mov edi, dp
+                        mov ecx, width
+                        sub edi, 21   // (png_pass_inc[pass] - 1)*pixel_bytes
+loop_pass0:
+                        movd mm0, [esi]     ; X X X X X v2 v1 v0
+                        pand mm0, const4    ; 0 0 0 0 0 v2 v1 v0
+                        movq mm1, mm0       ; 0 0 0 0 0 v2 v1 v0
+                        psllq mm0, 16       ; 0 0 0 v2 v1 v0 0 0
+                        movq mm2, mm0       ; 0 0 0 v2 v1 v0 0 0
+                        psllq mm0, 24       ; v2 v1 v0 0 0 0 0 0
+                        psrlq mm1, 8        ; 0 0 0 0 0 0 v2 v1
+                        por mm0, mm2        ; v2 v1 v0 v2 v1 v0 0 0
+                        por mm0, mm1        ; v2 v1 v0 v2 v1 v0 v2 v1
+                        movq mm3, mm0       ; v2 v1 v0 v2 v1 v0 v2 v1
+                        psllq mm0, 16       ; v0 v2 v1 v0 v2 v1 0 0
+                        movq mm4, mm3       ; v2 v1 v0 v2 v1 v0 v2 v1
+                        punpckhdq mm3, mm0  ; v0 v2 v1 v0 v2 v1 v0 v2
+                        movq [edi+16] , mm4
+                        psrlq mm0, 32       ; 0 0 0 0 v0 v2 v1 v0
+                        movq [edi+8] , mm3
+                        punpckldq mm0, mm4  ; v1 v0 v2 v1 v0 v2 v1 v0
+                        sub esi, 3
+                        movq [edi], mm0
+                        sub edi, 24
+                        //sub esi, 3
+                        dec ecx
+                        jnz loop_pass0
+                        EMMS
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     _asm
+                     {
+                        mov esi, sptr
+                        mov edi, dp
+                        mov ecx, width
+                        sub edi, 9   // (png_pass_inc[pass] - 1)*pixel_bytes
+loop_pass2:
+                        movd mm0, [esi]     ; X X X X X v2 v1 v0
+                        pand mm0, const4    ; 0 0 0 0 0 v2 v1 v0
+                        movq mm1, mm0       ; 0 0 0 0 0 v2 v1 v0
+                        psllq mm0, 16       ; 0 0 0 v2 v1 v0 0 0
+                        movq mm2, mm0       ; 0 0 0 v2 v1 v0 0 0
+                        psllq mm0, 24       ; v2 v1 v0 0 0 0 0 0
+                        psrlq mm1, 8        ; 0 0 0 0 0 0 v2 v1
+                        por mm0, mm2        ; v2 v1 v0 v2 v1 v0 0 0
+                        por mm0, mm1        ; v2 v1 v0 v2 v1 v0 v2 v1
+                        movq [edi+4], mm0   ; move to memory
+                        psrlq mm0, 16       ; 0 0 v2 v1 v0 v2 v1 v0
+                        movd [edi], mm0     ; move to memory
+                        sub esi, 3
+                        sub edi, 12
+                        dec ecx
+                        jnz loop_pass2
+                        EMMS
+                     }
+                  }
+                  else if (width) /* && ((pass == 4) || (pass == 5)) */
+                  {
+                     int width_mmx = ((width >> 1) << 1) - 8;
+                     if (width_mmx < 0)
+                         width_mmx = 0;
+                     width -= width_mmx;        // 8 or 9 pix, 24 or 27 bytes
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub esi, 3
+                           sub edi, 9
+loop_pass4:
+                           movq mm0, [esi]     ; X X v2 v1 v0 v5 v4 v3
+                           movq mm7, mm0       ; X X v2 v1 v0 v5 v4 v3
+                           movq mm6, mm0       ; X X v2 v1 v0 v5 v4 v3
+                           psllq mm0, 24       ; v1 v0 v5 v4 v3 0 0 0
+                           pand mm7, const4    ; 0 0 0 0 0 v5 v4 v3
+                           psrlq mm6, 24       ; 0 0 0 X X v2 v1 v0
+                           por mm0, mm7        ; v1 v0 v5 v4 v3 v5 v4 v3
+                           movq mm5, mm6       ; 0 0 0 X X v2 v1 v0
+                           psllq mm6, 8        ; 0 0 X X v2 v1 v0 0
+                           movq [edi], mm0     ; move quad to memory
+                           psrlq mm5, 16       ; 0 0 0 0 0 X X v2
+                           pand mm5, const6    ; 0 0 0 0 0 0 0 v2
+                           por mm6, mm5        ; 0 0 X X v2 v1 v0 v2
+                           movd [edi+8], mm6   ; move double to memory
+                           sub esi, 6
+                           sub edi, 12
+                           sub ecx, 2
+                           jnz loop_pass4
+                           EMMS
+                        }
+                     }
+
+                     sptr -= width_mmx*3;
+                     dp -= width_mmx*6;
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+
+                        png_memcpy(v, sptr, 3);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           png_memcpy(dp, v, 3);
+                           dp -= 3;
+                        }
+                        sptr -= 3;
+                     }
+                  }
+               } /* end of pixel_bytes == 3 */
+
+               else if (pixel_bytes == 1)
+               {
+                  if (((pass == 0) || (pass == 1)) && width)
+                  {
+                     int width_mmx = ((width >> 2) << 2);
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub edi, 31
+                           sub esi, 3
+loop1_pass0:
+                           movd mm0, [esi]     ; X X X X v0 v1 v2 v3
+                           movq mm1, mm0       ; X X X X v0 v1 v2 v3
+                           punpcklbw mm0, mm0  ; v0 v0 v1 v1 v2 v2 v3 v3
+                           movq mm2, mm0       ; v0 v0 v1 v1 v2 v2 v3 v3
+                           punpcklwd mm0, mm0  ; v2 v2 v2 v2 v3 v3 v3 v3
+                           movq mm3, mm0       ; v2 v2 v2 v2 v3 v3 v3 v3
+                           punpckldq mm0, mm0  ; v3 v3 v3 v3 v3 v3 v3 v3
+                           punpckhdq mm3, mm3  ; v2 v2 v2 v2 v2 v2 v2 v2
+                           movq [edi], mm0     ; move to memory v3
+                           punpckhwd mm2, mm2  ; v0 v0 v0 v0 v1 v1 v1 v1
+                           movq [edi+8], mm3   ; move to memory v2
+                           movq mm4, mm2       ; v0 v0 v0 v0 v1 v1 v1 v1
+                           punpckldq mm2, mm2  ; v1 v1 v1 v1 v1 v1 v1 v1
+                           punpckhdq mm4, mm4  ; v0 v0 v0 v0 v0 v0 v0 v0
+                           movq [edi+16], mm2  ; move to memory v1
+                           movq [edi+24], mm4  ; move to memory v0
+                           sub esi, 4
+                           sub edi, 32
+                           sub ecx, 4
+                           jnz loop1_pass0
+                           EMMS
+                        }
+                     }
+
+                     sptr -= width_mmx;
+                     dp -= width_mmx*8;
+                     for (i = width; i; i--)
+                     {
+                        int j;
+
+                       /* I simplified this part in version 1.0.4e
+                        * here and in several other instances where
+                        * pixel_bytes == 1  -- GR-P
+                        *
+                        * Original code:
+                        *
+                        * png_byte v[8];
+                        * png_memcpy(v, sptr, pixel_bytes);
+                        * for (j = 0; j < png_pass_inc[pass]; j++)
+                        * {
+                        *    png_memcpy(dp, v, pixel_bytes);
+                        *    dp -= pixel_bytes;
+                        * }
+                        * sptr -= pixel_bytes;
+                        *
+                        * Replacement code is in the next three lines:
+                        */
+
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                           *dp-- = *sptr;
+                        sptr--;
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     int width_mmx = ((width >> 2) << 2);
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub edi, 15
+                           sub esi, 3
+loop1_pass2:
+                           movd mm0, [esi]     ; X X X X v0 v1 v2 v3
+                           punpcklbw mm0, mm0  ; v0 v0 v1 v1 v2 v2 v3 v3
+                           movq mm1, mm0       ; v0 v0 v1 v1 v2 v2 v3 v3
+                           punpcklwd mm0, mm0  ; v2 v2 v2 v2 v3 v3 v3 v3
+                           punpckhwd mm1, mm1  ; v0 v0 v0 v0 v1 v1 v1 v1
+                           movq [edi], mm0     ; move to memory v2 and v3
+                           sub esi, 4
+                           movq [edi+8], mm1   ; move to memory v1     and v0
+                           sub edi, 16
+                           sub ecx, 4
+                           jnz loop1_pass2
+                           EMMS
+                        }
+                     }
+
+                     sptr -= width_mmx;
+                     dp -= width_mmx*4;
+                     for (i = width; i; i--)
+                     {
+                        int j;
+
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           *dp-- = *sptr;
+                        }
+                        sptr --;
+                     }
+                  }
+                  else if (width) /* && ((pass == 4) || (pass == 5))) */
+                  {
+                     int width_mmx = ((width >> 3) << 3);
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub edi, 15
+                           sub esi, 7
+loop1_pass4:
+                           movq mm0, [esi]     ; v0 v1 v2 v3 v4 v5 v6 v7
+                           movq mm1, mm0       ; v0 v1 v2 v3 v4 v5 v6 v7
+                           punpcklbw mm0, mm0  ; v4 v4 v5 v5 v6 v6 v7 v7
+                           //movq mm1, mm0     ; v0 v0 v1 v1 v2 v2 v3 v3
+                           punpckhbw mm1, mm1  ;v0 v0 v1 v1 v2 v2 v3 v3
+                           movq [edi+8], mm1   ; move to memory v0 v1 v2 and v3
+                           sub esi, 8
+                           movq [edi], mm0     ; move to memory v4 v5 v6 and v7
+                           //sub esi, 4
+                           sub edi, 16
+                           sub ecx, 8
+                           jnz loop1_pass4
+                           EMMS
+                        }
+                     }
+
+                     sptr -= width_mmx;
+                     dp -= width_mmx*2;
+                     for (i = width; i; i--)
+                     {
+                        int j;
+
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           *dp-- = *sptr;
+                        }
+                        sptr --;
+                     }
+                  }
+               } /* end of pixel_bytes == 1 */
+
+               else if (pixel_bytes == 2)
+               {
+                  if (((pass == 0) || (pass == 1)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1);
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub esi, 2
+                           sub edi, 30
+loop2_pass0:
+                           movd mm0, [esi]        ; X X X X v1 v0 v3 v2
+                           punpcklwd mm0, mm0     ; v1 v0 v1 v0 v3 v2 v3 v2
+                           movq mm1, mm0          ; v1 v0 v1 v0 v3 v2 v3 v2
+                           punpckldq mm0, mm0     ; v3 v2 v3 v2 v3 v2 v3 v2
+                           punpckhdq mm1, mm1     ; v1 v0 v1 v0 v1 v0 v1 v0
+                           movq [edi], mm0
+                           movq [edi + 8], mm0
+                           movq [edi + 16], mm1
+                           movq [edi + 24], mm1
+                           sub esi, 4
+                           sub edi, 32
+                           sub ecx, 2
+                           jnz loop2_pass0
+                           EMMS
+                        }
+                     }
+
+                     sptr -= (width_mmx*2 - 2);            // sign fixed
+                     dp -= (width_mmx*16 - 2);            // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 2;
+                        png_memcpy(v, sptr, 2);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 2;
+                           png_memcpy(dp, v, 2);
+                        }
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub esi, 2
+                           sub edi, 14
+loop2_pass2:
+                           movd mm0, [esi]        ; X X X X v1 v0 v3 v2
+                           punpcklwd mm0, mm0     ; v1 v0 v1 v0 v3 v2 v3 v2
+                           movq mm1, mm0          ; v1 v0 v1 v0 v3 v2 v3 v2
+                           punpckldq mm0, mm0     ; v3 v2 v3 v2 v3 v2 v3 v2
+                           punpckhdq mm1, mm1     ; v1 v0 v1 v0 v1 v0 v1 v0
+                           movq [edi], mm0
+                           sub esi, 4
+                           movq [edi + 8], mm1
+                           //sub esi, 4
+                           sub edi, 16
+                           sub ecx, 2
+                           jnz loop2_pass2
+                           EMMS
+                        }
+                     }
+
+                     sptr -= (width_mmx*2 - 2);            // sign fixed
+                     dp -= (width_mmx*8 - 2);            // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 2;
+                        png_memcpy(v, sptr, 2);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 2;
+                           png_memcpy(dp, v, 2);
+                        }
+                     }
+                  }
+                  else if (width)  // pass == 4 or 5
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub esi, 2
+                           sub edi, 6
+loop2_pass4:
+                           movd mm0, [esi]        ; X X X X v1 v0 v3 v2
+                           punpcklwd mm0, mm0     ; v1 v0 v1 v0 v3 v2 v3 v2
+                           sub esi, 4
+                           movq [edi], mm0
+                           sub edi, 8
+                           sub ecx, 2
+                           jnz loop2_pass4
+                           EMMS
+                        }
+                     }
+
+                     sptr -= (width_mmx*2 - 2);            // sign fixed
+                     dp -= (width_mmx*4 - 2);            // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 2;
+                        png_memcpy(v, sptr, 2);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 2;
+                           png_memcpy(dp, v, 2);
+                        }
+                     }
+                  }
+               } /* end of pixel_bytes == 2 */
+
+               else if (pixel_bytes == 4)
+               {
+                  if (((pass == 0) || (pass == 1)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub esi, 4
+                           sub edi, 60
+loop4_pass0:
+                           movq mm0, [esi]        ; v3 v2 v1 v0 v7 v6 v5 v4
+                           movq mm1, mm0          ; v3 v2 v1 v0 v7 v6 v5 v4
+                           punpckldq mm0, mm0     ; v7 v6 v5 v4 v7 v6 v5 v4
+                           punpckhdq mm1, mm1     ; v3 v2 v1 v0 v3 v2 v1 v0
+                           movq [edi], mm0
+                           movq [edi + 8], mm0
+                           movq [edi + 16], mm0
+                           movq [edi + 24], mm0
+                           movq [edi+32], mm1
+                           movq [edi + 40], mm1
+                           movq [edi+ 48], mm1
+                           sub esi, 8
+                           movq [edi + 56], mm1
+                           sub edi, 64
+                           sub ecx, 2
+                           jnz loop4_pass0
+                           EMMS
+                        }
+                     }
+
+                     sptr -= (width_mmx*4 - 4);            // sign fixed
+                     dp -= (width_mmx*32 - 4);            // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 4;
+                        png_memcpy(v, sptr, 4);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 4;
+                           png_memcpy(dp, v, 4);
+                        }
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub esi, 4
+                           sub edi, 28
+loop4_pass2:
+                           movq mm0, [esi]      ; v3 v2 v1 v0 v7 v6 v5 v4
+                           movq mm1, mm0        ; v3 v2 v1 v0 v7 v6 v5 v4
+                           punpckldq mm0, mm0   ; v7 v6 v5 v4 v7 v6 v5 v4
+                           punpckhdq mm1, mm1   ; v3 v2 v1 v0 v3 v2 v1 v0
+                           movq [edi], mm0
+                           movq [edi + 8], mm0
+                           movq [edi+16], mm1
+                           movq [edi + 24], mm1
+                           sub esi, 8
+                           sub edi, 32
+                           sub ecx, 2
+                           jnz loop4_pass2
+                           EMMS
+                        }
+                     }
+
+                     sptr -= (width_mmx*4 - 4);            // sign fixed
+                     dp -= (width_mmx*16 - 4);            // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 4;
+                        png_memcpy(v, sptr, 4);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 4;
+                           png_memcpy(dp, v, 4);
+                        }
+                     }
+                  }
+                  else if (width)  // pass == 4 or 5
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub esi, 4
+                           sub edi, 12
+loop4_pass4:
+                           movq mm0, [esi]      ; v3 v2 v1 v0 v7 v6 v5 v4
+                           movq mm1, mm0        ; v3 v2 v1 v0 v7 v6 v5 v4
+                           punpckldq mm0, mm0   ; v7 v6 v5 v4 v7 v6 v5 v4
+                           punpckhdq mm1, mm1   ; v3 v2 v1 v0 v3 v2 v1 v0
+                           movq [edi], mm0
+                           sub esi, 8
+                           movq [edi + 8], mm1
+                           sub edi, 16
+                           sub ecx, 2
+                           jnz loop4_pass4
+                           EMMS
+                        }
+                     }
+
+                     sptr -= (width_mmx*4 - 4);          // sign fixed
+                     dp -= (width_mmx*8 - 4);            // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 4;
+                        png_memcpy(v, sptr, 4);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 4;
+                           png_memcpy(dp, v, 4);
+                        }
+                     }
+                  }
+
+               } /* end of pixel_bytes == 4 */
+
+               else if (pixel_bytes == 6)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, 6);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, 6);
+                        dp -= 6;
+                     }
+                     sptr -= 6;
+                  }
+               } /* end of pixel_bytes == 6 */
+
+               else
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr-= pixel_bytes;
+                  }
+               }
+            } /* end of mmx_supported */
+
+            else /* MMX not supported:  use modified C code - takes advantage
+                  * of inlining of memcpy for a constant */
+            {
+               if (pixel_bytes == 1)
+               {
+                  for (i = width; i; i--)
+                  {
+                     int j;
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                        *dp-- = *sptr;
+                     sptr--;
+                  }
+               }
+               else if (pixel_bytes == 3)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr -= pixel_bytes;
+                  }
+               }
+               else if (pixel_bytes == 2)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr -= pixel_bytes;
+                  }
+               }
+               else if (pixel_bytes == 4)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr -= pixel_bytes;
+                  }
+               }
+               else if (pixel_bytes == 6)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr -= pixel_bytes;
+                  }
+               }
+               else
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr -= pixel_bytes;
+                  }
+               }
+
+            } /* end of MMX not supported */
+            break;
+         }
+      } /* end switch (row_info->pixel_depth) */
+
+      row_info->width = final_width;
+
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width);
+   }
+
+}
+
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+
+
+// These variables are utilized in the functions below.  They are declared
+// globally here to ensure alignment on 8-byte boundaries.
+
+union uAll {
+   __int64 use;
+   double  align;
+} LBCarryMask = {0x0101010101010101},
+  HBClearMask = {0x7f7f7f7f7f7f7f7f},
+  ActiveMask, ActiveMask2, ActiveMaskEnd, ShiftBpp, ShiftRem;
+
+
+// Optimized code for PNG Average filter decoder
+void /* PRIVATE */
+png_read_filter_row_mmx_avg(png_row_infop row_info, png_bytep row
+                            , png_bytep prev_row)
+{
+   int bpp;
+   png_uint_32 FullLength;
+   png_uint_32 MMXLength;
+   //png_uint_32 len;
+   int diff;
+
+   bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel
+   FullLength  = row_info->rowbytes; // # of bytes to filter
+   _asm {
+         // Init address pointers and offset
+         mov edi, row          // edi ==> Avg(x)
+         xor ebx, ebx          // ebx ==> x
+         mov edx, edi
+         mov esi, prev_row           // esi ==> Prior(x)
+         sub edx, bpp          // edx ==> Raw(x-bpp)
+
+         xor eax, eax
+         // Compute the Raw value for the first bpp bytes
+         //    Raw(x) = Avg(x) + (Prior(x)/2)
+davgrlp:
+         mov al, [esi + ebx]   // Load al with Prior(x)
+         inc ebx
+         shr al, 1             // divide by 2
+         add al, [edi+ebx-1]   // Add Avg(x); -1 to offset inc ebx
+         cmp ebx, bpp
+         mov [edi+ebx-1], al    // Write back Raw(x);
+                            // mov does not affect flags; -1 to offset inc ebx
+         jb davgrlp
+         // get # of bytes to alignment
+         mov diff, edi         // take start of row
+         add diff, ebx         // add bpp
+         add diff, 0xf         // add 7 + 8 to incr past alignment boundary
+         and diff, 0xfffffff8  // mask to alignment boundary
+         sub diff, edi         // subtract from start ==> value ebx at alignment
+         jz davggo
+         // fix alignment
+         // Compute the Raw value for the bytes upto the alignment boundary
+         //    Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
+         xor ecx, ecx
+davglp1:
+         xor eax, eax
+         mov cl, [esi + ebx]        // load cl with Prior(x)
+         mov al, [edx + ebx]  // load al with Raw(x-bpp)
+         add ax, cx
+         inc ebx
+         shr ax, 1            // divide by 2
+         add al, [edi+ebx-1]  // Add Avg(x); -1 to offset inc ebx
+         cmp ebx, diff              // Check if at alignment boundary
+         mov [edi+ebx-1], al        // Write back Raw(x);
+                            // mov does not affect flags; -1 to offset inc ebx
+         jb davglp1               // Repeat until at alignment boundary
+davggo:
+         mov eax, FullLength
+         mov ecx, eax
+         sub eax, ebx          // subtract alignment fix
+         and eax, 0x00000007   // calc bytes over mult of 8
+         sub ecx, eax          // drop over bytes from original length
+         mov MMXLength, ecx
+   } // end _asm block
+   // Now do the math for the rest of the row
+   switch ( bpp )
+   {
+      case 3:
+      {
+         ActiveMask.use  = 0x0000000000ffffff;
+         ShiftBpp.use = 24;    // == 3 * 8
+         ShiftRem.use = 40;    // == 64 - 24
+         _asm {
+            // Re-init address pointers and offset
+            movq mm7, ActiveMask
+            mov ebx, diff      // ebx ==> x = offset to alignment boundary
+            movq mm5, LBCarryMask
+            mov edi, row       // edi ==> Avg(x)
+            movq mm4, HBClearMask
+            mov esi, prev_row        // esi ==> Prior(x)
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm2, [edi + ebx - 8]  // Load previous aligned 8 bytes
+                               // (we correct position in loop below)
+davg3lp:
+            movq mm0, [edi + ebx]      // Load mm0 with Avg(x)
+            // Add (Prev_row/2) to Average
+            movq mm3, mm5
+            psrlq mm2, ShiftRem      // Correct position Raw(x-bpp) data
+            movq mm1, [esi + ebx]    // Load mm1 with Prior(x)
+            movq mm6, mm7
+            pand mm3, mm1      // get lsb for each prev_row byte
+            psrlq mm1, 1       // divide prev_row bytes by 2
+            pand  mm1, mm4     // clear invalid bit 7 of each byte
+            paddb mm0, mm1     // add (Prev_row/2) to Avg for each byte
+            // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry
+            movq mm1, mm3      // now use mm1 for getting LBCarrys
+            pand mm1, mm2      // get LBCarrys for each byte where both
+                               // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1       // divide raw bytes by 2
+            pand  mm2, mm4     // clear invalid bit 7 of each byte
+            paddb mm2, mm1     // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6      // Leave only Active Group 1 bytes to add to Avg
+            paddb mm0, mm2     // add (Raw/2) + LBCarrys to Avg for each Active
+                               //  byte
+            // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry
+            psllq mm6, ShiftBpp  // shift the mm6 mask to cover bytes 3-5
+            movq mm2, mm0        // mov updated Raws to mm2
+            psllq mm2, ShiftBpp  // shift data to position correctly
+            movq mm1, mm3        // now use mm1 for getting LBCarrys
+            pand mm1, mm2      // get LBCarrys for each byte where both
+                               // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1       // divide raw bytes by 2
+            pand  mm2, mm4     // clear invalid bit 7 of each byte
+            paddb mm2, mm1     // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6      // Leave only Active Group 2 bytes to add to Avg
+            paddb mm0, mm2     // add (Raw/2) + LBCarrys to Avg for each Active
+                               //  byte
+
+            // Add 3rd active group (Raw(x-bpp)/2) to Average with LBCarry
+            psllq mm6, ShiftBpp  // shift the mm6 mask to cover the last two
+                                 // bytes
+            movq mm2, mm0        // mov updated Raws to mm2
+            psllq mm2, ShiftBpp  // shift data to position correctly
+                              // Data only needs to be shifted once here to
+                              // get the correct x-bpp offset.
+            movq mm1, mm3     // now use mm1 for getting LBCarrys
+            pand mm1, mm2     // get LBCarrys for each byte where both
+                              // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1      // divide raw bytes by 2
+            pand  mm2, mm4    // clear invalid bit 7 of each byte
+            paddb mm2, mm1    // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6     // Leave only Active Group 2 bytes to add to Avg
+            add ebx, 8
+            paddb mm0, mm2    // add (Raw/2) + LBCarrys to Avg for each Active
+                              // byte
+
+            // Now ready to write back to memory
+            movq [edi + ebx - 8], mm0
+            // Move updated Raw(x) to use as Raw(x-bpp) for next loop
+            cmp ebx, MMXLength
+            movq mm2, mm0     // mov updated Raw(x) to mm2
+            jb davg3lp
+         } // end _asm block
+      }
+      break;
+
+      case 6:
+      case 4:
+      case 7:
+      case 5:
+      {
+         ActiveMask.use  = 0xffffffffffffffff;  // use shift below to clear
+                                                // appropriate inactive bytes
+         ShiftBpp.use = bpp << 3;
+         ShiftRem.use = 64 - ShiftBpp.use;
+         _asm {
+            movq mm4, HBClearMask
+            // Re-init address pointers and offset
+            mov ebx, diff       // ebx ==> x = offset to alignment boundary
+            // Load ActiveMask and clear all bytes except for 1st active group
+            movq mm7, ActiveMask
+            mov edi, row         // edi ==> Avg(x)
+            psrlq mm7, ShiftRem
+            mov esi, prev_row    // esi ==> Prior(x)
+            movq mm6, mm7
+            movq mm5, LBCarryMask
+            psllq mm6, ShiftBpp  // Create mask for 2nd active group
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm2, [edi + ebx - 8]  // Load previous aligned 8 bytes
+                                 // (we correct position in loop below)
+davg4lp:
+            movq mm0, [edi + ebx]
+            psrlq mm2, ShiftRem  // shift data to position correctly
+            movq mm1, [esi + ebx]
+            // Add (Prev_row/2) to Average
+            movq mm3, mm5
+            pand mm3, mm1     // get lsb for each prev_row byte
+            psrlq mm1, 1      // divide prev_row bytes by 2
+            pand  mm1, mm4    // clear invalid bit 7 of each byte
+            paddb mm0, mm1    // add (Prev_row/2) to Avg for each byte
+            // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry
+            movq mm1, mm3     // now use mm1 for getting LBCarrys
+            pand mm1, mm2     // get LBCarrys for each byte where both
+                              // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1      // divide raw bytes by 2
+            pand  mm2, mm4    // clear invalid bit 7 of each byte
+            paddb mm2, mm1    // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm7     // Leave only Active Group 1 bytes to add to Avg
+            paddb mm0, mm2    // add (Raw/2) + LBCarrys to Avg for each Active
+                              // byte
+            // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry
+            movq mm2, mm0     // mov updated Raws to mm2
+            psllq mm2, ShiftBpp // shift data to position correctly
+            add ebx, 8
+            movq mm1, mm3     // now use mm1 for getting LBCarrys
+            pand mm1, mm2     // get LBCarrys for each byte where both
+                              // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1      // divide raw bytes by 2
+            pand  mm2, mm4    // clear invalid bit 7 of each byte
+            paddb mm2, mm1    // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6     // Leave only Active Group 2 bytes to add to Avg
+            paddb mm0, mm2    // add (Raw/2) + LBCarrys to Avg for each Active
+                              // byte
+            cmp ebx, MMXLength
+            // Now ready to write back to memory
+            movq [edi + ebx - 8], mm0
+            // Prep Raw(x-bpp) for next loop
+            movq mm2, mm0     // mov updated Raws to mm2
+            jb davg4lp
+         } // end _asm block
+      }
+      break;
+      case 2:
+      {
+         ActiveMask.use  = 0x000000000000ffff;
+         ShiftBpp.use = 16;   // == 2 * 8     [BUGFIX]
+         ShiftRem.use = 48;   // == 64 - 16   [BUGFIX]
+         _asm {
+            // Load ActiveMask
+            movq mm7, ActiveMask
+            // Re-init address pointers and offset
+            mov ebx, diff     // ebx ==> x = offset to alignment boundary
+            movq mm5, LBCarryMask
+            mov edi, row      // edi ==> Avg(x)
+            movq mm4, HBClearMask
+            mov esi, prev_row  // esi ==> Prior(x)
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm2, [edi + ebx - 8]  // Load previous aligned 8 bytes
+                              // (we correct position in loop below)
+davg2lp:
+            movq mm0, [edi + ebx]
+            psrlq mm2, ShiftRem  // shift data to position correctly   [BUGFIX]
+            movq mm1, [esi + ebx]
+            // Add (Prev_row/2) to Average
+            movq mm3, mm5
+            pand mm3, mm1     // get lsb for each prev_row byte
+            psrlq mm1, 1      // divide prev_row bytes by 2
+            pand  mm1, mm4    // clear invalid bit 7 of each byte
+            movq mm6, mm7
+            paddb mm0, mm1    // add (Prev_row/2) to Avg for each byte
+            // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry
+            movq mm1, mm3     // now use mm1 for getting LBCarrys
+            pand mm1, mm2     // get LBCarrys for each byte where both
+                              // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1      // divide raw bytes by 2
+            pand  mm2, mm4    // clear invalid bit 7 of each byte
+            paddb mm2, mm1    // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6     // Leave only Active Group 1 bytes to add to Avg
+            paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte
+            // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry
+            psllq mm6, ShiftBpp // shift the mm6 mask to cover bytes 2 & 3
+            movq mm2, mm0       // mov updated Raws to mm2
+            psllq mm2, ShiftBpp // shift data to position correctly
+            movq mm1, mm3       // now use mm1 for getting LBCarrys
+            pand mm1, mm2       // get LBCarrys for each byte where both
+                                // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1        // divide raw bytes by 2
+            pand  mm2, mm4      // clear invalid bit 7 of each byte
+            paddb mm2, mm1      // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6       // Leave only Active Group 2 bytes to add to Avg
+            paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte
+
+            // Add rdd active group (Raw(x-bpp)/2) to Average with LBCarry
+            psllq mm6, ShiftBpp // shift the mm6 mask to cover bytes 4 & 5
+            movq mm2, mm0       // mov updated Raws to mm2
+            psllq mm2, ShiftBpp // shift data to position correctly
+                                // Data only needs to be shifted once here to
+                                // get the correct x-bpp offset.
+            movq mm1, mm3       // now use mm1 for getting LBCarrys
+            pand mm1, mm2       // get LBCarrys for each byte where both
+                                // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1        // divide raw bytes by 2
+            pand  mm2, mm4      // clear invalid bit 7 of each byte
+            paddb mm2, mm1      // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6       // Leave only Active Group 2 bytes to add to Avg
+            paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte
+
+            // Add 4th active group (Raw(x-bpp)/2) to Average with LBCarry
+            psllq mm6, ShiftBpp  // shift the mm6 mask to cover bytes 6 & 7
+            movq mm2, mm0        // mov updated Raws to mm2
+            psllq mm2, ShiftBpp  // shift data to position correctly
+                                 // Data only needs to be shifted once here to
+                                 // get the correct x-bpp offset.
+            add ebx, 8
+            movq mm1, mm3    // now use mm1 for getting LBCarrys
+            pand mm1, mm2    // get LBCarrys for each byte where both
+                             // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1     // divide raw bytes by 2
+            pand  mm2, mm4   // clear invalid bit 7 of each byte
+            paddb mm2, mm1   // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6    // Leave only Active Group 2 bytes to add to Avg
+            paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte
+
+            cmp ebx, MMXLength
+            // Now ready to write back to memory
+            movq [edi + ebx - 8], mm0
+            // Prep Raw(x-bpp) for next loop
+            movq mm2, mm0    // mov updated Raws to mm2
+            jb davg2lp
+        } // end _asm block
+      }
+      break;
+
+      case 1:                 // bpp == 1
+      {
+         _asm {
+            // Re-init address pointers and offset
+            mov ebx, diff     // ebx ==> x = offset to alignment boundary
+            mov edi, row      // edi ==> Avg(x)
+            cmp ebx, FullLength  // Test if offset at end of array
+            jnb davg1end
+            // Do Paeth decode for remaining bytes
+            mov esi, prev_row    // esi ==> Prior(x)
+            mov edx, edi
+            xor ecx, ecx         // zero ecx before using cl & cx in loop below
+            sub edx, bpp         // edx ==> Raw(x-bpp)
+davg1lp:
+            // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
+            xor eax, eax
+            mov cl, [esi + ebx]  // load cl with Prior(x)
+            mov al, [edx + ebx]  // load al with Raw(x-bpp)
+            add ax, cx
+            inc ebx
+            shr ax, 1            // divide by 2
+            add al, [edi+ebx-1]  // Add Avg(x); -1 to offset inc ebx
+            cmp ebx, FullLength  // Check if at end of array
+            mov [edi+ebx-1], al  // Write back Raw(x);
+                         // mov does not affect flags; -1 to offset inc ebx
+            jb davg1lp
+davg1end:
+         } // end _asm block
+      }
+      return;
+
+      case 8:             // bpp == 8
+      {
+         _asm {
+            // Re-init address pointers and offset
+            mov ebx, diff           // ebx ==> x = offset to alignment boundary
+            movq mm5, LBCarryMask
+            mov edi, row            // edi ==> Avg(x)
+            movq mm4, HBClearMask
+            mov esi, prev_row       // esi ==> Prior(x)
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm2, [edi + ebx - 8]  // Load previous aligned 8 bytes
+                                // (NO NEED to correct position in loop below)
+davg8lp:
+            movq mm0, [edi + ebx]
+            movq mm3, mm5
+            movq mm1, [esi + ebx]
+            add ebx, 8
+            pand mm3, mm1       // get lsb for each prev_row byte
+            psrlq mm1, 1        // divide prev_row bytes by 2
+            pand mm3, mm2       // get LBCarrys for each byte where both
+                                // lsb's were == 1
+            psrlq mm2, 1        // divide raw bytes by 2
+            pand  mm1, mm4      // clear invalid bit 7 of each byte
+            paddb mm0, mm3      // add LBCarrys to Avg for each byte
+            pand  mm2, mm4      // clear invalid bit 7 of each byte
+            paddb mm0, mm1      // add (Prev_row/2) to Avg for each byte
+            paddb mm0, mm2      // add (Raw/2) to Avg for each byte
+            cmp ebx, MMXLength
+            movq [edi + ebx - 8], mm0
+            movq mm2, mm0       // reuse as Raw(x-bpp)
+            jb davg8lp
+        } // end _asm block
+      }
+      break;
+      default:                  // bpp greater than 8
+      {
+        _asm {
+            movq mm5, LBCarryMask
+            // Re-init address pointers and offset
+            mov ebx, diff       // ebx ==> x = offset to alignment boundary
+            mov edi, row        // edi ==> Avg(x)
+            movq mm4, HBClearMask
+            mov edx, edi
+            mov esi, prev_row   // esi ==> Prior(x)
+            sub edx, bpp        // edx ==> Raw(x-bpp)
+davgAlp:
+            movq mm0, [edi + ebx]
+            movq mm3, mm5
+            movq mm1, [esi + ebx]
+            pand mm3, mm1       // get lsb for each prev_row byte
+            movq mm2, [edx + ebx]
+            psrlq mm1, 1        // divide prev_row bytes by 2
+            pand mm3, mm2       // get LBCarrys for each byte where both
+                                // lsb's were == 1
+            psrlq mm2, 1        // divide raw bytes by 2
+            pand  mm1, mm4      // clear invalid bit 7 of each byte
+            paddb mm0, mm3      // add LBCarrys to Avg for each byte
+            pand  mm2, mm4      // clear invalid bit 7 of each byte
+            paddb mm0, mm1      // add (Prev_row/2) to Avg for each byte
+            add ebx, 8
+            paddb mm0, mm2      // add (Raw/2) to Avg for each byte
+            cmp ebx, MMXLength
+            movq [edi + ebx - 8], mm0
+            jb davgAlp
+        } // end _asm block
+      }
+      break;
+   }                         // end switch ( bpp )
+
+   _asm {
+         // MMX acceleration complete now do clean-up
+         // Check if any remaining bytes left to decode
+         mov ebx, MMXLength    // ebx ==> x = offset bytes remaining after MMX
+         mov edi, row          // edi ==> Avg(x)
+         cmp ebx, FullLength   // Test if offset at end of array
+         jnb davgend
+         // Do Paeth decode for remaining bytes
+         mov esi, prev_row     // esi ==> Prior(x)
+         mov edx, edi
+         xor ecx, ecx          // zero ecx before using cl & cx in loop below
+         sub edx, bpp          // edx ==> Raw(x-bpp)
+davglp2:
+         // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
+         xor eax, eax
+         mov cl, [esi + ebx]   // load cl with Prior(x)
+         mov al, [edx + ebx]   // load al with Raw(x-bpp)
+         add ax, cx
+         inc ebx
+         shr ax, 1              // divide by 2
+         add al, [edi+ebx-1]    // Add Avg(x); -1 to offset inc ebx
+         cmp ebx, FullLength    // Check if at end of array
+         mov [edi+ebx-1], al    // Write back Raw(x);
+                          // mov does not affect flags; -1 to offset inc ebx
+         jb davglp2
+davgend:
+         emms             // End MMX instructions; prep for possible FP instrs.
+   } // end _asm block
+}
+
+// Optimized code for PNG Paeth filter decoder
+void /* PRIVATE */
+png_read_filter_row_mmx_paeth(png_row_infop row_info, png_bytep row,
+                              png_bytep prev_row)
+{
+   png_uint_32 FullLength;
+   png_uint_32 MMXLength;
+   //png_uint_32 len;
+   int bpp;
+   int diff;
+   //int ptemp;
+   int patemp, pbtemp, pctemp;
+
+   bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel
+   FullLength  = row_info->rowbytes; // # of bytes to filter
+   _asm
+   {
+         xor ebx, ebx        // ebx ==> x offset
+         mov edi, row
+         xor edx, edx        // edx ==> x-bpp offset
+         mov esi, prev_row
+         xor eax, eax
+
+         // Compute the Raw value for the first bpp bytes
+         // Note: the formula works out to be always
+         //   Paeth(x) = Raw(x) + Prior(x)      where x < bpp
+dpthrlp:
+         mov al, [edi + ebx]
+         add al, [esi + ebx]
+         inc ebx
+         cmp ebx, bpp
+         mov [edi + ebx - 1], al
+         jb dpthrlp
+         // get # of bytes to alignment
+         mov diff, edi         // take start of row
+         add diff, ebx         // add bpp
+         xor ecx, ecx
+         add diff, 0xf         // add 7 + 8 to incr past alignment boundary
+         and diff, 0xfffffff8  // mask to alignment boundary
+         sub diff, edi         // subtract from start ==> value ebx at alignment
+         jz dpthgo
+         // fix alignment
+dpthlp1:
+         xor eax, eax
+         // pav = p - a = (a + b - c) - a = b - c
+         mov al, [esi + ebx]   // load Prior(x) into al
+         mov cl, [esi + edx]   // load Prior(x-bpp) into cl
+         sub eax, ecx          // subtract Prior(x-bpp)
+         mov patemp, eax       // Save pav for later use
+         xor eax, eax
+         // pbv = p - b = (a + b - c) - b = a - c
+         mov al, [edi + edx]   // load Raw(x-bpp) into al
+         sub eax, ecx          // subtract Prior(x-bpp)
+         mov ecx, eax
+         // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+         add eax, patemp       // pcv = pav + pbv
+         // pc = abs(pcv)
+         test eax, 0x80000000
+         jz dpthpca
+         neg eax               // reverse sign of neg values
+dpthpca:
+         mov pctemp, eax       // save pc for later use
+         // pb = abs(pbv)
+         test ecx, 0x80000000
+         jz dpthpba
+         neg ecx               // reverse sign of neg values
+dpthpba:
+         mov pbtemp, ecx       // save pb for later use
+         // pa = abs(pav)
+         mov eax, patemp
+         test eax, 0x80000000
+         jz dpthpaa
+         neg eax               // reverse sign of neg values
+dpthpaa:
+         mov patemp, eax       // save pa for later use
+         // test if pa <= pb
+         cmp eax, ecx
+         jna dpthabb
+         // pa > pb; now test if pb <= pc
+         cmp ecx, pctemp
+         jna dpthbbc
+         // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
+         jmp dpthpaeth
+dpthbbc:
+         // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
+         mov cl, [esi + ebx]   // load Prior(x) into cl
+         jmp dpthpaeth
+dpthabb:
+         // pa <= pb; now test if pa <= pc
+         cmp eax, pctemp
+         jna dpthabc
+         // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
+         jmp dpthpaeth
+dpthabc:
+         // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
+         mov cl, [edi + edx]  // load Raw(x-bpp) into cl
+dpthpaeth:
+         inc ebx
+         inc edx
+         // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
+         add [edi + ebx - 1], cl
+         cmp ebx, diff
+         jb dpthlp1
+dpthgo:
+         mov ecx, FullLength
+         mov eax, ecx
+         sub eax, ebx          // subtract alignment fix
+         and eax, 0x00000007   // calc bytes over mult of 8
+         sub ecx, eax          // drop over bytes from original length
+         mov MMXLength, ecx
+   } // end _asm block
+   // Now do the math for the rest of the row
+   switch ( bpp )
+   {
+      case 3:
+      {
+         ActiveMask.use = 0x0000000000ffffff;
+         ActiveMaskEnd.use = 0xffff000000000000;
+         ShiftBpp.use = 24;    // == bpp(3) * 8
+         ShiftRem.use = 40;    // == 64 - 24
+         _asm
+         {
+            mov ebx, diff
+            mov edi, row
+            mov esi, prev_row
+            pxor mm0, mm0
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm1, [edi+ebx-8]
+dpth3lp:
+            psrlq mm1, ShiftRem     // shift last 3 bytes to 1st 3 bytes
+            movq mm2, [esi + ebx]   // load b=Prior(x)
+            punpcklbw mm1, mm0      // Unpack High bytes of a
+            movq mm3, [esi+ebx-8]   // Prep c=Prior(x-bpp) bytes
+            punpcklbw mm2, mm0      // Unpack High bytes of b
+            psrlq mm3, ShiftRem     // shift last 3 bytes to 1st 3 bytes
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            punpcklbw mm3, mm0      // Unpack High bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            psubw mm4, mm3
+            pxor mm7, mm7
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4    // Create mask pav bytes < 0
+            paddw mm6, mm5
+            pand mm0, mm4       // Only pav bytes < 0 in mm7
+            pcmpgtw mm7, mm5    // Create mask pbv bytes < 0
+            psubw mm4, mm0
+            pand mm7, mm5       // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6    // Create mask pcv bytes < 0
+            pand mm0, mm6       // Only pav bytes < 0 in mm7
+            psubw mm5, mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5    // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6       // pab > pc?
+            pxor mm1, mm1
+            pand mm3, mm7
+            pandn mm7, mm0
+            paddw mm7, mm3
+            pxor mm0, mm0
+            packuswb mm7, mm1
+            movq mm3, [esi + ebx]   // load c=Prior(x-bpp)
+            pand mm7, ActiveMask
+            movq mm2, mm3           // load b=Prior(x) step 1
+            paddb mm7, [edi + ebx]  // add Paeth predictor with Raw(x)
+            punpcklbw mm3, mm0      // Unpack High bytes of c
+            movq [edi + ebx], mm7   // write back updated value
+            movq mm1, mm7           // Now mm1 will be used as Raw(x-bpp)
+            // Now do Paeth for 2nd set of bytes (3-5)
+            psrlq mm2, ShiftBpp     // load b=Prior(x) step 2
+            punpcklbw mm1, mm0      // Unpack High bytes of a
+            pxor mm7, mm7
+            punpcklbw mm2, mm0      // Unpack High bytes of b
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            psubw mm5, mm3
+            psubw mm4, mm3
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) =
+            //       pav + pbv = pbv + pav
+            movq mm6, mm5
+            paddw mm6, mm4
+
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm5       // Create mask pbv bytes < 0
+            pcmpgtw mm7, mm4       // Create mask pav bytes < 0
+            pand mm0, mm5          // Only pbv bytes < 0 in mm0
+            pand mm7, mm4          // Only pav bytes < 0 in mm7
+            psubw mm5, mm0
+            psubw mm4, mm7
+            psubw mm5, mm0
+            psubw mm4, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
+            pand mm0, mm6          // Only pav bytes < 0 in mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5       // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6       // pab > pc?
+            movq mm2, [esi + ebx]  // load b=Prior(x)
+            pand mm3, mm7
+            pandn mm7, mm0
+            pxor mm1, mm1
+            paddw mm7, mm3
+            pxor mm0, mm0
+            packuswb mm7, mm1
+            movq mm3, mm2           // load c=Prior(x-bpp) step 1
+            pand mm7, ActiveMask
+            punpckhbw mm2, mm0      // Unpack High bytes of b
+            psllq mm7, ShiftBpp     // Shift bytes to 2nd group of 3 bytes
+             // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            paddb mm7, [edi + ebx]  // add Paeth predictor with Raw(x)
+            psllq mm3, ShiftBpp     // load c=Prior(x-bpp) step 2
+            movq [edi + ebx], mm7   // write back updated value
+            movq mm1, mm7
+            punpckhbw mm3, mm0      // Unpack High bytes of c
+            psllq mm1, ShiftBpp     // Shift bytes
+                                    // Now mm1 will be used as Raw(x-bpp)
+            // Now do Paeth for 3rd, and final, set of bytes (6-7)
+            pxor mm7, mm7
+            punpckhbw mm1, mm0      // Unpack High bytes of a
+            psubw mm4, mm3
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+            pxor mm0, mm0
+            paddw mm6, mm5
+
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4    // Create mask pav bytes < 0
+            pcmpgtw mm7, mm5    // Create mask pbv bytes < 0
+            pand mm0, mm4       // Only pav bytes < 0 in mm7
+            pand mm7, mm5       // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6    // Create mask pcv bytes < 0
+            pand mm0, mm6       // Only pav bytes < 0 in mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5    // pa > pb?
+            movq mm0, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            pandn mm0, mm1
+            pandn mm7, mm4
+            paddw mm0, mm2
+            paddw mm7, mm5
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6    // pab > pc?
+            pand mm3, mm7
+            pandn mm7, mm0
+            paddw mm7, mm3
+            pxor mm1, mm1
+            packuswb mm1, mm7
+            // Step ebx to next set of 8 bytes and repeat loop til done
+            add ebx, 8
+            pand mm1, ActiveMaskEnd
+            paddb mm1, [edi + ebx - 8] // add Paeth predictor with Raw(x)
+
+            cmp ebx, MMXLength
+            pxor mm0, mm0              // pxor does not affect flags
+            movq [edi + ebx - 8], mm1  // write back updated value
+                                 // mm1 will be used as Raw(x-bpp) next loop
+                           // mm3 ready to be used as Prior(x-bpp) next loop
+            jb dpth3lp
+         } // end _asm block
+      }
+      break;
+
+      case 6:
+      case 7:
+      case 5:
+      {
+         ActiveMask.use  = 0x00000000ffffffff;
+         ActiveMask2.use = 0xffffffff00000000;
+         ShiftBpp.use = bpp << 3;    // == bpp * 8
+         ShiftRem.use = 64 - ShiftBpp.use;
+         _asm
+         {
+            mov ebx, diff
+            mov edi, row
+            mov esi, prev_row
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm1, [edi+ebx-8]
+            pxor mm0, mm0
+dpth6lp:
+            // Must shift to position Raw(x-bpp) data
+            psrlq mm1, ShiftRem
+            // Do first set of 4 bytes
+            movq mm3, [esi+ebx-8]      // read c=Prior(x-bpp) bytes
+            punpcklbw mm1, mm0      // Unpack Low bytes of a
+            movq mm2, [esi + ebx]   // load b=Prior(x)
+            punpcklbw mm2, mm0      // Unpack Low bytes of b
+            // Must shift to position Prior(x-bpp) data
+            psrlq mm3, ShiftRem
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            punpcklbw mm3, mm0      // Unpack Low bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            psubw mm4, mm3
+            pxor mm7, mm7
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4    // Create mask pav bytes < 0
+            paddw mm6, mm5
+            pand mm0, mm4       // Only pav bytes < 0 in mm7
+            pcmpgtw mm7, mm5    // Create mask pbv bytes < 0
+            psubw mm4, mm0
+            pand mm7, mm5       // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6    // Create mask pcv bytes < 0
+            pand mm0, mm6       // Only pav bytes < 0 in mm7
+            psubw mm5, mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5    // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6    // pab > pc?
+            pxor mm1, mm1
+            pand mm3, mm7
+            pandn mm7, mm0
+            paddw mm7, mm3
+            pxor mm0, mm0
+            packuswb mm7, mm1
+            movq mm3, [esi + ebx - 8]  // load c=Prior(x-bpp)
+            pand mm7, ActiveMask
+            psrlq mm3, ShiftRem
+            movq mm2, [esi + ebx]      // load b=Prior(x) step 1
+            paddb mm7, [edi + ebx]     // add Paeth predictor with Raw(x)
+            movq mm6, mm2
+            movq [edi + ebx], mm7      // write back updated value
+            movq mm1, [edi+ebx-8]
+            psllq mm6, ShiftBpp
+            movq mm5, mm7
+            psrlq mm1, ShiftRem
+            por mm3, mm6
+            psllq mm5, ShiftBpp
+            punpckhbw mm3, mm0         // Unpack High bytes of c
+            por mm1, mm5
+            // Do second set of 4 bytes
+            punpckhbw mm2, mm0         // Unpack High bytes of b
+            punpckhbw mm1, mm0         // Unpack High bytes of a
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            psubw mm4, mm3
+            pxor mm7, mm7
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
+            paddw mm6, mm5
+            pand mm0, mm4          // Only pav bytes < 0 in mm7
+            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
+            psubw mm4, mm0
+            pand mm7, mm5          // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
+            pand mm0, mm6          // Only pav bytes < 0 in mm7
+            psubw mm5, mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5       // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6           // pab > pc?
+            pxor mm1, mm1
+            pand mm3, mm7
+            pandn mm7, mm0
+            pxor mm1, mm1
+            paddw mm7, mm3
+            pxor mm0, mm0
+            // Step ex to next set of 8 bytes and repeat loop til done
+            add ebx, 8
+            packuswb mm1, mm7
+            paddb mm1, [edi + ebx - 8]     // add Paeth predictor with Raw(x)
+            cmp ebx, MMXLength
+            movq [edi + ebx - 8], mm1      // write back updated value
+                                // mm1 will be used as Raw(x-bpp) next loop
+            jb dpth6lp
+         } // end _asm block
+      }
+      break;
+
+      case 4:
+      {
+         ActiveMask.use  = 0x00000000ffffffff;
+         _asm {
+            mov ebx, diff
+            mov edi, row
+            mov esi, prev_row
+            pxor mm0, mm0
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm1, [edi+ebx-8]    // Only time should need to read
+                                     //  a=Raw(x-bpp) bytes
+dpth4lp:
+            // Do first set of 4 bytes
+            movq mm3, [esi+ebx-8]    // read c=Prior(x-bpp) bytes
+            punpckhbw mm1, mm0       // Unpack Low bytes of a
+            movq mm2, [esi + ebx]    // load b=Prior(x)
+            punpcklbw mm2, mm0       // Unpack High bytes of b
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            punpckhbw mm3, mm0       // Unpack High bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            psubw mm4, mm3
+            pxor mm7, mm7
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
+            paddw mm6, mm5
+            pand mm0, mm4          // Only pav bytes < 0 in mm7
+            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
+            psubw mm4, mm0
+            pand mm7, mm5          // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
+            pand mm0, mm6          // Only pav bytes < 0 in mm7
+            psubw mm5, mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5       // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6       // pab > pc?
+            pxor mm1, mm1
+            pand mm3, mm7
+            pandn mm7, mm0
+            paddw mm7, mm3
+            pxor mm0, mm0
+            packuswb mm7, mm1
+            movq mm3, [esi + ebx]      // load c=Prior(x-bpp)
+            pand mm7, ActiveMask
+            movq mm2, mm3              // load b=Prior(x) step 1
+            paddb mm7, [edi + ebx]     // add Paeth predictor with Raw(x)
+            punpcklbw mm3, mm0         // Unpack High bytes of c
+            movq [edi + ebx], mm7      // write back updated value
+            movq mm1, mm7              // Now mm1 will be used as Raw(x-bpp)
+            // Do second set of 4 bytes
+            punpckhbw mm2, mm0         // Unpack Low bytes of b
+            punpcklbw mm1, mm0         // Unpack Low bytes of a
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            psubw mm4, mm3
+            pxor mm7, mm7
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
+            paddw mm6, mm5
+            pand mm0, mm4          // Only pav bytes < 0 in mm7
+            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
+            psubw mm4, mm0
+            pand mm7, mm5          // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
+            pand mm0, mm6          // Only pav bytes < 0 in mm7
+            psubw mm5, mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5       // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6       // pab > pc?
+            pxor mm1, mm1
+            pand mm3, mm7
+            pandn mm7, mm0
+            pxor mm1, mm1
+            paddw mm7, mm3
+            pxor mm0, mm0
+            // Step ex to next set of 8 bytes and repeat loop til done
+            add ebx, 8
+            packuswb mm1, mm7
+            paddb mm1, [edi + ebx - 8]     // add Paeth predictor with Raw(x)
+            cmp ebx, MMXLength
+            movq [edi + ebx - 8], mm1      // write back updated value
+                                // mm1 will be used as Raw(x-bpp) next loop
+            jb dpth4lp
+         } // end _asm block
+      }
+      break;
+      case 8:                          // bpp == 8
+      {
+         ActiveMask.use  = 0x00000000ffffffff;
+         _asm {
+            mov ebx, diff
+            mov edi, row
+            mov esi, prev_row
+            pxor mm0, mm0
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm1, [edi+ebx-8]      // Only time should need to read
+                                       //  a=Raw(x-bpp) bytes
+dpth8lp:
+            // Do first set of 4 bytes
+            movq mm3, [esi+ebx-8]      // read c=Prior(x-bpp) bytes
+            punpcklbw mm1, mm0         // Unpack Low bytes of a
+            movq mm2, [esi + ebx]      // load b=Prior(x)
+            punpcklbw mm2, mm0         // Unpack Low bytes of b
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            punpcklbw mm3, mm0         // Unpack Low bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            psubw mm4, mm3
+            pxor mm7, mm7
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
+            paddw mm6, mm5
+            pand mm0, mm4          // Only pav bytes < 0 in mm7
+            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
+            psubw mm4, mm0
+            pand mm7, mm5          // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
+            pand mm0, mm6          // Only pav bytes < 0 in mm7
+            psubw mm5, mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5       // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6       // pab > pc?
+            pxor mm1, mm1
+            pand mm3, mm7
+            pandn mm7, mm0
+            paddw mm7, mm3
+            pxor mm0, mm0
+            packuswb mm7, mm1
+            movq mm3, [esi+ebx-8]    // read c=Prior(x-bpp) bytes
+            pand mm7, ActiveMask
+            movq mm2, [esi + ebx]    // load b=Prior(x)
+            paddb mm7, [edi + ebx]   // add Paeth predictor with Raw(x)
+            punpckhbw mm3, mm0       // Unpack High bytes of c
+            movq [edi + ebx], mm7    // write back updated value
+            movq mm1, [edi+ebx-8]    // read a=Raw(x-bpp) bytes
+
+            // Do second set of 4 bytes
+            punpckhbw mm2, mm0       // Unpack High bytes of b
+            punpckhbw mm1, mm0       // Unpack High bytes of a
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            psubw mm4, mm3
+            pxor mm7, mm7
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
+            paddw mm6, mm5
+            pand mm0, mm4          // Only pav bytes < 0 in mm7
+            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
+            psubw mm4, mm0
+            pand mm7, mm5          // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
+            pand mm0, mm6          // Only pav bytes < 0 in mm7
+            psubw mm5, mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5       // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6       // pab > pc?
+            pxor mm1, mm1
+            pand mm3, mm7
+            pandn mm7, mm0
+            pxor mm1, mm1
+            paddw mm7, mm3
+            pxor mm0, mm0
+            // Step ex to next set of 8 bytes and repeat loop til done
+            add ebx, 8
+            packuswb mm1, mm7
+            paddb mm1, [edi + ebx - 8]     // add Paeth predictor with Raw(x)
+            cmp ebx, MMXLength
+            movq [edi + ebx - 8], mm1      // write back updated value
+                            // mm1 will be used as Raw(x-bpp) next loop
+            jb dpth8lp
+         } // end _asm block
+      }
+      break;
+
+      case 1:                // bpp = 1
+      case 2:                // bpp = 2
+      default:               // bpp > 8
+      {
+         _asm {
+            mov ebx, diff
+            cmp ebx, FullLength
+            jnb dpthdend
+            mov edi, row
+            mov esi, prev_row
+            // Do Paeth decode for remaining bytes
+            mov edx, ebx
+            xor ecx, ecx        // zero ecx before using cl & cx in loop below
+            sub edx, bpp        // Set edx = ebx - bpp
+dpthdlp:
+            xor eax, eax
+            // pav = p - a = (a + b - c) - a = b - c
+            mov al, [esi + ebx]        // load Prior(x) into al
+            mov cl, [esi + edx]        // load Prior(x-bpp) into cl
+            sub eax, ecx                 // subtract Prior(x-bpp)
+            mov patemp, eax                 // Save pav for later use
+            xor eax, eax
+            // pbv = p - b = (a + b - c) - b = a - c
+            mov al, [edi + edx]        // load Raw(x-bpp) into al
+            sub eax, ecx                 // subtract Prior(x-bpp)
+            mov ecx, eax
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            add eax, patemp                 // pcv = pav + pbv
+            // pc = abs(pcv)
+            test eax, 0x80000000
+            jz dpthdpca
+            neg eax                     // reverse sign of neg values
+dpthdpca:
+            mov pctemp, eax             // save pc for later use
+            // pb = abs(pbv)
+            test ecx, 0x80000000
+            jz dpthdpba
+            neg ecx                     // reverse sign of neg values
+dpthdpba:
+            mov pbtemp, ecx             // save pb for later use
+            // pa = abs(pav)
+            mov eax, patemp
+            test eax, 0x80000000
+            jz dpthdpaa
+            neg eax                     // reverse sign of neg values
+dpthdpaa:
+            mov patemp, eax             // save pa for later use
+            // test if pa <= pb
+            cmp eax, ecx
+            jna dpthdabb
+            // pa > pb; now test if pb <= pc
+            cmp ecx, pctemp
+            jna dpthdbbc
+            // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+            mov cl, [esi + edx]  // load Prior(x-bpp) into cl
+            jmp dpthdpaeth
+dpthdbbc:
+            // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
+            mov cl, [esi + ebx]        // load Prior(x) into cl
+            jmp dpthdpaeth
+dpthdabb:
+            // pa <= pb; now test if pa <= pc
+            cmp eax, pctemp
+            jna dpthdabc
+            // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+            mov cl, [esi + edx]  // load Prior(x-bpp) into cl
+            jmp dpthdpaeth
+dpthdabc:
+            // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
+            mov cl, [edi + edx]  // load Raw(x-bpp) into cl
+dpthdpaeth:
+            inc ebx
+            inc edx
+            // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
+            add [edi + ebx - 1], cl
+            cmp ebx, FullLength
+            jb dpthdlp
+dpthdend:
+         } // end _asm block
+      }
+      return;                   // No need to go further with this one
+   }                         // end switch ( bpp )
+   _asm
+   {
+         // MMX acceleration complete now do clean-up
+         // Check if any remaining bytes left to decode
+         mov ebx, MMXLength
+         cmp ebx, FullLength
+         jnb dpthend
+         mov edi, row
+         mov esi, prev_row
+         // Do Paeth decode for remaining bytes
+         mov edx, ebx
+         xor ecx, ecx         // zero ecx before using cl & cx in loop below
+         sub edx, bpp         // Set edx = ebx - bpp
+dpthlp2:
+         xor eax, eax
+         // pav = p - a = (a + b - c) - a = b - c
+         mov al, [esi + ebx]  // load Prior(x) into al
+         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
+         sub eax, ecx         // subtract Prior(x-bpp)
+         mov patemp, eax      // Save pav for later use
+         xor eax, eax
+         // pbv = p - b = (a + b - c) - b = a - c
+         mov al, [edi + edx]  // load Raw(x-bpp) into al
+         sub eax, ecx         // subtract Prior(x-bpp)
+         mov ecx, eax
+         // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+         add eax, patemp      // pcv = pav + pbv
+         // pc = abs(pcv)
+         test eax, 0x80000000
+         jz dpthpca2
+         neg eax              // reverse sign of neg values
+dpthpca2:
+         mov pctemp, eax      // save pc for later use
+         // pb = abs(pbv)
+         test ecx, 0x80000000
+         jz dpthpba2
+         neg ecx              // reverse sign of neg values
+dpthpba2:
+         mov pbtemp, ecx      // save pb for later use
+         // pa = abs(pav)
+         mov eax, patemp
+         test eax, 0x80000000
+         jz dpthpaa2
+         neg eax              // reverse sign of neg values
+dpthpaa2:
+         mov patemp, eax      // save pa for later use
+         // test if pa <= pb
+         cmp eax, ecx
+         jna dpthabb2
+         // pa > pb; now test if pb <= pc
+         cmp ecx, pctemp
+         jna dpthbbc2
+         // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
+         jmp dpthpaeth2
+dpthbbc2:
+         // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
+         mov cl, [esi + ebx]        // load Prior(x) into cl
+         jmp dpthpaeth2
+dpthabb2:
+         // pa <= pb; now test if pa <= pc
+         cmp eax, pctemp
+         jna dpthabc2
+         // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
+         jmp dpthpaeth2
+dpthabc2:
+         // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
+         mov cl, [edi + edx]  // load Raw(x-bpp) into cl
+dpthpaeth2:
+         inc ebx
+         inc edx
+         // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
+         add [edi + ebx - 1], cl
+         cmp ebx, FullLength
+         jb dpthlp2
+dpthend:
+         emms             // End MMX instructions; prep for possible FP instrs.
+   } // end _asm block
+}
+
+// Optimized code for PNG Sub filter decoder
+void /* PRIVATE */
+png_read_filter_row_mmx_sub(png_row_infop row_info, png_bytep row)
+{
+   //int test;
+   int bpp;
+   png_uint_32 FullLength;
+   png_uint_32 MMXLength;
+   int diff;
+
+   bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel
+   FullLength  = row_info->rowbytes - bpp; // # of bytes to filter
+   _asm {
+        mov edi, row
+        mov esi, edi               // lp = row
+        add edi, bpp               // rp = row + bpp
+        xor eax, eax
+        // get # of bytes to alignment
+        mov diff, edi               // take start of row
+        add diff, 0xf               // add 7 + 8 to incr past
+                                        // alignment boundary
+        xor ebx, ebx
+        and diff, 0xfffffff8        // mask to alignment boundary
+        sub diff, edi               // subtract from start ==> value
+                                        //  ebx at alignment
+        jz dsubgo
+        // fix alignment
+dsublp1:
+        mov al, [esi+ebx]
+        add [edi+ebx], al
+        inc ebx
+        cmp ebx, diff
+        jb dsublp1
+dsubgo:
+        mov ecx, FullLength
+        mov edx, ecx
+        sub edx, ebx                  // subtract alignment fix
+        and edx, 0x00000007           // calc bytes over mult of 8
+        sub ecx, edx                  // drop over bytes from length
+        mov MMXLength, ecx
+   } // end _asm block
+
+   // Now do the math for the rest of the row
+   switch ( bpp )
+   {
+        case 3:
+        {
+         ActiveMask.use  = 0x0000ffffff000000;
+         ShiftBpp.use = 24;       // == 3 * 8
+         ShiftRem.use  = 40;      // == 64 - 24
+         _asm {
+            mov edi, row
+            movq mm7, ActiveMask  // Load ActiveMask for 2nd active byte group
+            mov esi, edi              // lp = row
+            add edi, bpp          // rp = row + bpp
+            movq mm6, mm7
+            mov ebx, diff
+            psllq mm6, ShiftBpp   // Move mask in mm6 to cover 3rd active
+                                  // byte group
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm1, [edi+ebx-8]
+dsub3lp:
+            psrlq mm1, ShiftRem   // Shift data for adding 1st bpp bytes
+                          // no need for mask; shift clears inactive bytes
+            // Add 1st active group
+            movq mm0, [edi+ebx]
+            paddb mm0, mm1
+            // Add 2nd active group
+            movq mm1, mm0         // mov updated Raws to mm1
+            psllq mm1, ShiftBpp   // shift data to position correctly
+            pand mm1, mm7         // mask to use only 2nd active group
+            paddb mm0, mm1
+            // Add 3rd active group
+            movq mm1, mm0         // mov updated Raws to mm1
+            psllq mm1, ShiftBpp   // shift data to position correctly
+            pand mm1, mm6         // mask to use only 3rd active group
+            add ebx, 8
+            paddb mm0, mm1
+            cmp ebx, MMXLength
+            movq [edi+ebx-8], mm0     // Write updated Raws back to array
+            // Prep for doing 1st add at top of loop
+            movq mm1, mm0
+            jb dsub3lp
+         } // end _asm block
+      }
+      break;
+
+      case 1:
+      {
+         // Placed here just in case this is a duplicate of the
+         // non-MMX code for the SUB filter in png_read_filter_row below
+         //
+         //         png_bytep rp;
+         //         png_bytep lp;
+         //         png_uint_32 i;
+         //         bpp = (row_info->pixel_depth + 7) >> 3;
+         //         for (i = (png_uint_32)bpp, rp = row + bpp, lp = row;
+         //            i < row_info->rowbytes; i++, rp++, lp++)
+         //      {
+         //            *rp = (png_byte)(((int)(*rp) + (int)(*lp)) & 0xff);
+         //      }
+         _asm {
+            mov ebx, diff
+            mov edi, row
+            cmp ebx, FullLength
+            jnb dsub1end
+            mov esi, edi          // lp = row
+            xor eax, eax
+            add edi, bpp      // rp = row + bpp
+dsub1lp:
+            mov al, [esi+ebx]
+            add [edi+ebx], al
+            inc ebx
+            cmp ebx, FullLength
+            jb dsub1lp
+dsub1end:
+         } // end _asm block
+      }
+      return;
+
+      case 6:
+      case 7:
+      case 4:
+      case 5:
+      {
+         ShiftBpp.use = bpp << 3;
+         ShiftRem.use = 64 - ShiftBpp.use;
+         _asm {
+            mov edi, row
+            mov ebx, diff
+            mov esi, edi               // lp = row
+            add edi, bpp           // rp = row + bpp
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm1, [edi+ebx-8]
+dsub4lp:
+            psrlq mm1, ShiftRem // Shift data for adding 1st bpp bytes
+                          // no need for mask; shift clears inactive bytes
+            movq mm0, [edi+ebx]
+            paddb mm0, mm1
+            // Add 2nd active group
+            movq mm1, mm0          // mov updated Raws to mm1
+            psllq mm1, ShiftBpp    // shift data to position correctly
+                                   // there is no need for any mask
+                                   // since shift clears inactive bits/bytes
+            add ebx, 8
+            paddb mm0, mm1
+            cmp ebx, MMXLength
+            movq [edi+ebx-8], mm0
+            movq mm1, mm0          // Prep for doing 1st add at top of loop
+            jb dsub4lp
+         } // end _asm block
+      }
+      break;
+
+      case 2:
+      {
+         ActiveMask.use  = 0x00000000ffff0000;
+         ShiftBpp.use = 16;       // == 2 * 8
+         ShiftRem.use = 48;       // == 64 - 16
+         _asm {
+            movq mm7, ActiveMask  // Load ActiveMask for 2nd active byte group
+            mov ebx, diff
+            movq mm6, mm7
+            mov edi, row
+            psllq mm6, ShiftBpp     // Move mask in mm6 to cover 3rd active
+                                    //  byte group
+            mov esi, edi            // lp = row
+            movq mm5, mm6
+            add edi, bpp            // rp = row + bpp
+            psllq mm5, ShiftBpp     // Move mask in mm5 to cover 4th active
+                                    //  byte group
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm1, [edi+ebx-8]
+dsub2lp:
+            // Add 1st active group
+            psrlq mm1, ShiftRem     // Shift data for adding 1st bpp bytes
+                                    // no need for mask; shift clears inactive
+                                    //  bytes
+            movq mm0, [edi+ebx]
+            paddb mm0, mm1
+            // Add 2nd active group
+            movq mm1, mm0           // mov updated Raws to mm1
+            psllq mm1, ShiftBpp     // shift data to position correctly
+            pand mm1, mm7           // mask to use only 2nd active group
+            paddb mm0, mm1
+            // Add 3rd active group
+            movq mm1, mm0           // mov updated Raws to mm1
+            psllq mm1, ShiftBpp     // shift data to position correctly
+            pand mm1, mm6           // mask to use only 3rd active group
+            paddb mm0, mm1
+            // Add 4th active group
+            movq mm1, mm0           // mov updated Raws to mm1
+            psllq mm1, ShiftBpp     // shift data to position correctly
+            pand mm1, mm5           // mask to use only 4th active group
+            add ebx, 8
+            paddb mm0, mm1
+            cmp ebx, MMXLength
+            movq [edi+ebx-8], mm0   // Write updated Raws back to array
+            movq mm1, mm0           // Prep for doing 1st add at top of loop
+            jb dsub2lp
+         } // end _asm block
+      }
+      break;
+      case 8:
+      {
+         _asm {
+            mov edi, row
+            mov ebx, diff
+            mov esi, edi            // lp = row
+            add edi, bpp            // rp = row + bpp
+            mov ecx, MMXLength
+            movq mm7, [edi+ebx-8]   // PRIME the pump (load the first
+                                    // Raw(x-bpp) data set
+            and ecx, 0x0000003f     // calc bytes over mult of 64
+dsub8lp:
+            movq mm0, [edi+ebx]     // Load Sub(x) for 1st 8 bytes
+            paddb mm0, mm7
+            movq mm1, [edi+ebx+8]   // Load Sub(x) for 2nd 8 bytes
+            movq [edi+ebx], mm0    // Write Raw(x) for 1st 8 bytes
+                                   // Now mm0 will be used as Raw(x-bpp) for
+                                   // the 2nd group of 8 bytes.  This will be
+                                   // repeated for each group of 8 bytes with
+                                   // the 8th group being used as the Raw(x-bpp)
+                                   // for the 1st group of the next loop.
+            paddb mm1, mm0
+            movq mm2, [edi+ebx+16]  // Load Sub(x) for 3rd 8 bytes
+            movq [edi+ebx+8], mm1   // Write Raw(x) for 2nd 8 bytes
+            paddb mm2, mm1
+            movq mm3, [edi+ebx+24]  // Load Sub(x) for 4th 8 bytes
+            movq [edi+ebx+16], mm2  // Write Raw(x) for 3rd 8 bytes
+            paddb mm3, mm2
+            movq mm4, [edi+ebx+32]  // Load Sub(x) for 5th 8 bytes
+            movq [edi+ebx+24], mm3  // Write Raw(x) for 4th 8 bytes
+            paddb mm4, mm3
+            movq mm5, [edi+ebx+40]  // Load Sub(x) for 6th 8 bytes
+            movq [edi+ebx+32], mm4  // Write Raw(x) for 5th 8 bytes
+            paddb mm5, mm4
+            movq mm6, [edi+ebx+48]  // Load Sub(x) for 7th 8 bytes
+            movq [edi+ebx+40], mm5  // Write Raw(x) for 6th 8 bytes
+            paddb mm6, mm5
+            movq mm7, [edi+ebx+56]  // Load Sub(x) for 8th 8 bytes
+            movq [edi+ebx+48], mm6  // Write Raw(x) for 7th 8 bytes
+            add ebx, 64
+            paddb mm7, mm6
+            cmp ebx, ecx
+            movq [edi+ebx-8], mm7   // Write Raw(x) for 8th 8 bytes
+            jb dsub8lp
+            cmp ebx, MMXLength
+            jnb dsub8lt8
+dsub8lpA:
+            movq mm0, [edi+ebx]
+            add ebx, 8
+            paddb mm0, mm7
+            cmp ebx, MMXLength
+            movq [edi+ebx-8], mm0   // use -8 to offset early add to ebx
+            movq mm7, mm0           // Move calculated Raw(x) data to mm1 to
+                                    // be the new Raw(x-bpp) for the next loop
+            jb dsub8lpA
+dsub8lt8:
+         } // end _asm block
+      }
+      break;
+
+      default:                // bpp greater than 8 bytes
+      {
+         _asm {
+            mov ebx, diff
+            mov edi, row
+            mov esi, edi           // lp = row
+            add edi, bpp           // rp = row + bpp
+dsubAlp:
+            movq mm0, [edi+ebx]
+            movq mm1, [esi+ebx]
+            add ebx, 8
+            paddb mm0, mm1
+            cmp ebx, MMXLength
+            movq [edi+ebx-8], mm0  // mov does not affect flags; -8 to offset
+                                   //  add ebx
+            jb dsubAlp
+         } // end _asm block
+      }
+      break;
+
+   } // end switch ( bpp )
+
+   _asm {
+        mov ebx, MMXLength
+        mov edi, row
+        cmp ebx, FullLength
+        jnb dsubend
+        mov esi, edi               // lp = row
+        xor eax, eax
+        add edi, bpp               // rp = row + bpp
+dsublp2:
+        mov al, [esi+ebx]
+        add [edi+ebx], al
+        inc ebx
+        cmp ebx, FullLength
+        jb dsublp2
+dsubend:
+        emms             // End MMX instructions; prep for possible FP instrs.
+   } // end _asm block
+}
+
+// Optimized code for PNG Up filter decoder
+void /* PRIVATE */
+png_read_filter_row_mmx_up(png_row_infop row_info, png_bytep row,
+   png_bytep prev_row)
+{
+   png_uint_32 len;
+   len  = row_info->rowbytes;       // # of bytes to filter
+   _asm {
+      mov edi, row
+      // get # of bytes to alignment
+      mov ecx, edi
+      xor ebx, ebx
+      add ecx, 0x7
+      xor eax, eax
+      and ecx, 0xfffffff8
+      mov esi, prev_row
+      sub ecx, edi
+      jz dupgo
+      // fix alignment
+duplp1:
+      mov al, [edi+ebx]
+      add al, [esi+ebx]
+      inc ebx
+      cmp ebx, ecx
+      mov [edi + ebx-1], al  // mov does not affect flags; -1 to offset inc ebx
+      jb duplp1
+dupgo:
+      mov ecx, len
+      mov edx, ecx
+      sub edx, ebx                  // subtract alignment fix
+      and edx, 0x0000003f           // calc bytes over mult of 64
+      sub ecx, edx                  // drop over bytes from length
+      // Unrolled loop - use all MMX registers and interleave to reduce
+      // number of branch instructions (loops) and reduce partial stalls
+duploop:
+      movq mm1, [esi+ebx]
+      movq mm0, [edi+ebx]
+      movq mm3, [esi+ebx+8]
+      paddb mm0, mm1
+      movq mm2, [edi+ebx+8]
+      movq [edi+ebx], mm0
+      paddb mm2, mm3
+      movq mm5, [esi+ebx+16]
+      movq [edi+ebx+8], mm2
+      movq mm4, [edi+ebx+16]
+      movq mm7, [esi+ebx+24]
+      paddb mm4, mm5
+      movq mm6, [edi+ebx+24]
+      movq [edi+ebx+16], mm4
+      paddb mm6, mm7
+      movq mm1, [esi+ebx+32]
+      movq [edi+ebx+24], mm6
+      movq mm0, [edi+ebx+32]
+      movq mm3, [esi+ebx+40]
+      paddb mm0, mm1
+      movq mm2, [edi+ebx+40]
+      movq [edi+ebx+32], mm0
+      paddb mm2, mm3
+      movq mm5, [esi+ebx+48]
+      movq [edi+ebx+40], mm2
+      movq mm4, [edi+ebx+48]
+      movq mm7, [esi+ebx+56]
+      paddb mm4, mm5
+      movq mm6, [edi+ebx+56]
+      movq [edi+ebx+48], mm4
+      add ebx, 64
+      paddb mm6, mm7
+      cmp ebx, ecx
+      movq [edi+ebx-8], mm6 // (+56)movq does not affect flags;
+                                     // -8 to offset add ebx
+      jb duploop
+
+      cmp edx, 0                     // Test for bytes over mult of 64
+      jz dupend
+
+
+      // 2 lines added by lcreeve at netins.net
+      // (mail 11 Jul 98 in png-implement list)
+      cmp edx, 8 //test for less than 8 bytes
+      jb duplt8
+
+
+      add ecx, edx
+      and edx, 0x00000007           // calc bytes over mult of 8
+      sub ecx, edx                  // drop over bytes from length
+      jz duplt8
+      // Loop using MMX registers mm0 & mm1 to update 8 bytes simultaneously
+duplpA:
+      movq mm1, [esi+ebx]
+      movq mm0, [edi+ebx]
+      add ebx, 8
+      paddb mm0, mm1
+      cmp ebx, ecx
+      movq [edi+ebx-8], mm0 // movq does not affect flags; -8 to offset add ebx
+      jb duplpA
+      cmp edx, 0            // Test for bytes over mult of 8
+      jz dupend
+duplt8:
+      xor eax, eax
+      add ecx, edx          // move over byte count into counter
+      // Loop using x86 registers to update remaining bytes
+duplp2:
+      mov al, [edi + ebx]
+      add al, [esi + ebx]
+      inc ebx
+      cmp ebx, ecx
+      mov [edi + ebx-1], al // mov does not affect flags; -1 to offset inc ebx
+      jb duplp2
+dupend:
+      // Conversion of filtered row completed
+      emms          // End MMX instructions; prep for possible FP instrs.
+   } // end _asm block
+}
+
+
+// Optimized png_read_filter_row routines
+void /* PRIVATE */
+png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep
+   row, png_bytep prev_row, int filter)
+{
+#ifdef PNG_DEBUG
+   char filnm[10];
+#endif
+
+   if (mmx_supported == 2) {
+#if !defined(PNG_1_0_X)
+       /* this should have happened in png_init_mmx_flags() already */
+       png_warning(png_ptr, "asm_flags may not have been initialized");
+#endif
+       png_mmx_support();
+   }
+
+#ifdef PNG_DEBUG
+   png_debug(1, "in png_read_filter_row\n");
+   switch (filter)
+   {
+      case 0: sprintf(filnm, "none");
+         break;
+#if !defined(PNG_1_0_X)
+      case 1: sprintf(filnm, "sub-%s",
+        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)? "MMX" : "x86");
+         break;
+      case 2: sprintf(filnm, "up-%s",
+        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP)? "MMX" : "x86");
+         break;
+      case 3: sprintf(filnm, "avg-%s",
+        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)? "MMX" : "x86");
+         break;
+      case 4: sprintf(filnm, "Paeth-%s",
+        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)? "MMX":"x86");
+         break;
+#else
+      case 1: sprintf(filnm, "sub");
+         break;
+      case 2: sprintf(filnm, "up");
+         break;
+      case 3: sprintf(filnm, "avg");
+         break;
+      case 4: sprintf(filnm, "Paeth");
+         break;
+#endif
+      default: sprintf(filnm, "unknw");
+         break;
+   }
+   png_debug2(0,"row=%5d, %s, ", png_ptr->row_number, filnm);
+   png_debug2(0, "pd=%2d, b=%d, ", (int)row_info->pixel_depth,
+      (int)((row_info->pixel_depth + 7) >> 3));
+   png_debug1(0,"len=%8d, ", row_info->rowbytes);
+#endif /* PNG_DEBUG */
+
+   switch (filter)
+   {
+      case PNG_FILTER_VALUE_NONE:
+         break;
+
+      case PNG_FILTER_VALUE_SUB:
+      {
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_sub(row_info, row);
+         }
+         else
+         {
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_bytep rp = row + bpp;
+            png_bytep lp = row;
+
+            for (i = bpp; i < istop; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff);
+               rp++;
+            }
+         }
+         break;
+      }
+
+      case PNG_FILTER_VALUE_UP:
+      {
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_up(row_info, row, prev_row);
+         }
+         else
+         {
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+
+            for (i = 0; i < istop; ++i)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+               rp++;
+            }
+         }
+         break;
+      }
+
+      case PNG_FILTER_VALUE_AVG:
+      {
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_avg(row_info, row, prev_row);
+         }
+         else
+         {
+            png_uint_32 i;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+            png_bytep lp = row;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_uint_32 istop = row_info->rowbytes - bpp;
+
+            for (i = 0; i < bpp; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) +
+                  ((int)(*pp++) >> 1)) & 0xff);
+               rp++;
+            }
+
+            for (i = 0; i < istop; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) +
+                  ((int)(*pp++ + *lp++) >> 1)) & 0xff);
+               rp++;
+            }
+         }
+         break;
+      }
+
+      case PNG_FILTER_VALUE_PAETH:
+      {
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_paeth(row_info, row, prev_row);
+         }
+         else
+         {
+            png_uint_32 i;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+            png_bytep lp = row;
+            png_bytep cp = prev_row;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_uint_32 istop=row_info->rowbytes - bpp;
+
+            for (i = 0; i < bpp; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+               rp++;
+            }
+
+            for (i = 0; i < istop; i++)   // use leftover rp,pp
+            {
+               int a, b, c, pa, pb, pc, p;
+
+               a = *lp++;
+               b = *pp++;
+               c = *cp++;
+
+               p = b - c;
+               pc = a - c;
+
+#ifdef PNG_USE_ABS
+               pa = abs(p);
+               pb = abs(pc);
+               pc = abs(p + pc);
+#else
+               pa = p < 0 ? -p : p;
+               pb = pc < 0 ? -pc : pc;
+               pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+               /*
+                  if (pa <= pb && pa <= pc)
+                     p = a;
+                  else if (pb <= pc)
+                     p = b;
+                  else
+                     p = c;
+                */
+
+               p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
+
+               *rp = (png_byte)(((int)(*rp) + p) & 0xff);
+               rp++;
+            }
+         }
+         break;
+      }
+
+      default:
+         png_warning(png_ptr, "Ignoring bad row filter type");
+         *row=0;
+         break;
+   }
+}
+
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED && PNG_USE_PNGVCRD */
diff --git a/Utilities/GDAL/frmts/png/libpng/pngwio.c b/Utilities/GDAL/frmts/png/libpng/pngwio.c
new file mode 100644
index 0000000000..d5d61f49e0
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngwio.c
@@ -0,0 +1,228 @@
+
+/* pngwio.c - functions for data output
+ *
+ * libpng 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This file provides a location for all output.  Users who need
+ * special handling are expected to write functions that have the same
+ * arguments as these and perform similar functions, but that possibly
+ * use different output methods.  Note that you shouldn't change these
+ * functions, but rather write replacement functions and then change
+ * them at run time with png_set_write_fn(...).
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+#ifdef PNG_WRITE_SUPPORTED
+
+/* Write the data to whatever output you are using.  The default routine
+   writes to a file pointer.  Note that this routine sometimes gets called
+   with very small lengths, so you should implement some kind of simple
+   buffering if you are using unbuffered writes.  This should never be asked
+   to write more than 64K on a 16 bit machine.  */
+
+void /* PRIVATE */
+png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   if (png_ptr->write_data_fn != NULL )
+      (*(png_ptr->write_data_fn))(png_ptr, data, length);
+   else
+      png_error(png_ptr, "Call to NULL write function");
+}
+
+#if !defined(PNG_NO_STDIO)
+/* This is the function that does the actual writing of data.  If you are
+   not writing to a standard C stream, you should create a replacement
+   write_data function and use it at run time with png_set_write_fn(), rather
+   than changing the library. */
+#ifndef USE_FAR_KEYWORD
+void PNGAPI
+png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_uint_32 check;
+
+#if defined(_WIN32_WCE)
+   if ( !WriteFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) )
+      check = 0;
+#else
+   check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr));
+#endif
+   if (check != length)
+      png_error(png_ptr, "Write Error");
+}
+#else
+/* this is the model-independent version. Since the standard I/O library
+   can't handle far buffers in the medium and small models, we have to copy
+   the data.
+*/
+
+#define NEAR_BUF_SIZE 1024
+#define MIN(a,b) (a <= b ? a : b)
+
+void PNGAPI
+png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_uint_32 check;
+   png_byte *near_data;  /* Needs to be "png_byte *" instead of "png_bytep" */
+   png_FILE_p io_ptr;
+
+   /* Check if data really is near. If so, use usual code. */
+   near_data = (png_byte *)CVT_PTR_NOCHECK(data);
+   io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr);
+   if ((png_bytep)near_data == data)
+   {
+#if defined(_WIN32_WCE)
+      if ( !WriteFile(io_ptr, near_data, length, &check, NULL) )
+         check = 0;
+#else
+      check = fwrite(near_data, 1, length, io_ptr);
+#endif
+   }
+   else
+   {
+      png_byte buf[NEAR_BUF_SIZE];
+      png_size_t written, remaining, err;
+      check = 0;
+      remaining = length;
+      do
+      {
+         written = MIN(NEAR_BUF_SIZE, remaining);
+         png_memcpy(buf, data, written); /* copy far buffer to near buffer */
+#if defined(_WIN32_WCE)
+         if ( !WriteFile(io_ptr, buf, written, &err, NULL) )
+            err = 0;
+#else
+         err = fwrite(buf, 1, written, io_ptr);
+#endif
+         if (err != written)
+            break;
+         else
+            check += err;
+         data += written;
+         remaining -= written;
+      }
+      while (remaining != 0);
+   }
+   if (check != length)
+      png_error(png_ptr, "Write Error");
+}
+
+#endif
+#endif
+
+/* This function is called to output any data pending writing (normally
+   to disk).  After png_flush is called, there should be no data pending
+   writing in any buffers. */
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+void /* PRIVATE */
+png_flush(png_structp png_ptr)
+{
+   if (png_ptr->output_flush_fn != NULL)
+      (*(png_ptr->output_flush_fn))(png_ptr);
+}
+
+#if !defined(PNG_NO_STDIO)
+void PNGAPI
+png_default_flush(png_structp png_ptr)
+{
+#if !defined(_WIN32_WCE)
+   png_FILE_p io_ptr;
+   io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr));
+   if (io_ptr != NULL)
+      fflush(io_ptr);
+#endif
+}
+#endif
+#endif
+
+/* This function allows the application to supply new output functions for
+   libpng if standard C streams aren't being used.
+
+   This function takes as its arguments:
+   png_ptr       - pointer to a png output data structure
+   io_ptr        - pointer to user supplied structure containing info about
+                   the output functions.  May be NULL.
+   write_data_fn - pointer to a new output function that takes as its
+                   arguments a pointer to a png_struct, a pointer to
+                   data to be written, and a 32-bit unsigned int that is
+                   the number of bytes to be written.  The new write
+                   function should call png_error(png_ptr, "Error msg")
+                   to exit and output any fatal error messages.
+   flush_data_fn - pointer to a new flush function that takes as its
+                   arguments a pointer to a png_struct.  After a call to
+                   the flush function, there should be no data in any buffers
+                   or pending transmission.  If the output method doesn't do
+                   any buffering of ouput, a function prototype must still be
+                   supplied although it doesn't have to do anything.  If
+                   PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile
+                   time, output_flush_fn will be ignored, although it must be
+                   supplied for compatibility. */
+void PNGAPI
+png_set_write_fn(png_structp png_ptr, png_voidp io_ptr,
+   png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)
+{
+   png_ptr->io_ptr = io_ptr;
+
+#if !defined(PNG_NO_STDIO)
+   if (write_data_fn != NULL)
+      png_ptr->write_data_fn = write_data_fn;
+   else
+      png_ptr->write_data_fn = png_default_write_data;
+#else
+   png_ptr->write_data_fn = write_data_fn;
+#endif
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+#if !defined(PNG_NO_STDIO)
+   if (output_flush_fn != NULL)
+      png_ptr->output_flush_fn = output_flush_fn;
+   else
+      png_ptr->output_flush_fn = png_default_flush;
+#else
+   png_ptr->output_flush_fn = output_flush_fn;
+#endif
+#endif /* PNG_WRITE_FLUSH_SUPPORTED */
+
+   /* It is an error to read while writing a png file */
+   if (png_ptr->read_data_fn != NULL)
+   {
+      png_ptr->read_data_fn = NULL;
+      png_warning(png_ptr,
+         "Attempted to set both read_data_fn and write_data_fn in");
+      png_warning(png_ptr,
+         "the same structure.  Resetting read_data_fn to NULL.");
+   }
+}
+
+#if defined(USE_FAR_KEYWORD)
+#if defined(_MSC_VER)
+void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check)
+{
+   void *near_ptr;
+   void FAR *far_ptr;
+   FP_OFF(near_ptr) = FP_OFF(ptr);
+   far_ptr = (void FAR *)near_ptr;
+   if(check != 0)
+      if(FP_SEG(ptr) != FP_SEG(far_ptr))
+         png_error(png_ptr,"segment lost in conversion");
+   return(near_ptr);
+}
+#  else
+void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check)
+{
+   void *near_ptr;
+   void FAR *far_ptr;
+   near_ptr = (void FAR *)ptr;
+   far_ptr = (void FAR *)near_ptr;
+   if(check != 0)
+      if(far_ptr != ptr)
+         png_error(png_ptr,"segment lost in conversion");
+   return(near_ptr);
+}
+#   endif
+#   endif
+#endif /* PNG_WRITE_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/png/libpng/pngwrite.c b/Utilities/GDAL/frmts/png/libpng/pngwrite.c
new file mode 100644
index 0000000000..3246fdaafc
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngwrite.c
@@ -0,0 +1,1464 @@
+
+/* pngwrite.c - general routines to write a PNG file
+ *
+ * libpng 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+/* get internal access to png.h */
+#define PNG_INTERNAL
+#include "png.h"
+#ifdef PNG_WRITE_SUPPORTED
+
+/* Writes all the PNG information.  This is the suggested way to use the
+ * library.  If you have a new chunk to add, make a function to write it,
+ * and put it in the correct location here.  If you want the chunk written
+ * after the image data, put it in png_write_end().  I strongly encourage
+ * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing
+ * the chunk, as that will keep the code from breaking if you want to just
+ * write a plain PNG file.  If you have long comments, I suggest writing
+ * them in png_write_end(), and compressing them.
+ */
+void PNGAPI
+png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_write_info_before_PLTE\n");
+   if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
+   {
+   png_write_sig(png_ptr); /* write PNG signature */
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&(png_ptr->mng_features_permitted))
+   {
+      png_warning(png_ptr,"MNG features are not allowed in a PNG datastream\n");
+      png_ptr->mng_features_permitted=0;
+   }
+#endif
+   /* write IHDR information. */
+   png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
+      info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
+      info_ptr->filter_type,
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+      info_ptr->interlace_type);
+#else
+      0);
+#endif
+   /* the rest of these check to see if the valid field has the appropriate
+      flag set, and if it does, writes the chunk. */
+#if defined(PNG_WRITE_gAMA_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_gAMA)
+   {
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+      png_write_gAMA(png_ptr, info_ptr->gamma);
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+      png_write_gAMA_fixed(png_ptr, info_ptr->int_gamma);
+#  endif
+#endif
+   }
+#endif
+#if defined(PNG_WRITE_sRGB_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_sRGB)
+      png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent);
+#endif
+#if defined(PNG_WRITE_iCCP_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_iCCP)
+      png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE,
+                     info_ptr->iccp_profile, (int)info_ptr->iccp_proflen);
+#endif
+#if defined(PNG_WRITE_sBIT_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_sBIT)
+      png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
+#endif
+#if defined(PNG_WRITE_cHRM_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_cHRM)
+   {
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+      png_write_cHRM(png_ptr,
+         info_ptr->x_white, info_ptr->y_white,
+         info_ptr->x_red, info_ptr->y_red,
+         info_ptr->x_green, info_ptr->y_green,
+         info_ptr->x_blue, info_ptr->y_blue);
+#else
+#  ifdef PNG_FIXED_POINT_SUPPORTED
+      png_write_cHRM_fixed(png_ptr,
+         info_ptr->int_x_white, info_ptr->int_y_white,
+         info_ptr->int_x_red, info_ptr->int_y_red,
+         info_ptr->int_x_green, info_ptr->int_y_green,
+         info_ptr->int_x_blue, info_ptr->int_y_blue);
+#  endif
+#endif
+   }
+#endif
+#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
+   if (info_ptr->unknown_chunks_num)
+   {
+       png_unknown_chunk *up;
+
+       png_debug(5, "writing extra chunks\n");
+
+       for (up = info_ptr->unknown_chunks;
+            up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
+            up++)
+       {
+         int keep=png_handle_as_unknown(png_ptr, up->name);
+         if (keep != PNG_HANDLE_CHUNK_NEVER &&
+            up->location && !(up->location & PNG_HAVE_PLTE) &&
+            !(up->location & PNG_HAVE_IDAT) &&
+            ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
+            (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
+         {
+            png_write_chunk(png_ptr, up->name, up->data, up->size);
+         }
+       }
+   }
+#endif
+      png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;
+   }
+}
+
+void PNGAPI
+png_write_info(png_structp png_ptr, png_infop info_ptr)
+{
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
+   int i;
+#endif
+
+   png_debug(1, "in png_write_info\n");
+
+   png_write_info_before_PLTE(png_ptr, info_ptr);
+
+   if (info_ptr->valid & PNG_INFO_PLTE)
+      png_write_PLTE(png_ptr, info_ptr->palette,
+         (png_uint_32)info_ptr->num_palette);
+   else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      png_error(png_ptr, "Valid palette required for paletted images\n");
+
+#if defined(PNG_WRITE_tRNS_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_tRNS)
+      {
+#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+         /* invert the alpha channel (in tRNS) */
+         if ((png_ptr->transformations & PNG_INVERT_ALPHA) &&
+            info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+         {
+            int j;
+            for (j=0; j<(int)info_ptr->num_trans; j++)
+               info_ptr->trans[j] = (png_byte)(255 - info_ptr->trans[j]);
+         }
+#endif
+      png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values),
+         info_ptr->num_trans, info_ptr->color_type);
+      }
+#endif
+#if defined(PNG_WRITE_bKGD_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_bKGD)
+      png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
+#endif
+#if defined(PNG_WRITE_hIST_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_hIST)
+      png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
+#endif
+#if defined(PNG_WRITE_oFFs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_oFFs)
+      png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
+         info_ptr->offset_unit_type);
+#endif
+#if defined(PNG_WRITE_pCAL_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_pCAL)
+      png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
+         info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
+         info_ptr->pcal_units, info_ptr->pcal_params);
+#endif
+#if defined(PNG_WRITE_sCAL_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_sCAL)
+#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO)
+      png_write_sCAL(png_ptr, (int)info_ptr->scal_unit,
+          info_ptr->scal_pixel_width, info_ptr->scal_pixel_height);
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+      png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit,
+          info_ptr->scal_s_width, info_ptr->scal_s_height);
+#else
+      png_warning(png_ptr,
+          "png_write_sCAL not supported; sCAL chunk not written.\n");
+#endif
+#endif
+#endif
+#if defined(PNG_WRITE_pHYs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_pHYs)
+      png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
+         info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
+#endif
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_tIME)
+   {
+      png_write_tIME(png_ptr, &(info_ptr->mod_time));
+      png_ptr->mode |= PNG_WROTE_tIME;
+   }
+#endif
+#if defined(PNG_WRITE_sPLT_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_sPLT)
+     for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
+       png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);
+#endif
+#if defined(PNG_WRITE_TEXT_SUPPORTED)
+   /* Check to see if we need to write text chunks */
+   for (i = 0; i < info_ptr->num_text; i++)
+   {
+      png_debug2(2, "Writing header text chunk %d, type %d\n", i,
+         info_ptr->text[i].compression);
+      /* an internationalized chunk? */
+      if (info_ptr->text[i].compression > 0)
+      {
+#if defined(PNG_WRITE_iTXt_SUPPORTED)
+          /* write international chunk */
+          png_write_iTXt(png_ptr,
+                         info_ptr->text[i].compression,
+                         info_ptr->text[i].key,
+                         info_ptr->text[i].lang,
+                         info_ptr->text[i].lang_key,
+                         info_ptr->text[i].text);
+#else
+          png_warning(png_ptr, "Unable to write international text\n");
+#endif
+          /* Mark this chunk as written */
+          info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+      }
+      /* If we want a compressed text chunk */
+      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)
+      {
+#if defined(PNG_WRITE_zTXt_SUPPORTED)
+         /* write compressed chunk */
+         png_write_zTXt(png_ptr, info_ptr->text[i].key,
+            info_ptr->text[i].text, 0,
+            info_ptr->text[i].compression);
+#else
+         png_warning(png_ptr, "Unable to write compressed text\n");
+#endif
+         /* Mark this chunk as written */
+         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
+      }
+      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
+      {
+#if defined(PNG_WRITE_tEXt_SUPPORTED)
+         /* write uncompressed chunk */
+         png_write_tEXt(png_ptr, info_ptr->text[i].key,
+                         info_ptr->text[i].text,
+                         0);
+#else
+         png_warning(png_ptr, "Unable to write uncompressed text\n");
+#endif
+         /* Mark this chunk as written */
+         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+      }
+   }
+#endif
+#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
+   if (info_ptr->unknown_chunks_num)
+   {
+       png_unknown_chunk *up;
+
+       png_debug(5, "writing extra chunks\n");
+
+       for (up = info_ptr->unknown_chunks;
+            up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
+            up++)
+       {
+         int keep=png_handle_as_unknown(png_ptr, up->name);
+         if (keep != PNG_HANDLE_CHUNK_NEVER &&
+            up->location && (up->location & PNG_HAVE_PLTE) &&
+            !(up->location & PNG_HAVE_IDAT) &&
+            ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
+            (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
+         {
+            png_write_chunk(png_ptr, up->name, up->data, up->size);
+         }
+       }
+   }
+#endif
+}
+
+/* Writes the end of the PNG file.  If you don't want to write comments or
+ * time information, you can pass NULL for info.  If you already wrote these
+ * in png_write_info(), do not write them again here.  If you have long
+ * comments, I suggest writing them here, and compressing them.
+ */
+void PNGAPI
+png_write_end(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_write_end\n");
+   if (!(png_ptr->mode & PNG_HAVE_IDAT))
+      png_error(png_ptr, "No IDATs written into file");
+
+   /* see if user wants us to write information chunks */
+   if (info_ptr != NULL)
+   {
+#if defined(PNG_WRITE_TEXT_SUPPORTED)
+      int i; /* local index variable */
+#endif
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+      /* check to see if user has supplied a time chunk */
+      if ((info_ptr->valid & PNG_INFO_tIME) &&
+         !(png_ptr->mode & PNG_WROTE_tIME))
+         png_write_tIME(png_ptr, &(info_ptr->mod_time));
+#endif
+#if defined(PNG_WRITE_TEXT_SUPPORTED)
+      /* loop through comment chunks */
+      for (i = 0; i < info_ptr->num_text; i++)
+      {
+         png_debug2(2, "Writing trailer text chunk %d, type %d\n", i,
+            info_ptr->text[i].compression);
+         /* an internationalized chunk? */
+         if (info_ptr->text[i].compression > 0)
+         {
+#if defined(PNG_WRITE_iTXt_SUPPORTED)
+             /* write international chunk */
+             png_write_iTXt(png_ptr,
+                         info_ptr->text[i].compression,
+                         info_ptr->text[i].key,
+                         info_ptr->text[i].lang,
+                         info_ptr->text[i].lang_key,
+                         info_ptr->text[i].text);
+#else
+             png_warning(png_ptr, "Unable to write international text\n");
+#endif
+             /* Mark this chunk as written */
+             info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+         }
+         else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
+         {
+#if defined(PNG_WRITE_zTXt_SUPPORTED)
+            /* write compressed chunk */
+            png_write_zTXt(png_ptr, info_ptr->text[i].key,
+               info_ptr->text[i].text, 0,
+               info_ptr->text[i].compression);
+#else
+            png_warning(png_ptr, "Unable to write compressed text\n");
+#endif
+            /* Mark this chunk as written */
+            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
+         }
+         else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
+         {
+#if defined(PNG_WRITE_tEXt_SUPPORTED)
+            /* write uncompressed chunk */
+            png_write_tEXt(png_ptr, info_ptr->text[i].key,
+               info_ptr->text[i].text, 0);
+#else
+            png_warning(png_ptr, "Unable to write uncompressed text\n");
+#endif
+
+            /* Mark this chunk as written */
+            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+         }
+      }
+#endif
+#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
+   if (info_ptr->unknown_chunks_num)
+   {
+       png_unknown_chunk *up;
+
+       png_debug(5, "writing extra chunks\n");
+
+       for (up = info_ptr->unknown_chunks;
+            up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
+            up++)
+       {
+         int keep=png_handle_as_unknown(png_ptr, up->name);
+         if (keep != PNG_HANDLE_CHUNK_NEVER &&
+            up->location && (up->location & PNG_AFTER_IDAT) &&
+            ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
+            (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
+         {
+            png_write_chunk(png_ptr, up->name, up->data, up->size);
+         }
+       }
+   }
+#endif
+   }
+
+   png_ptr->mode |= PNG_AFTER_IDAT;
+
+   /* write end of PNG file */
+   png_write_IEND(png_ptr);
+#if 0
+/* This flush, added in libpng-1.0.8,  causes some applications to crash
+   because they do not set png_ptr->output_flush_fn */
+   png_flush(png_ptr);
+#endif
+}
+
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+#if !defined(_WIN32_WCE)
+/* "time.h" functions are not supported on WindowsCE */
+void PNGAPI
+png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime)
+{
+   png_debug(1, "in png_convert_from_struct_tm\n");
+   ptime->year = (png_uint_16)(1900 + ttime->tm_year);
+   ptime->month = (png_byte)(ttime->tm_mon + 1);
+   ptime->day = (png_byte)ttime->tm_mday;
+   ptime->hour = (png_byte)ttime->tm_hour;
+   ptime->minute = (png_byte)ttime->tm_min;
+   ptime->second = (png_byte)ttime->tm_sec;
+}
+
+void PNGAPI
+png_convert_from_time_t(png_timep ptime, time_t ttime)
+{
+   struct tm *tbuf;
+
+   png_debug(1, "in png_convert_from_time_t\n");
+   tbuf = gmtime(&ttime);
+   png_convert_from_struct_tm(ptime, tbuf);
+}
+#endif
+#endif
+
+/* Initialize png_ptr structure, and allocate any memory needed */
+png_structp PNGAPI
+png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn,
+      warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL));
+}
+
+/* Alternate initialize png_ptr structure, and allocate any memory needed */
+png_structp PNGAPI
+png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+   png_malloc_ptr malloc_fn, png_free_ptr free_fn)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+   png_structp png_ptr;
+#ifdef PNG_SETJMP_SUPPORTED
+#ifdef USE_FAR_KEYWORD
+   jmp_buf jmpbuf;
+#endif
+#endif
+   int i;
+   png_debug(1, "in png_create_write_struct\n");
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG,
+      (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr);
+#else
+   png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
+#endif /* PNG_USER_MEM_SUPPORTED */
+   if (png_ptr == NULL)
+      return (NULL);
+
+#if !defined(PNG_1_0_X)
+#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
+   png_init_mmx_flags(png_ptr);   /* 1.2.0 addition */
+#endif
+#endif /* PNG_1_0_X */
+
+   /* added at libpng-1.2.6 */
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   png_ptr->user_width_max=PNG_USER_WIDTH_MAX;
+   png_ptr->user_height_max=PNG_USER_HEIGHT_MAX;
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+#ifdef USE_FAR_KEYWORD
+   if (setjmp(jmpbuf))
+#else
+   if (setjmp(png_ptr->jmpbuf))
+#endif
+   {
+      png_free(png_ptr, png_ptr->zbuf);
+      png_ptr->zbuf=NULL;
+      png_destroy_struct(png_ptr);
+      return (NULL);
+   }
+#ifdef USE_FAR_KEYWORD
+   png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf));
+#endif
+#endif
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn);
+#endif /* PNG_USER_MEM_SUPPORTED */
+   png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
+
+   i=0;
+   do
+   {
+     if(user_png_ver[i] != png_libpng_ver[i])
+        png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+   } while (png_libpng_ver[i++]);
+
+   if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
+   {
+     /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
+      * we must recompile any applications that use any older library version.
+      * For versions after libpng 1.0, we will be compatible, so we need
+      * only check the first digit.
+      */
+     if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
+         (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) ||
+         (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
+     {
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+        char msg[80];
+        if (user_png_ver)
+        {
+          sprintf(msg, "Application was compiled with png.h from libpng-%.20s",
+             user_png_ver);
+          png_warning(png_ptr, msg);
+        }
+        sprintf(msg, "Application  is  running with png.c from libpng-%.20s",
+           png_libpng_ver);
+        png_warning(png_ptr, msg);
+#endif
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+        png_ptr->flags=0;
+#endif
+        png_error(png_ptr,
+           "Incompatible libpng version in application and library");
+     }
+   }
+
+   /* initialize zbuf - compression buffer */
+   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+   png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
+      (png_uint_32)png_ptr->zbuf_size);
+
+   png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL,
+      png_flush_ptr_NULL);
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
+      1, png_doublep_NULL, png_doublep_NULL);
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+/* Applications that neglect to set up their own setjmp() and then encounter
+   a png_error() will longjmp here.  Since the jmpbuf is then meaningless we
+   abort instead of returning. */
+#ifdef USE_FAR_KEYWORD
+   if (setjmp(jmpbuf))
+      PNG_ABORT();
+   png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf));
+#else
+   if (setjmp(png_ptr->jmpbuf))
+      PNG_ABORT();
+#endif
+#endif
+   return (png_ptr);
+}
+
+/* Initialize png_ptr structure, and allocate any memory needed */
+#undef png_write_init
+void PNGAPI
+png_write_init(png_structp png_ptr)
+{
+   /* We only come here via pre-1.0.7-compiled applications */
+   png_write_init_2(png_ptr, "1.0.6 or earlier", 0, 0);
+}
+
+void PNGAPI
+png_write_init_2(png_structp png_ptr, png_const_charp user_png_ver,
+   png_size_t png_struct_size, png_size_t png_info_size)
+{
+   /* We only come here via pre-1.0.12-compiled applications */
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+   if(png_sizeof(png_struct) > png_struct_size ||
+      png_sizeof(png_info) > png_info_size)
+   {
+      char msg[80];
+      png_ptr->warning_fn=NULL;
+      if (user_png_ver)
+      {
+        sprintf(msg, "Application was compiled with png.h from libpng-%.20s",
+           user_png_ver);
+        png_warning(png_ptr, msg);
+      }
+      sprintf(msg, "Application  is  running with png.c from libpng-%.20s",
+         png_libpng_ver);
+      png_warning(png_ptr, msg);
+   }
+#endif
+   if(png_sizeof(png_struct) > png_struct_size)
+     {
+       png_ptr->error_fn=NULL;
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+       png_ptr->flags=0;
+#endif
+       png_error(png_ptr,
+       "The png struct allocated by the application for writing is too small.");
+     }
+   if(png_sizeof(png_info) > png_info_size)
+     {
+       png_ptr->error_fn=NULL;
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+       png_ptr->flags=0;
+#endif
+       png_error(png_ptr,
+       "The info struct allocated by the application for writing is too small.");
+     }
+   png_write_init_3(&png_ptr, user_png_ver, png_struct_size);
+}
+
+
+void PNGAPI
+png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver,
+   png_size_t png_struct_size)
+{
+   png_structp png_ptr=*ptr_ptr;
+#ifdef PNG_SETJMP_SUPPORTED
+   jmp_buf tmp_jmp; /* to save current jump buffer */
+#endif
+   int i = 0;
+   do
+   {
+     if (user_png_ver[i] != png_libpng_ver[i])
+     {
+#ifdef PNG_LEGACY_SUPPORTED
+       png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+#else
+       png_ptr->warning_fn=NULL;
+       png_warning(png_ptr,
+     "Application uses deprecated png_write_init() and should be recompiled.");
+       break;
+#endif
+     }
+   } while (png_libpng_ver[i++]);
+
+   png_debug(1, "in png_write_init_3\n");
+
+#ifdef PNG_SETJMP_SUPPORTED
+   /* save jump buffer and error functions */
+   png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf));
+#endif
+
+   if (png_sizeof(png_struct) > png_struct_size)
+     {
+       png_destroy_struct(png_ptr);
+       png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
+       *ptr_ptr = png_ptr;
+     }
+
+   /* reset all variables to 0 */
+   png_memset(png_ptr, 0, png_sizeof (png_struct));
+
+   /* added at libpng-1.2.6 */
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   png_ptr->user_width_max=PNG_USER_WIDTH_MAX;
+   png_ptr->user_height_max=PNG_USER_HEIGHT_MAX;
+#endif
+
+#if !defined(PNG_1_0_X)
+#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
+   png_init_mmx_flags(png_ptr);   /* 1.2.0 addition */
+#endif
+#endif /* PNG_1_0_X */
+
+#ifdef PNG_SETJMP_SUPPORTED
+   /* restore jump buffer */
+   png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf));
+#endif
+
+   png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL,
+      png_flush_ptr_NULL);
+
+   /* initialize zbuf - compression buffer */
+   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+   png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
+      (png_uint_32)png_ptr->zbuf_size);
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
+      1, png_doublep_NULL, png_doublep_NULL);
+#endif
+}
+
+/* Write a few rows of image data.  If the image is interlaced,
+ * either you will have to write the 7 sub images, or, if you
+ * have called png_set_interlace_handling(), you will have to
+ * "write" the image seven times.
+ */
+void PNGAPI
+png_write_rows(png_structp png_ptr, png_bytepp row,
+   png_uint_32 num_rows)
+{
+   png_uint_32 i; /* row counter */
+   png_bytepp rp; /* row pointer */
+
+   png_debug(1, "in png_write_rows\n");
+   /* loop through the rows */
+   for (i = 0, rp = row; i < num_rows; i++, rp++)
+   {
+      png_write_row(png_ptr, *rp);
+   }
+}
+
+/* Write the image.  You only need to call this function once, even
+ * if you are writing an interlaced image.
+ */
+void PNGAPI
+png_write_image(png_structp png_ptr, png_bytepp image)
+{
+   png_uint_32 i; /* row index */
+   int pass, num_pass; /* pass variables */
+   png_bytepp rp; /* points to current row */
+
+   png_debug(1, "in png_write_image\n");
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+   /* intialize interlace handling.  If image is not interlaced,
+      this will set pass to 1 */
+   num_pass = png_set_interlace_handling(png_ptr);
+#else
+   num_pass = 1;
+#endif
+   /* loop through passes */
+   for (pass = 0; pass < num_pass; pass++)
+   {
+      /* loop through image */
+      for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
+      {
+         png_write_row(png_ptr, *rp);
+      }
+   }
+}
+
+/* called by user to write a row of image data */
+void PNGAPI
+png_write_row(png_structp png_ptr, png_bytep row)
+{
+   png_debug2(1, "in png_write_row (row %ld, pass %d)\n",
+      png_ptr->row_number, png_ptr->pass);
+   /* initialize transformations and other stuff if first time */
+   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
+   {
+   /* make sure we wrote the header info */
+   if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
+      png_error(png_ptr,
+         "png_write_info was never called before png_write_row.");
+
+   /* check for transforms that have been set but were defined out */
+#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_MONO)
+      png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
+   if (png_ptr->transformations & PNG_FILLER)
+      png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && defined(PNG_READ_PACKSWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACKSWAP)
+      png_warning(png_ptr, "PNG_WRITE_PACKSWAP_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACK)
+      png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
+   if (png_ptr->transformations & PNG_SHIFT)
+      png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
+   if (png_ptr->transformations & PNG_BGR)
+      png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_BYTES)
+      png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined.");
+#endif
+
+      png_write_start_row(png_ptr);
+   }
+
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+   /* if interlaced and not interested in row, return */
+   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+   {
+      switch (png_ptr->pass)
+      {
+         case 0:
+            if (png_ptr->row_number & 0x07)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 1:
+            if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 2:
+            if ((png_ptr->row_number & 0x07) != 4)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 3:
+            if ((png_ptr->row_number & 0x03) || png_ptr->width < 3)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 4:
+            if ((png_ptr->row_number & 0x03) != 2)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 5:
+            if ((png_ptr->row_number & 0x01) || png_ptr->width < 2)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 6:
+            if (!(png_ptr->row_number & 0x01))
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+      }
+   }
+#endif
+
+   /* set up row info for transformations */
+   png_ptr->row_info.color_type = png_ptr->color_type;
+   png_ptr->row_info.width = png_ptr->usr_width;
+   png_ptr->row_info.channels = png_ptr->usr_channels;
+   png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth;
+   png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth *
+      png_ptr->row_info.channels);
+
+   png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
+      png_ptr->row_info.width);
+
+   png_debug1(3, "row_info->color_type = %d\n", png_ptr->row_info.color_type);
+   png_debug1(3, "row_info->width = %lu\n", png_ptr->row_info.width);
+   png_debug1(3, "row_info->channels = %d\n", png_ptr->row_info.channels);
+   png_debug1(3, "row_info->bit_depth = %d\n", png_ptr->row_info.bit_depth);
+   png_debug1(3, "row_info->pixel_depth = %d\n", png_ptr->row_info.pixel_depth);
+   png_debug1(3, "row_info->rowbytes = %lu\n", png_ptr->row_info.rowbytes);
+
+   /* Copy user's row into buffer, leaving room for filter byte. */
+   png_memcpy_check(png_ptr, png_ptr->row_buf + 1, row,
+      png_ptr->row_info.rowbytes);
+
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+   /* handle interlacing */
+   if (png_ptr->interlaced && png_ptr->pass < 6 &&
+      (png_ptr->transformations & PNG_INTERLACE))
+   {
+      png_do_write_interlace(&(png_ptr->row_info),
+         png_ptr->row_buf + 1, png_ptr->pass);
+      /* this should always get caught above, but still ... */
+      if (!(png_ptr->row_info.width))
+      {
+         png_write_finish_row(png_ptr);
+         return;
+      }
+   }
+#endif
+
+   /* handle other transformations */
+   if (png_ptr->transformations)
+      png_do_write_transformations(png_ptr);
+
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   /* Write filter_method 64 (intrapixel differencing) only if
+    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
+    * 2. Libpng did not write a PNG signature (this filter_method is only
+    *    used in PNG datastreams that are embedded in MNG datastreams) and
+    * 3. The application called png_permit_mng_features with a mask that
+    *    included PNG_FLAG_MNG_FILTER_64 and
+    * 4. The filter_method is 64 and
+    * 5. The color_type is RGB or RGBA
+    */
+   if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+      (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
+   {
+      /* Intrapixel differencing */
+      png_do_write_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   }
+#endif
+
+   /* Find a filter if necessary, filter the row and write it out. */
+   png_write_find_filter(png_ptr, &(png_ptr->row_info));
+
+   if (png_ptr->write_row_fn != NULL)
+      (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
+}
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+/* Set the automatic flush interval or 0 to turn flushing off */
+void PNGAPI
+png_set_flush(png_structp png_ptr, int nrows)
+{
+   png_debug(1, "in png_set_flush\n");
+   png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
+}
+
+/* flush the current output buffers now */
+void PNGAPI
+png_write_flush(png_structp png_ptr)
+{
+   int wrote_IDAT;
+
+   png_debug(1, "in png_write_flush\n");
+   /* We have already written out all of the data */
+   if (png_ptr->row_number >= png_ptr->num_rows)
+     return;
+
+   do
+   {
+      int ret;
+
+      /* compress the data */
+      ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
+      wrote_IDAT = 0;
+
+      /* check for compression errors */
+      if (ret != Z_OK)
+      {
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+
+      if (!(png_ptr->zstream.avail_out))
+      {
+         /* write the IDAT and reset the zlib output buffer */
+         png_write_IDAT(png_ptr, png_ptr->zbuf,
+                        png_ptr->zbuf_size);
+         png_ptr->zstream.next_out = png_ptr->zbuf;
+         png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+         wrote_IDAT = 1;
+      }
+   } while(wrote_IDAT == 1);
+
+   /* If there is any data left to be output, write it into a new IDAT */
+   if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
+   {
+      /* write the IDAT and reset the zlib output buffer */
+      png_write_IDAT(png_ptr, png_ptr->zbuf,
+                     png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+      png_ptr->zstream.next_out = png_ptr->zbuf;
+      png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+   }
+   png_ptr->flush_rows = 0;
+   png_flush(png_ptr);
+}
+#endif /* PNG_WRITE_FLUSH_SUPPORTED */
+
+/* free all memory used by the write */
+void PNGAPI
+png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
+{
+   png_structp png_ptr = NULL;
+   png_infop info_ptr = NULL;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_free_ptr free_fn = NULL;
+   png_voidp mem_ptr = NULL;
+#endif
+
+   png_debug(1, "in png_destroy_write_struct\n");
+   if (png_ptr_ptr != NULL)
+   {
+      png_ptr = *png_ptr_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+      free_fn = png_ptr->free_fn;
+      mem_ptr = png_ptr->mem_ptr;
+#endif
+   }
+
+   if (info_ptr_ptr != NULL)
+      info_ptr = *info_ptr_ptr;
+
+   if (info_ptr != NULL)
+   {
+      png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+      if (png_ptr->num_chunk_list)
+      {
+         png_free(png_ptr, png_ptr->chunk_list);
+         png_ptr->chunk_list=NULL;
+         png_ptr->num_chunk_list=0;
+      }
+#endif
+
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn,
+         (png_voidp)mem_ptr);
+#else
+      png_destroy_struct((png_voidp)info_ptr);
+#endif
+      *info_ptr_ptr = NULL;
+   }
+
+   if (png_ptr != NULL)
+   {
+      png_write_destroy(png_ptr);
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn,
+         (png_voidp)mem_ptr);
+#else
+      png_destroy_struct((png_voidp)png_ptr);
+#endif
+      *png_ptr_ptr = NULL;
+   }
+}
+
+
+/* Free any memory used in png_ptr struct (old method) */
+void /* PRIVATE */
+png_write_destroy(png_structp png_ptr)
+{
+#ifdef PNG_SETJMP_SUPPORTED
+   jmp_buf tmp_jmp; /* save jump buffer */
+#endif
+   png_error_ptr error_fn;
+   png_error_ptr warning_fn;
+   png_voidp error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_free_ptr free_fn;
+#endif
+
+   png_debug(1, "in png_write_destroy\n");
+   /* free any memory zlib uses */
+   deflateEnd(&png_ptr->zstream);
+
+   /* free our memory.  png_free checks NULL for us. */
+   png_free(png_ptr, png_ptr->zbuf);
+   png_free(png_ptr, png_ptr->row_buf);
+   png_free(png_ptr, png_ptr->prev_row);
+   png_free(png_ptr, png_ptr->sub_row);
+   png_free(png_ptr, png_ptr->up_row);
+   png_free(png_ptr, png_ptr->avg_row);
+   png_free(png_ptr, png_ptr->paeth_row);
+
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+   png_free(png_ptr, png_ptr->time_buffer);
+#endif
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   png_free(png_ptr, png_ptr->prev_filters);
+   png_free(png_ptr, png_ptr->filter_weights);
+   png_free(png_ptr, png_ptr->inv_filter_weights);
+   png_free(png_ptr, png_ptr->filter_costs);
+   png_free(png_ptr, png_ptr->inv_filter_costs);
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+   /* reset structure */
+   png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf));
+#endif
+
+   error_fn = png_ptr->error_fn;
+   warning_fn = png_ptr->warning_fn;
+   error_ptr = png_ptr->error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   free_fn = png_ptr->free_fn;
+#endif
+
+   png_memset(png_ptr, 0, png_sizeof (png_struct));
+
+   png_ptr->error_fn = error_fn;
+   png_ptr->warning_fn = warning_fn;
+   png_ptr->error_ptr = error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_ptr->free_fn = free_fn;
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+   png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf));
+#endif
+}
+
+/* Allow the application to select one or more row filters to use. */
+void PNGAPI
+png_set_filter(png_structp png_ptr, int method, int filters)
+{
+   png_debug(1, "in png_set_filter\n");
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+      (method == PNG_INTRAPIXEL_DIFFERENCING))
+         method = PNG_FILTER_TYPE_BASE;
+#endif
+   if (method == PNG_FILTER_TYPE_BASE)
+   {
+      switch (filters & (PNG_ALL_FILTERS | 0x07))
+      {
+         case 5:
+         case 6:
+         case 7: png_warning(png_ptr, "Unknown row filter for method 0");
+         case PNG_FILTER_VALUE_NONE:  png_ptr->do_filter=PNG_FILTER_NONE; break;
+         case PNG_FILTER_VALUE_SUB:   png_ptr->do_filter=PNG_FILTER_SUB;  break;
+         case PNG_FILTER_VALUE_UP:    png_ptr->do_filter=PNG_FILTER_UP;   break;
+         case PNG_FILTER_VALUE_AVG:   png_ptr->do_filter=PNG_FILTER_AVG;  break;
+         case PNG_FILTER_VALUE_PAETH: png_ptr->do_filter=PNG_FILTER_PAETH;break;
+         default: png_ptr->do_filter = (png_byte)filters; break;
+      }
+
+      /* If we have allocated the row_buf, this means we have already started
+       * with the image and we should have allocated all of the filter buffers
+       * that have been selected.  If prev_row isn't already allocated, then
+       * it is too late to start using the filters that need it, since we
+       * will be missing the data in the previous row.  If an application
+       * wants to start and stop using particular filters during compression,
+       * it should start out with all of the filters, and then add and
+       * remove them after the start of compression.
+       */
+      if (png_ptr->row_buf != NULL)
+      {
+         if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL)
+         {
+            png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
+              (png_ptr->rowbytes + 1));
+            png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
+         }
+
+         if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL)
+         {
+            if (png_ptr->prev_row == NULL)
+            {
+               png_warning(png_ptr, "Can't add Up filter after starting");
+               png_ptr->do_filter &= ~PNG_FILTER_UP;
+            }
+            else
+            {
+               png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
+                  (png_ptr->rowbytes + 1));
+               png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
+            }
+         }
+
+         if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL)
+         {
+            if (png_ptr->prev_row == NULL)
+            {
+               png_warning(png_ptr, "Can't add Average filter after starting");
+               png_ptr->do_filter &= ~PNG_FILTER_AVG;
+            }
+            else
+            {
+               png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
+                  (png_ptr->rowbytes + 1));
+               png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
+            }
+         }
+
+         if ((png_ptr->do_filter & PNG_FILTER_PAETH) &&
+             png_ptr->paeth_row == NULL)
+         {
+            if (png_ptr->prev_row == NULL)
+            {
+               png_warning(png_ptr, "Can't add Paeth filter after starting");
+               png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH);
+            }
+            else
+            {
+               png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
+                  (png_ptr->rowbytes + 1));
+               png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
+            }
+         }
+
+         if (png_ptr->do_filter == PNG_NO_FILTERS)
+            png_ptr->do_filter = PNG_FILTER_NONE;
+      }
+   }
+   else
+      png_error(png_ptr, "Unknown custom filter method");
+}
+
+/* This allows us to influence the way in which libpng chooses the "best"
+ * filter for the current scanline.  While the "minimum-sum-of-absolute-
+ * differences metric is relatively fast and effective, there is some
+ * question as to whether it can be improved upon by trying to keep the
+ * filtered data going to zlib more consistent, hopefully resulting in
+ * better compression.
+ */
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)      /* GRR 970116 */
+void PNGAPI
+png_set_filter_heuristics(png_structp png_ptr, int heuristic_method,
+   int num_weights, png_doublep filter_weights,
+   png_doublep filter_costs)
+{
+   int i;
+
+   png_debug(1, "in png_set_filter_heuristics\n");
+   if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST)
+   {
+      png_warning(png_ptr, "Unknown filter heuristic method");
+      return;
+   }
+
+   if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT)
+   {
+      heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED;
+   }
+
+   if (num_weights < 0 || filter_weights == NULL ||
+      heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED)
+   {
+      num_weights = 0;
+   }
+
+   png_ptr->num_prev_filters = (png_byte)num_weights;
+   png_ptr->heuristic_method = (png_byte)heuristic_method;
+
+   if (num_weights > 0)
+   {
+      if (png_ptr->prev_filters == NULL)
+      {
+         png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)(png_sizeof(png_byte) * num_weights));
+
+         /* To make sure that the weighting starts out fairly */
+         for (i = 0; i < num_weights; i++)
+         {
+            png_ptr->prev_filters[i] = 255;
+         }
+      }
+
+      if (png_ptr->filter_weights == NULL)
+      {
+         png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,
+            (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
+
+         png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,
+            (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
+         for (i = 0; i < num_weights; i++)
+         {
+            png_ptr->inv_filter_weights[i] =
+            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
+         }
+      }
+
+      for (i = 0; i < num_weights; i++)
+      {
+         if (filter_weights[i] < 0.0)
+         {
+            png_ptr->inv_filter_weights[i] =
+            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
+         }
+         else
+         {
+            png_ptr->inv_filter_weights[i] =
+               (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5);
+            png_ptr->filter_weights[i] =
+               (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5);
+         }
+      }
+   }
+
+   /* If, in the future, there are other filter methods, this would
+    * need to be based on png_ptr->filter.
+    */
+   if (png_ptr->filter_costs == NULL)
+   {
+      png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr,
+         (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
+
+      png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,
+         (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
+
+      for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
+      {
+         png_ptr->inv_filter_costs[i] =
+         png_ptr->filter_costs[i] = PNG_COST_FACTOR;
+      }
+   }
+
+   /* Here is where we set the relative costs of the different filters.  We
+    * should take the desired compression level into account when setting
+    * the costs, so that Paeth, for instance, has a high relative cost at low
+    * compression levels, while it has a lower relative cost at higher
+    * compression settings.  The filter types are in order of increasing
+    * relative cost, so it would be possible to do this with an algorithm.
+    */
+   for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
+   {
+      if (filter_costs == NULL || filter_costs[i] < 0.0)
+      {
+         png_ptr->inv_filter_costs[i] =
+         png_ptr->filter_costs[i] = PNG_COST_FACTOR;
+      }
+      else if (filter_costs[i] >= 1.0)
+      {
+         png_ptr->inv_filter_costs[i] =
+            (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5);
+         png_ptr->filter_costs[i] =
+            (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5);
+      }
+   }
+}
+#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
+
+void PNGAPI
+png_set_compression_level(png_structp png_ptr, int level)
+{
+   png_debug(1, "in png_set_compression_level\n");
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL;
+   png_ptr->zlib_level = level;
+}
+
+void PNGAPI
+png_set_compression_mem_level(png_structp png_ptr, int mem_level)
+{
+   png_debug(1, "in png_set_compression_mem_level\n");
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL;
+   png_ptr->zlib_mem_level = mem_level;
+}
+
+void PNGAPI
+png_set_compression_strategy(png_structp png_ptr, int strategy)
+{
+   png_debug(1, "in png_set_compression_strategy\n");
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
+   png_ptr->zlib_strategy = strategy;
+}
+
+void PNGAPI
+png_set_compression_window_bits(png_structp png_ptr, int window_bits)
+{
+   if (window_bits > 15)
+      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
+   else if (window_bits < 8)
+      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
+#ifndef WBITS_8_OK
+   /* avoid libpng bug with 256-byte windows */
+   if (window_bits == 8)
+     {
+       png_warning(png_ptr, "Compression window is being reset to 512");
+       window_bits=9;
+     }
+#endif
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS;
+   png_ptr->zlib_window_bits = window_bits;
+}
+
+void PNGAPI
+png_set_compression_method(png_structp png_ptr, int method)
+{
+   png_debug(1, "in png_set_compression_method\n");
+   if (method != 8)
+      png_warning(png_ptr, "Only compression method 8 is supported by PNG");
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD;
+   png_ptr->zlib_method = method;
+}
+
+void PNGAPI
+png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn)
+{
+   png_ptr->write_row_fn = write_row_fn;
+}
+
+#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+void PNGAPI
+png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
+   write_user_transform_fn)
+{
+   png_debug(1, "in png_set_write_user_transform_fn\n");
+   png_ptr->transformations |= PNG_USER_TRANSFORM;
+   png_ptr->write_user_transform_fn = write_user_transform_fn;
+}
+#endif
+
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+void PNGAPI
+png_write_png(png_structp png_ptr, png_infop info_ptr,
+              int transforms, voidp params)
+{
+#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+   /* invert the alpha channel from opacity to transparency */
+   if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
+       png_set_invert_alpha(png_ptr);
+#endif
+
+   /* Write the file header information. */
+   png_write_info(png_ptr, info_ptr);
+
+   /* ------ these transformations don't touch the info structure ------- */
+
+#if defined(PNG_WRITE_INVERT_SUPPORTED)
+   /* invert monochrome pixels */
+   if (transforms & PNG_TRANSFORM_INVERT_MONO)
+       png_set_invert_mono(png_ptr);
+#endif
+
+#if defined(PNG_WRITE_SHIFT_SUPPORTED)
+   /* Shift the pixels up to a legal bit depth and fill in
+    * as appropriate to correctly scale the image.
+    */
+   if ((transforms & PNG_TRANSFORM_SHIFT)
+               && (info_ptr->valid & PNG_INFO_sBIT))
+       png_set_shift(png_ptr, &info_ptr->sig_bit);
+#endif
+
+#if defined(PNG_WRITE_PACK_SUPPORTED)
+   /* pack pixels into bytes */
+   if (transforms & PNG_TRANSFORM_PACKING)
+       png_set_packing(png_ptr);
+#endif
+
+#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+   /* swap location of alpha bytes from ARGB to RGBA */
+   if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
+       png_set_swap_alpha(png_ptr);
+#endif
+
+#if defined(PNG_WRITE_FILLER_SUPPORTED)
+   /* Get rid of filler (OR ALPHA) bytes, pack XRGB/RGBX/ARGB/RGBA into
+    * RGB (4 channels -> 3 channels). The second parameter is not used.
+    */
+   if (transforms & PNG_TRANSFORM_STRIP_FILLER)
+       png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
+#endif
+
+#if defined(PNG_WRITE_BGR_SUPPORTED)
+   /* flip BGR pixels to RGB */
+   if (transforms & PNG_TRANSFORM_BGR)
+       png_set_bgr(png_ptr);
+#endif
+
+#if defined(PNG_WRITE_SWAP_SUPPORTED)
+   /* swap bytes of 16-bit files to most significant byte first */
+   if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
+       png_set_swap(png_ptr);
+#endif
+
+#if defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+   /* swap bits of 1, 2, 4 bit packed pixel formats */
+   if (transforms & PNG_TRANSFORM_PACKSWAP)
+       png_set_packswap(png_ptr);
+#endif
+
+   /* ----------------------- end of transformations ------------------- */
+
+   /* write the bits */
+   if (info_ptr->valid & PNG_INFO_IDAT)
+       png_write_image(png_ptr, info_ptr->row_pointers);
+
+   /* It is REQUIRED to call this to finish writing the rest of the file */
+   png_write_end(png_ptr, info_ptr);
+
+   if(transforms == 0 || params == NULL)
+      /* quiet compiler warnings */ return;
+}
+#endif
+#endif /* PNG_WRITE_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/png/libpng/pngwtran.c b/Utilities/GDAL/frmts/png/libpng/pngwtran.c
new file mode 100644
index 0000000000..f1c8b3e62a
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngwtran.c
@@ -0,0 +1,563 @@
+
+/* pngwtran.c - transforms the data in a row for PNG writers
+ *
+ * libpng version 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+#ifdef PNG_WRITE_SUPPORTED
+
+/* Transform the data according to the user's wishes.  The order of
+ * transformations is significant.
+ */
+void /* PRIVATE */
+png_do_write_transformations(png_structp png_ptr)
+{
+   png_debug(1, "in png_do_write_transformations\n");
+
+   if (png_ptr == NULL)
+      return;
+
+#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+   if (png_ptr->transformations & PNG_USER_TRANSFORM)
+      if(png_ptr->write_user_transform_fn != NULL)
+        (*(png_ptr->write_user_transform_fn)) /* user write transform function */
+          (png_ptr,                    /* png_ptr */
+           &(png_ptr->row_info),       /* row_info:     */
+             /*  png_uint_32 width;          width of row */
+             /*  png_uint_32 rowbytes;       number of bytes in row */
+             /*  png_byte color_type;        color type of pixels */
+             /*  png_byte bit_depth;         bit depth of samples */
+             /*  png_byte channels;          number of channels (1-4) */
+             /*  png_byte pixel_depth;       bits per pixel (depth*channels) */
+           png_ptr->row_buf + 1);      /* start of pixel data for row */
+#endif
+#if defined(PNG_WRITE_FILLER_SUPPORTED)
+   if (png_ptr->transformations & PNG_FILLER)
+      png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         png_ptr->flags);
+#endif
+#if defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACKSWAP)
+      png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_PACK_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACK)
+      png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         (png_uint_32)png_ptr->bit_depth);
+#endif
+#if defined(PNG_WRITE_SWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_BYTES)
+      png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_SHIFT_SUPPORTED)
+   if (png_ptr->transformations & PNG_SHIFT)
+      png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         &(png_ptr->shift));
+#endif
+#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_ALPHA)
+      png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_ALPHA)
+      png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_BGR_SUPPORTED)
+   if (png_ptr->transformations & PNG_BGR)
+      png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_INVERT_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_MONO)
+      png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+}
+
+#if defined(PNG_WRITE_PACK_SUPPORTED)
+/* Pack pixels into bytes.  Pass the true bit depth in bit_depth.  The
+ * row_info bit depth should be 8 (one pixel per byte).  The channels
+ * should be 1 (this only happens on grayscale and paletted images).
+ */
+void /* PRIVATE */
+png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth)
+{
+   png_debug(1, "in png_do_pack\n");
+   if (row_info->bit_depth == 8 &&
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+      row_info->channels == 1)
+   {
+      switch ((int)bit_depth)
+      {
+         case 1:
+         {
+            png_bytep sp, dp;
+            int mask, v;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            sp = row;
+            dp = row;
+            mask = 0x80;
+            v = 0;
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (*sp != 0)
+                  v |= mask;
+               sp++;
+               if (mask > 1)
+                  mask >>= 1;
+               else
+               {
+                  mask = 0x80;
+                  *dp = (png_byte)v;
+                  dp++;
+                  v = 0;
+               }
+            }
+            if (mask != 0x80)
+               *dp = (png_byte)v;
+            break;
+         }
+         case 2:
+         {
+            png_bytep sp, dp;
+            int shift, v;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            sp = row;
+            dp = row;
+            shift = 6;
+            v = 0;
+            for (i = 0; i < row_width; i++)
+            {
+               png_byte value;
+
+               value = (png_byte)(*sp & 0x03);
+               v |= (value << shift);
+               if (shift == 0)
+               {
+                  shift = 6;
+                  *dp = (png_byte)v;
+                  dp++;
+                  v = 0;
+               }
+               else
+                  shift -= 2;
+               sp++;
+            }
+            if (shift != 6)
+               *dp = (png_byte)v;
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp, dp;
+            int shift, v;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            sp = row;
+            dp = row;
+            shift = 4;
+            v = 0;
+            for (i = 0; i < row_width; i++)
+            {
+               png_byte value;
+
+               value = (png_byte)(*sp & 0x0f);
+               v |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 4;
+                  *dp = (png_byte)v;
+                  dp++;
+                  v = 0;
+               }
+               else
+                  shift -= 4;
+
+               sp++;
+            }
+            if (shift != 4)
+               *dp = (png_byte)v;
+            break;
+         }
+      }
+      row_info->bit_depth = (png_byte)bit_depth;
+      row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels);
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
+         row_info->width);
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_SHIFT_SUPPORTED)
+/* Shift pixel values to take advantage of whole range.  Pass the
+ * true number of bits in bit_depth.  The row should be packed
+ * according to row_info->bit_depth.  Thus, if you had a row of
+ * bit depth 4, but the pixels only had values from 0 to 7, you
+ * would pass 3 as bit_depth, and this routine would translate the
+ * data to 0 to 15.
+ */
+void /* PRIVATE */
+png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth)
+{
+   png_debug(1, "in png_do_shift\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL &&
+#else
+   if (
+#endif
+      row_info->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      int shift_start[4], shift_dec[4];
+      int channels = 0;
+
+      if (row_info->color_type & PNG_COLOR_MASK_COLOR)
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->red;
+         shift_dec[channels] = bit_depth->red;
+         channels++;
+         shift_start[channels] = row_info->bit_depth - bit_depth->green;
+         shift_dec[channels] = bit_depth->green;
+         channels++;
+         shift_start[channels] = row_info->bit_depth - bit_depth->blue;
+         shift_dec[channels] = bit_depth->blue;
+         channels++;
+      }
+      else
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->gray;
+         shift_dec[channels] = bit_depth->gray;
+         channels++;
+      }
+      if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->alpha;
+         shift_dec[channels] = bit_depth->alpha;
+         channels++;
+      }
+
+      /* with low row depths, could only be grayscale, so one channel */
+      if (row_info->bit_depth < 8)
+      {
+         png_bytep bp = row;
+         png_uint_32 i;
+         png_byte mask;
+         png_uint_32 row_bytes = row_info->rowbytes;
+
+         if (bit_depth->gray == 1 && row_info->bit_depth == 2)
+            mask = 0x55;
+         else if (row_info->bit_depth == 4 && bit_depth->gray == 3)
+            mask = 0x11;
+         else
+            mask = 0xff;
+
+         for (i = 0; i < row_bytes; i++, bp++)
+         {
+            png_uint_16 v;
+            int j;
+
+            v = *bp;
+            *bp = 0;
+            for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0])
+            {
+               if (j > 0)
+                  *bp |= (png_byte)((v << j) & 0xff);
+               else
+                  *bp |= (png_byte)((v >> (-j)) & mask);
+            }
+         }
+      }
+      else if (row_info->bit_depth == 8)
+      {
+         png_bytep bp = row;
+         png_uint_32 i;
+         png_uint_32 istop = channels * row_info->width;
+
+         for (i = 0; i < istop; i++, bp++)
+         {
+
+            png_uint_16 v;
+            int j;
+            int c = (int)(i%channels);
+
+            v = *bp;
+            *bp = 0;
+            for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
+            {
+               if (j > 0)
+                  *bp |= (png_byte)((v << j) & 0xff);
+               else
+                  *bp |= (png_byte)((v >> (-j)) & 0xff);
+            }
+         }
+      }
+      else
+      {
+         png_bytep bp;
+         png_uint_32 i;
+         png_uint_32 istop = channels * row_info->width;
+
+         for (bp = row, i = 0; i < istop; i++)
+         {
+            int c = (int)(i%channels);
+            png_uint_16 value, v;
+            int j;
+
+            v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1));
+            value = 0;
+            for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
+            {
+               if (j > 0)
+                  value |= (png_uint_16)((v << j) & (png_uint_16)0xffff);
+               else
+                  value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff);
+            }
+            *bp++ = (png_byte)(value >> 8);
+            *bp++ = (png_byte)(value & 0xff);
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+void /* PRIVATE */
+png_do_write_swap_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_write_swap_alpha\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         /* This converts from ARGB to RGBA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save;
+            }
+         }
+         /* This converts from AARRGGBB to RRGGBBAA */
+         else
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save[2];
+               save[0] = *(sp++);
+               save[1] = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save[0];
+               *(dp++) = save[1];
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         /* This converts from AG to GA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save;
+            }
+         }
+         /* This converts from AAGG to GGAA */
+         else
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save[2];
+               save[0] = *(sp++);
+               save[1] = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save[0];
+               *(dp++) = save[1];
+            }
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+void /* PRIVATE */
+png_do_write_invert_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_write_invert_alpha\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         /* This inverts the alpha channel in RGBA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = (png_byte)(255 - *(sp++));
+            }
+         }
+         /* This inverts the alpha channel in RRGGBBAA */
+         else
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = (png_byte)(255 - *(sp++));
+               *(dp++) = (png_byte)(255 - *(sp++));
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         /* This inverts the alpha channel in GA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               *(dp++) = *(sp++);
+               *(dp++) = (png_byte)(255 - *(sp++));
+            }
+         }
+         /* This inverts the alpha channel in GGAA */
+         else
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = (png_byte)(255 - *(sp++));
+               *(dp++) = (png_byte)(255 - *(sp++));
+            }
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+/* undoes intrapixel differencing  */
+void /* PRIVATE */
+png_do_write_intrapixel(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_write_intrapixel\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       (row_info->color_type & PNG_COLOR_MASK_COLOR))
+   {
+      int bytes_per_pixel;
+      png_uint_32 row_width = row_info->width;
+      if (row_info->bit_depth == 8)
+      {
+         png_bytep rp;
+         png_uint_32 i;
+
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+            bytes_per_pixel = 3;
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            bytes_per_pixel = 4;
+         else
+            return;
+
+         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+         {
+            *(rp)   = (png_byte)((*rp     - *(rp+1))&0xff);
+            *(rp+2) = (png_byte)((*(rp+2) - *(rp+1))&0xff);
+         }
+      }
+      else if (row_info->bit_depth == 16)
+      {
+         png_bytep rp;
+         png_uint_32 i;
+
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+            bytes_per_pixel = 6;
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            bytes_per_pixel = 8;
+         else
+            return;
+
+         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+         {
+            png_uint_32 s0   = (*(rp  ) << 8) | *(rp+1);
+            png_uint_32 s1   = (*(rp+2) << 8) | *(rp+3);
+            png_uint_32 s2   = (*(rp+4) << 8) | *(rp+5);
+            png_uint_32 red  = (png_uint_32)((s0-s1) & 0xffffL);
+            png_uint_32 blue = (png_uint_32)((s2-s1) & 0xffffL);
+            *(rp  ) = (png_byte)((red >> 8) & 0xff);
+            *(rp+1) = (png_byte)(red & 0xff);
+            *(rp+4) = (png_byte)((blue >> 8) & 0xff);
+            *(rp+5) = (png_byte)(blue & 0xff);
+         }
+      }
+   }
+}
+#endif /* PNG_MNG_FEATURES_SUPPORTED */
+#endif /* PNG_WRITE_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/png/libpng/pngwutil.c b/Utilities/GDAL/frmts/png/libpng/pngwutil.c
new file mode 100644
index 0000000000..dd7b150b75
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/libpng/pngwutil.c
@@ -0,0 +1,2730 @@
+
+/* pngwutil.c - utilities to write a PNG file
+ *
+ * libpng version 1.2.8 - December 3, 2004
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+#ifdef PNG_WRITE_SUPPORTED
+
+/* Place a 32-bit number into a buffer in PNG byte order.  We work
+ * with unsigned numbers for convenience, although one supported
+ * ancillary chunk uses signed (two's complement) numbers.
+ */
+void /* PRIVATE */
+png_save_uint_32(png_bytep buf, png_uint_32 i)
+{
+   buf[0] = (png_byte)((i >> 24) & 0xff);
+   buf[1] = (png_byte)((i >> 16) & 0xff);
+   buf[2] = (png_byte)((i >> 8) & 0xff);
+   buf[3] = (png_byte)(i & 0xff);
+}
+
+#if defined(PNG_WRITE_pCAL_SUPPORTED) || defined(PNG_WRITE_oFFs_SUPPORTED)
+/* The png_save_int_32 function assumes integers are stored in two's
+ * complement format.  If this isn't the case, then this routine needs to
+ * be modified to write data in two's complement format.
+ */
+void /* PRIVATE */
+png_save_int_32(png_bytep buf, png_int_32 i)
+{
+   buf[0] = (png_byte)((i >> 24) & 0xff);
+   buf[1] = (png_byte)((i >> 16) & 0xff);
+   buf[2] = (png_byte)((i >> 8) & 0xff);
+   buf[3] = (png_byte)(i & 0xff);
+}
+#endif
+
+/* Place a 16-bit number into a buffer in PNG byte order.
+ * The parameter is declared unsigned int, not png_uint_16,
+ * just to avoid potential problems on pre-ANSI C compilers.
+ */
+void /* PRIVATE */
+png_save_uint_16(png_bytep buf, unsigned int i)
+{
+   buf[0] = (png_byte)((i >> 8) & 0xff);
+   buf[1] = (png_byte)(i & 0xff);
+}
+
+/* Write a PNG chunk all at once.  The type is an array of ASCII characters
+ * representing the chunk name.  The array must be at least 4 bytes in
+ * length, and does not need to be null terminated.  To be safe, pass the
+ * pre-defined chunk names here, and if you need a new one, define it
+ * where the others are defined.  The length is the length of the data.
+ * All the data must be present.  If that is not possible, use the
+ * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end()
+ * functions instead.
+ */
+void PNGAPI
+png_write_chunk(png_structp png_ptr, png_bytep chunk_name,
+   png_bytep data, png_size_t length)
+{
+   png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length);
+   png_write_chunk_data(png_ptr, data, length);
+   png_write_chunk_end(png_ptr);
+}
+
+/* Write the start of a PNG chunk.  The type is the chunk type.
+ * The total_length is the sum of the lengths of all the data you will be
+ * passing in png_write_chunk_data().
+ */
+void PNGAPI
+png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name,
+   png_uint_32 length)
+{
+   png_byte buf[4];
+   png_debug2(0, "Writing %s chunk (%lu bytes)\n", chunk_name, length);
+
+   /* write the length */
+   png_save_uint_32(buf, length);
+   png_write_data(png_ptr, buf, (png_size_t)4);
+
+   /* write the chunk name */
+   png_write_data(png_ptr, chunk_name, (png_size_t)4);
+   /* reset the crc and run it over the chunk name */
+   png_reset_crc(png_ptr);
+   png_calculate_crc(png_ptr, chunk_name, (png_size_t)4);
+}
+
+/* Write the data of a PNG chunk started with png_write_chunk_start().
+ * Note that multiple calls to this function are allowed, and that the
+ * sum of the lengths from these calls *must* add up to the total_length
+ * given to png_write_chunk_start().
+ */
+void PNGAPI
+png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   /* write the data, and run the CRC over it */
+   if (data != NULL && length > 0)
+   {
+      png_calculate_crc(png_ptr, data, length);
+      png_write_data(png_ptr, data, length);
+   }
+}
+
+/* Finish a chunk started with png_write_chunk_start(). */
+void PNGAPI
+png_write_chunk_end(png_structp png_ptr)
+{
+   png_byte buf[4];
+
+   /* write the crc */
+   png_save_uint_32(buf, png_ptr->crc);
+
+   png_write_data(png_ptr, buf, (png_size_t)4);
+}
+
+/* Simple function to write the signature.  If we have already written
+ * the magic bytes of the signature, or more likely, the PNG stream is
+ * being embedded into another stream and doesn't need its own signature,
+ * we should call png_set_sig_bytes() to tell libpng how many of the
+ * bytes have already been written.
+ */
+void /* PRIVATE */
+png_write_sig(png_structp png_ptr)
+{
+   png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
+   /* write the rest of the 8 byte signature */
+   png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes],
+      (png_size_t)8 - png_ptr->sig_bytes);
+   if(png_ptr->sig_bytes < 3)
+      png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
+}
+
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED)
+/*
+ * This pair of functions encapsulates the operation of (a) compressing a
+ * text string, and (b) issuing it later as a series of chunk data writes.
+ * The compression_state structure is shared context for these functions
+ * set up by the caller in order to make the whole mess thread-safe.
+ */
+
+typedef struct
+{
+    char *input;   /* the uncompressed input data */
+    int input_len;   /* its length */
+    int num_output_ptr; /* number of output pointers used */
+    int max_output_ptr; /* size of output_ptr */
+    png_charpp output_ptr; /* array of pointers to output */
+} compression_state;
+
+/* compress given text into storage in the png_ptr structure */
+static int /* PRIVATE */
+png_text_compress(png_structp png_ptr,
+        png_charp text, png_size_t text_len, int compression,
+        compression_state *comp)
+{
+   int ret;
+
+   comp->num_output_ptr = comp->max_output_ptr = 0;
+   comp->output_ptr = NULL;
+   comp->input = NULL;
+
+   /* we may just want to pass the text right through */
+   if (compression == PNG_TEXT_COMPRESSION_NONE)
+   {
+       comp->input = text;
+       comp->input_len = text_len;
+       return((int)text_len);
+   }
+
+   if (compression >= PNG_TEXT_COMPRESSION_LAST)
+   {
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+      char msg[50];
+      sprintf(msg, "Unknown compression type %d", compression);
+      png_warning(png_ptr, msg);
+#else
+      png_warning(png_ptr, "Unknown compression type");
+#endif
+   }
+
+   /* We can't write the chunk until we find out how much data we have,
+    * which means we need to run the compressor first and save the
+    * output.  This shouldn't be a problem, as the vast majority of
+    * comments should be reasonable, but we will set up an array of
+    * malloc'd pointers to be sure.
+    *
+    * If we knew the application was well behaved, we could simplify this
+    * greatly by assuming we can always malloc an output buffer large
+    * enough to hold the compressed text ((1001 * text_len / 1000) + 12)
+    * and malloc this directly.  The only time this would be a bad idea is
+    * if we can't malloc more than 64K and we have 64K of random input
+    * data, or if the input string is incredibly large (although this
+    * wouldn't cause a failure, just a slowdown due to swapping).
+    */
+
+   /* set up the compression buffers */
+   png_ptr->zstream.avail_in = (uInt)text_len;
+   png_ptr->zstream.next_in = (Bytef *)text;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+   png_ptr->zstream.next_out = (Bytef *)png_ptr->zbuf;
+
+   /* this is the same compression loop as in png_write_row() */
+   do
+   {
+      /* compress the data */
+      ret = deflate(&png_ptr->zstream, Z_NO_FLUSH);
+      if (ret != Z_OK)
+      {
+         /* error */
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+      /* check to see if we need more room */
+      if (!(png_ptr->zstream.avail_out))
+      {
+         /* make sure the output array has room */
+         if (comp->num_output_ptr >= comp->max_output_ptr)
+         {
+            int old_max;
+
+            old_max = comp->max_output_ptr;
+            comp->max_output_ptr = comp->num_output_ptr + 4;
+            if (comp->output_ptr != NULL)
+            {
+               png_charpp old_ptr;
+
+               old_ptr = comp->output_ptr;
+               comp->output_ptr = (png_charpp)png_malloc(png_ptr,
+                  (png_uint_32)(comp->max_output_ptr *
+                  png_sizeof (png_charpp)));
+               png_memcpy(comp->output_ptr, old_ptr, old_max
+                  * png_sizeof (png_charp));
+               png_free(png_ptr, old_ptr);
+            }
+            else
+               comp->output_ptr = (png_charpp)png_malloc(png_ptr,
+                  (png_uint_32)(comp->max_output_ptr *
+                  png_sizeof (png_charp)));
+         }
+
+         /* save the data */
+         comp->output_ptr[comp->num_output_ptr] = (png_charp)png_malloc(png_ptr,
+            (png_uint_32)png_ptr->zbuf_size);
+         png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf,
+            png_ptr->zbuf_size);
+         comp->num_output_ptr++;
+
+         /* and reset the buffer */
+         png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+         png_ptr->zstream.next_out = png_ptr->zbuf;
+      }
+   /* continue until we don't have any more to compress */
+   } while (png_ptr->zstream.avail_in);
+
+   /* finish the compression */
+   do
+   {
+      /* tell zlib we are finished */
+      ret = deflate(&png_ptr->zstream, Z_FINISH);
+
+      if (ret == Z_OK)
+      {
+         /* check to see if we need more room */
+         if (!(png_ptr->zstream.avail_out))
+         {
+            /* check to make sure our output array has room */
+            if (comp->num_output_ptr >= comp->max_output_ptr)
+            {
+               int old_max;
+
+               old_max = comp->max_output_ptr;
+               comp->max_output_ptr = comp->num_output_ptr + 4;
+               if (comp->output_ptr != NULL)
+               {
+                  png_charpp old_ptr;
+
+                  old_ptr = comp->output_ptr;
+                  /* This could be optimized to realloc() */
+                  comp->output_ptr = (png_charpp)png_malloc(png_ptr,
+                     (png_uint_32)(comp->max_output_ptr *
+                     png_sizeof (png_charpp)));
+                  png_memcpy(comp->output_ptr, old_ptr,
+                     old_max * png_sizeof (png_charp));
+                  png_free(png_ptr, old_ptr);
+               }
+               else
+                  comp->output_ptr = (png_charpp)png_malloc(png_ptr,
+                     (png_uint_32)(comp->max_output_ptr *
+                     png_sizeof (png_charp)));
+            }
+
+            /* save off the data */
+            comp->output_ptr[comp->num_output_ptr] =
+               (png_charp)png_malloc(png_ptr, (png_uint_32)png_ptr->zbuf_size);
+            png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf,
+               png_ptr->zbuf_size);
+            comp->num_output_ptr++;
+
+            /* and reset the buffer pointers */
+            png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+            png_ptr->zstream.next_out = png_ptr->zbuf;
+         }
+      }
+      else if (ret != Z_STREAM_END)
+      {
+         /* we got an error */
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+   } while (ret != Z_STREAM_END);
+
+   /* text length is number of buffers plus last buffer */
+   text_len = png_ptr->zbuf_size * comp->num_output_ptr;
+   if (png_ptr->zstream.avail_out < png_ptr->zbuf_size)
+      text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out;
+
+   return((int)text_len);
+}
+
+/* ship the compressed text out via chunk writes */
+static void /* PRIVATE */
+png_write_compressed_data_out(png_structp png_ptr, compression_state *comp)
+{
+   int i;
+
+   /* handle the no-compression case */
+   if (comp->input)
+   {
+       png_write_chunk_data(png_ptr, (png_bytep)comp->input,
+                            (png_size_t)comp->input_len);
+       return;
+   }
+
+   /* write saved output buffers, if any */
+   for (i = 0; i < comp->num_output_ptr; i++)
+   {
+      png_write_chunk_data(png_ptr,(png_bytep)comp->output_ptr[i],
+         png_ptr->zbuf_size);
+      png_free(png_ptr, comp->output_ptr[i]);
+      comp->output_ptr[i]=NULL;
+   }
+   if (comp->max_output_ptr != 0)
+      png_free(png_ptr, comp->output_ptr);
+      comp->output_ptr=NULL;
+   /* write anything left in zbuf */
+   if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size)
+      png_write_chunk_data(png_ptr, png_ptr->zbuf,
+         png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+
+   /* reset zlib for another zTXt/iTXt or image data */
+   deflateReset(&png_ptr->zstream);
+   png_ptr->zstream.data_type = Z_BINARY;
+}
+#endif
+
+/* Write the IHDR chunk, and update the png_struct with the necessary
+ * information.  Note that the rest of this code depends upon this
+ * information being correct.
+ */
+void /* PRIVATE */
+png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height,
+   int bit_depth, int color_type, int compression_type, int filter_type,
+   int interlace_type)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_IHDR;
+#endif
+   png_byte buf[13]; /* buffer to store the IHDR info */
+
+   png_debug(1, "in png_write_IHDR\n");
+   /* Check that we have valid input data from the application info */
+   switch (color_type)
+   {
+      case PNG_COLOR_TYPE_GRAY:
+         switch (bit_depth)
+         {
+            case 1:
+            case 2:
+            case 4:
+            case 8:
+            case 16: png_ptr->channels = 1; break;
+            default: png_error(png_ptr,"Invalid bit depth for grayscale image");
+         }
+         break;
+      case PNG_COLOR_TYPE_RGB:
+         if (bit_depth != 8 && bit_depth != 16)
+            png_error(png_ptr, "Invalid bit depth for RGB image");
+         png_ptr->channels = 3;
+         break;
+      case PNG_COLOR_TYPE_PALETTE:
+         switch (bit_depth)
+         {
+            case 1:
+            case 2:
+            case 4:
+            case 8: png_ptr->channels = 1; break;
+            default: png_error(png_ptr, "Invalid bit depth for paletted image");
+         }
+         break;
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+         if (bit_depth != 8 && bit_depth != 16)
+            png_error(png_ptr, "Invalid bit depth for grayscale+alpha image");
+         png_ptr->channels = 2;
+         break;
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+         if (bit_depth != 8 && bit_depth != 16)
+            png_error(png_ptr, "Invalid bit depth for RGBA image");
+         png_ptr->channels = 4;
+         break;
+      default:
+         png_error(png_ptr, "Invalid image color type specified");
+   }
+
+   if (compression_type != PNG_COMPRESSION_TYPE_BASE)
+   {
+      png_warning(png_ptr, "Invalid compression type specified");
+      compression_type = PNG_COMPRESSION_TYPE_BASE;
+   }
+
+   /* Write filter_method 64 (intrapixel differencing) only if
+    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
+    * 2. Libpng did not write a PNG signature (this filter_method is only
+    *    used in PNG datastreams that are embedded in MNG datastreams) and
+    * 3. The application called png_permit_mng_features with a mask that
+    *    included PNG_FLAG_MNG_FILTER_64 and
+    * 4. The filter_method is 64 and
+    * 5. The color_type is RGB or RGBA
+    */
+   if (
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+      !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+      ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) &&
+      (color_type == PNG_COLOR_TYPE_RGB ||
+       color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
+      (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) &&
+#endif
+      filter_type != PNG_FILTER_TYPE_BASE)
+   {
+      png_warning(png_ptr, "Invalid filter type specified");
+      filter_type = PNG_FILTER_TYPE_BASE;
+   }
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   if (interlace_type != PNG_INTERLACE_NONE &&
+      interlace_type != PNG_INTERLACE_ADAM7)
+   {
+      png_warning(png_ptr, "Invalid interlace type specified");
+      interlace_type = PNG_INTERLACE_ADAM7;
+   }
+#else
+   interlace_type=PNG_INTERLACE_NONE;
+#endif
+
+   /* save off the relevent information */
+   png_ptr->bit_depth = (png_byte)bit_depth;
+   png_ptr->color_type = (png_byte)color_type;
+   png_ptr->interlaced = (png_byte)interlace_type;
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   png_ptr->filter_type = (png_byte)filter_type;
+#endif
+   png_ptr->compression_type = (png_byte)compression_type;
+   png_ptr->width = width;
+   png_ptr->height = height;
+
+   png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels);
+   png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width);
+   /* set the usr info, so any transformations can modify it */
+   png_ptr->usr_width = png_ptr->width;
+   png_ptr->usr_bit_depth = png_ptr->bit_depth;
+   png_ptr->usr_channels = png_ptr->channels;
+
+   /* pack the header information into the buffer */
+   png_save_uint_32(buf, width);
+   png_save_uint_32(buf + 4, height);
+   buf[8] = (png_byte)bit_depth;
+   buf[9] = (png_byte)color_type;
+   buf[10] = (png_byte)compression_type;
+   buf[11] = (png_byte)filter_type;
+   buf[12] = (png_byte)interlace_type;
+
+   /* write the chunk */
+   png_write_chunk(png_ptr, (png_bytep)png_IHDR, buf, (png_size_t)13);
+
+   /* initialize zlib with PNG info */
+   png_ptr->zstream.zalloc = png_zalloc;
+   png_ptr->zstream.zfree = png_zfree;
+   png_ptr->zstream.opaque = (voidpf)png_ptr;
+   if (!(png_ptr->do_filter))
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
+         png_ptr->bit_depth < 8)
+         png_ptr->do_filter = PNG_FILTER_NONE;
+      else
+         png_ptr->do_filter = PNG_ALL_FILTERS;
+   }
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY))
+   {
+      if (png_ptr->do_filter != PNG_FILTER_NONE)
+         png_ptr->zlib_strategy = Z_FILTERED;
+      else
+         png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY;
+   }
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL))
+      png_ptr->zlib_level = Z_DEFAULT_COMPRESSION;
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL))
+      png_ptr->zlib_mem_level = 8;
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS))
+      png_ptr->zlib_window_bits = 15;
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD))
+      png_ptr->zlib_method = 8;
+   deflateInit2(&png_ptr->zstream, png_ptr->zlib_level,
+      png_ptr->zlib_method, png_ptr->zlib_window_bits,
+      png_ptr->zlib_mem_level, png_ptr->zlib_strategy);
+   png_ptr->zstream.next_out = png_ptr->zbuf;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+   /* libpng is not interested in zstream.data_type */
+   /* set it to a predefined value, to avoid its evaluation inside zlib */
+   png_ptr->zstream.data_type = Z_BINARY;
+
+   png_ptr->mode = PNG_HAVE_IHDR;
+}
+
+/* write the palette.  We are careful not to trust png_color to be in the
+ * correct order for PNG, so people can redefine it to any convenient
+ * structure.
+ */
+void /* PRIVATE */
+png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_PLTE;
+#endif
+   png_uint_32 i;
+   png_colorp pal_ptr;
+   png_byte buf[3];
+
+   png_debug(1, "in png_write_PLTE\n");
+   if ((
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+        !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) &&
+#endif
+        num_pal == 0) || num_pal > 256)
+   {
+     if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+     {
+        png_error(png_ptr, "Invalid number of colors in palette");
+     }
+     else
+     {
+        png_warning(png_ptr, "Invalid number of colors in palette");
+        return;
+     }
+   }
+
+   if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR))
+   {
+      png_warning(png_ptr,
+        "Ignoring request to write a PLTE chunk in grayscale PNG");
+      return;
+   }
+
+   png_ptr->num_palette = (png_uint_16)num_pal;
+   png_debug1(3, "num_palette = %d\n", png_ptr->num_palette);
+
+   png_write_chunk_start(png_ptr, (png_bytep)png_PLTE, num_pal * 3);
+#ifndef PNG_NO_POINTER_INDEXING
+   for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++)
+   {
+      buf[0] = pal_ptr->red;
+      buf[1] = pal_ptr->green;
+      buf[2] = pal_ptr->blue;
+      png_write_chunk_data(png_ptr, buf, (png_size_t)3);
+   }
+#else
+   /* This is a little slower but some buggy compilers need to do this instead */
+   pal_ptr=palette;
+   for (i = 0; i < num_pal; i++)
+   {
+      buf[0] = pal_ptr[i].red;
+      buf[1] = pal_ptr[i].green;
+      buf[2] = pal_ptr[i].blue;
+      png_write_chunk_data(png_ptr, buf, (png_size_t)3);
+   }
+#endif
+   png_write_chunk_end(png_ptr);
+   png_ptr->mode |= PNG_HAVE_PLTE;
+}
+
+/* write an IDAT chunk */
+void /* PRIVATE */
+png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_IDAT;
+#endif
+   png_debug(1, "in png_write_IDAT\n");
+
+   /* Optimize the CMF field in the zlib stream. */
+   /* This hack of the zlib stream is compliant to the stream specification. */
+   if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
+       png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
+   {
+      unsigned int z_cmf = data[0];  /* zlib compression method and flags */
+      if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
+      {
+         /* Avoid memory underflows and multiplication overflows. */
+         /* The conditions below are practically always satisfied;
+            however, they still must be checked. */
+         if (length >= 2 &&
+             png_ptr->height < 16384 && png_ptr->width < 16384)
+         {
+            png_uint_32 uncompressed_idat_size = png_ptr->height *
+               ((png_ptr->width *
+               png_ptr->channels * png_ptr->bit_depth + 15) >> 3);
+            unsigned int z_cinfo = z_cmf >> 4;
+            unsigned int half_z_window_size = 1 << (z_cinfo + 7);
+            while (uncompressed_idat_size <= half_z_window_size &&
+                   half_z_window_size >= 256)
+            {
+               z_cinfo--;
+               half_z_window_size >>= 1;
+            }
+            z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
+            if (data[0] != (png_byte)z_cmf)
+            {
+               data[0] = (png_byte)z_cmf;
+               data[1] &= 0xe0;
+               data[1] += (png_byte)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f);
+            }
+         }
+      }
+      else
+         png_error(png_ptr,
+            "Invalid zlib compression method or flags in IDAT");
+   }
+
+   png_write_chunk(png_ptr, (png_bytep)png_IDAT, data, length);
+   png_ptr->mode |= PNG_HAVE_IDAT;
+}
+
+/* write an IEND chunk */
+void /* PRIVATE */
+png_write_IEND(png_structp png_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_IEND;
+#endif
+   png_debug(1, "in png_write_IEND\n");
+   png_write_chunk(png_ptr, (png_bytep)png_IEND, png_bytep_NULL,
+     (png_size_t)0);
+   png_ptr->mode |= PNG_HAVE_IEND;
+}
+
+#if defined(PNG_WRITE_gAMA_SUPPORTED)
+/* write a gAMA chunk */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+void /* PRIVATE */
+png_write_gAMA(png_structp png_ptr, double file_gamma)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_gAMA;
+#endif
+   png_uint_32 igamma;
+   png_byte buf[4];
+
+   png_debug(1, "in png_write_gAMA\n");
+   /* file_gamma is saved in 1/100,000ths */
+   igamma = (png_uint_32)(file_gamma * 100000.0 + 0.5);
+   png_save_uint_32(buf, igamma);
+   png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4);
+}
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+void /* PRIVATE */
+png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_gAMA;
+#endif
+   png_byte buf[4];
+
+   png_debug(1, "in png_write_gAMA\n");
+   /* file_gamma is saved in 1/100,000ths */
+   png_save_uint_32(buf, (png_uint_32)file_gamma);
+   png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4);
+}
+#endif
+#endif
+
+#if defined(PNG_WRITE_sRGB_SUPPORTED)
+/* write a sRGB chunk */
+void /* PRIVATE */
+png_write_sRGB(png_structp png_ptr, int srgb_intent)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_sRGB;
+#endif
+   png_byte buf[1];
+
+   png_debug(1, "in png_write_sRGB\n");
+   if(srgb_intent >= PNG_sRGB_INTENT_LAST)
+         png_warning(png_ptr,
+            "Invalid sRGB rendering intent specified");
+   buf[0]=(png_byte)srgb_intent;
+   png_write_chunk(png_ptr, (png_bytep)png_sRGB, buf, (png_size_t)1);
+}
+#endif
+
+#if defined(PNG_WRITE_iCCP_SUPPORTED)
+/* write an iCCP chunk */
+void /* PRIVATE */
+png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type,
+   png_charp profile, int profile_len)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_iCCP;
+#endif
+   png_size_t name_len;
+   png_charp new_name;
+   compression_state comp;
+
+   png_debug(1, "in png_write_iCCP\n");
+   if (name == NULL || (name_len = png_check_keyword(png_ptr, name,
+      &new_name)) == 0)
+   {
+      png_warning(png_ptr, "Empty keyword in iCCP chunk");
+      return;
+   }
+
+   if (compression_type != PNG_COMPRESSION_TYPE_BASE)
+      png_warning(png_ptr, "Unknown compression type in iCCP chunk");
+
+   if (profile == NULL)
+      profile_len = 0;
+
+   if (profile_len)
+       profile_len = png_text_compress(png_ptr, profile, (png_size_t)profile_len,
+          PNG_COMPRESSION_TYPE_BASE, &comp);
+
+   /* make sure we include the NULL after the name and the compression type */
+   png_write_chunk_start(png_ptr, (png_bytep)png_iCCP,
+          (png_uint_32)name_len+profile_len+2);
+   new_name[name_len+1]=0x00;
+   png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 2);
+
+   if (profile_len)
+      png_write_compressed_data_out(png_ptr, &comp);
+
+   png_write_chunk_end(png_ptr);
+   png_free(png_ptr, new_name);
+}
+#endif
+
+#if defined(PNG_WRITE_sPLT_SUPPORTED)
+/* write a sPLT chunk */
+void /* PRIVATE */
+png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_sPLT;
+#endif
+   png_size_t name_len;
+   png_charp new_name;
+   png_byte entrybuf[10];
+   int entry_size = (spalette->depth == 8 ? 6 : 10);
+   int palette_size = entry_size * spalette->nentries;
+   png_sPLT_entryp ep;
+#ifdef PNG_NO_POINTER_INDEXING
+   int i;
+#endif
+
+   png_debug(1, "in png_write_sPLT\n");
+   if (spalette->name == NULL || (name_len = png_check_keyword(png_ptr,
+      spalette->name, &new_name))==0)
+   {
+      png_warning(png_ptr, "Empty keyword in sPLT chunk");
+      return;
+   }
+
+   /* make sure we include the NULL after the name */
+   png_write_chunk_start(png_ptr, (png_bytep)png_sPLT,
+          (png_uint_32)(name_len + 2 + palette_size));
+   png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 1);
+   png_write_chunk_data(png_ptr, (png_bytep)&spalette->depth, 1);
+
+   /* loop through each palette entry, writing appropriately */
+#ifndef PNG_NO_POINTER_INDEXING
+   for (ep = spalette->entries; ep<spalette->entries+spalette->nentries; ep++)
+   {
+       if (spalette->depth == 8)
+       {
+           entrybuf[0] = (png_byte)ep->red;
+           entrybuf[1] = (png_byte)ep->green;
+           entrybuf[2] = (png_byte)ep->blue;
+           entrybuf[3] = (png_byte)ep->alpha;
+           png_save_uint_16(entrybuf + 4, ep->frequency);
+       }
+       else
+       {
+           png_save_uint_16(entrybuf + 0, ep->red);
+           png_save_uint_16(entrybuf + 2, ep->green);
+           png_save_uint_16(entrybuf + 4, ep->blue);
+           png_save_uint_16(entrybuf + 6, ep->alpha);
+           png_save_uint_16(entrybuf + 8, ep->frequency);
+       }
+       png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size);
+   }
+#else
+   ep=spalette->entries;
+   for (i=0; i>spalette->nentries; i++)
+   {
+       if (spalette->depth == 8)
+       {
+           entrybuf[0] = (png_byte)ep[i].red;
+           entrybuf[1] = (png_byte)ep[i].green;
+           entrybuf[2] = (png_byte)ep[i].blue;
+           entrybuf[3] = (png_byte)ep[i].alpha;
+           png_save_uint_16(entrybuf + 4, ep[i].frequency);
+       }
+       else
+       {
+           png_save_uint_16(entrybuf + 0, ep[i].red);
+           png_save_uint_16(entrybuf + 2, ep[i].green);
+           png_save_uint_16(entrybuf + 4, ep[i].blue);
+           png_save_uint_16(entrybuf + 6, ep[i].alpha);
+           png_save_uint_16(entrybuf + 8, ep[i].frequency);
+       }
+       png_write_chunk_data(png_ptr, entrybuf, entry_size);
+   }
+#endif
+
+   png_write_chunk_end(png_ptr);
+   png_free(png_ptr, new_name);
+}
+#endif
+
+#if defined(PNG_WRITE_sBIT_SUPPORTED)
+/* write the sBIT chunk */
+void /* PRIVATE */
+png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_sBIT;
+#endif
+   png_byte buf[4];
+   png_size_t size;
+
+   png_debug(1, "in png_write_sBIT\n");
+   /* make sure we don't depend upon the order of PNG_COLOR_8 */
+   if (color_type & PNG_COLOR_MASK_COLOR)
+   {
+      png_byte maxbits;
+
+      maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 :
+                png_ptr->usr_bit_depth);
+      if (sbit->red == 0 || sbit->red > maxbits ||
+          sbit->green == 0 || sbit->green > maxbits ||
+          sbit->blue == 0 || sbit->blue > maxbits)
+      {
+         png_warning(png_ptr, "Invalid sBIT depth specified");
+         return;
+      }
+      buf[0] = sbit->red;
+      buf[1] = sbit->green;
+      buf[2] = sbit->blue;
+      size = 3;
+   }
+   else
+   {
+      if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth)
+      {
+         png_warning(png_ptr, "Invalid sBIT depth specified");
+         return;
+      }
+      buf[0] = sbit->gray;
+      size = 1;
+   }
+
+   if (color_type & PNG_COLOR_MASK_ALPHA)
+   {
+      if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth)
+      {
+         png_warning(png_ptr, "Invalid sBIT depth specified");
+         return;
+      }
+      buf[size++] = sbit->alpha;
+   }
+
+   png_write_chunk(png_ptr, (png_bytep)png_sBIT, buf, size);
+}
+#endif
+
+#if defined(PNG_WRITE_cHRM_SUPPORTED)
+/* write the cHRM chunk */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+void /* PRIVATE */
+png_write_cHRM(png_structp png_ptr, double white_x, double white_y,
+   double red_x, double red_y, double green_x, double green_y,
+   double blue_x, double blue_y)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_cHRM;
+#endif
+   png_byte buf[32];
+   png_uint_32 itemp;
+
+   png_debug(1, "in png_write_cHRM\n");
+   /* each value is saved in 1/100,000ths */
+   if (white_x < 0 || white_x > 0.8 || white_y < 0 || white_y > 0.8 ||
+       white_x + white_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM white point specified");
+#if !defined(PNG_NO_CONSOLE_IO)
+      fprintf(stderr,"white_x=%f, white_y=%f\n",white_x, white_y);
+#endif
+      return;
+   }
+   itemp = (png_uint_32)(white_x * 100000.0 + 0.5);
+   png_save_uint_32(buf, itemp);
+   itemp = (png_uint_32)(white_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 4, itemp);
+
+   if (red_x < 0 || red_x > 0.8 || red_y < 0 || red_y > 0.8 ||
+       red_x + red_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM red point specified");
+      return;
+   }
+   itemp = (png_uint_32)(red_x * 100000.0 + 0.5);
+   png_save_uint_32(buf + 8, itemp);
+   itemp = (png_uint_32)(red_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 12, itemp);
+
+   if (green_x < 0 || green_x > 0.8 || green_y < 0 || green_y > 0.8 ||
+       green_x + green_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM green point specified");
+      return;
+   }
+   itemp = (png_uint_32)(green_x * 100000.0 + 0.5);
+   png_save_uint_32(buf + 16, itemp);
+   itemp = (png_uint_32)(green_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 20, itemp);
+
+   if (blue_x < 0 || blue_x > 0.8 || blue_y < 0 || blue_y > 0.8 ||
+       blue_x + blue_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM blue point specified");
+      return;
+   }
+   itemp = (png_uint_32)(blue_x * 100000.0 + 0.5);
+   png_save_uint_32(buf + 24, itemp);
+   itemp = (png_uint_32)(blue_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 28, itemp);
+
+   png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32);
+}
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+void /* PRIVATE */
+png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x,
+   png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y,
+   png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x,
+   png_fixed_point blue_y)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_cHRM;
+#endif
+   png_byte buf[32];
+
+   png_debug(1, "in png_write_cHRM\n");
+   /* each value is saved in 1/100,000ths */
+   if (white_x > 80000L || white_y > 80000L || white_x + white_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid fixed cHRM white point specified");
+#if !defined(PNG_NO_CONSOLE_IO)
+      fprintf(stderr,"white_x=%ld, white_y=%ld\n",white_x, white_y);
+#endif
+      return;
+   }
+   png_save_uint_32(buf, (png_uint_32)white_x);
+   png_save_uint_32(buf + 4, (png_uint_32)white_y);
+
+   if (red_x > 80000L || red_y > 80000L || red_x + red_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid cHRM fixed red point specified");
+      return;
+   }
+   png_save_uint_32(buf + 8, (png_uint_32)red_x);
+   png_save_uint_32(buf + 12, (png_uint_32)red_y);
+
+   if (green_x > 80000L || green_y > 80000L || green_x + green_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid fixed cHRM green point specified");
+      return;
+   }
+   png_save_uint_32(buf + 16, (png_uint_32)green_x);
+   png_save_uint_32(buf + 20, (png_uint_32)green_y);
+
+   if (blue_x > 80000L || blue_y > 80000L || blue_x + blue_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid fixed cHRM blue point specified");
+      return;
+   }
+   png_save_uint_32(buf + 24, (png_uint_32)blue_x);
+   png_save_uint_32(buf + 28, (png_uint_32)blue_y);
+
+   png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32);
+}
+#endif
+#endif
+
+#if defined(PNG_WRITE_tRNS_SUPPORTED)
+/* write the tRNS chunk */
+void /* PRIVATE */
+png_write_tRNS(png_structp png_ptr, png_bytep trans, png_color_16p tran,
+   int num_trans, int color_type)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_tRNS;
+#endif
+   png_byte buf[6];
+
+   png_debug(1, "in png_write_tRNS\n");
+   if (color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette)
+      {
+         png_warning(png_ptr,"Invalid number of transparent colors specified");
+         return;
+      }
+      /* write the chunk out as it is */
+      png_write_chunk(png_ptr, (png_bytep)png_tRNS, trans, (png_size_t)num_trans);
+   }
+   else if (color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      /* one 16 bit value */
+      if(tran->gray >= (1 << png_ptr->bit_depth))
+      {
+         png_warning(png_ptr,
+           "Ignoring attempt to write tRNS chunk out-of-range for bit_depth");
+         return;
+      }
+      png_save_uint_16(buf, tran->gray);
+      png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)2);
+   }
+   else if (color_type == PNG_COLOR_TYPE_RGB)
+   {
+      /* three 16 bit values */
+      png_save_uint_16(buf, tran->red);
+      png_save_uint_16(buf + 2, tran->green);
+      png_save_uint_16(buf + 4, tran->blue);
+      if(png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]))
+         {
+            png_warning(png_ptr,
+              "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8");
+            return;
+         }
+      png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)6);
+   }
+   else
+   {
+      png_warning(png_ptr, "Can't write tRNS with an alpha channel");
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_bKGD_SUPPORTED)
+/* write the background chunk */
+void /* PRIVATE */
+png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_bKGD;
+#endif
+   png_byte buf[6];
+
+   png_debug(1, "in png_write_bKGD\n");
+   if (color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+          (png_ptr->num_palette ||
+          (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) &&
+#endif
+         back->index > png_ptr->num_palette)
+      {
+         png_warning(png_ptr, "Invalid background palette index");
+         return;
+      }
+      buf[0] = back->index;
+      png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)1);
+   }
+   else if (color_type & PNG_COLOR_MASK_COLOR)
+   {
+      png_save_uint_16(buf, back->red);
+      png_save_uint_16(buf + 2, back->green);
+      png_save_uint_16(buf + 4, back->blue);
+      if(png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]))
+         {
+            png_warning(png_ptr,
+              "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8");
+            return;
+         }
+      png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)6);
+   }
+   else
+   {
+      if(back->gray >= (1 << png_ptr->bit_depth))
+      {
+         png_warning(png_ptr,
+           "Ignoring attempt to write bKGD chunk out-of-range for bit_depth");
+         return;
+      }
+      png_save_uint_16(buf, back->gray);
+      png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)2);
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_hIST_SUPPORTED)
+/* write the histogram */
+void /* PRIVATE */
+png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_hIST;
+#endif
+   int i;
+   png_byte buf[3];
+
+   png_debug(1, "in png_write_hIST\n");
+   if (num_hist > (int)png_ptr->num_palette)
+   {
+      png_debug2(3, "num_hist = %d, num_palette = %d\n", num_hist,
+         png_ptr->num_palette);
+      png_warning(png_ptr, "Invalid number of histogram entries specified");
+      return;
+   }
+
+   png_write_chunk_start(png_ptr, (png_bytep)png_hIST, (png_uint_32)(num_hist * 2));
+   for (i = 0; i < num_hist; i++)
+   {
+      png_save_uint_16(buf, hist[i]);
+      png_write_chunk_data(png_ptr, buf, (png_size_t)2);
+   }
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
+    defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
+/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification,
+ * and if invalid, correct the keyword rather than discarding the entire
+ * chunk.  The PNG 1.0 specification requires keywords 1-79 characters in
+ * length, forbids leading or trailing whitespace, multiple internal spaces,
+ * and the non-break space (0x80) from ISO 8859-1.  Returns keyword length.
+ *
+ * The new_key is allocated to hold the corrected keyword and must be freed
+ * by the calling routine.  This avoids problems with trying to write to
+ * static keywords without having to have duplicate copies of the strings.
+ */
+png_size_t /* PRIVATE */
+png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key)
+{
+   png_size_t key_len;
+   png_charp kp, dp;
+   int kflag;
+   int kwarn=0;
+
+   png_debug(1, "in png_check_keyword\n");
+   *new_key = NULL;
+
+   if (key == NULL || (key_len = png_strlen(key)) == 0)
+   {
+      png_warning(png_ptr, "zero length keyword");
+      return ((png_size_t)0);
+   }
+
+   png_debug1(2, "Keyword to be checked is '%s'\n", key);
+
+   *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2));
+   if (*new_key == NULL)
+   {
+      png_warning(png_ptr, "Out of memory while procesing keyword");
+      return ((png_size_t)0);
+   }
+
+   /* Replace non-printing characters with a blank and print a warning */
+   for (kp = key, dp = *new_key; *kp != '\0'; kp++, dp++)
+   {
+      if (*kp < 0x20 || (*kp > 0x7E && (png_byte)*kp < 0xA1))
+      {
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+         char msg[40];
+
+         sprintf(msg, "invalid keyword character 0x%02X", *kp);
+         png_warning(png_ptr, msg);
+#else
+         png_warning(png_ptr, "invalid character in keyword");
+#endif
+         *dp = ' ';
+      }
+      else
+      {
+         *dp = *kp;
+      }
+   }
+   *dp = '\0';
+
+   /* Remove any trailing white space. */
+   kp = *new_key + key_len - 1;
+   if (*kp == ' ')
+   {
+      png_warning(png_ptr, "trailing spaces removed from keyword");
+
+      while (*kp == ' ')
+      {
+        *(kp--) = '\0';
+        key_len--;
+      }
+   }
+
+   /* Remove any leading white space. */
+   kp = *new_key;
+   if (*kp == ' ')
+   {
+      png_warning(png_ptr, "leading spaces removed from keyword");
+
+      while (*kp == ' ')
+      {
+        kp++;
+        key_len--;
+      }
+   }
+
+   png_debug1(2, "Checking for multiple internal spaces in '%s'\n", kp);
+
+   /* Remove multiple internal spaces. */
+   for (kflag = 0, dp = *new_key; *kp != '\0'; kp++)
+   {
+      if (*kp == ' ' && kflag == 0)
+      {
+         *(dp++) = *kp;
+         kflag = 1;
+      }
+      else if (*kp == ' ')
+      {
+         key_len--;
+         kwarn=1;
+      }
+      else
+      {
+         *(dp++) = *kp;
+         kflag = 0;
+      }
+   }
+   *dp = '\0';
+   if(kwarn)
+      png_warning(png_ptr, "extra interior spaces removed from keyword");
+
+   if (key_len == 0)
+   {
+      png_free(png_ptr, *new_key);
+      *new_key=NULL;
+      png_warning(png_ptr, "Zero length keyword");
+   }
+
+   if (key_len > 79)
+   {
+      png_warning(png_ptr, "keyword length must be 1 - 79 characters");
+      new_key[79] = '\0';
+      key_len = 79;
+   }
+
+   return (key_len);
+}
+#endif
+
+#if defined(PNG_WRITE_tEXt_SUPPORTED)
+/* write a tEXt chunk */
+void /* PRIVATE */
+png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text,
+   png_size_t text_len)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_tEXt;
+#endif
+   png_size_t key_len;
+   png_charp new_key;
+
+   png_debug(1, "in png_write_tEXt\n");
+   if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0)
+   {
+      png_warning(png_ptr, "Empty keyword in tEXt chunk");
+      return;
+   }
+
+   if (text == NULL || *text == '\0')
+      text_len = 0;
+   else
+      text_len = png_strlen(text);
+
+   /* make sure we include the 0 after the key */
+   png_write_chunk_start(png_ptr, (png_bytep)png_tEXt, (png_uint_32)key_len+text_len+1);
+   /*
+    * We leave it to the application to meet PNG-1.0 requirements on the
+    * contents of the text.  PNG-1.0 through PNG-1.2 discourage the use of
+    * any non-Latin-1 characters except for NEWLINE.  ISO PNG will forbid them.
+    * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
+    */
+   png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1);
+   if (text_len)
+      png_write_chunk_data(png_ptr, (png_bytep)text, text_len);
+
+   png_write_chunk_end(png_ptr);
+   png_free(png_ptr, new_key);
+}
+#endif
+
+#if defined(PNG_WRITE_zTXt_SUPPORTED)
+/* write a compressed text chunk */
+void /* PRIVATE */
+png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text,
+   png_size_t text_len, int compression)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_zTXt;
+#endif
+   png_size_t key_len;
+   char buf[1];
+   png_charp new_key;
+   compression_state comp;
+
+   png_debug(1, "in png_write_zTXt\n");
+
+   if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0)
+   {
+      png_warning(png_ptr, "Empty keyword in zTXt chunk");
+      return;
+   }
+
+   if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE)
+   {
+      png_write_tEXt(png_ptr, new_key, text, (png_size_t)0);
+      png_free(png_ptr, new_key);
+      return;
+   }
+
+   text_len = png_strlen(text);
+
+   png_free(png_ptr, new_key);
+
+   /* compute the compressed data; do it now for the length */
+   text_len = png_text_compress(png_ptr, text, text_len, compression,
+       &comp);
+
+   /* write start of chunk */
+   png_write_chunk_start(png_ptr, (png_bytep)png_zTXt, (png_uint_32)
+      (key_len+text_len+2));
+   /* write key */
+   png_write_chunk_data(png_ptr, (png_bytep)key, key_len + 1);
+   buf[0] = (png_byte)compression;
+   /* write compression */
+   png_write_chunk_data(png_ptr, (png_bytep)buf, (png_size_t)1);
+   /* write the compressed data */
+   png_write_compressed_data_out(png_ptr, &comp);
+
+   /* close the chunk */
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#if defined(PNG_WRITE_iTXt_SUPPORTED)
+/* write an iTXt chunk */
+void /* PRIVATE */
+png_write_iTXt(png_structp png_ptr, int compression, png_charp key,
+    png_charp lang, png_charp lang_key, png_charp text)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_iTXt;
+#endif
+   png_size_t lang_len, key_len, lang_key_len, text_len;
+   png_charp new_lang, new_key;
+   png_byte cbuf[2];
+   compression_state comp;
+
+   png_debug(1, "in png_write_iTXt\n");
+
+   if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0)
+   {
+      png_warning(png_ptr, "Empty keyword in iTXt chunk");
+      return;
+   }
+   if (lang == NULL || (lang_len = png_check_keyword(png_ptr, lang, &new_lang))==0)
+   {
+      png_warning(png_ptr, "Empty language field in iTXt chunk");
+      new_lang = NULL;
+      lang_len = 0;
+   }
+
+   if (lang_key == NULL)
+     lang_key_len = 0;
+   else
+     lang_key_len = png_strlen(lang_key);
+
+   if (text == NULL)
+      text_len = 0;
+   else
+     text_len = png_strlen(text);
+
+   /* compute the compressed data; do it now for the length */
+   text_len = png_text_compress(png_ptr, text, text_len, compression-2,
+      &comp);
+
+
+   /* make sure we include the compression flag, the compression byte,
+    * and the NULs after the key, lang, and lang_key parts */
+
+   png_write_chunk_start(png_ptr, (png_bytep)png_iTXt,
+          (png_uint_32)(
+        5 /* comp byte, comp flag, terminators for key, lang and lang_key */
+        + key_len
+        + lang_len
+        + lang_key_len
+        + text_len));
+
+   /*
+    * We leave it to the application to meet PNG-1.0 requirements on the
+    * contents of the text.  PNG-1.0 through PNG-1.2 discourage the use of
+    * any non-Latin-1 characters except for NEWLINE.  ISO PNG will forbid them.
+    * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
+    */
+   png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1);
+
+   /* set the compression flag */
+   if (compression == PNG_ITXT_COMPRESSION_NONE || \
+       compression == PNG_TEXT_COMPRESSION_NONE)
+       cbuf[0] = 0;
+   else /* compression == PNG_ITXT_COMPRESSION_zTXt */
+       cbuf[0] = 1;
+   /* set the compression method */
+   cbuf[1] = 0;
+   png_write_chunk_data(png_ptr, cbuf, 2);
+
+   cbuf[0] = 0;
+   png_write_chunk_data(png_ptr, (new_lang ? (png_bytep)new_lang : cbuf), lang_len + 1);
+   png_write_chunk_data(png_ptr, (lang_key ? (png_bytep)lang_key : cbuf), lang_key_len + 1);
+   png_write_compressed_data_out(png_ptr, &comp);
+
+   png_write_chunk_end(png_ptr);
+   png_free(png_ptr, new_key);
+   if (new_lang)
+     png_free(png_ptr, new_lang);
+}
+#endif
+
+#if defined(PNG_WRITE_oFFs_SUPPORTED)
+/* write the oFFs chunk */
+void /* PRIVATE */
+png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset,
+   int unit_type)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_oFFs;
+#endif
+   png_byte buf[9];
+
+   png_debug(1, "in png_write_oFFs\n");
+   if (unit_type >= PNG_OFFSET_LAST)
+      png_warning(png_ptr, "Unrecognized unit type for oFFs chunk");
+
+   png_save_int_32(buf, x_offset);
+   png_save_int_32(buf + 4, y_offset);
+   buf[8] = (png_byte)unit_type;
+
+   png_write_chunk(png_ptr, (png_bytep)png_oFFs, buf, (png_size_t)9);
+}
+#endif
+
+#if defined(PNG_WRITE_pCAL_SUPPORTED)
+/* write the pCAL chunk (described in the PNG extensions document) */
+void /* PRIVATE */
+png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0,
+   png_int_32 X1, int type, int nparams, png_charp units, png_charpp params)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_pCAL;
+#endif
+   png_size_t purpose_len, units_len, total_len;
+   png_uint_32p params_len;
+   png_byte buf[10];
+   png_charp new_purpose;
+   int i;
+
+   png_debug1(1, "in png_write_pCAL (%d parameters)\n", nparams);
+   if (type >= PNG_EQUATION_LAST)
+      png_warning(png_ptr, "Unrecognized equation type for pCAL chunk");
+
+   purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1;
+   png_debug1(3, "pCAL purpose length = %d\n", (int)purpose_len);
+   units_len = png_strlen(units) + (nparams == 0 ? 0 : 1);
+   png_debug1(3, "pCAL units length = %d\n", (int)units_len);
+   total_len = purpose_len + units_len + 10;
+
+   params_len = (png_uint_32p)png_malloc(png_ptr, (png_uint_32)(nparams
+      *png_sizeof(png_uint_32)));
+
+   /* Find the length of each parameter, making sure we don't count the
+      null terminator for the last parameter. */
+   for (i = 0; i < nparams; i++)
+   {
+      params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1);
+      png_debug2(3, "pCAL parameter %d length = %lu\n", i, params_len[i]);
+      total_len += (png_size_t)params_len[i];
+   }
+
+   png_debug1(3, "pCAL total length = %d\n", (int)total_len);
+   png_write_chunk_start(png_ptr, (png_bytep)png_pCAL, (png_uint_32)total_len);
+   png_write_chunk_data(png_ptr, (png_bytep)new_purpose, purpose_len);
+   png_save_int_32(buf, X0);
+   png_save_int_32(buf + 4, X1);
+   buf[8] = (png_byte)type;
+   buf[9] = (png_byte)nparams;
+   png_write_chunk_data(png_ptr, buf, (png_size_t)10);
+   png_write_chunk_data(png_ptr, (png_bytep)units, (png_size_t)units_len);
+
+   png_free(png_ptr, new_purpose);
+
+   for (i = 0; i < nparams; i++)
+   {
+      png_write_chunk_data(png_ptr, (png_bytep)params[i],
+         (png_size_t)params_len[i]);
+   }
+
+   png_free(png_ptr, params_len);
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#if defined(PNG_WRITE_sCAL_SUPPORTED)
+/* write the sCAL chunk */
+#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO)
+void /* PRIVATE */
+png_write_sCAL(png_structp png_ptr, int unit, double width,double height)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_sCAL;
+#endif
+   png_size_t total_len;
+   char wbuf[32], hbuf[32];
+   png_byte bunit = unit;
+
+   png_debug(1, "in png_write_sCAL\n");
+
+#if defined(_WIN32_WCE)
+/* sprintf() function is not supported on WindowsCE */
+   {
+      wchar_t wc_buf[32];
+      swprintf(wc_buf, TEXT("%12.12e"), width);
+      WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, wbuf, 32, NULL, NULL);
+      swprintf(wc_buf, TEXT("%12.12e"), height);
+      WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, hbuf, 32, NULL, NULL);
+   }
+#else
+   sprintf(wbuf, "%12.12e", width);
+   sprintf(hbuf, "%12.12e", height);
+#endif
+   total_len = 1 + png_strlen(wbuf)+1 + png_strlen(hbuf);
+
+   png_debug1(3, "sCAL total length = %d\n", (int)total_len);
+   png_write_chunk_start(png_ptr, (png_bytep)png_sCAL, (png_uint_32)total_len);
+   png_write_chunk_data(png_ptr, (png_bytep)&bunit, 1);
+   png_write_chunk_data(png_ptr, (png_bytep)wbuf, png_strlen(wbuf)+1);
+   png_write_chunk_data(png_ptr, (png_bytep)hbuf, png_strlen(hbuf));
+
+   png_write_chunk_end(png_ptr);
+}
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+void /* PRIVATE */
+png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width,
+   png_charp height)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_sCAL;
+#endif
+   png_size_t total_len;
+   char wbuf[32], hbuf[32];
+   png_byte bunit = unit;
+
+   png_debug(1, "in png_write_sCAL_s\n");
+
+   png_strcpy(wbuf,(const char *)width);
+   png_strcpy(hbuf,(const char *)height);
+   total_len = 1 + png_strlen(wbuf)+1 + png_strlen(hbuf);
+
+   png_debug1(3, "sCAL total length = %d\n", total_len);
+   png_write_chunk_start(png_ptr, (png_bytep)png_sCAL, (png_uint_32)total_len);
+   png_write_chunk_data(png_ptr, (png_bytep)&bunit, 1);
+   png_write_chunk_data(png_ptr, (png_bytep)wbuf, png_strlen(wbuf)+1);
+   png_write_chunk_data(png_ptr, (png_bytep)hbuf, png_strlen(hbuf));
+
+   png_write_chunk_end(png_ptr);
+}
+#endif
+#endif
+#endif
+
+#if defined(PNG_WRITE_pHYs_SUPPORTED)
+/* write the pHYs chunk */
+void /* PRIVATE */
+png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit,
+   png_uint_32 y_pixels_per_unit,
+   int unit_type)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_pHYs;
+#endif
+   png_byte buf[9];
+
+   png_debug(1, "in png_write_pHYs\n");
+   if (unit_type >= PNG_RESOLUTION_LAST)
+      png_warning(png_ptr, "Unrecognized unit type for pHYs chunk");
+
+   png_save_uint_32(buf, x_pixels_per_unit);
+   png_save_uint_32(buf + 4, y_pixels_per_unit);
+   buf[8] = (png_byte)unit_type;
+
+   png_write_chunk(png_ptr, (png_bytep)png_pHYs, buf, (png_size_t)9);
+}
+#endif
+
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+/* Write the tIME chunk.  Use either png_convert_from_struct_tm()
+ * or png_convert_from_time_t(), or fill in the structure yourself.
+ */
+void /* PRIVATE */
+png_write_tIME(png_structp png_ptr, png_timep mod_time)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_tIME;
+#endif
+   png_byte buf[7];
+
+   png_debug(1, "in png_write_tIME\n");
+   if (mod_time->month  > 12 || mod_time->month  < 1 ||
+       mod_time->day    > 31 || mod_time->day    < 1 ||
+       mod_time->hour   > 23 || mod_time->second > 60)
+   {
+      png_warning(png_ptr, "Invalid time specified for tIME chunk");
+      return;
+   }
+
+   png_save_uint_16(buf, mod_time->year);
+   buf[2] = mod_time->month;
+   buf[3] = mod_time->day;
+   buf[4] = mod_time->hour;
+   buf[5] = mod_time->minute;
+   buf[6] = mod_time->second;
+
+   png_write_chunk(png_ptr, (png_bytep)png_tIME, buf, (png_size_t)7);
+}
+#endif
+
+/* initializes the row writing capability of libpng */
+void /* PRIVATE */
+png_write_start_row(png_structp png_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* start of interlace block */
+   int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* offset to next interlace block */
+   int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* start of interlace block in the y direction */
+   int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* offset to next interlace block in the y direction */
+   int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+#endif
+
+   png_size_t buf_size;
+
+   png_debug(1, "in png_write_start_row\n");
+   buf_size = (png_size_t)(PNG_ROWBYTES(
+      png_ptr->usr_channels*png_ptr->usr_bit_depth,png_ptr->width)+1);
+
+   /* set up row buffer */
+   png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size);
+   png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE;
+
+   /* set up filtering buffer, if using this filter */
+   if (png_ptr->do_filter & PNG_FILTER_SUB)
+   {
+      png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
+         (png_ptr->rowbytes + 1));
+      png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
+   }
+
+   /* We only need to keep the previous row if we are using one of these. */
+   if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH))
+   {
+     /* set up previous row buffer */
+      png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size);
+      png_memset(png_ptr->prev_row, 0, buf_size);
+
+      if (png_ptr->do_filter & PNG_FILTER_UP)
+      {
+         png_ptr->up_row = (png_bytep )png_malloc(png_ptr,
+            (png_ptr->rowbytes + 1));
+         png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
+      }
+
+      if (png_ptr->do_filter & PNG_FILTER_AVG)
+      {
+         png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
+            (png_ptr->rowbytes + 1));
+         png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
+      }
+
+      if (png_ptr->do_filter & PNG_FILTER_PAETH)
+      {
+         png_ptr->paeth_row = (png_bytep )png_malloc(png_ptr,
+            (png_ptr->rowbytes + 1));
+         png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
+      }
+   }
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   /* if interlaced, we need to set up width and height of pass */
+   if (png_ptr->interlaced)
+   {
+      if (!(png_ptr->transformations & PNG_INTERLACE))
+      {
+         png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
+            png_pass_ystart[0]) / png_pass_yinc[0];
+         png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 -
+            png_pass_start[0]) / png_pass_inc[0];
+      }
+      else
+      {
+         png_ptr->num_rows = png_ptr->height;
+         png_ptr->usr_width = png_ptr->width;
+      }
+   }
+   else
+#endif
+   {
+      png_ptr->num_rows = png_ptr->height;
+      png_ptr->usr_width = png_ptr->width;
+   }
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+   png_ptr->zstream.next_out = png_ptr->zbuf;
+}
+
+/* Internal use only.  Called when finished processing a row of data. */
+void /* PRIVATE */
+png_write_finish_row(png_structp png_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* start of interlace block */
+   int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* offset to next interlace block */
+   int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* start of interlace block in the y direction */
+   int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* offset to next interlace block in the y direction */
+   int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+#endif
+
+   int ret;
+
+   png_debug(1, "in png_write_finish_row\n");
+   /* next row */
+   png_ptr->row_number++;
+
+   /* see if we are done */
+   if (png_ptr->row_number < png_ptr->num_rows)
+      return;
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   /* if interlaced, go to next pass */
+   if (png_ptr->interlaced)
+   {
+      png_ptr->row_number = 0;
+      if (png_ptr->transformations & PNG_INTERLACE)
+      {
+         png_ptr->pass++;
+      }
+      else
+      {
+         /* loop until we find a non-zero width or height pass */
+         do
+         {
+            png_ptr->pass++;
+            if (png_ptr->pass >= 7)
+               break;
+            png_ptr->usr_width = (png_ptr->width +
+               png_pass_inc[png_ptr->pass] - 1 -
+               png_pass_start[png_ptr->pass]) /
+               png_pass_inc[png_ptr->pass];
+            png_ptr->num_rows = (png_ptr->height +
+               png_pass_yinc[png_ptr->pass] - 1 -
+               png_pass_ystart[png_ptr->pass]) /
+               png_pass_yinc[png_ptr->pass];
+            if (png_ptr->transformations & PNG_INTERLACE)
+               break;
+         } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0);
+
+      }
+
+      /* reset the row above the image for the next pass */
+      if (png_ptr->pass < 7)
+      {
+         if (png_ptr->prev_row != NULL)
+            png_memset(png_ptr->prev_row, 0,
+               (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels*
+               png_ptr->usr_bit_depth,png_ptr->width))+1);
+         return;
+      }
+   }
+#endif
+
+   /* if we get here, we've just written the last row, so we need
+      to flush the compressor */
+   do
+   {
+      /* tell the compressor we are done */
+      ret = deflate(&png_ptr->zstream, Z_FINISH);
+      /* check for an error */
+      if (ret == Z_OK)
+      {
+         /* check to see if we need more room */
+         if (!(png_ptr->zstream.avail_out))
+         {
+            png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+            png_ptr->zstream.next_out = png_ptr->zbuf;
+            png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+         }
+      }
+      else if (ret != Z_STREAM_END)
+      {
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+   } while (ret != Z_STREAM_END);
+
+   /* write any extra space */
+   if (png_ptr->zstream.avail_out < png_ptr->zbuf_size)
+   {
+      png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size -
+         png_ptr->zstream.avail_out);
+   }
+
+   deflateReset(&png_ptr->zstream);
+   png_ptr->zstream.data_type = Z_BINARY;
+}
+
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+/* Pick out the correct pixels for the interlace pass.
+ * The basic idea here is to go through the row with a source
+ * pointer and a destination pointer (sp and dp), and copy the
+ * correct pixels for the pass.  As the row gets compacted,
+ * sp will always be >= dp, so we should never overwrite anything.
+ * See the default: case for the easiest code to understand.
+ */
+void /* PRIVATE */
+png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* start of interlace block */
+   int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* offset to next interlace block */
+   int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+#endif
+
+   png_debug(1, "in png_do_write_interlace\n");
+   /* we don't have to do anything on the last pass (6) */
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL && pass < 6)
+#else
+   if (pass < 6)
+#endif
+   {
+      /* each pixel depth is handled separately */
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            dp = row;
+            d = 0;
+            shift = 7;
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 3);
+               value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 7;
+                  *dp++ = (png_byte)d;
+                  d = 0;
+               }
+               else
+                  shift--;
+
+            }
+            if (shift != 7)
+               *dp = (png_byte)d;
+            break;
+         }
+         case 2:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            dp = row;
+            shift = 6;
+            d = 0;
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 2);
+               value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 6;
+                  *dp++ = (png_byte)d;
+                  d = 0;
+               }
+               else
+                  shift -= 2;
+            }
+            if (shift != 6)
+                   *dp = (png_byte)d;
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            dp = row;
+            shift = 4;
+            d = 0;
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 1);
+               value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 4;
+                  *dp++ = (png_byte)d;
+                  d = 0;
+               }
+               else
+                  shift -= 4;
+            }
+            if (shift != 4)
+               *dp = (png_byte)d;
+            break;
+         }
+         default:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+            png_size_t pixel_bytes;
+
+            /* start at the beginning */
+            dp = row;
+            /* find out how many bytes each pixel takes up */
+            pixel_bytes = (row_info->pixel_depth >> 3);
+            /* loop through the row, only looking at the pixels that
+               matter */
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               /* find out where the original pixel is */
+               sp = row + (png_size_t)i * pixel_bytes;
+               /* move the pixel */
+               if (dp != sp)
+                  png_memcpy(dp, sp, pixel_bytes);
+               /* next pixel */
+               dp += pixel_bytes;
+            }
+            break;
+         }
+      }
+      /* set new row width */
+      row_info->width = (row_info->width +
+         png_pass_inc[pass] - 1 -
+         png_pass_start[pass]) /
+         png_pass_inc[pass];
+         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
+            row_info->width);
+   }
+}
+#endif
+
+/* This filters the row, chooses which filter to use, if it has not already
+ * been specified by the application, and then writes the row out with the
+ * chosen filter.
+ */
+#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1)
+#define PNG_HISHIFT 10
+#define PNG_LOMASK ((png_uint_32)0xffffL)
+#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT))
+void /* PRIVATE */
+png_write_find_filter(png_structp png_ptr, png_row_infop row_info)
+{
+   png_bytep prev_row, best_row, row_buf;
+   png_uint_32 mins, bpp;
+   png_byte filter_to_do = png_ptr->do_filter;
+   png_uint_32 row_bytes = row_info->rowbytes;
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   int num_p_filters = (int)png_ptr->num_prev_filters;
+#endif
+
+   png_debug(1, "in png_write_find_filter\n");
+   /* find out how many bytes offset each pixel is */
+   bpp = (row_info->pixel_depth + 7) >> 3;
+
+   prev_row = png_ptr->prev_row;
+   best_row = row_buf = png_ptr->row_buf;
+   mins = PNG_MAXSUM;
+
+   /* The prediction method we use is to find which method provides the
+    * smallest value when summing the absolute values of the distances
+    * from zero, using anything >= 128 as negative numbers.  This is known
+    * as the "minimum sum of absolute differences" heuristic.  Other
+    * heuristics are the "weighted minimum sum of absolute differences"
+    * (experimental and can in theory improve compression), and the "zlib
+    * predictive" method (not implemented yet), which does test compressions
+    * of lines using different filter methods, and then chooses the
+    * (series of) filter(s) that give minimum compressed data size (VERY
+    * computationally expensive).
+    *
+    * GRR 980525:  consider also
+    *   (1) minimum sum of absolute differences from running average (i.e.,
+    *       keep running sum of non-absolute differences & count of bytes)
+    *       [track dispersion, too?  restart average if dispersion too large?]
+    *  (1b) minimum sum of absolute differences from sliding average, probably
+    *       with window size <= deflate window (usually 32K)
+    *   (2) minimum sum of squared differences from zero or running average
+    *       (i.e., ~ root-mean-square approach)
+    */
+
+
+   /* We don't need to test the 'no filter' case if this is the only filter
+    * that has been chosen, as it doesn't actually do anything to the data.
+    */
+   if ((filter_to_do & PNG_FILTER_NONE) &&
+       filter_to_do != PNG_FILTER_NONE)
+   {
+      png_bytep rp;
+      png_uint_32 sum = 0;
+      png_uint_32 i;
+      int v;
+
+      for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)
+      {
+         v = *rp;
+         sum += (v < 128) ? v : 256 - v;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         png_uint_32 sumhi, sumlo;
+         int j;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */
+
+         /* Reduce the sum if we match any of the previous rows */
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE)
+            {
+               sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         /* Factor in the cost of this filter (this is here for completeness,
+          * but it makes no sense to have a "cost" for the NONE filter, as
+          * it has the minimum possible computational cost - none).
+          */
+         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+      mins = sum;
+   }
+
+   /* sub filter */
+   if (filter_to_do == PNG_FILTER_SUB)
+   /* it's the only filter so no testing is needed */
+   {
+      png_bytep rp, lp, dp;
+      png_uint_32 i;
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
+           i++, rp++, dp++)
+      {
+         *dp = *rp;
+      }
+      for (lp = row_buf + 1; i < row_bytes;
+         i++, rp++, lp++, dp++)
+      {
+         *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
+      }
+      best_row = png_ptr->sub_row;
+   }
+
+   else if (filter_to_do & PNG_FILTER_SUB)
+   {
+      png_bytep rp, dp, lp;
+      png_uint_32 sum = 0, lmins = mins;
+      png_uint_32 i;
+      int v;
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      /* We temporarily increase the "minimum sum" by the factor we
+       * would reduce the sum of this filter, so that we can do the
+       * early exit comparison without scaling the sum each time.
+       */
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 lmhi, lmlo;
+         lmlo = lmins & PNG_LOMASK;
+         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)
+            {
+               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+            PNG_COST_SHIFT;
+         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+            PNG_COST_SHIFT;
+
+         if (lmhi > PNG_HIMASK)
+            lmins = PNG_MAXSUM;
+         else
+            lmins = (lmhi << PNG_HISHIFT) + lmlo;
+      }
+#endif
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
+           i++, rp++, dp++)
+      {
+         v = *dp = *rp;
+
+         sum += (v < 128) ? v : 256 - v;
+      }
+      for (lp = row_buf + 1; i < row_bytes;
+         i++, rp++, lp++, dp++)
+      {
+         v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+
+         if (sum > lmins)  /* We are already worse, don't continue. */
+            break;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 sumhi, sumlo;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)
+            {
+               sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+
+      if (sum < mins)
+      {
+         mins = sum;
+         best_row = png_ptr->sub_row;
+      }
+   }
+
+   /* up filter */
+   if (filter_to_do == PNG_FILTER_UP)
+   {
+      png_bytep rp, dp, pp;
+      png_uint_32 i;
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
+           pp = prev_row + 1; i < row_bytes;
+           i++, rp++, pp++, dp++)
+      {
+         *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff);
+      }
+      best_row = png_ptr->up_row;
+   }
+
+   else if (filter_to_do & PNG_FILTER_UP)
+   {
+      png_bytep rp, dp, pp;
+      png_uint_32 sum = 0, lmins = mins;
+      png_uint_32 i;
+      int v;
+
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 lmhi, lmlo;
+         lmlo = lmins & PNG_LOMASK;
+         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)
+            {
+               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>
+            PNG_COST_SHIFT;
+         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>
+            PNG_COST_SHIFT;
+
+         if (lmhi > PNG_HIMASK)
+            lmins = PNG_MAXSUM;
+         else
+            lmins = (lmhi << PNG_HISHIFT) + lmlo;
+      }
+#endif
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
+           pp = prev_row + 1; i < row_bytes; i++)
+      {
+         v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+
+         if (sum > lmins)  /* We are already worse, don't continue. */
+            break;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 sumhi, sumlo;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)
+            {
+               sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+
+      if (sum < mins)
+      {
+         mins = sum;
+         best_row = png_ptr->up_row;
+      }
+   }
+
+   /* avg filter */
+   if (filter_to_do == PNG_FILTER_AVG)
+   {
+      png_bytep rp, dp, pp, lp;
+      png_uint_32 i;
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
+           pp = prev_row + 1; i < bpp; i++)
+      {
+         *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
+      }
+      for (lp = row_buf + 1; i < row_bytes; i++)
+      {
+         *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2))
+                 & 0xff);
+      }
+      best_row = png_ptr->avg_row;
+   }
+
+   else if (filter_to_do & PNG_FILTER_AVG)
+   {
+      png_bytep rp, dp, pp, lp;
+      png_uint_32 sum = 0, lmins = mins;
+      png_uint_32 i;
+      int v;
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 lmhi, lmlo;
+         lmlo = lmins & PNG_LOMASK;
+         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG)
+            {
+               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>
+            PNG_COST_SHIFT;
+         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>
+            PNG_COST_SHIFT;
+
+         if (lmhi > PNG_HIMASK)
+            lmins = PNG_MAXSUM;
+         else
+            lmins = (lmhi << PNG_HISHIFT) + lmlo;
+      }
+#endif
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
+           pp = prev_row + 1; i < bpp; i++)
+      {
+         v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+      }
+      for (lp = row_buf + 1; i < row_bytes; i++)
+      {
+         v = *dp++ =
+          (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+
+         if (sum > lmins)  /* We are already worse, don't continue. */
+            break;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 sumhi, sumlo;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE)
+            {
+               sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+
+      if (sum < mins)
+      {
+         mins = sum;
+         best_row = png_ptr->avg_row;
+      }
+   }
+
+   /* Paeth filter */
+   if (filter_to_do == PNG_FILTER_PAETH)
+   {
+      png_bytep rp, dp, pp, cp, lp;
+      png_uint_32 i;
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
+           pp = prev_row + 1; i < bpp; i++)
+      {
+         *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
+      }
+
+      for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
+      {
+         int a, b, c, pa, pb, pc, p;
+
+         b = *pp++;
+         c = *cp++;
+         a = *lp++;
+
+         p = b - c;
+         pc = a - c;
+
+#ifdef PNG_USE_ABS
+         pa = abs(p);
+         pb = abs(pc);
+         pc = abs(p + pc);
+#else
+         pa = p < 0 ? -p : p;
+         pb = pc < 0 ? -pc : pc;
+         pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+         p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
+
+         *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
+      }
+      best_row = png_ptr->paeth_row;
+   }
+
+   else if (filter_to_do & PNG_FILTER_PAETH)
+   {
+      png_bytep rp, dp, pp, cp, lp;
+      png_uint_32 sum = 0, lmins = mins;
+      png_uint_32 i;
+      int v;
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 lmhi, lmlo;
+         lmlo = lmins & PNG_LOMASK;
+         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)
+            {
+               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+            PNG_COST_SHIFT;
+         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+            PNG_COST_SHIFT;
+
+         if (lmhi > PNG_HIMASK)
+            lmins = PNG_MAXSUM;
+         else
+            lmins = (lmhi << PNG_HISHIFT) + lmlo;
+      }
+#endif
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
+           pp = prev_row + 1; i < bpp; i++)
+      {
+         v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+      }
+
+      for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
+      {
+         int a, b, c, pa, pb, pc, p;
+
+         b = *pp++;
+         c = *cp++;
+         a = *lp++;
+
+#ifndef PNG_SLOW_PAETH
+         p = b - c;
+         pc = a - c;
+#ifdef PNG_USE_ABS
+         pa = abs(p);
+         pb = abs(pc);
+         pc = abs(p + pc);
+#else
+         pa = p < 0 ? -p : p;
+         pb = pc < 0 ? -pc : pc;
+         pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+         p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
+#else /* PNG_SLOW_PAETH */
+         p = a + b - c;
+         pa = abs(p - a);
+         pb = abs(p - b);
+         pc = abs(p - c);
+         if (pa <= pb && pa <= pc)
+            p = a;
+         else if (pb <= pc)
+            p = b;
+         else
+            p = c;
+#endif /* PNG_SLOW_PAETH */
+
+         v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+
+         if (sum > lmins)  /* We are already worse, don't continue. */
+            break;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 sumhi, sumlo;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)
+            {
+               sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+
+      if (sum < mins)
+      {
+         best_row = png_ptr->paeth_row;
+      }
+   }
+
+   /* Do the actual writing of the filtered row data from the chosen filter. */
+
+   png_write_filtered_row(png_ptr, best_row);
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   /* Save the type of filter we picked this time for future calculations */
+   if (png_ptr->num_prev_filters > 0)
+   {
+      int j;
+      for (j = 1; j < num_p_filters; j++)
+      {
+         png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1];
+      }
+      png_ptr->prev_filters[j] = best_row[0];
+   }
+#endif
+}
+
+
+/* Do the actual writing of a previously filtered row. */
+void /* PRIVATE */
+png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row)
+{
+   png_debug(1, "in png_write_filtered_row\n");
+   png_debug1(2, "filter = %d\n", filtered_row[0]);
+   /* set up the zlib input buffer */
+
+   png_ptr->zstream.next_in = filtered_row;
+   png_ptr->zstream.avail_in = (uInt)png_ptr->row_info.rowbytes + 1;
+   /* repeat until we have compressed all the data */
+   do
+   {
+      int ret; /* return of zlib */
+
+      /* compress the data */
+      ret = deflate(&png_ptr->zstream, Z_NO_FLUSH);
+      /* check for compression errors */
+      if (ret != Z_OK)
+      {
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+
+      /* see if it is time to write another IDAT */
+      if (!(png_ptr->zstream.avail_out))
+      {
+         /* write the IDAT and reset the zlib output buffer */
+         png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+         png_ptr->zstream.next_out = png_ptr->zbuf;
+         png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+      }
+   /* repeat until all data has been compressed */
+   } while (png_ptr->zstream.avail_in);
+
+   /* swap the current and previous rows */
+   if (png_ptr->prev_row != NULL)
+   {
+      png_bytep tptr;
+
+      tptr = png_ptr->prev_row;
+      png_ptr->prev_row = png_ptr->row_buf;
+      png_ptr->row_buf = tptr;
+   }
+
+   /* finish row - updates counters and flushes zlib if last row */
+   png_write_finish_row(png_ptr);
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+   png_ptr->flush_rows++;
+
+   if (png_ptr->flush_dist > 0 &&
+       png_ptr->flush_rows >= png_ptr->flush_dist)
+   {
+      png_write_flush(png_ptr);
+   }
+#endif
+}
+#endif /* PNG_WRITE_SUPPORTED */
diff --git a/Utilities/GDAL/frmts/png/makefile.vc b/Utilities/GDAL/frmts/png/makefile.vc
new file mode 100644
index 0000000000..2d28082ae1
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/makefile.vc
@@ -0,0 +1,28 @@
+GDAL_ROOT	=	..\..
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+OBJ	=	\
+	pngdataset.obj
+
+
+#EXTRAFLAGS = 	-I..\zlib -Ilibpng
+!IFDEF PNG_EXTERNAL_LIB
+EXTRAFLAGS = 	-I..\zlib -I$(PNGDIR)
+!ELSE
+EXTRAFLAGS = 	-I..\zlib -Ilibpng
+!ENDIF
+
+default:	$(OBJ)
+	copy *.obj ..\o
+!IFNDEF PNG_EXTERNAL_LIB
+	cd libpng
+	$(MAKE) /f makefile.vc
+	cd ..
+!ENDIF
+
+clean:
+	-del *.obj
+	cd libpng
+	$(MAKE) /f makefile.vc clean
+	cd ..
+
diff --git a/Utilities/GDAL/frmts/png/pngdataset.cpp b/Utilities/GDAL/frmts/png/pngdataset.cpp
new file mode 100644
index 0000000000..18614853fa
--- /dev/null
+++ b/Utilities/GDAL/frmts/png/pngdataset.cpp
@@ -0,0 +1,1231 @@
+/******************************************************************************
+ * $Id: pngdataset.cpp,v 1.37 2006/04/06 18:54:51 fwarmerdam Exp $
+ *
+ * Project:  PNG Driver
+ * Purpose:  Implement GDAL PNG Support
+ * Author:   Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * ISSUES:
+ *  o CollectMetadata() will only capture TEXT chunks before the image 
+ *    data as the code is currently structured. 
+ *  o Interlaced images are read entirely into memory for use.  This is
+ *    bad for large images.
+ *  o Image reading is always strictly sequential.  Reading backwards will
+ *    cause the file to be rewound, and access started again from the
+ *    beginning. 
+ *  o 1, 2 and 4 bit data promoted to 8 bit. 
+ *  o Transparency values not currently read and applied to palette.
+ *  o 16 bit alpha values are not scaled by to eight bit. 
+ *  o I should install setjmp()/longjmp() based error trapping for PNG calls.
+ *    Currently a failure in png libraries will result in a complete
+ *    application termination. 
+ * 
+ * $Log: pngdataset.cpp,v $
+ * Revision 1.37  2006/04/06 18:54:51  fwarmerdam
+ * Added NBITS metadata item.
+ *
+ * Revision 1.36  2006/03/23 20:38:44  fwarmerdam
+ * Don't try to destroy NULL png struct.
+ * http://bugzilla.remotesensing.org/show_bug.cgi?id=1136
+ *
+ * Revision 1.35  2005/11/07 20:37:03  fwarmerdam
+ * use setjmp/longjump for error trapping
+ *
+ * Revision 1.34  2005/09/15 02:37:48  fwarmerdam
+ * added support NODATA_VALUES for RGB images (read and write)
+ *
+ * Revision 1.33  2005/09/12 18:55:10  fwarmerdam
+ * Fixed serious bug with reading multi-band 16bit files, and with
+ * writing 16bit files on LSB systems.
+ *
+ * Revision 1.32  2005/09/11 19:11:59  fwarmerdam
+ * Redirect IO through VSI.
+ *
+ * Revision 1.31  2005/05/17 19:05:52  fwarmerdam
+ * Allow flowthrough to pam for geotransform & nodata.
+ *
+ * Revision 1.30  2005/04/27 16:35:45  fwarmerdam
+ * PAM enable
+ *
+ * Revision 1.29  2004/08/26 21:20:08  warmerda
+ * Adjusted so png_access_version_number() is not called for old libpng
+ * versions (pre 1.2).
+ *
+ * Revision 1.28  2004/08/25 13:42:37  warmerda
+ * Added png version checking after png_create_read_struct() as per suggestion from
+ * Ben Discoe.
+ *
+ * Revision 1.27  2004/05/28 16:05:54  warmerda
+ * fix bug in bGeoTransformValid setting reading worldfiles
+ *
+ * Revision 1.26  2004/01/29 18:48:01  warmerda
+ * Changed to do the swapping ourseleves.  The png_set_swap() function didn't
+ * seem to be having the desired effect
+ *
+ * Revision 1.25  2004/01/29 14:55:26  warmerda
+ * ensure 16bit files are byte swapped as needed
+ *
+ * Revision 1.24  2003/09/15 20:45:00  warmerda
+ * add pngw and pgw support
+ */
+
+#include "gdal_pam.h"
+#include "png.h"
+#include "cpl_string.h"
+#include <setjmp.h>
+
+CPL_CVSID("$Id: pngdataset.cpp,v 1.37 2006/04/06 18:54:51 fwarmerdam Exp $");
+
+CPL_C_START
+void	GDALRegister_PNG(void);
+CPL_C_END
+
+static void
+png_vsi_read_data(png_structp png_ptr, png_bytep data, png_size_t length);
+
+static void
+png_vsi_write_data(png_structp png_ptr, png_bytep data, png_size_t length);
+
+static void png_vsi_flush(png_structp png_ptr);
+
+static void png_gdal_error( png_structp png_ptr, const char *error_message );
+static void png_gdal_warning( png_structp png_ptr, const char *error_message );
+
+/************************************************************************/
+/* ==================================================================== */
+/*				PNGDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class PNGRasterBand;
+
+class PNGDataset : public GDALPamDataset
+{
+    friend class PNGRasterBand;
+
+    FILE        *fpImage;
+    png_structp hPNG;
+    png_infop   psPNGInfo;
+    int         nBitDepth;
+    int         nColorType; /* PNG_COLOR_TYPE_* */
+    int         bInterlaced;
+
+    int         nBufferStartLine;
+    int         nBufferLines;
+    int         nLastLineRead;
+    GByte      *pabyBuffer;
+
+    GDALColorTable *poColorTable;
+
+    int	   bGeoTransformValid;
+    double adfGeoTransform[6];
+
+    int		bHaveNoData;
+    double 	dfNoDataValue;
+
+    void        CollectMetadata();
+    CPLErr      LoadScanline( int );
+    void        Restart();
+
+  public:
+                 PNGDataset();
+                 ~PNGDataset();
+
+    static GDALDataset *Open( GDALOpenInfo * );
+
+    virtual CPLErr GetGeoTransform( double * );
+    virtual void FlushCache( void );
+
+    // semi-private.
+    jmp_buf     sSetJmpContext;
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            PNGRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class PNGRasterBand : public GDALPamRasterBand
+{
+    friend class PNGDataset;
+
+  public:
+
+                   PNGRasterBand( PNGDataset *, int );
+
+    virtual CPLErr IReadBlock( int, int, void * );
+
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual GDALColorTable *GetColorTable();
+    virtual double GetNoDataValue( int *pbSuccess = NULL );
+};
+
+
+/************************************************************************/
+/*                           PNGRasterBand()                            */
+/************************************************************************/
+
+PNGRasterBand::PNGRasterBand( PNGDataset *poDS, int nBand )
+
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+
+    if( poDS->nBitDepth == 16 )
+        eDataType = GDT_UInt16;
+    else
+        eDataType = GDT_Byte;
+
+    nBlockXSize = poDS->nRasterXSize;;
+    nBlockYSize = 1;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr PNGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+
+{
+    PNGDataset	*poGDS = (PNGDataset *) poDS;
+    CPLErr      eErr;
+    GByte       *pabyScanline;
+    int         i, nPixelSize, nPixelOffset, nXSize = GetXSize();
+    
+    CPLAssert( nBlockXOff == 0 );
+
+    if( poGDS->nBitDepth == 16 )
+        nPixelSize = 2;
+    else
+        nPixelSize = 1;
+    nPixelOffset = poGDS->nBands * nPixelSize;
+
+/* -------------------------------------------------------------------- */
+/*      Load the desired scanline into the working buffer.              */
+/* -------------------------------------------------------------------- */
+    eErr = poGDS->LoadScanline( nBlockYOff );
+    if( eErr != CE_None )
+        return eErr;
+
+    pabyScanline = poGDS->pabyBuffer 
+        + (nBlockYOff - poGDS->nBufferStartLine) * nPixelOffset * nXSize
+        + nPixelSize * (nBand - 1);
+
+/* -------------------------------------------------------------------- */
+/*      Transfer between the working buffer the the callers buffer.     */
+/* -------------------------------------------------------------------- */
+    if( nPixelSize == nPixelOffset )
+        memcpy( pImage, pabyScanline, nPixelSize * nXSize );
+    else if( nPixelSize == 1 )
+    {
+        for( i = 0; i < nXSize; i++ )
+            ((GByte *) pImage)[i] = pabyScanline[i*nPixelOffset];
+    }
+    else 
+    {
+        CPLAssert( nPixelSize == 2 );
+        for( i = 0; i < nXSize; i++ )
+        {
+            ((GUInt16 *) pImage)[i] = 
+                *((GUInt16 *) (pabyScanline+i*nPixelOffset));
+        }
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp PNGRasterBand::GetColorInterpretation()
+
+{
+    PNGDataset	*poGDS = (PNGDataset *) poDS;
+
+    if( poGDS->nColorType == PNG_COLOR_TYPE_GRAY )
+        return GCI_GrayIndex;
+
+    else if( poGDS->nColorType == PNG_COLOR_TYPE_GRAY_ALPHA )
+    {
+        if( nBand == 1 )
+            return GCI_GrayIndex;
+        else
+            return GCI_AlphaBand;
+    }
+
+    else  if( poGDS->nColorType == PNG_COLOR_TYPE_PALETTE )
+        return GCI_PaletteIndex;
+
+    else  if( poGDS->nColorType == PNG_COLOR_TYPE_RGB
+              || poGDS->nColorType == PNG_COLOR_TYPE_RGB_ALPHA )
+    {
+        if( nBand == 1 )
+            return GCI_RedBand;
+        else if( nBand == 2 )
+            return GCI_GreenBand;
+        else if( nBand == 3 )
+            return GCI_BlueBand;
+        else 
+            return GCI_AlphaBand;
+    }
+    else
+        return GCI_GrayIndex;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *PNGRasterBand::GetColorTable()
+
+{
+    PNGDataset	*poGDS = (PNGDataset *) poDS;
+
+    if( nBand == 1 )
+        return poGDS->poColorTable;
+    else
+        return NULL;
+}
+
+/************************************************************************/
+/*                           GetNoDataValue()                           */
+/************************************************************************/
+
+double PNGRasterBand::GetNoDataValue( int *pbSuccess )
+
+{
+    PNGDataset *poPDS = (PNGDataset *) poDS;
+
+    if( poPDS->bHaveNoData )
+    {
+        if( pbSuccess != NULL )
+            *pbSuccess = poPDS->bHaveNoData;
+        return poPDS->dfNoDataValue;
+    }
+    else
+    {
+        return GDALPamRasterBand::GetNoDataValue( pbSuccess );
+    }
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             PNGDataset                               */
+/* ==================================================================== */
+/************************************************************************/
+
+
+/************************************************************************/
+/*                            PNGDataset()                            */
+/************************************************************************/
+
+PNGDataset::PNGDataset()
+
+{
+    hPNG = NULL;
+    psPNGInfo = NULL;
+    pabyBuffer = NULL;
+    nBufferStartLine = 0;
+    nBufferLines = 0;
+    nLastLineRead = -1;
+    poColorTable = NULL;
+
+    bGeoTransformValid = FALSE;
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+
+    bHaveNoData = FALSE;
+    dfNoDataValue = -1;
+}
+
+/************************************************************************/
+/*                           ~PNGDataset()                            */
+/************************************************************************/
+
+PNGDataset::~PNGDataset()
+
+{
+    FlushCache();
+
+    if( hPNG != NULL )
+        png_destroy_read_struct( &hPNG, &psPNGInfo, NULL );
+
+    if( fpImage )
+        VSIFCloseL( fpImage );
+
+    if( poColorTable != NULL )
+        delete poColorTable;
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr PNGDataset::GetGeoTransform( double * padfTransform )
+
+{
+
+    if( bGeoTransformValid )
+    {
+        memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
+        return CE_None;
+    }
+    else
+        return GDALPamDataset::GetGeoTransform( padfTransform );
+}
+
+/************************************************************************/
+/*                             FlushCache()                             */
+/*                                                                      */
+/*      We override this so we can also flush out local tiff strip      */
+/*      cache if need be.                                               */
+/************************************************************************/
+
+void PNGDataset::FlushCache()
+
+{
+    GDALPamDataset::FlushCache();
+
+    if( pabyBuffer != NULL )
+    {
+        CPLFree( pabyBuffer );
+        pabyBuffer = NULL;
+        nBufferStartLine = 0;
+        nBufferLines = 0;
+    }
+}
+
+/************************************************************************/
+/*                              Restart()                               */
+/*                                                                      */
+/*      Restart reading from the beginning of the file.                 */
+/************************************************************************/
+
+void PNGDataset::Restart()
+
+{
+    png_destroy_read_struct( &hPNG, &psPNGInfo, NULL );
+
+    hPNG = png_create_read_struct( PNG_LIBPNG_VER_STRING, this, NULL, NULL );
+
+    png_set_error_fn( hPNG, this, png_gdal_error, png_gdal_warning );
+    if( setjmp( sSetJmpContext ) != 0 )
+        return;
+
+    psPNGInfo = png_create_info_struct( hPNG );
+
+    VSIFSeekL( fpImage, 0, SEEK_SET );
+    png_set_read_fn( hPNG, fpImage, png_vsi_read_data );
+    png_read_info( hPNG, psPNGInfo );
+
+    if( nBitDepth < 8 )
+        png_set_packing( hPNG );
+
+    nLastLineRead = -1;
+}
+
+
+/************************************************************************/
+/*                            LoadScanline()                            */
+/************************************************************************/
+
+CPLErr PNGDataset::LoadScanline( int nLine )
+
+{
+    int   i;
+    int   nPixelOffset;
+
+    CPLAssert( nLine >= 0 && nLine < GetRasterYSize() );
+
+    if( nLine >= nBufferStartLine && nLine < nBufferStartLine + nBufferLines)
+        return CE_None;
+
+    if( nBitDepth == 16 )
+        nPixelOffset = 2 * GetRasterCount();
+    else
+        nPixelOffset = 1 * GetRasterCount();
+
+    if( setjmp( sSetJmpContext ) != 0 )
+        return CE_Failure;
+
+/* -------------------------------------------------------------------- */
+/*      If the file is interlaced, we will load the entire image        */
+/*      into memory using the high level API.                           */
+/* -------------------------------------------------------------------- */
+    if( bInterlaced )
+    {
+        png_bytep	*png_rows;
+        
+        CPLAssert( pabyBuffer == NULL );
+
+        if( nLastLineRead != -1 )
+        {
+            Restart();
+            if( setjmp( sSetJmpContext ) != 0 )
+                return CE_Failure;
+        }
+
+        nBufferStartLine = 0;
+        nBufferLines = GetRasterYSize();
+        pabyBuffer = (GByte *) 
+            VSIMalloc(nPixelOffset*GetRasterXSize()*GetRasterYSize());
+        
+        if( pabyBuffer == NULL )
+        {
+            CPLError( CE_Failure, CPLE_OutOfMemory, 
+                      "Unable to allocate buffer for whole interlaced PNG"
+                      "image of size %dx%d.\n", 
+                      GetRasterXSize(), GetRasterYSize() );
+            return CE_Failure;
+        }
+
+        png_rows = (png_bytep*)CPLMalloc(sizeof(png_bytep) * GetRasterYSize());
+        for( i = 0; i < GetRasterYSize(); i++ )
+            png_rows[i] = pabyBuffer + i * nPixelOffset * GetRasterXSize();
+
+        png_read_image( hPNG, png_rows );
+
+        CPLFree( png_rows );
+
+        nLastLineRead = GetRasterYSize() - 1;
+
+        return CE_None;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Ensure we have space allocated for one scanline                 */
+/* -------------------------------------------------------------------- */
+    if( pabyBuffer == NULL )
+        pabyBuffer = (GByte *) CPLMalloc(nPixelOffset * GetRasterXSize());
+
+/* -------------------------------------------------------------------- */
+/*      Otherwise we just try to read the requested row.  Do we need    */
+/*      to rewind and start over?                                       */
+/* -------------------------------------------------------------------- */
+    if( nLine <= nLastLineRead )
+    {
+        Restart();
+        if( setjmp( sSetJmpContext ) != 0 )
+            return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read till we get the desired row.                               */
+/* -------------------------------------------------------------------- */
+    png_bytep      row;
+
+    row = pabyBuffer;
+    while( nLine > nLastLineRead )
+    {
+        png_read_rows( hPNG, &row, NULL, 1 );
+        nLastLineRead++;
+    }
+
+    nBufferStartLine = nLine;
+    nBufferLines = 1;
+
+/* -------------------------------------------------------------------- */
+/*      Do swap on LSB machines.  16bit PNG data is stored in MSB       */
+/*      format.                                                         */
+/* -------------------------------------------------------------------- */
+#ifdef CPL_LSB
+    if( nBitDepth == 16 )
+        GDALSwapWords( row, 2, GetRasterXSize() * GetRasterCount(), 2 );
+#endif
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          CollectMetadata()                           */
+/*                                                                      */
+/*      We normally do this after reading up to the image, but be       */
+/*      forwarned ... we can missing text chunks this way.              */
+/*                                                                      */
+/*      We turn each PNG text chunk into one metadata item.  It         */
+/*      might be nice to preserve language information though we        */
+/*      don't try to now.                                               */
+/************************************************************************/
+
+void PNGDataset::CollectMetadata()
+
+{
+    int   nTextCount;
+    png_textp text_ptr;
+
+    if( nBitDepth < 8 )
+    {
+        for( int iBand = 0; iBand < nBands; iBand++ )
+        {
+            GetRasterBand(iBand+1)->SetMetadataItem( "NBITS", 
+                         CPLString().Printf( "%ld", nBitDepth ) );
+        }
+    }
+
+    if( png_get_text( hPNG, psPNGInfo, &text_ptr, &nTextCount ) == 0 )
+        return;
+
+    for( int iText = 0; iText < nTextCount; iText++ )
+    {
+        char	*pszTag = CPLStrdup(text_ptr[iText].key);
+
+        for( int i = 0; pszTag[i] != '\0'; i++ )
+        {
+            if( pszTag[i] == ' ' || pszTag[i] == '=' || pszTag[i] == ':' )
+                pszTag[i] = '_';
+        }
+
+        SetMetadataItem( pszTag, text_ptr[iText].text );
+        CPLFree( pszTag );
+    }
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *PNGDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+/* -------------------------------------------------------------------- */
+/*	First we check to see if the file has the expected header	*/
+/*	bytes.								*/    
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->nHeaderBytes < 4 )
+        return NULL;
+
+    if( png_sig_cmp(poOpenInfo->pabyHeader, (png_size_t)0, 
+                    poOpenInfo->nHeaderBytes) != 0 )
+        return NULL;
+
+    if( poOpenInfo->eAccess == GA_Update )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "The PNG driver does not support update access to existing"
+                  " datasets.\n" );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Open a file handle using large file API.                        */
+/* -------------------------------------------------------------------- */
+    FILE *fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
+    if( fp == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "Unexpected failure of VSIFOpenL(%s) in PNG Open()", 
+                  poOpenInfo->pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    PNGDataset 	*poDS;
+
+    poDS = new PNGDataset();
+
+    poDS->fpImage = fp;
+    poDS->eAccess = poOpenInfo->eAccess;
+    
+    poDS->hPNG = png_create_read_struct( PNG_LIBPNG_VER_STRING, poDS, 
+                                         NULL, NULL );
+    if (poDS->hPNG == NULL)
+    {
+#if LIBPNG_VER_MINOR >= 2 || LIBPNG_VER_MAJOR > 1
+        int version = png_access_version_number();
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "The PNG driver failed to access libpng with version '%s',"
+                  " library is actually version '%d'.\n",
+                  PNG_LIBPNG_VER_STRING, version);
+#else
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "The PNG driver failed to in png_create_read_struct().\n"
+                  "This may be due to version compatibility problems." );
+#endif
+        delete poDS;
+        return NULL;
+    }
+
+    poDS->psPNGInfo = png_create_info_struct( poDS->hPNG );
+
+/* -------------------------------------------------------------------- */
+/*      Setup error handling.                                           */
+/* -------------------------------------------------------------------- */
+    png_set_error_fn( poDS->hPNG, poDS, png_gdal_error, png_gdal_warning );
+
+    if( setjmp( poDS->sSetJmpContext ) != 0 )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*	Read pre-image data after ensuring the file is rewound.         */
+/* -------------------------------------------------------------------- */
+    /* we should likely do a setjmp() here */
+
+    png_set_read_fn( poDS->hPNG, poDS->fpImage, png_vsi_read_data );
+    png_read_info( poDS->hPNG, poDS->psPNGInfo );
+
+/* -------------------------------------------------------------------- */
+/*      Capture some information from the file that is of interest.     */
+/* -------------------------------------------------------------------- */
+    poDS->nRasterXSize = png_get_image_width( poDS->hPNG, poDS->psPNGInfo);
+    poDS->nRasterYSize = png_get_image_height( poDS->hPNG,poDS->psPNGInfo);
+
+    poDS->nBands = png_get_channels( poDS->hPNG, poDS->psPNGInfo );
+    poDS->nBitDepth = png_get_bit_depth( poDS->hPNG, poDS->psPNGInfo );
+    poDS->bInterlaced = png_get_interlace_type( poDS->hPNG, poDS->psPNGInfo ) 
+        != PNG_INTERLACE_NONE;
+
+    poDS->nColorType = png_get_color_type( poDS->hPNG, poDS->psPNGInfo );
+
+    if( poDS->nColorType == PNG_COLOR_TYPE_PALETTE 
+        && poDS->nBands > 1 )
+    {
+        CPLDebug( "GDAL", "PNG Driver got %d from png_get_channels(),\n"
+                  "but this kind of image (paletted) can only have one band.\n"
+                  "Correcting and continuing, but this may indicate a bug!",
+                  poDS->nBands );
+        poDS->nBands = 1;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      We want to treat 1,2,4 bit images as eight bit.  This call      */
+/*      causes libpng to unpack the image.                              */
+/* -------------------------------------------------------------------- */
+    if( poDS->nBitDepth < 8 )
+        png_set_packing( poDS->hPNG );
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    for( int iBand = 0; iBand < poDS->nBands; iBand++ )
+        poDS->SetBand( iBand+1, new PNGRasterBand( poDS, iBand+1 ) );
+
+/* -------------------------------------------------------------------- */
+/*      Is there a palette?  Note: we should also read back and         */
+/*      apply transparency values if available.                         */
+/* -------------------------------------------------------------------- */
+    if( poDS->nColorType == PNG_COLOR_TYPE_PALETTE )
+    {
+        png_color *pasPNGPalette;
+        int	nColorCount;
+        GDALColorEntry oEntry;
+        unsigned char *trans = NULL;
+        png_color_16 *trans_values = NULL;
+        int	num_trans = 0;
+        int	nNoDataIndex = -1;
+
+        if( png_get_PLTE( poDS->hPNG, poDS->psPNGInfo, 
+                          &pasPNGPalette, &nColorCount ) == 0 )
+            nColorCount = 0;
+
+        png_get_tRNS( poDS->hPNG, poDS->psPNGInfo, 
+                      &trans, &num_trans, &trans_values );
+
+        poDS->poColorTable = new GDALColorTable();
+
+        for( int iColor = nColorCount - 1; iColor >= 0; iColor-- )
+        {
+            oEntry.c1 = pasPNGPalette[iColor].red;
+            oEntry.c2 = pasPNGPalette[iColor].green;
+            oEntry.c3 = pasPNGPalette[iColor].blue;
+
+            if( iColor < num_trans )
+            {
+                oEntry.c4 = trans[iColor];
+                if( oEntry.c4 == 0 )
+                {
+                    if( nNoDataIndex == -1 )
+                        nNoDataIndex = iColor;
+                    else
+                        nNoDataIndex = -2;
+                }
+            }
+            else
+                oEntry.c4 = 255;
+
+            poDS->poColorTable->SetColorEntry( iColor, &oEntry );
+        }
+
+        /*
+        ** Special hack to an index as the no data value, as long as it
+        ** is the _only_ transparent color in the palette.
+        */
+        if( nNoDataIndex > -1 )
+        {
+            poDS->bHaveNoData = TRUE;
+            poDS->dfNoDataValue = nNoDataIndex;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for transparency values in greyscale images.              */
+/* -------------------------------------------------------------------- */
+    if( poDS->nColorType == PNG_COLOR_TYPE_GRAY )
+    {
+        png_color_16 *trans_values = NULL;
+        unsigned char *trans;
+        int num_trans;
+
+        if( png_get_tRNS( poDS->hPNG, poDS->psPNGInfo, 
+                          &trans, &num_trans, &trans_values ) != 0 
+            && trans_values != NULL )
+        {
+            poDS->bHaveNoData = TRUE;
+            poDS->dfNoDataValue = trans_values->gray;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for nodata color for RGB images.                          */
+/* -------------------------------------------------------------------- */
+    if( poDS->nColorType == PNG_COLOR_TYPE_RGB ) 
+    {
+        png_color_16 *trans_values = NULL;
+        unsigned char *trans;
+        int num_trans;
+
+        if( png_get_tRNS( poDS->hPNG, poDS->psPNGInfo, 
+                          &trans, &num_trans, &trans_values ) != 0 
+            && trans_values != NULL )
+        {
+            CPLString oNDValue;
+
+            oNDValue.Printf( "%d %d %d", 
+                             trans_values->red, 
+                             trans_values->green, 
+                             trans_values->blue );
+            poDS->SetMetadataItem( "NODATA_VALUES", oNDValue.c_str() );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Extract any text chunks as "metadata".                          */
+/* -------------------------------------------------------------------- */
+    poDS->CollectMetadata();
+
+/* -------------------------------------------------------------------- */
+/*      Open overviews.                                                 */
+/* -------------------------------------------------------------------- */
+    poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+/* -------------------------------------------------------------------- */
+/*      Check for world file.                                           */
+/* -------------------------------------------------------------------- */
+    poDS->bGeoTransformValid = 
+        GDALReadWorldFile( poOpenInfo->pszFilename, NULL, 
+                           poDS->adfGeoTransform );
+
+    if( !poDS->bGeoTransformValid )
+        poDS->bGeoTransformValid = 
+            GDALReadWorldFile( poOpenInfo->pszFilename, ".wld", 
+                               poDS->adfGeoTransform );
+
+    if( !poDS->bGeoTransformValid )
+        poDS->bGeoTransformValid = 
+            GDALReadWorldFile( poOpenInfo->pszFilename, ".tfw", 
+                               poDS->adfGeoTransform );
+    if( !poDS->bGeoTransformValid )
+        poDS->bGeoTransformValid = 
+            GDALReadWorldFile( poOpenInfo->pszFilename, ".tifw", 
+                               poDS->adfGeoTransform );
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                           PNGCreateCopy()                            */
+/************************************************************************/
+
+static GDALDataset *
+PNGCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, 
+               int bStrict, char ** papszOptions, 
+               GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    int  nBands = poSrcDS->GetRasterCount();
+    int  nXSize = poSrcDS->GetRasterXSize();
+    int  nYSize = poSrcDS->GetRasterYSize();
+
+/* -------------------------------------------------------------------- */
+/*      Some some rudimentary checks                                    */
+/* -------------------------------------------------------------------- */
+    if( nBands != 1 && nBands != 2 && nBands != 3 && nBands != 4 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "PNG driver doesn't support %d bands.  Must be 1 (grey),\n"
+                  "2 (grey+alpha), 3 (rgb) or 4 (rgba) bands.\n", 
+                  nBands );
+
+        return NULL;
+    }
+
+    if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte 
+        && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16
+        && bStrict )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "PNG driver doesn't support data type %s. "
+                  "Only eight bit (Byte) and sixteen bit (UInt16) bands supported.\n", 
+                  GDALGetDataTypeName( 
+                      poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
+
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Setup some parameters.                                          */
+/* -------------------------------------------------------------------- */
+    int  nColorType=0, nBitDepth;
+    GDALDataType eType;
+
+    if( nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() == NULL )
+        nColorType = PNG_COLOR_TYPE_GRAY;
+    else if( nBands == 1 )
+        nColorType = PNG_COLOR_TYPE_PALETTE;
+    else if( nBands == 2 )
+        nColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
+    else if( nBands == 3 )
+        nColorType = PNG_COLOR_TYPE_RGB;
+    else if( nBands == 4 )
+        nColorType = PNG_COLOR_TYPE_RGB_ALPHA;
+
+    if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16 )
+    {
+        eType = GDT_Byte;
+        nBitDepth = 8;
+    }
+    else 
+    {
+        eType = GDT_UInt16;
+        nBitDepth = 16;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create the dataset.                                             */
+/* -------------------------------------------------------------------- */
+    FILE	*fpImage;
+
+    fpImage = VSIFOpenL( pszFilename, "wb" );
+    if( fpImage == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "Unable to create png file %s.\n", 
+                  pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize PNG access to the file.                              */
+/* -------------------------------------------------------------------- */
+    png_structp hPNG;
+    png_infop   psPNGInfo;
+    
+    hPNG = png_create_write_struct( PNG_LIBPNG_VER_STRING, 
+                                    NULL, NULL, NULL );
+    psPNGInfo = png_create_info_struct( hPNG );
+    
+    png_set_write_fn( hPNG, fpImage, png_vsi_write_data, png_vsi_flush );
+
+    png_set_IHDR( hPNG, psPNGInfo, nXSize, nYSize, 
+                  nBitDepth, nColorType, PNG_INTERLACE_NONE, 
+                  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE );
+
+/* -------------------------------------------------------------------- */
+/*      Try to handle nodata values as a tRNS block (note for           */
+/*      paletted images, we save the effect to apply as part of         */
+/*      palette).                                                       */
+/* -------------------------------------------------------------------- */
+    int		bHaveNoData = FALSE;
+    double	dfNoDataValue = -1;
+    png_color_16 sTRNSColor;
+
+    dfNoDataValue = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bHaveNoData );
+
+    if( (nColorType == PNG_COLOR_TYPE_GRAY )
+        && dfNoDataValue > 0 && dfNoDataValue < 65536 )
+    {
+        sTRNSColor.gray = (png_uint_16) dfNoDataValue;
+        png_set_tRNS( hPNG, psPNGInfo, NULL, 0, &sTRNSColor );
+    }
+
+    // RGB case.
+    if( nColorType == PNG_COLOR_TYPE_RGB 
+        && poSrcDS->GetMetadataItem( "NODATA_VALUES" ) != NULL )
+    {
+        char **papszValues = CSLTokenizeString(
+            poSrcDS->GetMetadataItem( "NODATA_VALUES" ) );
+        
+        if( CSLCount(papszValues) >= 3 )
+        {
+            sTRNSColor.red   = (png_uint_16) atoi(papszValues[0]);
+            sTRNSColor.green = (png_uint_16) atoi(papszValues[1]);
+            sTRNSColor.blue  = (png_uint_16) atoi(papszValues[2]);
+            png_set_tRNS( hPNG, psPNGInfo, NULL, 0, &sTRNSColor );
+        }
+
+        CSLDestroy( papszValues );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Write palette if there is one.  Technically, I think it is      */
+/*      possible to write 16bit palettes for PNG, but we will omit      */
+/*      this for now.                                                   */
+/* -------------------------------------------------------------------- */
+    png_color	*pasPNGColors = NULL;
+    unsigned char	*pabyAlpha = NULL;
+
+    if( nColorType == PNG_COLOR_TYPE_PALETTE )
+    {
+        GDALColorTable	*poCT;
+        GDALColorEntry  sEntry;
+        int		iColor, bFoundTrans = FALSE;
+
+        poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
+
+        pasPNGColors = (png_color *) CPLMalloc(sizeof(png_color) *
+                                               poCT->GetColorEntryCount());
+
+        for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ )
+        {
+            poCT->GetColorEntryAsRGB( iColor, &sEntry );
+            if( sEntry.c4 != 255 )
+                bFoundTrans = TRUE;
+
+            pasPNGColors[iColor].red = (png_byte) sEntry.c1;
+            pasPNGColors[iColor].green = (png_byte) sEntry.c2;
+            pasPNGColors[iColor].blue = (png_byte) sEntry.c3;
+        }
+        
+        png_set_PLTE( hPNG, psPNGInfo, pasPNGColors, 
+                      poCT->GetColorEntryCount() );
+
+/* -------------------------------------------------------------------- */
+/*      If we have transparent elements in the palette we need to       */
+/*      write a transparency block.                                     */
+/* -------------------------------------------------------------------- */
+        if( bFoundTrans || bHaveNoData )
+        {
+
+            pabyAlpha = (unsigned char *)CPLMalloc(poCT->GetColorEntryCount());
+
+            for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ )
+            {
+                poCT->GetColorEntryAsRGB( iColor, &sEntry );
+                pabyAlpha[iColor] = (unsigned char) sEntry.c4;
+
+                if( bHaveNoData && iColor == (int) dfNoDataValue )
+                    pabyAlpha[iColor] = 0;
+            }
+
+            png_set_tRNS( hPNG, psPNGInfo, pabyAlpha, 
+                          poCT->GetColorEntryCount(), NULL );
+        }
+    }
+
+    png_write_info( hPNG, psPNGInfo );
+
+/* -------------------------------------------------------------------- */
+/*      Loop over image, copying image data.                            */
+/* -------------------------------------------------------------------- */
+    GByte 	*pabyScanline;
+    CPLErr      eErr;
+    int         nWordSize = nBitDepth/8;
+
+    pabyScanline = (GByte *) CPLMalloc( nBands * nXSize * nWordSize );
+
+    for( int iLine = 0; iLine < nYSize; iLine++ )
+    {
+        png_bytep       row = pabyScanline;
+
+        for( int iBand = 0; iBand < nBands; iBand++ )
+        {
+            GDALRasterBand * poBand = poSrcDS->GetRasterBand( iBand+1 );
+            eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, 
+                                     pabyScanline + iBand*nWordSize, 
+                                     nXSize, 1, eType,
+                                     nBands * nWordSize, 
+                                     nBands * nXSize * nWordSize );
+        }
+
+#ifdef CPL_LSB
+        if( nBitDepth == 16 )
+            GDALSwapWords( row, 2, nXSize * nBands, 2 );
+#endif
+        png_write_rows( hPNG, &row, 1 );
+    }
+
+    CPLFree( pabyScanline );
+
+    png_write_end( hPNG, psPNGInfo );
+    png_destroy_write_struct( &hPNG, &psPNGInfo );
+
+    VSIFCloseL( fpImage );
+
+    CPLFree( pabyAlpha );
+    CPLFree( pasPNGColors );
+
+/* -------------------------------------------------------------------- */
+/*      Do we need a world file?                                          */
+/* -------------------------------------------------------------------- */
+    if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) )
+    {
+    	double      adfGeoTransform[6];
+	
+	poSrcDS->GetGeoTransform( adfGeoTransform );
+	GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Re-open dataset, and copy any auxilary pam information.         */
+/* -------------------------------------------------------------------- */
+    PNGDataset *poDS = (PNGDataset *) GDALOpen( pszFilename, GA_ReadOnly );
+
+    if( poDS )
+        poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                         png_vsi_read_data()                          */
+/*                                                                      */
+/*      Read data callback through VSI.                                 */
+/************************************************************************/
+static void
+png_vsi_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+
+{
+   png_size_t check;
+
+   /* fread() returns 0 on error, so it is OK to store this in a png_size_t
+    * instead of an int, which is what fread() actually returns.
+    */
+   check = (png_size_t)VSIFReadL(data, (png_size_t)1, length,
+                                 (png_FILE_p)png_ptr->io_ptr);
+
+   if (check != length)
+      png_error(png_ptr, "Read Error");
+}
+
+/************************************************************************/
+/*                         png_vsi_write_data()                         */
+/************************************************************************/
+
+static void
+png_vsi_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_uint_32 check;
+
+   check = VSIFWriteL(data, 1, length, (png_FILE_p)(png_ptr->io_ptr));
+
+   if (check != length)
+      png_error(png_ptr, "Write Error");
+}
+
+/************************************************************************/
+/*                           png_vsi_flush()                            */
+/************************************************************************/
+static void png_vsi_flush(png_structp png_ptr)
+{
+    VSIFFlushL( (png_FILE_p)(png_ptr->io_ptr) );
+}
+
+/************************************************************************/
+/*                           png_gdal_error()                           */
+/************************************************************************/
+
+static void png_gdal_error( png_structp png_ptr, const char *error_message )
+{
+    CPLError( CE_Failure, CPLE_AppDefined, 
+              "libpng: %s", error_message );
+
+    // We have to use longjmp instead of a C++ exception because 
+    // libpng is generally not built as C++ and so won't honour unwind
+    // semantics.  Ugg. 
+
+    PNGDataset *poDS = (PNGDataset *) png_ptr->error_ptr;
+
+    longjmp( poDS->sSetJmpContext, 1 );
+}
+
+/************************************************************************/
+/*                          png_gdal_warning()                          */
+/************************************************************************/
+
+static void png_gdal_warning( png_structp png_ptr, const char *error_message )
+{
+    CPLError( CE_Warning, CPLE_AppDefined, 
+              "libpng: %s", error_message );
+}
+
+/************************************************************************/
+/*                          GDALRegister_PNG()                        */
+/************************************************************************/
+
+void GDALRegister_PNG()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "PNG" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "PNG" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "Portable Network Graphics" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_various.html#PNG" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "png" );
+        poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/png" );
+
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte UInt16" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, 
+"<CreationOptionList>\n"
+"   <Option name='WORLDFILE' type='boolean' description='Create world file'/>\n"
+"</CreationOptionList>\n" );
+
+        poDriver->pfnOpen = PNGDataset::Open;
+        poDriver->pfnCreateCopy = PNGCreateCopy;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/rik/GNUmakefile b/Utilities/GDAL/frmts/rik/GNUmakefile
new file mode 100644
index 0000000000..4feabca28e
--- /dev/null
+++ b/Utilities/GDAL/frmts/rik/GNUmakefile
@@ -0,0 +1,19 @@
+
+include ../../GDALmake.opt
+
+ifeq ($(LIBZ_SETTING),internal)
+XTRA_OPT =	-I../zlib
+else
+XTRA_OPT =
+endif
+
+OBJ	=	rikdataset.o
+
+CPPFLAGS	:=	$(XTRA_OPT) $(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/rik/frmt_rik.html b/Utilities/GDAL/frmts/rik/frmt_rik.html
new file mode 100644
index 0000000000..ce9172a1e5
--- /dev/null
+++ b/Utilities/GDAL/frmts/rik/frmt_rik.html
@@ -0,0 +1,20 @@
+<html>
+<head>
+<title>RIK -- Swedish Grid Maps</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>RIK -- Swedish Grid Maps</h1>
+
+Supported by GDAL for read access. This format is used in maps issued by the
+swedish organization Lantm�teriet. Supports versions 1, 2 and 3 of the RIK
+format, but only 8 bits per pixel.<p>
+
+This driver is based on the work done in the
+<a href="http://sourceforge.net/projects/trikpanel/">TRikPanel</a> project.<p>
+
+NOTE: Implemented as <tt>gdal/frmts/rik/rikdataset.cpp</tt>.<p>
+
+</body>
+</html>
diff --git a/Utilities/GDAL/frmts/rik/makefile.vc b/Utilities/GDAL/frmts/rik/makefile.vc
new file mode 100644
index 0000000000..c58ea1397f
--- /dev/null
+++ b/Utilities/GDAL/frmts/rik/makefile.vc
@@ -0,0 +1,15 @@
+
+OBJ	=	rikdataset.obj
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+EXTRAFLAGS = 	-I..\zlib
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/rik/rikdataset.cpp b/Utilities/GDAL/frmts/rik/rikdataset.cpp
new file mode 100644
index 0000000000..54716ccfc1
--- /dev/null
+++ b/Utilities/GDAL/frmts/rik/rikdataset.cpp
@@ -0,0 +1,1205 @@
+/******************************************************************************
+ * $Id: rikdataset.cpp,v 1.15 2006/01/26 17:20:01 fwarmerdam Exp $
+ *
+ * Project:  RIK Reader
+ * Purpose:  All code for RIK Reader
+ * Author:   Daniel Wallner, daniel.wallner@bredband.net
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Daniel Wallner <daniel.wallner@bredband.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: rikdataset.cpp,v $
+ * Revision 1.15  2006/01/26 17:20:01  fwarmerdam
+ * Changed to include <zlib.h> instead of "zlib.h" as suggested by
+ * Markus N.
+ *
+ * Revision 1.14  2005/11/15 01:19:30  dwallner
+ * Allow zero block offsets
+ *
+ * Revision 1.13  2005/09/20 12:26:59  dwallner
+ * added missing TOWGS84 in wkt
+ *
+ * Revision 1.12  2005/09/11 04:12:42  fwarmerdam
+ * The large file API can't be used on poOpenInfo->fp which is produced
+ * by the small file API.
+ *
+ * Revision 1.11  2005/09/09 04:54:21  fwarmerdam
+ * avoid use of dynamic array sizes, fails on VC6
+ *
+ * Revision 1.10  2005/09/06 21:58:11  dwallner
+ * zlib/rik3 support
+ *
+ * Revision 1.9  2005/09/06 03:06:01  dwallner
+ * lzw image distortion resolved
+ *
+ * Revision 1.8  2005/09/06 02:01:58  dwallner
+ * lzw read alignment problems fixed
+ *
+ * Revision 1.7  2005/09/04 09:43:03  dwallner
+ * recognize all compression codes
+ *
+ * Revision 1.6  2005/08/25 22:36:30  dwallner
+ * print header type in debug output
+ *
+ * Revision 1.5  2005/08/25 21:35:03  dwallner
+ * GetProjectionRef was completely wrong
+ *
+ * Revision 1.4  2005/08/18 21:06:52  dwallner
+ * RIK3 header support
+ *
+ * Revision 1.3  2005/08/17 16:11:18  dwallner
+ * old and new ANSI compability fix
+ *
+ * Revision 1.2  2005/08/17 15:44:23  fwarmerdam
+ * patch up for win32/vc6 compatibility
+ *
+ * Revision 1.1  2005/08/17 07:20:55  dwallner
+ * Import
+ *
+ *
+ */
+
+#include <float.h>
+#include <zlib.h>
+#include "gdal_pam.h"
+
+CPL_CVSID("$Id: rikdataset.cpp,v 1.15 2006/01/26 17:20:01 fwarmerdam Exp $");
+
+CPL_C_START
+void	GDALRegister_RIK(void);
+CPL_C_END
+
+#define RIK_HEADER_DEBUG 0
+#define RIK_CLEAR_DEBUG 0
+#define RIK_PIXEL_DEBUG 0
+
+//#define RIK_SINGLE_BLOCK 0
+
+#define RIK_ALLOW_BLOCK_ERRORS 1
+
+//
+// The RIK file format information was extracted from the trikpanel project:
+// http://sourceforge.net/projects/trikpanel/
+//
+// A RIK file consists of the following elements:
+//
+// +--------------------+
+// | Magic "RIK3"       | (Only in RIK version 3)
+// +--------------------+
+// | Map name           | (The first two bytes is the string length)
+// +--------------------+
+// | Header             | (Three different formats exists)
+// +--------------------+
+// | Color palette      |
+// +--------------------+
+// | Block offset array | (Only in compressed formats)
+// +--------------------+
+// | Image blocks       |
+// +--------------------+
+//
+// All numbers are stored in little endian.
+//
+// There are four different image block formats:
+//
+// 1. Uncompressed image block
+//
+//   A stream of palette indexes.
+//
+// 2. RLE image block
+//
+//   The RLE image block is a stream of byte pairs:
+//   |  Run length - 1 (byte)  |  Pixel value (byte)  |  Run length - 1 ...
+//
+// 3. LZW image block
+//
+//   The LZW image block uses the same LZW encoding as a GIF file
+//   except that there is no EOF code and maximum code length is 13 bits.
+//   These blocks are upside down compared to GDAL.
+//
+// 4. ZLIB image block
+//
+//   These blocks are upside down compared to GDAL.
+//
+
+typedef struct
+{
+    GUInt16     iUnknown;
+    double      fSouth;         // Map bounds
+    double      fWest;
+    double      fNorth;
+    double      fEast;
+    GUInt32     iScale;         // Source map scale
+    float       iMPPNum;        // Meters per pixel numerator
+    GUInt32     iMPPDen;        // Meters per pixel denominator
+                                // Only used if fSouth < 4000000
+    GUInt32     iBlockWidth;
+    GUInt32     iBlockHeight;
+    GUInt32     iHorBlocks;     // Number of horizontal blocks
+    GUInt32     iVertBlocks;    // Number of vertical blocks
+                                // Only used if fSouth >= 4000000
+    GByte       iBitsPerPixel;
+    GByte       iOptions;
+} RIKHeader;
+
+/************************************************************************/
+/* ==================================================================== */
+/*				RIKDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class RIKRasterBand;
+
+class RIKDataset : public GDALPamDataset
+{
+    friend class RIKRasterBand;
+
+    FILE        *fp;
+
+    double      fTransform[6];
+
+    GUInt32     nBlockXSize;
+    GUInt32     nBlockYSize;
+    GUInt32     nHorBlocks;
+    GUInt32     nVertBlocks;
+    GUInt32     nFileSize;
+    GUInt32     *pOffsets;
+    GByte       options;
+
+    GDALColorTable *poColorTable;
+
+  public:
+    ~RIKDataset();
+
+    static GDALDataset *Open( GDALOpenInfo * );
+
+    CPLErr 	GetGeoTransform( double * padfTransform );
+    const char *GetProjectionRef();
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            RIKRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class RIKRasterBand : public GDALPamRasterBand
+{
+    friend class RIKDataset;
+
+  public:
+
+    RIKRasterBand( RIKDataset *, int );
+
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual GDALColorTable *GetColorTable();
+};
+
+/************************************************************************/
+/*                           RIKRasterBand()                            */
+/************************************************************************/
+
+RIKRasterBand::RIKRasterBand( RIKDataset *poDS, int nBand )
+
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+
+    eDataType = GDT_Byte;
+
+    nBlockXSize = poDS->nBlockXSize;
+    nBlockYSize = poDS->nBlockYSize;
+}
+
+/************************************************************************/
+/*                             GetNextLZWCode()                         */
+/************************************************************************/
+
+static int GetNextLZWCode( int codeBits,
+                           GByte *blockData,
+                           GUInt32 &filePos,
+                           GUInt32 &fileAlign,
+                           int &bitsTaken )
+
+{
+    if( filePos == fileAlign )
+    {
+        fileAlign += codeBits;
+    }
+
+    const int BitMask[] = {
+        0x0000, 0x0001, 0x0003, 0x0007,
+        0x000f, 0x001f, 0x003f, 0x007f };
+
+    int ret = 0;
+    int bitsLeftToGo = codeBits;
+
+    while( bitsLeftToGo > 0 )
+    {
+        int tmp;
+
+        tmp = blockData[filePos];
+        tmp = tmp >> bitsTaken;
+
+        if( bitsLeftToGo < 8 )
+            tmp &= BitMask[bitsLeftToGo];
+
+        tmp = tmp << (codeBits - bitsLeftToGo);
+
+        ret |= tmp;
+
+        bitsLeftToGo -= (8 - bitsTaken);
+        bitsTaken = 0;
+
+        if( bitsLeftToGo < 0 )
+            bitsTaken = 8 + bitsLeftToGo;
+
+        if( bitsTaken == 0 )
+            filePos++;
+    }
+
+#if RIK_PIXEL_DEBUG
+    printf( "c%03X\n", ret );
+#endif
+
+    return ret;
+}
+
+/************************************************************************/
+/*                             OutputPixel()                            */
+/************************************************************************/
+
+static void OutputPixel( GByte pixel,
+                         void * image,
+                         GUInt32 imageWidth,
+                         GUInt32 lineBreak,
+                         int &imageLine,
+                         GUInt32 &imagePos )
+
+{
+    if( imagePos < imageWidth && imageLine >= 0)
+        ((GByte *) image)[imagePos + imageLine * imageWidth] = pixel;
+
+    imagePos++;
+
+#if RIK_PIXEL_DEBUG
+    printf( "_%02X %d\n", pixel, imagePos );
+#endif
+
+    // Check if we need to change line
+
+    if( imagePos == lineBreak )
+    {
+#if RIK_PIXEL_DEBUG
+        printf( "\n%d\n", imageLine );
+#endif
+
+        imagePos = 0;
+
+        imageLine--;
+    }
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr RIKRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+
+{
+    RIKDataset *poRDS = (RIKDataset *) poDS;
+    GByte *blockData;
+    GUInt32 blocks;
+    GUInt32 nBlockIndex;
+    GUInt32 nBlockOffset;
+    GUInt32 nBlockSize;
+
+    blocks = poRDS->nHorBlocks * poRDS->nVertBlocks;
+    nBlockIndex = nBlockXOff + nBlockYOff * poRDS->nHorBlocks;
+    nBlockOffset = poRDS->pOffsets[nBlockIndex];
+
+    nBlockSize = poRDS->nFileSize;
+    for( GUInt32 bi = nBlockIndex + 1; bi < blocks; bi++ )
+    {
+        if( poRDS->pOffsets[bi] )
+        {
+            nBlockSize = poRDS->pOffsets[bi];
+            break;
+        }
+    }
+    nBlockSize -= nBlockOffset;
+
+    GUInt32 pixels;
+
+    pixels = poRDS->nBlockXSize * poRDS->nBlockYSize;
+
+    if( !nBlockOffset || !nBlockSize
+#ifdef RIK_SINGLE_BLOCK
+        || nBlockIndex != RIK_SINGLE_BLOCK
+#endif
+        )
+    {
+        for( GUInt32 i = 0; i < pixels; i++ )
+        ((GByte *) pImage)[i] = 0;
+        return CE_None;
+    }
+
+    VSIFSeek( poRDS->fp, nBlockOffset, SEEK_SET );
+
+/* -------------------------------------------------------------------- */
+/*      Read uncompressed block.                                        */
+/* -------------------------------------------------------------------- */
+
+    if( poRDS->options == 0x00 || poRDS->options == 0x40 )
+    {
+        VSIFRead( pImage, 1, nBlockSize, poRDS->fp );
+        return CE_None;
+    }
+
+    // Read block to memory
+    blockData = (GByte *) CPLMalloc(nBlockSize);
+    VSIFRead( blockData, 1, nBlockSize, poRDS->fp );
+
+    GUInt32 filePos = 0;
+    GUInt32 imagePos = 0;
+
+/* -------------------------------------------------------------------- */
+/*      Read RLE block.                                                 */
+/* -------------------------------------------------------------------- */
+
+    if( poRDS->options == 0x01 ||
+        poRDS->options == 0x41 ) do
+    {
+        GByte count = blockData[filePos++];
+        GByte color = blockData[filePos++];
+
+        for (GByte i = 0; i <= count; i++)
+        {
+            ((GByte *) pImage)[imagePos++] = color;
+        }
+    } while( filePos < nBlockSize && imagePos < pixels );
+
+/* -------------------------------------------------------------------- */
+/*      Read LZW block.                                                 */
+/* -------------------------------------------------------------------- */
+
+    else if( poRDS->options == 0x0b )
+    {
+        const bool LZW_HAS_CLEAR_CODE = !!(blockData[4] & 0x80);
+        const int LZW_MAX_BITS = blockData[4] & 0x1f; // Max 13
+        const int LZW_BITS_PER_PIXEL = 8;
+        const int LZW_OFFSET = 5;
+
+        const int LZW_CLEAR = 1 << LZW_BITS_PER_PIXEL;
+        const int LZW_CODES = 1 << LZW_MAX_BITS;
+        const int LZW_NO_SUCH_CODE = LZW_CODES + 1;
+
+        int lastAdded = LZW_HAS_CLEAR_CODE ? LZW_CLEAR : LZW_CLEAR - 1;
+        int codeBits = LZW_BITS_PER_PIXEL + 1;
+
+        int code;
+        int lastCode;
+        GByte lastOutput;
+        int bitsTaken = 0;
+
+        int prefix[8192];      // only need LZW_CODES for size.
+        GByte character[8192]; // only need LZW_CODES for size.
+
+        int i;
+
+        for( i = 0; i < LZW_CLEAR; i++ )
+            character[i] = i;
+        for( i = 0; i < LZW_CODES; i++ )
+            prefix[i] = LZW_NO_SUCH_CODE;
+
+        filePos = LZW_OFFSET;
+        GUInt32 fileAlign = LZW_OFFSET;
+        int imageLine = poRDS->nBlockYSize - 1;
+
+        GUInt32 lineBreak = poRDS->nBlockXSize;
+
+        // 32 bit alignment
+        lineBreak += 3;
+        lineBreak &= 0xfffffffc;
+
+        code = GetNextLZWCode( codeBits, blockData, filePos,
+                               fileAlign, bitsTaken );
+
+        OutputPixel( code, pImage, poRDS->nBlockXSize,
+                     lineBreak, imageLine, imagePos );
+        lastOutput = code;
+
+        while( imageLine >= 0 &&
+               (imageLine || imagePos < poRDS->nBlockXSize - 1) &&
+               filePos < nBlockSize ) try
+        {
+            lastCode = code;
+            code = GetNextLZWCode( codeBits, blockData,
+                                   filePos, fileAlign, bitsTaken );
+            if( VSIFEof( poRDS->fp ) )
+            {
+                CPLFree( blockData );
+                CPLError( CE_Failure, CPLE_AppDefined,
+                          "RIK decompression failed. "
+                          "Read past end of file.\n" );
+                return CE_Failure;
+            }
+
+            if( LZW_HAS_CLEAR_CODE && code == LZW_CLEAR )
+            {
+#if RIK_CLEAR_DEBUG
+                CPLDebug( "RIK",
+                          "Clearing block %d\n"
+                          " x=%d y=%d\n"
+                          " pos=%d size=%d\n",
+                          nBlockIndex,
+                          imagePos, imageLine,
+                          filePos, nBlockSize );
+#endif
+
+                // Clear prefix table
+                for( i = LZW_CLEAR; i < LZW_CODES; i++ )
+                    prefix[i] = LZW_NO_SUCH_CODE;
+                lastAdded = LZW_CLEAR;
+                codeBits = LZW_BITS_PER_PIXEL + 1;
+
+                filePos = fileAlign;
+                bitsTaken = 0;
+
+                code = GetNextLZWCode( codeBits, blockData,
+                                       filePos, fileAlign, bitsTaken );
+
+                if( code > lastAdded )
+                {
+                    throw "Clear Error";
+                }
+
+                OutputPixel( code, pImage, poRDS->nBlockXSize,
+                             lineBreak, imageLine, imagePos );
+                lastOutput = code;
+            }
+            else
+            {
+                // Set-up decoding
+
+                GByte stack[8192]; // only need LZW_CODES for size.
+
+                int stackPtr = 0;
+                int decodeCode = code;
+
+                if( code == lastAdded + 1 )
+                {
+                    // Handle special case
+                    *stack = lastOutput;
+                    stackPtr = 1;
+                    decodeCode = lastCode;
+       	        }
+                else if( code > lastAdded + 1 )
+                {
+                    throw "Too high code";
+                }
+
+                // Decode
+
+                i = 0;
+                while( ++i < LZW_CODES &&
+       	               decodeCode >= LZW_CLEAR &&
+       	               decodeCode < LZW_NO_SUCH_CODE )
+                {
+                    stack[stackPtr++] = character[decodeCode];
+                    decodeCode = prefix[decodeCode];
+                }
+                stack[stackPtr++] = decodeCode;
+
+                if( i == LZW_CODES || decodeCode >= LZW_NO_SUCH_CODE )
+                {
+                    throw "Decode error";
+                }
+
+                // Output stack
+
+                lastOutput = stack[stackPtr - 1];
+
+                while( stackPtr != 0 && imagePos < pixels )
+                {
+                    OutputPixel( stack[--stackPtr], pImage, poRDS->nBlockXSize,
+                                 lineBreak, imageLine, imagePos );
+                }
+
+                // Add code to string table
+
+                if( lastCode != LZW_NO_SUCH_CODE &&
+                    lastAdded != LZW_CODES - 1 )
+                {
+                    prefix[++lastAdded] = lastCode;
+                    character[lastAdded] = lastOutput;
+                }
+
+                // Check if we need to use more bits
+
+                if( lastAdded == (1 << codeBits) - 1 &&
+                    codeBits != LZW_MAX_BITS )
+                {
+                     codeBits++;
+
+                     filePos = fileAlign;
+                     bitsTaken = 0;
+                }
+            }
+        }
+        catch (const char *errStr)
+        {
+#if RIK_ALLOW_BLOCK_ERRORS
+                CPLDebug( "RIK",
+                          "LZW Decompress Failed\n"
+                          " blocks: %d\n"
+                          " blockindex: %d\n"
+                          " blockoffset: %X\n"
+                          " blocksize: %d\n",
+                          blocks, nBlockIndex,
+                          nBlockOffset, nBlockSize );
+                break;
+#else
+                CPLFree( blockData );
+                CPLError( CE_Failure, CPLE_AppDefined,
+                          "RIK decompression failed. "
+                          "Corrupt image block." );
+                return CE_Failure;
+#endif
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read ZLIB block.                                                */
+/* -------------------------------------------------------------------- */
+
+    else if( poRDS->options == 0x0d )
+    {
+        uLong destLen = pixels;
+        Byte *upsideDown = (Byte *) CPLMalloc( pixels );
+
+        uncompress( upsideDown, &destLen, blockData, nBlockSize );
+
+        for (GUInt32 i = 0; i < poRDS->nBlockYSize; i++)
+        {
+            memcpy( ((Byte *)pImage) + poRDS->nBlockXSize * i,
+                    upsideDown + poRDS->nBlockXSize *
+                                 (poRDS->nBlockYSize - i - 1),
+                    poRDS->nBlockXSize );
+        }
+
+        CPLFree( upsideDown );
+    }
+
+    CPLFree( blockData );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp RIKRasterBand::GetColorInterpretation()
+
+{
+    return GCI_PaletteIndex;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *RIKRasterBand::GetColorTable()
+
+{
+    RIKDataset *poRDS = (RIKDataset *) poDS;
+
+    return poRDS->poColorTable;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*				RIKDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                            ~RIKDataset()                             */
+/************************************************************************/
+
+RIKDataset::~RIKDataset()
+
+{
+    FlushCache();
+    CPLFree( pOffsets );
+    if( fp != NULL )
+        VSIFClose( fp );
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr RIKDataset::GetGeoTransform( double * padfTransform )
+
+{
+    memcpy( padfTransform, &fTransform, sizeof(double) * 6 );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *RIKDataset::GetProjectionRef()
+
+{
+  return( "PROJCS[\"RT90 2.5 gon V\",GEOGCS[\"RT90\",DATUM[\"Rikets_koordinatsystem_1990\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],TOWGS84[414.1055246174,41.3265500042,603.0582474221,-0.8551163377,2.1413174055,-7.0227298286,0],AUTHORITY[\"EPSG\",\"6124\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4124\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",15.80827777777778],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",1500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"3021\"]]" );
+}
+
+/************************************************************************/
+/*                             GetRikString()                           */
+/************************************************************************/
+
+static GUInt16 GetRikString( FILE *fp,
+                             char *str,
+                             GUInt16 strLength )
+
+{
+    GUInt16 actLength;
+
+    VSIFRead( &actLength, 1, sizeof(actLength), fp );
+#ifdef CPL_MSB
+    CPL_SWAP16PTR( &actLength );
+#endif
+
+    if( actLength + 2 > strLength )
+    {
+        return actLength;
+    }
+
+    VSIFRead( str, 1, actLength, fp );
+
+    str[actLength] = '\0';
+
+    return actLength;
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *RIKDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    if( poOpenInfo->fp == NULL || poOpenInfo->nHeaderBytes < 50 )
+        return NULL;
+
+    bool rik3header = false;
+
+    if( EQUALN((const char *) poOpenInfo->pabyHeader, "RIK3", 4) )
+    {
+        rik3header = true;
+    }
+
+    if( rik3header )
+        VSIFSeek( poOpenInfo->fp, 4, SEEK_SET );
+    else
+        VSIFSeek( poOpenInfo->fp, 0, SEEK_SET );
+
+/* -------------------------------------------------------------------- */
+/*      Read the map name.                                              */
+/* -------------------------------------------------------------------- */
+
+    char name[1024];
+
+    GUInt16 nameLength = GetRikString( poOpenInfo->fp, name, sizeof(name) );
+
+    if( nameLength > sizeof(name) - 1 )
+    {
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read the header.                                                */
+/* -------------------------------------------------------------------- */
+
+    RIKHeader header;
+    double metersPerPixel;
+
+    char *headerType = "RIK3";
+
+    if( rik3header )
+    {
+/* -------------------------------------------------------------------- */
+/*      RIK3 header.                                                    */
+/* -------------------------------------------------------------------- */
+
+        // Read projection name
+
+        char projection[1024];
+
+        GUInt16 projLength = GetRikString( poOpenInfo->fp,
+                                           projection, sizeof(projection) );
+
+        if( projLength > sizeof(projection) - 1 )
+        {
+            // Unreasonable string length, assume wrong format
+            return NULL;
+        }
+
+        // Read unknown string
+
+        projLength = GetRikString( poOpenInfo->fp, projection, sizeof(projection) );
+
+        // Read map north edge
+
+        char tmpStr[16];
+
+        GUInt16 tmpLength = GetRikString( poOpenInfo->fp,
+                                          tmpStr, sizeof(tmpStr) );
+
+        if( tmpLength > sizeof(tmpStr) - 1 )
+        {
+            // Unreasonable string length, assume wrong format
+            return NULL;
+        }
+
+        header.fNorth = atof( tmpStr );
+
+        // Read map west edge
+
+        tmpLength = GetRikString( poOpenInfo->fp,
+                                  tmpStr, sizeof(tmpStr) );
+
+        if( tmpLength > sizeof(tmpStr) - 1 )
+        {
+            // Unreasonable string length, assume wrong format
+            return NULL;
+        }
+
+        header.fWest = atof( tmpStr );
+
+        // Read binary values
+
+        VSIFRead( &header.iScale, 1, sizeof(header.iScale), poOpenInfo->fp );
+        VSIFRead( &header.iMPPNum, 1, sizeof(header.iMPPNum), poOpenInfo->fp );
+        VSIFRead( &header.iBlockWidth, 1, sizeof(header.iBlockWidth), poOpenInfo->fp );
+        VSIFRead( &header.iBlockHeight, 1, sizeof(header.iBlockHeight), poOpenInfo->fp );
+        VSIFRead( &header.iHorBlocks, 1, sizeof(header.iHorBlocks), poOpenInfo->fp );
+        VSIFRead( &header.iVertBlocks, 1, sizeof(header.iVertBlocks), poOpenInfo->fp );
+#ifdef CPL_MSB
+        CPL_SWAP32PTR( &header.iScale );
+        CPL_SWAP32PTR( &header.iMPPNum );
+        CPL_SWAP32PTR( &header.iBlockWidth );
+        CPL_SWAP32PTR( &header.iBlockHeight );
+        CPL_SWAP32PTR( &header.iHorBlocks );
+        CPL_SWAP32PTR( &header.iVertBlocks );
+#endif
+
+        VSIFRead( &header.iBitsPerPixel, 1, sizeof(header.iBitsPerPixel), poOpenInfo->fp );
+        VSIFRead( &header.iOptions, 1, sizeof(header.iOptions), poOpenInfo->fp );
+        header.iUnknown = header.iOptions;
+        VSIFRead( &header.iOptions, 1, sizeof(header.iOptions), poOpenInfo->fp );
+
+        header.fSouth = header.fNorth -
+            header.iVertBlocks * header.iBlockHeight * header.iMPPNum;
+        header.fEast = header.fWest +
+            header.iHorBlocks * header.iBlockWidth * header.iMPPNum;
+
+        metersPerPixel = header.iMPPNum;
+    }
+    else
+    {
+/* -------------------------------------------------------------------- */
+/*      Old RIK header.                                                 */
+/* -------------------------------------------------------------------- */
+
+        VSIFRead( &header.iUnknown, 1, sizeof(header.iUnknown), poOpenInfo->fp );
+        VSIFRead( &header.fSouth, 1, sizeof(header.fSouth), poOpenInfo->fp );
+        VSIFRead( &header.fWest, 1, sizeof(header.fWest), poOpenInfo->fp );
+        VSIFRead( &header.fNorth, 1, sizeof(header.fNorth), poOpenInfo->fp );
+        VSIFRead( &header.fEast, 1, sizeof(header.fEast), poOpenInfo->fp );
+        VSIFRead( &header.iScale, 1, sizeof(header.iScale), poOpenInfo->fp );
+        VSIFRead( &header.iMPPNum, 1, sizeof(header.iMPPNum), poOpenInfo->fp );
+#ifdef CPL_MSB
+        CPL_SWAP64PTR( &header.fSouth );
+        CPL_SWAP64PTR( &header.fWest );
+        CPL_SWAP64PTR( &header.fNorth );
+        CPL_SWAP64PTR( &header.fEast );
+        CPL_SWAP32PTR( &header.iScale );
+        CPL_SWAP32PTR( &header.iMPPNum );
+#endif
+
+        if (!CPLIsFinite(header.fSouth) |
+            !CPLIsFinite(header.fWest) |
+            !CPLIsFinite(header.fNorth) |
+            !CPLIsFinite(header.fEast))
+            return NULL;
+
+        bool offsetBounds;
+
+        offsetBounds = header.fSouth < 4000000;
+
+        header.iMPPDen = 1;
+
+        if( offsetBounds )
+        {
+            header.fSouth += 4002995;
+            header.fNorth += 5004000;
+            header.fWest += 201000;
+            header.fEast += 302005;
+
+            VSIFRead( &header.iMPPDen, 1, sizeof(header.iMPPDen), poOpenInfo->fp );
+#ifdef CPL_MSB
+            CPL_SWAP32PTR( &header.iMPPDen );
+#endif
+
+            headerType = "RIK1";
+        }
+        else
+        {
+            headerType = "RIK2";
+        }
+
+        metersPerPixel = header.iMPPNum / double(header.iMPPDen);
+
+        VSIFRead( &header.iBlockWidth, 1, sizeof(header.iBlockWidth), poOpenInfo->fp );
+        VSIFRead( &header.iBlockHeight, 1, sizeof(header.iBlockHeight), poOpenInfo->fp );
+        VSIFRead( &header.iHorBlocks, 1, sizeof(header.iHorBlocks), poOpenInfo->fp );
+#ifdef CPL_MSB
+        CPL_SWAP32PTR( &header.iBlockWidth );
+        CPL_SWAP32PTR( &header.iBlockHeight );
+        CPL_SWAP32PTR( &header.iHorBlocks );
+#endif
+
+        if(( header.iBlockWidth > 2000 ) || ( header.iBlockWidth < 10 ) ||
+           ( header.iBlockHeight > 2000 ) || ( header.iBlockHeight < 10 ))
+           return NULL;
+
+        if( !offsetBounds )
+        {
+            VSIFRead( &header.iVertBlocks, 1, sizeof(header.iVertBlocks), poOpenInfo->fp );
+#ifdef CPL_MSB
+            CPL_SWAP32PTR( &header.iVertBlocks );
+#endif
+        }
+
+        if( offsetBounds || !header.iVertBlocks )
+        {
+            header.iVertBlocks = (GUInt32)
+                ceil( (header.fNorth - header.fSouth) /
+                      (header.iBlockHeight * metersPerPixel) );
+        }
+
+#if RIK_HEADER_DEBUG
+        CPLDebug( "RIK",
+                  "Original vertical blocks %d\n",
+                  header.iVertBlocks );
+#endif
+
+        VSIFRead( &header.iBitsPerPixel, 1, sizeof(header.iBitsPerPixel), poOpenInfo->fp );
+
+        if( header.iBitsPerPixel != 8 )
+        {
+            CPLError( CE_Failure, CPLE_OpenFailed,
+                      "File %s has unsupported number of bits per pixel.\n",
+                      poOpenInfo->pszFilename );
+            return NULL;
+        }
+
+        VSIFRead( &header.iOptions, 1, sizeof(header.iOptions), poOpenInfo->fp );
+
+        if( !header.iHorBlocks || !header.iVertBlocks )
+           return NULL;
+
+        if( header.iOptions != 0x00 && // Uncompressed
+            header.iOptions != 0x40 && // Uncompressed
+            header.iOptions != 0x01 && // RLE
+            header.iOptions != 0x41 && // RLE
+            header.iOptions != 0x0B && // LZW
+            header.iOptions != 0x0D )  // ZLIB
+        {
+            CPLError( CE_Failure, CPLE_OpenFailed,
+                      "Unknown map options.\n",
+                      poOpenInfo->pszFilename );
+            return NULL;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read the palette.                                               */
+/* -------------------------------------------------------------------- */
+
+    GByte palette[768];
+
+    GUInt16 i;
+    for( i = 0; i < 256; i++ )
+    {
+        VSIFRead( &palette[i * 3 + 2], 1, 1, poOpenInfo->fp );
+        VSIFRead( &palette[i * 3 + 1], 1, 1, poOpenInfo->fp );
+        VSIFRead( &palette[i * 3 + 0], 1, 1, poOpenInfo->fp );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Find block offsets.                                             */
+/* -------------------------------------------------------------------- */
+
+    GUInt32 blocks;
+    GUInt32 *offsets;
+
+    blocks = header.iHorBlocks * header.iVertBlocks;
+    offsets = (GUInt32 *)CPLMalloc( blocks * sizeof(GUInt32) );
+
+    if( !offsets )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Unable to allocate offset table.\n",
+                  poOpenInfo->pszFilename );
+        return NULL;
+    }
+
+    if( header.iOptions == 0x00 )
+    {
+        offsets[0] = VSIFTell( poOpenInfo->fp );
+
+        for( GUInt32 i = 1; i < blocks; i++ )
+        {
+            offsets[i] = offsets[i - 1] +
+                header.iBlockWidth * header.iBlockHeight;
+        }
+    }
+    else
+    {
+        for( GUInt32 i = 0; i < blocks; i++ )
+        {
+            VSIFRead( &offsets[i], 1, sizeof(offsets[i]), poOpenInfo->fp );
+#ifdef CPL_MSB
+            CPL_SWAP32PTR( &offsets[i] );
+#endif
+            if( rik3header )
+            {
+                GUInt32 blockSize;
+                VSIFRead( &blockSize, 1, sizeof(blockSize), poOpenInfo->fp );
+#ifdef CPL_MSB
+                CPL_SWAP32PTR( &blockSize );
+#endif
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Final checks.                                                   */
+/* -------------------------------------------------------------------- */
+
+    // File size
+
+    if( VSIFEof( poOpenInfo->fp ) )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Read past end of file.\n",
+                  poOpenInfo->pszFilename );
+        return NULL;
+    }
+
+    VSIFSeek( poOpenInfo->fp, 0, SEEK_END );
+    GUInt32 fileSize = VSIFTell( poOpenInfo->fp );
+
+#if RIK_HEADER_DEBUG
+    CPLDebug( "RIK",
+              "File size %d\n",
+              fileSize );
+#endif
+
+    // Make sure the offset table is valid
+
+    GUInt32 lastoffset = 0;
+
+    for( GUInt32 y = 0; y < header.iVertBlocks; y++)
+    {
+        for( GUInt32 x = 0; x < header.iHorBlocks; x++)
+        {
+            if( !offsets[x + y * header.iHorBlocks] )
+            {
+                continue;
+            }
+
+            if( offsets[x + y * header.iHorBlocks] >= fileSize )
+            {
+                if( !y )
+                {
+                    CPLError( CE_Failure, CPLE_OpenFailed,
+                              "File too short.\n",
+                              poOpenInfo->pszFilename );
+                    return NULL;
+                }
+                header.iVertBlocks = y;
+                break;
+            }
+
+            if( offsets[x + y * header.iHorBlocks] < lastoffset )
+            {
+                if( !y )
+                {
+                    CPLError( CE_Failure, CPLE_OpenFailed,
+                              "Corrupt offset table.\n",
+                              poOpenInfo->pszFilename );
+                    return NULL;
+                }
+                header.iVertBlocks = y;
+                break;
+            }
+
+            lastoffset = offsets[x + y * header.iHorBlocks];
+        }
+    }
+
+#if RIK_HEADER_DEBUG
+    CPLDebug( "RIK",
+              "first offset %d\n"
+              "last offset %d\n",
+              offsets[0],
+              lastoffset );
+#endif
+
+    char *compression = "RLE";
+
+    if( header.iOptions == 0x00 ||
+        header.iOptions == 0x40 )
+        compression = "Uncompressed";
+    if( header.iOptions == 0x0b )
+        compression = "LZW";
+    if( header.iOptions == 0x0d )
+        compression = "ZLIB";
+
+    CPLDebug( "RIK",
+              "RIK file parameters:\n"
+              " name: %s\n"
+              " header: %s\n"
+              " unknown: 0x%X\n"
+              " south: %lf\n"
+              " west: %lf\n"
+              " north: %lf\n"
+              " east: %lf\n"
+              " original scale: %d\n"
+              " meters per pixel: %lf\n"
+              " block width: %d\n"
+              " block height: %d\n"
+              " horizontal blocks: %d\n"
+              " vertical blocks: %d\n"
+              " bits per pixel: %d\n"
+              " options: 0x%X\n"
+              " compression: %s\n",
+              name, headerType, header.iUnknown,
+              header.fSouth, header.fWest, header.fNorth, header.fEast,
+              header.iScale, metersPerPixel,
+              header.iBlockWidth, header.iBlockHeight,
+              header.iHorBlocks, header.iVertBlocks,
+              header.iBitsPerPixel, header.iOptions, compression);
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+
+    RIKDataset 	*poDS;
+
+    poDS = new RIKDataset();
+
+    poDS->fp = poOpenInfo->fp;
+    poOpenInfo->fp = NULL;
+
+    poDS->fTransform[0] = header.fWest - metersPerPixel / 2.0;
+    poDS->fTransform[1] = metersPerPixel;
+    poDS->fTransform[2] = 0.0;
+    poDS->fTransform[3] = header.fNorth + metersPerPixel / 2.0;
+    poDS->fTransform[4] = 0.0;
+    poDS->fTransform[5] = -metersPerPixel;
+
+    poDS->nBlockXSize = header.iBlockWidth;
+    poDS->nBlockYSize = header.iBlockHeight;
+    poDS->nHorBlocks = header.iHorBlocks;
+    poDS->nVertBlocks = header.iVertBlocks;
+    poDS->pOffsets = offsets;
+    poDS->options = header.iOptions;
+    poDS->nFileSize = fileSize;
+
+    poDS->nRasterXSize = header.iBlockWidth * header.iHorBlocks;
+    poDS->nRasterYSize = header.iBlockHeight * header.iVertBlocks;
+
+    poDS->nBands = 1;
+
+    GDALColorEntry oEntry;
+    poDS->poColorTable = new GDALColorTable();
+    for( i = 0; i < 256; i++ )
+    {
+        oEntry.c1 = palette[i * 3 + 2]; // Red
+        oEntry.c2 = palette[i * 3 + 1]; // Green
+        oEntry.c3 = palette[i * 3];     // Blue
+        oEntry.c4 = 255;
+
+        poDS->poColorTable->SetColorEntry( i, &oEntry );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+
+    poDS->SetBand( 1, new RIKRasterBand( poDS, 1 ));
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                          GDALRegister_RIK()                          */
+/************************************************************************/
+
+void GDALRegister_RIK()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "RIK" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+
+        poDriver->SetDescription( "RIK" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
+                                   "Swedish Grid RIK (.rik)" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
+                                   "frmt_various.html#RIK" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "rik" );
+
+        poDriver->pfnOpen = RIKDataset::Open;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
diff --git a/Utilities/GDAL/frmts/rmf/GNUmakefile b/Utilities/GDAL/frmts/rmf/GNUmakefile
new file mode 100644
index 0000000000..03c2ce1c46
--- /dev/null
+++ b/Utilities/GDAL/frmts/rmf/GNUmakefile
@@ -0,0 +1,13 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	rmfdataset.o
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/rmf/frmt_rmf.html b/Utilities/GDAL/frmts/rmf/frmt_rmf.html
new file mode 100644
index 0000000000..465de58161
--- /dev/null
+++ b/Utilities/GDAL/frmts/rmf/frmt_rmf.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+	<title>RMF --- Raster Matrix Format</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>RMF --- Raster Matrix Format</h1>
+
+RMF is a simple tiled raster format used in the GIS "Integration" and
+"Panorama" GIS. The format itself has very poor capabilities.<p>
+
+There are two flavours of RMF called MTW and RSW. MTW supports 16-bit integer
+and 32/64-bit floating point data in a single channel and aimed to store DEM
+data. RSW is a general purpose raster, it supports single channel colormapped
+or three channel RGB images. Only 8-bit data can be stored in RSW. Simple
+georeferencing can be provided for both image types.<p>
+
+<h2>Creation Options</h2>
+<ul>
+	<li> <b>MTW=ON</b>: Force the generation of MTW matrix (RSW will be
+	created by default).<p>
+
+	<li> <b>BLOCKXSIZE=n</b>: Sets tile width, defaults to 256.<p>
+
+	<li> <b>BLOCKYSIZE=n</b>: Set tile height.  Tile height defaults to 256.<p>
+
+</ul>
+
+<h2>See Also:</h2>
+
+<ul>
+<li> Implemented as <tt>gdal/frmts/rmf/rmfdataset.cpp</tt>.<p>
+
+<li> <a href="http://www.gisinfo.ru/index_en.htm">"Panorama" GIS homepage</a><p>
+
+</ul>
+
+</body>
+</html>
+
diff --git a/Utilities/GDAL/frmts/rmf/makefile.vc b/Utilities/GDAL/frmts/rmf/makefile.vc
new file mode 100644
index 0000000000..7324aa7564
--- /dev/null
+++ b/Utilities/GDAL/frmts/rmf/makefile.vc
@@ -0,0 +1,13 @@
+
+OBJ	=	rmfdataset.obj
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/rmf/rmfdataset.cpp b/Utilities/GDAL/frmts/rmf/rmfdataset.cpp
new file mode 100644
index 0000000000..36fb50fccb
--- /dev/null
+++ b/Utilities/GDAL/frmts/rmf/rmfdataset.cpp
@@ -0,0 +1,1468 @@
+/******************************************************************************
+ * $Id: rmfdataset.cpp,v 1.11 2006/03/09 10:41:15 dron Exp $
+ *
+ * Project:  Raster Matrix Format
+ * Purpose:  Read/write raster files used in GIS "Integration"
+ *           (also known as "Panorama" GIS). 
+ * Author:   Andrey Kiselev, dron@remotesensing.org
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Andrey Kiselev <dron@remotesensing.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: rmfdataset.cpp,v $
+ * Revision 1.11  2006/03/09 10:41:15  dron
+ * Fixed problem with writing incomplete last blocks.
+ *
+ * Revision 1.10  2006/03/08 21:53:40  dron
+ * Mark header dirty when color table changed.
+ *
+ * Revision 1.9  2006/03/08 21:50:14  dron
+ * Do not forget to mark header dirty when changed.
+ *
+ * Revision 1.8  2005/10/19 16:39:31  dron
+ * Export projection info in newly created datasets.
+ *
+ * Revision 1.7  2005/10/12 12:07:46  dron
+ * Remove old projection handling code.
+ *
+ * Revision 1.6  2005/10/12 11:36:20  dron
+ * Fetch prohjection definition using the "Panorama" OSR interface.
+ *
+ * Revision 1.5  2005/08/12 13:22:46  fwarmerdam
+ * avoid initialization warning.
+ *
+ * Revision 1.4  2005/08/12 13:21:10  fwarmerdam
+ * Avoid warning about unused variable.
+ *
+ * Revision 1.3  2005/06/09 19:32:01  dron
+ * Fixed compilation on big-endian arch.
+ *
+ * Revision 1.2  2005/05/20 19:25:11  dron
+ * Fixed problem with the last line of tiles.
+ *
+ * Revision 1.1  2005/05/19 20:42:03  dron
+ * New.
+ *
+ */
+
+#include "gdal_priv.h"
+#include "ogr_spatialref.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: rmfdataset.cpp,v 1.11 2006/03/09 10:41:15 dron Exp $");
+
+CPL_C_START
+void    GDALRegister_RMF(void);
+CPL_C_END
+
+enum RMFType
+{
+    RMFT_RSW,       // Raster map
+    RMFT_MTW        // Digital elevation model
+};
+
+#define RMF_DEFAULT_BLOCKXSIZE 256
+#define RMF_DEFAULT_BLOCKYSIZE 256
+
+typedef struct
+{
+#define RMF_SIGNATURE_SIZE 4
+    char        szSignature[RMF_SIGNATURE_SIZE];    // "RSW" for raster
+                                                // map or "MTW" for DEM
+    GUInt32     iVersion;
+    GUInt32     nSize;                          // File size in bytes
+    GUInt32     nOvrOffset;                     // Offset to overview
+    GUInt32     iUserID;
+#define RMF_NAME_SIZE 32
+    GByte       byName[RMF_NAME_SIZE];
+    GUInt32     nBitDepth;                      // Number of bits per pixel
+    GUInt32     nHeight;                        // Image length
+    GUInt32     nWidth;                         // Image width
+    GUInt32     nXTiles;                        // Number of tiles in line
+    GUInt32     nYTiles;                        // Number of tiles in column
+    GUInt32     nTileHeight;
+    GUInt32     nTileWidth;
+    GUInt32     nLastTileHeight;
+    GUInt32     nLastTileWidth;
+    GUInt32     nROIOffset;
+    GUInt32     nROISize;
+    GUInt32     nClrTblOffset;                  // Position and size
+    GUInt32     nClrTblSize;                    // of the colour table
+    GUInt32     nTileTblOffset;                 // Position and size of the
+    GUInt32     nTileTblSize;                   // tile offsets/sizes table 
+    GInt32      iMapType;
+    GInt32      iProjection;
+    double      dfScale;
+    double      dfResolution;
+    double      dfPixelSize;
+    double      dfLLX;
+    double      dfLLY;
+    double      dfStdP1;
+    double      dfStdP2;
+    double      dfCenterLong;
+    double      dfCenterLat;
+    GByte       iCompression;
+    GByte       iMaskType;
+    GByte       iMaskStep;
+    GByte       iFrameFlag;
+    GUInt32     nFlagsTblOffset;
+    GUInt32     nFlagsTblSize;
+    GUInt32     nFileSize0;
+    GUInt32     nFileSize1;
+    GByte       iUnknown;
+    GByte       iGeorefFlag;
+    GByte       iInverse;
+#define RMF_INVISIBLE_COLORS_SIZE 32
+    GByte       abyInvisibleColors[RMF_INVISIBLE_COLORS_SIZE];
+    double      dfElevMinMax[2];
+    double      dfNoData;
+    GUInt32     iElevationUnit;
+    GByte       iElevationType;
+} RMFHeader;
+
+/************************************************************************/
+/* ==================================================================== */
+/*                              RMFDataset                              */
+/* ==================================================================== */
+/************************************************************************/
+
+class RMFDataset : public GDALDataset
+{
+    friend class RMFRasterBand;
+
+#define RMF_HEADER_SIZE 320
+    GByte           abyHeader[RMF_HEADER_SIZE];
+    RMFHeader       sHeader;
+    RMFType         eRMFType;
+    GUInt32         nXTiles;
+    GUInt32         nYTiles;
+    GUInt32         *paiTiles;
+    
+    GUInt32         nColorTableSize;
+    GByte           *pabyColorTable;
+    GDALColorTable  *poColorTable;
+    double          adfGeoTransform[6];
+    char            *pszProjection;
+
+    int             bHeaderDirty;
+
+    const char      *pszFilename;
+    FILE            *fp;
+
+  protected:
+    CPLErr              WriteHeader();
+
+  public:
+                RMFDataset();
+                ~RMFDataset();
+
+    static GDALDataset  *Open( GDALOpenInfo * );
+    static GDALDataset  *Create( const char *, int, int, int,
+                                 GDALDataType, char ** );
+    virtual void        FlushCache( void );
+
+    virtual CPLErr      GetGeoTransform( double * padfTransform );
+    virtual CPLErr      SetGeoTransform( double * );
+    virtual const char  *GetProjectionRef();
+    virtual CPLErr      SetProjection( const char * );
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            RMFRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class RMFRasterBand : public GDALRasterBand
+{
+    friend class RMFDataset;
+
+  protected:
+
+    unsigned int    iBytesPerSample;
+    GUInt32         nBlockSize, nBlockBytes;
+    GUInt32         nLastTileXBytes;
+    int             nDataSize;
+
+  public:
+
+                RMFRasterBand( RMFDataset *, int, GDALDataType );
+                ~RMFRasterBand();
+
+    virtual CPLErr          IReadBlock( int, int, void * );
+    virtual CPLErr          IWriteBlock( int, int, void * );
+    virtual GDALColorInterp GetColorInterpretation();
+    virtual GDALColorTable  *GetColorTable();
+    CPLErr                  SetColorTable( GDALColorTable * );
+};
+
+/************************************************************************/
+/*                           RMFRasterBand()                            */
+/************************************************************************/
+
+RMFRasterBand::RMFRasterBand( RMFDataset *poDS, int nBand,
+                              GDALDataType eType )
+{
+    this->poDS = poDS;
+    this->nBand = nBand;
+    eDataType = eType;
+    iBytesPerSample = poDS->sHeader.nBitDepth / 8;
+
+    nBlockXSize = poDS->sHeader.nTileWidth;
+    nBlockYSize = poDS->sHeader.nTileHeight;
+    nBlockSize = nBlockXSize * nBlockYSize;
+    nDataSize = GDALGetDataTypeSize( eDataType ) / 8;
+    nBlockBytes = nBlockSize * nDataSize;
+    nLastTileXBytes =
+        (poDS->GetRasterXSize() % poDS->sHeader.nTileWidth) * nDataSize;
+
+#if DEBUG
+    CPLDebug( "RMF",
+              "Band %d: tile width is %d, tile height is %d, "
+              " last tile width %d, last tile height %d, "
+              "bytes per sample is %d, data type size is %d",
+              nBand, nBlockXSize, nBlockYSize,
+              poDS->sHeader.nLastTileWidth, poDS->sHeader.nLastTileHeight,
+              iBytesPerSample, nDataSize );
+#endif
+}
+
+/************************************************************************/
+/*                           ~RMFRasterBand()                           */
+/************************************************************************/
+
+RMFRasterBand::~RMFRasterBand()
+{
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr RMFRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+{
+    RMFDataset  *poGDS = (RMFDataset *) poDS;
+    GUInt32     nTile = nBlockYOff * poGDS->nXTiles + nBlockXOff;
+    GUInt32     nTileBytes = poGDS->paiTiles[2 * nTile + 1];
+    GUInt32     i, nCurBlockYSize;
+
+    memset( pImage, 0, nBlockBytes );
+
+    if ( poGDS->sHeader.nLastTileHeight
+         && (GUInt32) nBlockYOff == poGDS->nYTiles - 1 )
+        nCurBlockYSize = poGDS->sHeader.nLastTileHeight;
+    else
+        nCurBlockYSize = nBlockYSize;
+
+    if ( VSIFSeekL( poGDS->fp, poGDS->paiTiles[2 * nTile], SEEK_SET ) < 0 )
+    {
+        // XXX: We will not report error here, because file just may be
+	// in update state and data for this block will be available later
+        if( poGDS->eAccess == GA_Update )
+            return CE_None;
+        else
+        {
+            CPLError( CE_Failure, CPLE_FileIO,
+                      "Can't seek to offset %ld in input file to read data.",
+                      poGDS->paiTiles[2 * nTile] );
+            return CE_Failure;
+        }
+    }
+
+    if ( poGDS->nBands == 1 &&
+         ( poGDS->sHeader.nBitDepth == 8
+           || poGDS->sHeader.nBitDepth == 16
+           || poGDS->sHeader.nBitDepth == 32
+           || poGDS->sHeader.nBitDepth == 64 ) )
+    {
+        if ( nTileBytes > nBlockBytes )
+            nTileBytes = nBlockBytes;
+    
+        if ( VSIFReadL( pImage, 1, nTileBytes, poGDS->fp ) < nTileBytes )
+        {
+            // XXX
+            if( poGDS->eAccess == GA_Update )
+                return CE_None;
+            else
+            {
+                CPLError( CE_Failure, CPLE_FileIO,
+                          "Can't read from offset %ld in input file.",
+                          poGDS->paiTiles[2 * nTile] );
+                return CE_Failure;
+            }
+        }
+
+#ifdef CPL_MSB
+        if ( poGDS->eRMFType == RMFT_MTW )
+        {
+            if ( poGDS->sHeader.nBitDepth == 16 )
+            {
+                for ( i = 0; i < nTileBytes; i += 2 )
+                    CPL_SWAP16PTR( (GByte*)pImage + i );
+            }
+
+            else if ( poGDS->sHeader.nBitDepth == 32 )
+            {
+                for ( i = 0; i < nTileBytes; i += 4 )
+                    CPL_SWAP32PTR( (GByte*)pImage + i );
+            }
+
+            else if ( poGDS->sHeader.nBitDepth == 64 )
+            {
+                for ( i = 0; i < nTileBytes; i += 8 )
+                    CPL_SWAPDOUBLE( (GByte*)pImage + i );
+            }
+        }
+#endif
+
+    }
+
+    else if ( poGDS->eRMFType == RMFT_RSW )
+    {
+        GByte   *pabyTile = (GByte *) CPLMalloc( nTileBytes );
+
+        if ( VSIFReadL( pabyTile, 1, nTileBytes, poGDS->fp ) < nTileBytes )
+        {
+            // XXX
+            if( poGDS->eAccess == GA_Update )
+            {
+                CPLFree( pabyTile );
+                return CE_None;
+            }
+            else
+            {
+                CPLError( CE_Failure, CPLE_FileIO,
+                          "Can't read from offset %ld in input file.",
+                          poGDS->paiTiles[2 * nTile] );
+                CPLFree( pabyTile );
+                return CE_Failure;
+            }
+        }
+
+        if ( poGDS->sHeader.nBitDepth == 24 || poGDS->sHeader.nBitDepth == 32 )
+        {
+            GUInt32 nTileSize = nTileBytes / iBytesPerSample;
+            for ( i = 0; i < nTileSize; i++ )
+            {
+                // Colour triplets in RMF file organized in reverse order:
+                // blue, green, red. When we have 32-bit BMP the forth byte
+                // in quadriplet should be discarded as it has no meaning.
+                // That is why we always use 3 byte count in the following
+                // pabyTemp index.
+                ((GByte *) pImage)[i] =
+                    pabyTile[i * iBytesPerSample + 3 - nBand];
+            }
+        }
+
+        else if ( poGDS->sHeader.nBitDepth == 16 )
+        {
+            for ( i = 0; i < nBlockSize; i++ )
+            {
+                switch ( nBand )
+                {
+                    case 1:
+                    ((GByte *) pImage)[i] = pabyTile[i + 1] & 0x1F;
+                    break;
+                    case 2:
+                    ((GByte *) pImage)[i] = ((pabyTile[i] & 0x03) << 3) |
+                                            ((pabyTile[i + 1] & 0xE0) >> 5);
+                    break;
+                    case 3:
+                    ((GByte *) pImage)[i] = (pabyTile[i] & 0x7c) >> 2;
+                    break;
+                    default:
+                    break;
+                }
+            }
+        }
+
+        else if ( poGDS->sHeader.nBitDepth == 4 )
+        {
+            GByte *pabyTemp = pabyTile;
+
+            for ( i = 0; i < nBlockSize; i++ )
+            {
+                // Most significant part of the byte represents leftmost pixel
+                if ( i & 0x01 )
+                    ((GByte *) pImage)[i] = *pabyTemp++ & 0x0F;
+                else
+                    ((GByte *) pImage)[i] = (*pabyTemp & 0xF0) >> 4;
+            }
+        }
+
+        else if ( poGDS->sHeader.nBitDepth == 1 )
+        {
+            GByte *pabyTemp = pabyTile;
+
+            for ( i = 0; i < nBlockSize; i++ )
+            {
+                switch ( i & 0x7 )
+                {
+                    case 0:
+                    ((GByte *) pImage)[i] = (*pabyTemp & 0x80) >> 7;
+                    break;
+                    case 1:
+                    ((GByte *) pImage)[i] = (*pabyTemp & 0x40) >> 6;
+                    break;
+                    case 2:
+                    ((GByte *) pImage)[i] = (*pabyTemp & 0x20) >> 5;
+                    break;
+                    case 3:
+                    ((GByte *) pImage)[i] = (*pabyTemp & 0x10) >> 4;
+                    break;
+                    case 4:
+                    ((GByte *) pImage)[i] = (*pabyTemp & 0x08) >> 3;
+                    break;
+                    case 5:
+                    ((GByte *) pImage)[i] = (*pabyTemp & 0x04) >> 2;
+                    break;
+                    case 6:
+                    ((GByte *) pImage)[i] = (*pabyTemp & 0x02) >> 1;
+                    break;
+                    case 7:
+                    ((GByte *) pImage)[i] = *pabyTemp++ & 0x01;
+                    break;
+                    default:
+                    break;
+                }
+            }
+        }
+
+        CPLFree( pabyTile );
+    }
+
+    if ( nLastTileXBytes
+         && (GUInt32) nBlockXOff == poGDS->nXTiles - 1 )
+    {
+        GUInt32 iRow;
+
+        for ( iRow = nCurBlockYSize - 1; iRow > 0; iRow-- )
+        {
+            memmove( (GByte *)pImage + nBlockXSize * iRow * nDataSize,
+                     (GByte *)pImage + iRow * nLastTileXBytes,
+                     nLastTileXBytes );
+        }
+        
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                            IWriteBlock()                             */
+/************************************************************************/
+
+CPLErr RMFRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
+                                   void * pImage )
+{
+    RMFDataset  *poGDS = (RMFDataset *)poDS;
+    GUInt32     nTile = nBlockYOff * poGDS->nXTiles + nBlockXOff;
+    GUInt32     nTileBytes = nDataSize * poGDS->nBands;
+    GUInt32     iInPixel, iOutPixel, nCurBlockYSize;
+    GByte       *pabyTile;
+
+    CPLAssert( poGDS != NULL
+               && nBlockXOff >= 0
+               && nBlockYOff >= 0
+               && pImage != NULL );
+
+    if ( poGDS->paiTiles[2 * nTile] )
+    {
+        if ( VSIFSeekL( poGDS->fp, poGDS->paiTiles[2 * nTile], SEEK_SET ) < 0 )
+        {
+            CPLError( CE_Failure, CPLE_FileIO,
+                "Can't seek to offset %ld in output file to write data.\n%s",
+                poGDS->paiTiles[2 * nTile], VSIStrerror( errno ) );
+            return CE_Failure;
+        }
+    }
+    else
+    {
+        if ( VSIFSeekL( poGDS->fp, 0, SEEK_END ) < 0 )
+        {
+            CPLError( CE_Failure, CPLE_FileIO,
+                "Can't seek to offset %ld in output file to write data.\n%s",
+                poGDS->paiTiles[2 * nTile], VSIStrerror( errno ) );
+            return CE_Failure;
+        }
+        poGDS->paiTiles[2 * nTile] = VSIFTellL( poGDS->fp );
+
+        poGDS->bHeaderDirty = TRUE;
+    }
+
+    if ( nLastTileXBytes
+         && (GUInt32) nBlockXOff == poGDS->nXTiles - 1 )
+        nTileBytes *= poGDS->sHeader.nLastTileWidth;
+    else
+        nTileBytes *= nBlockXSize;
+
+    if ( poGDS->sHeader.nLastTileHeight
+         && (GUInt32) nBlockYOff == poGDS->nYTiles - 1 )
+        nCurBlockYSize = poGDS->sHeader.nLastTileHeight;
+    else
+        nCurBlockYSize = nBlockYSize;
+
+    nTileBytes *= nCurBlockYSize;
+        
+    pabyTile = (GByte *) CPLMalloc( nTileBytes );
+    if ( !pabyTile )
+    {
+        CPLError( CE_Failure, CPLE_FileIO,
+                  "Can't allocate space for the tile buffer.\n%s",
+                  VSIStrerror( errno ) );
+        return CE_Failure;
+    }
+
+    if ( nLastTileXBytes
+         && (GUInt32) nBlockXOff == poGDS->nXTiles - 1 )
+    {
+        GUInt32 iRow;
+
+        if ( poGDS->nBands == 1 )
+        {
+            for ( iRow = 0; iRow < nCurBlockYSize; iRow++ )
+            {
+                memcpy( pabyTile + iRow * nLastTileXBytes,
+                         (GByte*)pImage + nBlockXSize * iRow * nDataSize,
+                         nLastTileXBytes );
+            }
+        }
+        else
+        {
+            memset( pabyTile, 0, nTileBytes );
+            if ( poGDS->paiTiles[2 * nTile + 1] )
+            {
+                VSIFReadL( pabyTile, 1, nTileBytes, poGDS->fp );
+                VSIFSeekL( poGDS->fp, poGDS->paiTiles[2 * nTile], SEEK_SET );
+            }
+
+            for ( iRow = 0; iRow < nCurBlockYSize; iRow++ )
+            {
+                for ( iInPixel = 0, iOutPixel = iBytesPerSample - nBand;
+                      iOutPixel < nLastTileXBytes;
+                      iInPixel++, iOutPixel += poGDS->nBands )
+                    (pabyTile + iRow * nLastTileXBytes)[iOutPixel] =
+                        ((GByte *) pImage + nBlockXSize * iRow * nDataSize)[iInPixel];
+            }
+        }
+    }
+    else
+    {
+        if ( poGDS->nBands == 1 )
+            memcpy( pabyTile, pImage, nTileBytes );
+        else
+        {
+            memset( pabyTile, 0, nTileBytes );
+            if ( poGDS->paiTiles[2 * nTile + 1] )
+            {
+                VSIFReadL( pabyTile, 1, nTileBytes, poGDS->fp );
+                VSIFSeekL( poGDS->fp, poGDS->paiTiles[2 * nTile], SEEK_SET );
+            }
+
+            for ( iInPixel = 0, iOutPixel = iBytesPerSample - nBand;
+                  iOutPixel < nTileBytes;
+                  iInPixel++, iOutPixel += poGDS->nBands )
+                pabyTile[iOutPixel] = ((GByte *) pImage)[iInPixel];
+
+        }
+    }
+    
+#ifdef CPL_MSB
+    if ( poGDS->eRMFType == RMFT_MTW )
+    {
+        GUInt32 i;
+
+        if ( poGDS->sHeader.nBitDepth == 16 )
+        {
+            for ( i = 0; i < nTileBytes; i += 2 )
+                CPL_SWAP16PTR( pabyTile + i );
+        }
+
+        else if ( poGDS->sHeader.nBitDepth == 32 )
+        {
+            for ( i = 0; i < nTileBytes; i += 4 )
+                CPL_SWAP32PTR( pabyTile + i );
+        }
+
+        else if ( poGDS->sHeader.nBitDepth == 64 )
+        {
+            for ( i = 0; i < nTileBytes; i += 8 )
+                CPL_SWAPDOUBLE( pabyTile + i );
+        }
+    }
+#endif
+
+    if ( VSIFWriteL( pabyTile, 1, nTileBytes, poGDS->fp ) < nTileBytes )
+    {
+        CPLError( CE_Failure, CPLE_FileIO,
+                  "Can't write block with X offset %d and Y offset %d.\n%s",
+                  nBlockXOff, nBlockYOff, VSIStrerror( errno ) );
+        CPLFree( pabyTile );
+        return CE_Failure;
+    }
+    
+    poGDS->paiTiles[2 * nTile + 1] = nTileBytes;
+    CPLFree( pabyTile );
+
+    poGDS->bHeaderDirty = TRUE;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *RMFRasterBand::GetColorTable()
+{
+    RMFDataset   *poGDS = (RMFDataset *) poDS;
+
+    return poGDS->poColorTable;
+}
+
+/************************************************************************/
+/*                           SetColorTable()                            */
+/************************************************************************/
+
+CPLErr RMFRasterBand::SetColorTable( GDALColorTable *poColorTable )
+{
+    RMFDataset  *poGDS = (RMFDataset *) poDS;
+
+    if ( poColorTable )
+    {
+        if ( poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1 )
+        {
+            GDALColorEntry  oEntry;
+            GUInt32         i;
+
+            if ( !poGDS->pabyColorTable )
+                return CE_Failure;
+
+            for( i = 0; i < poGDS->nColorTableSize; i++ )
+            {
+                poColorTable->GetColorEntryAsRGB( i, &oEntry );
+                poGDS->pabyColorTable[i * 4] = (GByte) oEntry.c1;     // Red
+                poGDS->pabyColorTable[i * 4 + 1] = (GByte) oEntry.c2; // Green
+                poGDS->pabyColorTable[i * 4 + 2] = (GByte) oEntry.c3; // Blue
+                poGDS->pabyColorTable[i * 4 + 3] = 0;
+            }
+
+            poGDS->bHeaderDirty = TRUE;
+        }
+    }
+    else
+        return CE_Failure;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp RMFRasterBand::GetColorInterpretation()
+{
+    RMFDataset      *poGDS = (RMFDataset *) poDS;
+
+    if( poGDS->nBands == 3 )
+    {
+        if( nBand == 1 )
+            return GCI_RedBand;
+        else if( nBand == 2 )
+            return GCI_GreenBand;
+        else if( nBand == 3 )
+            return GCI_BlueBand;
+        else
+            return GCI_Undefined;
+    }
+    else
+    {
+        if ( poGDS->eRMFType == RMFT_RSW )
+            return GCI_PaletteIndex;
+        else
+            return GCI_Undefined;
+    }
+}
+
+/************************************************************************/
+/*                           RMFDataset()                               */
+/************************************************************************/
+
+RMFDataset::RMFDataset()
+{
+    pszFilename = NULL;
+    fp = NULL;
+    nBands = 0;
+    nXTiles = 0;
+    nYTiles = 0;
+    paiTiles = NULL;
+    pszProjection = CPLStrdup( "" );
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+    pabyColorTable = NULL;
+    poColorTable = NULL;
+    memset( abyHeader, 0, RMF_HEADER_SIZE ); 
+    memset( &sHeader, 0, sizeof(sHeader) );
+
+    bHeaderDirty = FALSE;
+}
+
+/************************************************************************/
+/*                            ~RMFDataset()                             */
+/************************************************************************/
+
+RMFDataset::~RMFDataset()
+{
+    FlushCache();
+
+    if ( paiTiles )
+        CPLFree( paiTiles );
+    if ( pszProjection )
+        CPLFree( pszProjection );
+    if ( pabyColorTable )
+        CPLFree( pabyColorTable );
+    if ( poColorTable != NULL )
+        delete poColorTable;
+    if( fp != NULL )
+        VSIFCloseL( fp );
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr RMFDataset::GetGeoTransform( double * padfTransform )
+{
+    memcpy( padfTransform, adfGeoTransform, sizeof(adfGeoTransform[0]) * 6 );
+
+    if( sHeader.iGeorefFlag )
+        return CE_None;
+    else
+        return CE_Failure;
+}
+
+/************************************************************************/
+/*                          SetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr RMFDataset::SetGeoTransform( double * padfTransform )
+{
+    memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 );
+    sHeader.dfPixelSize = adfGeoTransform[1];
+    sHeader.dfLLX = adfGeoTransform[0];
+    sHeader.dfLLY = adfGeoTransform[3] - nRasterYSize * sHeader.dfPixelSize;
+    sHeader.iGeorefFlag = 1;
+
+    bHeaderDirty = TRUE;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *RMFDataset::GetProjectionRef()
+{
+    if( pszProjection )
+        return pszProjection;
+    else
+        return "";
+}
+
+/************************************************************************/
+/*                           SetProjection()                            */
+/************************************************************************/
+
+CPLErr RMFDataset::SetProjection( const char * pszNewProjection )
+
+{
+    if ( pszProjection )
+        CPLFree( pszProjection );
+    pszProjection = CPLStrdup( (pszNewProjection) ? pszNewProjection : "" );
+
+    bHeaderDirty = TRUE;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           WriteHeader()                              */
+/************************************************************************/
+
+#define RMF_WRITE_LONG(value, offset)               \
+{                                                   \
+    GInt32  iLong = CPL_LSBWORD32( value );         \
+    memcpy( abyHeader + (offset), &iLong, 4 );      \
+}
+
+#define RMF_WRITE_ULONG(value, offset)              \
+{                                                   \
+    GUInt32 iULong = CPL_LSBWORD32( value );        \
+    memcpy( abyHeader + (offset), &iULong, 4 );     \
+}
+
+#define RMF_WRITE_DOUBLE(value, offset)             \
+{                                                   \
+    double  dfDouble = (value);                     \
+    CPL_LSBPTR64( &dfDouble );                      \
+    memcpy( abyHeader + (offset), &dfDouble, 8 );   \
+}
+
+CPLErr RMFDataset::WriteHeader()
+{
+/* -------------------------------------------------------------------- */
+/*  Setup projection.                                                   */
+/* -------------------------------------------------------------------- */
+    if( pszProjection && !EQUAL( pszProjection, "" ) )
+    {
+        OGRSpatialReference oSRS;
+        long            iDatum, iEllips, iZone;
+        char            *pszProj =  pszProjection;
+
+        if ( oSRS.importFromWkt( &pszProj ) == OGRERR_NONE )
+        {
+            oSRS.exportToPanorama((long *)&sHeader.iProjection,
+                                  &iDatum, &iEllips, &iZone,
+                                  &sHeader.dfStdP1, &sHeader.dfStdP2,
+                                  &sHeader.dfCenterLat, &sHeader.dfCenterLong);
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*  Write out the file header.                                          */
+/* -------------------------------------------------------------------- */
+    memcpy( abyHeader, sHeader.szSignature, RMF_SIGNATURE_SIZE );
+    RMF_WRITE_ULONG( sHeader.iVersion, 4 );
+    // �����
+    RMF_WRITE_ULONG( sHeader.nOvrOffset, 12 );
+    RMF_WRITE_ULONG( sHeader.iUserID, 16 );
+    memcpy( abyHeader + 20, sHeader.byName, RMF_NAME_SIZE );
+    RMF_WRITE_ULONG( sHeader.nBitDepth, 52 );
+    RMF_WRITE_ULONG( sHeader.nHeight, 56 );
+    RMF_WRITE_ULONG( sHeader.nWidth, 60 );
+    RMF_WRITE_ULONG( sHeader.nXTiles, 64 );
+    RMF_WRITE_ULONG( sHeader.nYTiles, 68 );
+    RMF_WRITE_ULONG( sHeader.nTileHeight, 72 );
+    RMF_WRITE_ULONG( sHeader.nTileWidth, 76 );
+    RMF_WRITE_ULONG( sHeader.nLastTileHeight, 80 );
+    RMF_WRITE_ULONG( sHeader.nLastTileWidth, 84 );
+    RMF_WRITE_ULONG( sHeader.nROIOffset, 88 );
+    RMF_WRITE_ULONG( sHeader.nROISize, 92 );
+    RMF_WRITE_ULONG( sHeader.nClrTblOffset, 96 );
+    RMF_WRITE_ULONG( sHeader.nClrTblSize, 100 );
+    RMF_WRITE_ULONG( sHeader.nTileTblOffset, 104 );
+    RMF_WRITE_ULONG( sHeader.nTileTblSize, 108 );
+    RMF_WRITE_LONG( sHeader.iMapType, 124 );
+    RMF_WRITE_LONG( sHeader.iProjection, 128 );
+    RMF_WRITE_DOUBLE( sHeader.dfScale, 136 );
+    RMF_WRITE_DOUBLE( sHeader.dfResolution, 144 );
+    RMF_WRITE_DOUBLE( sHeader.dfPixelSize, 152 );
+    RMF_WRITE_DOUBLE( sHeader.dfLLX, 160 );
+    RMF_WRITE_DOUBLE( sHeader.dfLLY, 168 );
+    RMF_WRITE_DOUBLE( sHeader.dfStdP1, 176 );
+    RMF_WRITE_DOUBLE( sHeader.dfStdP2, 184 );
+    RMF_WRITE_DOUBLE( sHeader.dfCenterLong, 192 );
+    RMF_WRITE_DOUBLE( sHeader.dfCenterLat, 200 );
+    *(abyHeader + 208) = sHeader.iCompression;
+    *(abyHeader + 209) = sHeader.iMaskType;
+    *(abyHeader + 210) = sHeader.iMaskStep;
+    *(abyHeader + 211) = sHeader.iFrameFlag;
+    RMF_WRITE_ULONG( sHeader.nFlagsTblOffset, 212 );
+    RMF_WRITE_ULONG( sHeader.nFlagsTblSize, 216 );
+    RMF_WRITE_ULONG( sHeader.nFileSize0, 220 );
+    RMF_WRITE_ULONG( sHeader.nFileSize1, 224 );
+    *(abyHeader + 228) = sHeader.iUnknown;
+    *(abyHeader + 244) = sHeader.iGeorefFlag;
+    *(abyHeader + 245) = sHeader.iInverse;
+    memcpy( abyHeader + 248, sHeader.abyInvisibleColors,
+            RMF_INVISIBLE_COLORS_SIZE );
+    RMF_WRITE_DOUBLE( sHeader.dfElevMinMax[0], 280 );
+    RMF_WRITE_DOUBLE( sHeader.dfElevMinMax[1], 288 );
+    RMF_WRITE_DOUBLE( sHeader.dfNoData, 296 );
+    RMF_WRITE_ULONG( sHeader.iElevationUnit, 304 );
+    RMF_WRITE_ULONG( sHeader.iElevationType, 308 );
+
+    VSIFSeekL( fp, 0, SEEK_SET );
+    VSIFWriteL( abyHeader, 1, RMF_HEADER_SIZE, fp );
+
+/* -------------------------------------------------------------------- */
+/*  Write out the color table.                                          */
+/* -------------------------------------------------------------------- */
+    if ( sHeader.nClrTblOffset && sHeader.nClrTblSize )
+    {
+        VSIFSeekL( fp, sHeader.nClrTblOffset, SEEK_SET );
+        VSIFWriteL( pabyColorTable, 1, sHeader.nClrTblSize, fp );
+    }
+
+/* -------------------------------------------------------------------- */
+/*  Write out the block table.                                          */
+/* -------------------------------------------------------------------- */
+    VSIFWriteL( paiTiles, 1, sHeader.nTileTblSize, fp );
+
+    bHeaderDirty = FALSE;
+
+    return CE_None;
+}
+
+#undef RMF_WRITE_DOUBLE
+#undef RMF_WRITE_ULONG
+#undef RMF_WRITE_LONG
+
+/************************************************************************/
+/*                             FlushCache()                             */
+/************************************************************************/
+
+void RMFDataset::FlushCache()
+
+{
+    GDALDataset::FlushCache();
+
+    if ( !bHeaderDirty )
+        return;
+
+    if ( eRMFType == RMFT_MTW )
+        GDALComputeRasterMinMax( GetRasterBand(1), FALSE,
+                                 sHeader.dfElevMinMax );
+    WriteHeader();
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *RMFDataset::Open( GDALOpenInfo * poOpenInfo )
+{
+    if( poOpenInfo->fp == NULL )
+        return NULL;
+
+    if( memcmp(poOpenInfo->pabyHeader, "RSW\0", 4) != 0
+        && memcmp(poOpenInfo->pabyHeader, "MTW\0", 4) != 0 )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*  Create a corresponding GDALDataset.                                 */
+/* -------------------------------------------------------------------- */
+    RMFDataset      *poDS;
+
+    poDS = new RMFDataset();
+
+    if( poOpenInfo->eAccess == GA_ReadOnly )
+        poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
+    else
+        poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" );
+    if ( !poDS->fp )
+    {
+        delete poDS;
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*  Read the file header.                                               */
+/* -------------------------------------------------------------------- */
+#define RMF_READ_ULONG(value, offset)                               \
+    (value) = CPL_LSBWORD32(*(GUInt32*)(poDS->abyHeader + (offset)))
+
+#define RMF_READ_LONG(value, offset)                                \
+    (value) = CPL_LSBWORD32(*(GInt32*)(poDS->abyHeader + (offset)))
+
+#define RMF_READ_DOUBLE(value, offset)                              \
+{                                                                   \
+    (value) = *(double*)(poDS->abyHeader + (offset));               \
+    CPL_LSBPTR64(&(value));                                         \
+}
+
+    VSIFSeekL( poDS->fp, 0, SEEK_SET );
+    VSIFReadL( poDS->abyHeader, 1, RMF_HEADER_SIZE, poDS->fp );
+
+    memcpy( poDS->sHeader.szSignature, poDS->abyHeader, RMF_SIGNATURE_SIZE );
+    poDS->sHeader.szSignature[3] = '\0'; // Paranoid
+    RMF_READ_ULONG( poDS->sHeader.iVersion, 4 );
+    RMF_READ_ULONG( poDS->sHeader.nSize, 8 );
+    RMF_READ_ULONG( poDS->sHeader.nOvrOffset, 12 );
+    RMF_READ_ULONG( poDS->sHeader.iUserID, 16 );
+    memcpy( poDS->sHeader.byName, poDS->abyHeader + 20, RMF_NAME_SIZE );
+    poDS->sHeader.byName[31] = '\0';
+    RMF_READ_ULONG( poDS->sHeader.nBitDepth, 52 );
+    RMF_READ_ULONG( poDS->sHeader.nHeight, 56 );
+    RMF_READ_ULONG( poDS->sHeader.nWidth, 60 );
+    RMF_READ_ULONG( poDS->sHeader.nXTiles, 64 );
+    RMF_READ_ULONG( poDS->sHeader.nYTiles, 68 );
+    RMF_READ_ULONG( poDS->sHeader.nTileHeight, 72 );
+    RMF_READ_ULONG( poDS->sHeader.nTileWidth, 76 );
+    RMF_READ_ULONG( poDS->sHeader.nLastTileHeight, 80 );
+    RMF_READ_ULONG( poDS->sHeader.nLastTileWidth, 84 );
+    RMF_READ_ULONG( poDS->sHeader.nROIOffset, 88 );
+    RMF_READ_ULONG( poDS->sHeader.nROISize, 92 );
+    RMF_READ_ULONG( poDS->sHeader.nClrTblOffset, 96 );
+    RMF_READ_ULONG( poDS->sHeader.nClrTblSize, 100 );
+    RMF_READ_ULONG( poDS->sHeader.nTileTblOffset, 104 );
+    RMF_READ_ULONG( poDS->sHeader.nTileTblSize, 108 );
+    RMF_READ_LONG( poDS->sHeader.iMapType, 124 );
+    RMF_READ_LONG( poDS->sHeader.iProjection, 128 );
+    RMF_READ_DOUBLE( poDS->sHeader.dfScale, 136 );
+    RMF_READ_DOUBLE( poDS->sHeader.dfResolution, 144 );
+    RMF_READ_DOUBLE( poDS->sHeader.dfPixelSize, 152 );
+    RMF_READ_DOUBLE( poDS->sHeader.dfLLX, 160 );
+    RMF_READ_DOUBLE( poDS->sHeader.dfLLY, 168 );
+    RMF_READ_DOUBLE( poDS->sHeader.dfStdP1, 176 );
+    RMF_READ_DOUBLE( poDS->sHeader.dfStdP2, 184 );
+    RMF_READ_DOUBLE( poDS->sHeader.dfCenterLong, 192 );
+    RMF_READ_DOUBLE( poDS->sHeader.dfCenterLat, 200 );
+    poDS->sHeader.iCompression = *(poDS->abyHeader + 208);
+    poDS->sHeader.iMaskType = *(poDS->abyHeader + 209);
+    poDS->sHeader.iMaskStep = *(poDS->abyHeader + 210);
+    poDS->sHeader.iFrameFlag = *(poDS->abyHeader + 211);
+    RMF_READ_ULONG( poDS->sHeader.nFlagsTblOffset, 212 );
+    RMF_READ_ULONG( poDS->sHeader.nFlagsTblSize, 216 );
+    RMF_READ_ULONG( poDS->sHeader.nFileSize0, 220 );
+    RMF_READ_ULONG( poDS->sHeader.nFileSize1, 224 );
+    poDS->sHeader.iUnknown = *(poDS->abyHeader + 228);
+    poDS->sHeader.iGeorefFlag = *(poDS->abyHeader + 244);
+    poDS->sHeader.iInverse = *(poDS->abyHeader + 245);
+    memcpy( poDS->sHeader.abyInvisibleColors,
+            poDS->abyHeader + 248, RMF_INVISIBLE_COLORS_SIZE );
+    RMF_READ_DOUBLE( poDS->sHeader.dfElevMinMax[0], 280 );
+    RMF_READ_DOUBLE( poDS->sHeader.dfElevMinMax[1], 288 );
+    RMF_READ_DOUBLE( poDS->sHeader.dfNoData, 296 );
+    RMF_READ_ULONG( poDS->sHeader.iElevationUnit, 304 );
+    RMF_READ_ULONG( poDS->sHeader.iElevationType, 308 );
+
+#undef RMF_READ_DOUBLE
+#undef RMF_READ_LONG
+#undef RMF_READ_ULONG
+
+#ifdef DEBUG
+    CPLDebug( "RMF", "%s image has width %d, height %d, bit depth %d, "
+              "compression scheme %d",
+              poDS->sHeader.szSignature, poDS->sHeader.nWidth,
+              poDS->sHeader.nHeight, poDS->sHeader.nBitDepth,
+              poDS->sHeader.iCompression );
+    CPLDebug( "RMF", "Size %d, offset to overview 0x%x, user ID %d, "
+              "ROI offset 0x%x, ROI size %d",
+              poDS->sHeader.nSize, poDS->sHeader.nOvrOffset,
+              poDS->sHeader.iUserID, poDS->sHeader.nROIOffset,
+              poDS->sHeader.nROISize );
+    CPLDebug( "RMF", "Map type %d, projection %d, scale %f, resolution %f, ",
+              poDS->sHeader.iMapType, poDS->sHeader.iProjection,
+              poDS->sHeader.dfScale, poDS->sHeader.dfResolution );
+#endif
+
+/* -------------------------------------------------------------------- */
+/*  Read array of blocks offsets/sizes.                                 */
+/* -------------------------------------------------------------------- */
+    GUInt32 i;
+
+    if ( VSIFSeekL( poDS->fp, poDS->sHeader.nTileTblOffset, SEEK_SET ) < 0)
+    {
+        delete poDS;
+        return NULL;
+    }
+
+    poDS->paiTiles = (GUInt32 *)CPLMalloc( poDS->sHeader.nTileTblSize );
+    if ( !poDS->paiTiles )
+    {
+        delete poDS;
+        return NULL;
+    }
+
+    if ( VSIFReadL( poDS->paiTiles, 1,
+                    poDS->sHeader.nTileTblSize, poDS->fp ) < 0 )
+    {
+        CPLDebug( "RMF", "Can't read tiles offsets/sizes table." );
+        delete poDS;
+        return NULL;
+    }
+
+#if DEBUG
+    CPLDebug( "RMF", "List of block offsets/sizes:" );
+
+    for ( i = 0; i < poDS->sHeader.nTileTblSize / 4; i += 2 )
+        CPLDebug( "RMF", "    %d / %d",
+                  poDS->paiTiles[i], poDS->paiTiles[i + 1] );
+#endif
+
+/* -------------------------------------------------------------------- */
+/*  Set up essential image parameters.                                  */
+/* -------------------------------------------------------------------- */
+    GDALDataType eType = GDT_Byte;
+
+    poDS->eRMFType =
+        ( EQUAL( poDS->sHeader.szSignature, "MTW" ) ) ? RMFT_MTW : RMFT_RSW;
+    poDS->nRasterXSize = poDS->sHeader.nWidth;
+    poDS->nRasterYSize = poDS->sHeader.nHeight;
+
+    if ( poDS->eRMFType == RMFT_RSW )
+    {
+        switch ( poDS->sHeader.nBitDepth )
+        {
+            case 32:
+            case 24:
+            case 16:
+                poDS->nBands = 3;
+                break;
+            case 1:
+            case 4:
+            case 8:
+                {
+                    // Allocate memory for colour table and read it
+                    poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth;
+                    if ( poDS->nColorTableSize * 4
+                         > poDS->sHeader.nClrTblSize )
+                    {
+                        CPLDebug( "RMF",
+                                "Wrong color table size. Expected %d, got %d.",
+                                poDS->nColorTableSize * 4,
+                                poDS->sHeader.nClrTblSize );
+                        delete poDS;
+                        return NULL;
+                    }
+                    poDS->pabyColorTable =
+                        (GByte *)CPLMalloc( poDS->sHeader.nClrTblSize );
+                    if ( VSIFSeekL( poDS->fp, poDS->sHeader.nClrTblOffset,
+                                    SEEK_SET ) < 0 )
+                    {
+                        CPLDebug( "RMF",
+                                  "Can't seek to color table location." );
+                        delete poDS;
+                        return NULL;
+                    }
+                    if ( VSIFReadL( poDS->pabyColorTable, 1,
+                                    poDS->sHeader.nClrTblSize, poDS->fp )
+                         < poDS->sHeader.nClrTblSize )
+                    {
+                        CPLDebug( "RMF", "Can't read color table." );
+                        delete poDS;
+                        return NULL;
+                    }
+
+                    GDALColorEntry oEntry;
+                    poDS->poColorTable = new GDALColorTable();
+                    for( i = 0; i < poDS->nColorTableSize; i++ )
+                    {
+                        oEntry.c1 = poDS->pabyColorTable[i * 4];     // Red
+                        oEntry.c2 = poDS->pabyColorTable[i * 4 + 1]; // Green
+                        oEntry.c3 = poDS->pabyColorTable[i * 4 + 2]; // Blue
+                        oEntry.c4 = 255;                             // Alpha
+
+                        poDS->poColorTable->SetColorEntry( i, &oEntry );
+                    }
+                }
+                poDS->nBands = 1;
+                break;
+            default:
+                break;
+        }
+        eType = GDT_Byte;
+    }
+    else
+    {
+        poDS->nBands = 1;
+        if ( poDS->sHeader.nBitDepth == 8 )
+            eType = GDT_Byte;
+        else if ( poDS->sHeader.nBitDepth == 16 )
+            eType = GDT_Int16;
+        else if ( poDS->sHeader.nBitDepth == 32 )
+            eType = GDT_Int32;
+        else if ( poDS->sHeader.nBitDepth == 64 )
+            eType = GDT_Float64;
+    }
+
+    poDS->nXTiles = ( poDS->nRasterXSize + poDS->sHeader.nTileWidth - 1 ) /
+        poDS->sHeader.nTileWidth;
+    poDS->nYTiles = ( poDS->nRasterYSize + poDS->sHeader.nTileHeight - 1 ) /
+        poDS->sHeader.nTileHeight;
+
+#if DEBUG
+    CPLDebug( "RMF", "Image is %d tiles wide, %d tiles long",
+              poDS->nXTiles, poDS->nYTiles );
+#endif
+
+/* -------------------------------------------------------------------- */
+/*  Create band information objects.                                    */
+/* -------------------------------------------------------------------- */
+    int iBand;
+
+    for( iBand = 1; iBand <= poDS->nBands; iBand++ )
+        poDS->SetBand( iBand, new RMFRasterBand( poDS, iBand, eType ) );
+
+/* -------------------------------------------------------------------- */
+/*  Set up projection.                                                  */
+/* -------------------------------------------------------------------- */
+    if( poDS->sHeader.iProjection > 0 )
+    {
+        OGRSpatialReference oSRS;
+
+        oSRS.importFromPanorama( poDS->sHeader.iProjection, 0, 0, 0,
+                                 poDS->sHeader.dfStdP1, poDS->sHeader.dfStdP2,
+                                 poDS->sHeader.dfCenterLat,
+                                 poDS->sHeader.dfCenterLong );
+        if ( poDS->pszProjection )
+            CPLFree( poDS->pszProjection );
+        oSRS.exportToWkt( &poDS->pszProjection );
+    }
+
+/* -------------------------------------------------------------------- */
+/*  Set up georeferencing.                                              */
+/* -------------------------------------------------------------------- */
+    if ( (poDS->eRMFType == RMFT_RSW && poDS->sHeader.iGeorefFlag) ||
+         (poDS->eRMFType == RMFT_MTW && poDS->sHeader.dfPixelSize != 0.0) )
+    {
+        poDS->adfGeoTransform[0] = poDS->sHeader.dfLLX;
+        poDS->adfGeoTransform[3] = poDS->sHeader.dfLLY
+            + poDS->nRasterYSize * poDS->sHeader.dfPixelSize;
+        poDS->adfGeoTransform[1] = poDS->sHeader.dfPixelSize;
+        poDS->adfGeoTransform[5] = - poDS->sHeader.dfPixelSize;
+        poDS->adfGeoTransform[2] = 0.0;
+        poDS->adfGeoTransform[4] = 0.0;
+    }
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                               Create()                               */
+/************************************************************************/
+
+GDALDataset *RMFDataset::Create( const char * pszFilename,
+                                 int nXSize, int nYSize, int nBands,
+                                 GDALDataType eType, char **papszParmList )
+
+{
+    if ( nBands != 1 && nBands != 3 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported,
+                  "RMF driver doesn't support %d bands. Must be 1 or 3.\n",
+                  nBands );
+
+        return NULL;
+    }
+
+    if ( nBands == 1
+         && eType != GDT_Byte
+         && eType != GDT_Int16
+         && eType != GDT_Int32
+         && eType != GDT_Float64 )
+    {
+         CPLError( CE_Failure, CPLE_AppDefined,
+              "Attempt to create RMF dataset with an illegal data type (%s),\n"
+              "only Byte, Int16, Int32 and Float64 types supported "
+              "by the format for single-band images.\n",
+              GDALGetDataTypeName(eType) );
+
+        return NULL;
+    }
+
+    if ( nBands == 3 && eType != GDT_Byte )
+    {
+         CPLError( CE_Failure, CPLE_AppDefined,
+              "Attempt to create RMF dataset with an illegal data type (%s),\n"
+              "only Byte type supported by the format for three-band images.\n",
+              GDALGetDataTypeName(eType) );
+
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*  Create the dataset.                                                 */
+/* -------------------------------------------------------------------- */
+    RMFDataset      *poDS;
+
+    poDS = new RMFDataset();
+
+    poDS->fp = VSIFOpenL( pszFilename, "wb+" );
+    if( poDS->fp == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, "Unable to create file %s.\n",
+                  pszFilename );
+        return NULL;
+    }
+
+    poDS->pszFilename = pszFilename;
+
+/* -------------------------------------------------------------------- */
+/*  Fill the RMFHeader                                                  */
+/* -------------------------------------------------------------------- */
+    GUInt32     nTileSize, nCurPtr = 0;
+    GUInt32     nBlockXSize =
+        ( nXSize < RMF_DEFAULT_BLOCKXSIZE ) ? nXSize : RMF_DEFAULT_BLOCKXSIZE;
+    GUInt32     nBlockYSize =
+        ( nYSize < RMF_DEFAULT_BLOCKYSIZE ) ? nYSize : RMF_DEFAULT_BLOCKYSIZE;
+    const char  *pszValue;
+
+    if ( CSLFetchBoolean( papszParmList, "MTW", FALSE) )
+        poDS->eRMFType = RMFT_MTW;
+    else
+        poDS->eRMFType = RMFT_RSW;
+    if ( poDS->eRMFType == RMFT_MTW )
+        memcpy( poDS->sHeader.szSignature, "MTW\0", RMF_SIGNATURE_SIZE );
+    else
+        memcpy( poDS->sHeader.szSignature, "RSW\0", RMF_SIGNATURE_SIZE );
+    poDS->sHeader.iVersion = 0x0200;
+    poDS->sHeader.nOvrOffset = 0x00;
+    poDS->sHeader.iUserID = 0x00;
+    memset( poDS->sHeader.byName, 0, RMF_NAME_SIZE );
+    poDS->sHeader.nBitDepth = GDALGetDataTypeSize( eType ) * nBands;
+    poDS->sHeader.nHeight = nYSize;
+    poDS->sHeader.nWidth = nXSize;
+
+    pszValue = CSLFetchNameValue(papszParmList,"BLOCKXSIZE");
+    if( pszValue != NULL )
+        nBlockXSize = atoi( pszValue );
+
+    pszValue = CSLFetchNameValue(papszParmList,"BLOCKYSIZE");
+    if( pszValue != NULL )
+        nBlockYSize = atoi( pszValue );
+
+    poDS->sHeader.nTileWidth = nBlockXSize;
+    poDS->sHeader.nTileHeight = nBlockYSize;
+
+    poDS->nXTiles = poDS->sHeader.nXTiles =
+        ( nXSize + poDS->sHeader.nTileWidth - 1 ) / poDS->sHeader.nTileWidth;
+    poDS->nYTiles = poDS->sHeader.nYTiles =
+        ( nYSize + poDS->sHeader.nTileHeight - 1 ) / poDS->sHeader.nTileHeight;
+    poDS->sHeader.nLastTileHeight = nYSize % poDS->sHeader.nTileHeight;
+    if ( !poDS->sHeader.nLastTileHeight )
+        poDS->sHeader.nLastTileHeight = poDS->sHeader.nTileHeight;
+    poDS->sHeader.nLastTileWidth = nXSize % poDS->sHeader.nTileWidth;
+    if ( !poDS->sHeader.nLastTileWidth )
+        poDS->sHeader.nLastTileWidth = poDS->sHeader.nTileWidth;
+    
+    poDS->sHeader.nROIOffset = 0x00;
+    poDS->sHeader.nROISize = 0x00;
+    nCurPtr += RMF_HEADER_SIZE;
+
+    // Color table
+    if ( poDS->eRMFType == RMFT_RSW && nBands == 1 )
+    {
+        GUInt32 i;
+
+        poDS->sHeader.nClrTblOffset = nCurPtr;
+        poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth;
+        poDS->sHeader.nClrTblSize = poDS->nColorTableSize * 4;
+        poDS->pabyColorTable = (GByte *) CPLMalloc( poDS->sHeader.nClrTblSize );
+        for ( i = 0; i < poDS->nColorTableSize; i++ )
+        {
+            poDS->pabyColorTable[i * 4] =
+                poDS->pabyColorTable[i * 4 + 1] =
+                poDS->pabyColorTable[i * 4 + 2] = (GByte) i;
+                poDS->pabyColorTable[i * 4 + 3] = 0;
+        }
+        nCurPtr += poDS->sHeader.nClrTblSize;
+    }
+    else
+    {
+        poDS->sHeader.nClrTblOffset = 0x00;
+        poDS->sHeader.nClrTblSize = 0x00;
+    }
+
+    // Blocks table
+    poDS->sHeader.nTileTblOffset = nCurPtr;
+    poDS->sHeader.nTileTblSize =
+        poDS->sHeader.nXTiles * poDS->sHeader.nYTiles * 4 * 2;
+    poDS->paiTiles = (GUInt32 *)CPLMalloc( poDS->sHeader.nTileTblSize );
+    memset( poDS->paiTiles, 0, poDS->sHeader.nTileTblSize );
+    nCurPtr += poDS->sHeader.nTileTblSize;
+    nTileSize = poDS->sHeader.nTileWidth * poDS->sHeader.nTileHeight
+        * GDALGetDataTypeSize( eType ) / 8;
+    poDS->sHeader.nSize =
+        poDS->paiTiles[poDS->sHeader.nTileTblSize / 4 - 2] + nTileSize;
+
+    poDS->sHeader.iMapType = -1;
+    poDS->sHeader.iProjection = -1;
+    poDS->sHeader.dfScale = 10000.0;
+    poDS->sHeader.dfResolution = 100.0;
+    poDS->sHeader.iCompression = 0;
+    poDS->sHeader.iMaskType = 0;
+    poDS->sHeader.iMaskStep = 0;
+    poDS->sHeader.iFrameFlag = 0;
+    poDS->sHeader.nFlagsTblOffset = 0x00;
+    poDS->sHeader.nFlagsTblSize = 0x00;
+    poDS->sHeader.nFileSize0 = 0x00;
+    poDS->sHeader.nFileSize1 = 0x00;
+    poDS->sHeader.iUnknown = 0;
+    poDS->sHeader.iGeorefFlag = 0;
+    poDS->sHeader.iInverse = 0;
+    memset( poDS->sHeader.abyInvisibleColors, 0, RMF_INVISIBLE_COLORS_SIZE );
+    poDS->sHeader.dfElevMinMax[0] = 0.0;
+    poDS->sHeader.dfElevMinMax[1] = 0.0;
+    poDS->sHeader.dfNoData = 0.0;
+    poDS->sHeader.iElevationUnit = 0;
+    poDS->sHeader.iElevationType = 0;
+
+    poDS->nRasterXSize = nXSize;
+    poDS->nRasterYSize = nYSize;
+    poDS->eAccess = GA_Update;
+    poDS->nBands = nBands;
+
+    poDS->WriteHeader();
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    int         iBand;
+
+    for( iBand = 1; iBand <= poDS->nBands; iBand++ )
+        poDS->SetBand( iBand, new RMFRasterBand( poDS, iBand, eType ) );
+
+    return (GDALDataset *) poDS;
+}
+
+/************************************************************************/
+/*                        GDALRegister_RMF()                            */
+/************************************************************************/
+
+void GDALRegister_RMF()
+
+{
+    GDALDriver  *poDriver;
+
+    if( GDALGetDriverByName( "RMF" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+
+        poDriver->SetDescription( "RMF" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
+                                   "Raster Matrix Format" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
+                                   "frmt_rmf.html" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "rsw" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
+"<CreationOptionList>"
+"   <Option name='MTW' type='boolean' description='Create MTW DEM matrix'/>"
+"   <Option name='BLOCKXSIZE' type='int' description='Tile Width'/>"
+"   <Option name='BLOCKYSIZE' type='int' description='Tile Height'/>"
+"</CreationOptionList>" );
+
+        poDriver->pfnOpen = RMFDataset::Open;
+        poDriver->pfnCreate = RMFDataset::Create;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/rs2/GNUmakefile b/Utilities/GDAL/frmts/rs2/GNUmakefile
new file mode 100644
index 0000000000..3833142d62
--- /dev/null
+++ b/Utilities/GDAL/frmts/rs2/GNUmakefile
@@ -0,0 +1,13 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	rs2dataset.o
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/rs2/frmt_rs2.html b/Utilities/GDAL/frmts/rs2/frmt_rs2.html
new file mode 100644
index 0000000000..d2243f69a5
--- /dev/null
+++ b/Utilities/GDAL/frmts/rs2/frmt_rs2.html
@@ -0,0 +1,38 @@
+<html>
+<head>
+<title>RS2 -- RadarSat 2 XML Product</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>RS2 -- RadarSat 2 XML Product</h1>
+
+This driver will read some RadarSat 2 XML polarimetric products.  In
+particular complex products, and 16bit magnitude detected products.<p>
+
+The RadarSat 2 XML products are distributed with a primary XML file called
+product.xml, and a set of supporting XML data files with the actual imagery
+stored in TIFF files.  The RS2 driver will be used if the product.xml is
+selected, and it can treat all the imagery as one consistent dataset.<p>
+
+The complex products use "32bit void typed" TIFF files which are not 
+meaningfully readable normally.  The RS2 driver takes care of converting
+this into useful CInt16 format internally.<p>
+
+The RS2 driver also reads geolocation tiepoints from the product.xml file
+and represents them as GCPs on the dataset. <p>
+
+It is very likely that RadarSat International will be distributing 
+other sorts of datasets in this format; however, at this time this driver
+only supports specific RadarSat 2 polarimetric products.  All other will 
+be ignored, or result in various runtime errors.  It is hoped that this
+driver can be generalized with other product samples become available. <p>
+
+See Also:<p>
+
+<ul>
+<li> RadarSat document RN-RP-51-27.
+</ul>
+
+</body>
+</html>
diff --git a/Utilities/GDAL/frmts/rs2/makefile.vc b/Utilities/GDAL/frmts/rs2/makefile.vc
new file mode 100644
index 0000000000..3de3c18d3d
--- /dev/null
+++ b/Utilities/GDAL/frmts/rs2/makefile.vc
@@ -0,0 +1,13 @@
+
+OBJ = rs2dataset.obj
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/rs2/rs2dataset.cpp b/Utilities/GDAL/frmts/rs2/rs2dataset.cpp
new file mode 100644
index 0000000000..00612da422
--- /dev/null
+++ b/Utilities/GDAL/frmts/rs2/rs2dataset.cpp
@@ -0,0 +1,521 @@
+/******************************************************************************
+ * $Id: rs2dataset.cpp,v 1.5 2005/05/05 15:54:49 fwarmerdam Exp $
+ *
+ * Project:  Polarimetric Workstation
+ * Purpose:  Radarsat 2 - XML Products (product.xml) driver
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: rs2dataset.cpp,v $
+ * Revision 1.5  2005/05/05 15:54:49  fwarmerdam
+ * PAM Enabled
+ *
+ * Revision 1.4  2004/11/11 00:16:01  gwalter
+ * Polarmetric->Polarimetric.
+ *
+ * Revision 1.3  2004/11/02 19:38:19  fwarmerdam
+ * set help topic
+ *
+ * Revision 1.2  2004/10/30 18:54:58  fwarmerdam
+ * Added support for UInt16 data (magnitude detected), and also fixed
+ * so that the new "32bit void" tiff files can be read as CInt16.
+ *
+ * Revision 1.1  2004/10/21 20:03:09  fwarmerdam
+ * New
+ *
+ */
+
+#include "gdal_pam.h"
+#include "cpl_minixml.h"
+
+CPL_CVSID("$Id: rs2dataset.cpp,v 1.5 2005/05/05 15:54:49 fwarmerdam Exp $");
+
+CPL_C_START
+void	GDALRegister_RS2(void);
+CPL_C_END
+
+/************************************************************************/
+/* ==================================================================== */
+/*				RS2Dataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class RS2Dataset : public GDALPamDataset
+{
+    CPLXMLNode *psProduct;
+
+    int           nGCPCount;
+    GDAL_GCP      *pasGCPList;
+    char          *pszGCPProjection;
+
+  public:
+    		RS2Dataset();
+    	        ~RS2Dataset();
+    
+    virtual int    GetGCPCount();
+    virtual const char *GetGCPProjection();
+    virtual const GDAL_GCP *GetGCPs();
+
+    static GDALDataset *Open( GDALOpenInfo * );
+
+    CPLXMLNode *GetProduct() { return psProduct; }
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*			        RS2RasterBand                           */
+/* ==================================================================== */
+/************************************************************************/
+
+class RS2RasterBand : public GDALPamRasterBand
+{
+    GDALDataset     *poBandFile;
+
+  public:
+    		RS2RasterBand( RS2Dataset *poDSIn, 
+                               GDALDataType eDataTypeIn,
+                               const char *pszPole, 
+                               GDALDataset *poBandFile );
+    virtual     ~RS2RasterBand();
+    
+    virtual CPLErr IReadBlock( int, int, void * );
+
+    static GDALDataset *Open( GDALOpenInfo * );
+};
+
+/************************************************************************/
+/*                            RS2RasterBand                             */
+/************************************************************************/
+
+RS2RasterBand::RS2RasterBand( RS2Dataset *poDSIn,
+                              GDALDataType eDataTypeIn,
+                              const char *pszPole, 
+                              GDALDataset *poBandFileIn )
+
+{
+    GDALRasterBand *poSrcBand;
+
+    poDS = poDSIn;
+    poBandFile = poBandFileIn;
+
+    poSrcBand = poBandFile->GetRasterBand( 1 );
+
+    poSrcBand->GetBlockSize( &nBlockXSize, &nBlockYSize );
+    
+    eDataType = eDataTypeIn;
+
+    if( *pszPole != '\0' )
+        SetMetadataItem( "POLARIMETRIC_INTERP", pszPole );
+}
+
+/************************************************************************/
+/*                            RSRasterBand()                            */
+/************************************************************************/
+
+RS2RasterBand::~RS2RasterBand()
+
+{
+    if( poBandFile != NULL )
+        GDALClose( (GDALRasterBandH) poBandFile );
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr RS2RasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+
+{
+    int nRequestYSize;
+
+/* -------------------------------------------------------------------- */
+/*      If the last strip is partial, we need to avoid                  */
+/*      over-requesting.  We also need to initialize the extra part     */
+/*      of the block to zero.                                           */
+/* -------------------------------------------------------------------- */
+    if( (nBlockYOff + 1) * nBlockYSize > nRasterYSize )
+    {
+        nRequestYSize = nRasterYSize - nBlockYOff * nBlockYSize;
+        memset( pImage, 0, (GDALGetDataTypeSize( eDataType ) / 8) * nBlockXSize * nBlockYSize );
+    }
+    else
+    {
+        nRequestYSize = nBlockYSize;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Each complex component is a seperate sample in the TIFF file    */
+/*      (old way)                                                       */
+/* -------------------------------------------------------------------- */
+    if( eDataType == GDT_CInt16 && poBandFile->GetRasterCount() == 2 )
+        return 
+            poBandFile->RasterIO( GF_Read, 
+                                  nBlockXOff * nBlockXSize, 
+                                  nBlockYOff * nBlockYSize,
+                                  nBlockXSize, nRequestYSize,
+                                  pImage, nBlockXSize, nRequestYSize, 
+                                  GDT_Int16,
+                                  2, NULL, 4, nBlockXSize * 4, 2 );
+
+/* -------------------------------------------------------------------- */
+/*      File has one sample marked as sample format void, a 32bits.     */
+/* -------------------------------------------------------------------- */
+    else if( eDataType == GDT_CInt16 && poBandFile->GetRasterCount() == 1 )
+    {
+        CPLErr eErr;
+
+        eErr = 
+            poBandFile->RasterIO( GF_Read, 
+                                  nBlockXOff * nBlockXSize, 
+                                  nBlockYOff * nBlockYSize,
+                                  nBlockXSize, nRequestYSize, 
+                                  pImage, nBlockXSize, nRequestYSize, 
+                                  GDT_UInt32,
+                                  1, NULL, 4, nBlockXSize * 4, 0 );
+
+#ifdef CPL_LSB
+        // First, undo the 32bit swap. 
+        GDALSwapWords( pImage, 4, nBlockXSize * nBlockYSize, 4 );
+
+        // Then apply 16 bit swap. 
+        GDALSwapWords( pImage, 2, nBlockXSize * nBlockYSize * 2, 2 );
+#endif        
+
+        return eErr;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      The 16bit case is straight forward.  The underlying file        */
+/*      looks like a 16bit unsigned data too.                           */
+/* -------------------------------------------------------------------- */
+    else if( eDataType == GDT_UInt16 )
+        return 
+            poBandFile->RasterIO( GF_Read, 
+                                  nBlockXOff * nBlockXSize, 
+                                  nBlockYOff * nBlockYSize,
+                                  nBlockXSize, nRequestYSize, 
+                                  pImage, nBlockXSize, nRequestYSize,
+                                  GDT_UInt16,
+                                  1, NULL, 2, nBlockXSize * 2, 0 );
+    else
+    {
+        CPLAssert( FALSE );
+        return CE_Failure;
+    }
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*				RS2Dataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                             RS2Dataset()                             */
+/************************************************************************/
+
+RS2Dataset::RS2Dataset()
+{
+    psProduct = NULL;
+
+    nGCPCount = 0;
+    pasGCPList = NULL;
+    pszGCPProjection = CPLStrdup("");
+
+}
+
+/************************************************************************/
+/*                            ~RS2Dataset()                             */
+/************************************************************************/
+
+RS2Dataset::~RS2Dataset()
+
+{
+    FlushCache();
+
+    CPLDestroyXMLNode( psProduct );
+
+    CPLFree( pszGCPProjection );
+    if( nGCPCount > 0 )
+    {
+        GDALDeinitGCPs( nGCPCount, pasGCPList );
+        CPLFree( pasGCPList );
+    }
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *RS2Dataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Is this a RadardSat 2 Product.xml definition?                   */
+/* -------------------------------------------------------------------- */
+    if( strlen(poOpenInfo->pszFilename) < 11 
+        || !EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename)-11,
+                  "product.xml") )
+        return NULL;
+
+    if( poOpenInfo->nHeaderBytes < 100 )
+        return NULL;
+
+    if( strstr((const char *) poOpenInfo->pabyHeader, "/rs2" ) == NULL
+        || strstr((const char *) poOpenInfo->pabyHeader, "<product" ) == NULL)
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Ingest the Product.xml file.                                    */
+/* -------------------------------------------------------------------- */
+    CPLXMLNode *psProduct, *psImageAttributes;
+
+    psProduct = CPLParseXMLFile( poOpenInfo->pszFilename );
+    if( psProduct == NULL )
+        return NULL;
+
+    psImageAttributes = CPLGetXMLNode(psProduct, "=product.imageAttributes" );
+    if( psImageAttributes == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "Failed to find <imageAttributes> in document." );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create the dataset.                                             */
+/* -------------------------------------------------------------------- */
+    RS2Dataset *poDS = new RS2Dataset();
+
+    poDS->psProduct = psProduct;
+
+/* -------------------------------------------------------------------- */
+/*      Get overall image information.                                  */
+/* -------------------------------------------------------------------- */
+    poDS->nRasterXSize = 
+        atoi(CPLGetXMLValue( psImageAttributes, 
+                             "rasterAttributes.numberOfSamplesPerLine", 
+                             "-1" ));
+    poDS->nRasterYSize = 
+        atoi(CPLGetXMLValue( psImageAttributes, 
+                             "rasterAttributes.numberofLines", 
+                             "-1" ));
+
+/* -------------------------------------------------------------------- */
+/*      Get dataType (so we can recognise complex data), and the        */
+/*      bitsPerSample.                                                  */
+/* -------------------------------------------------------------------- */
+    GDALDataType eDataType;
+
+    const char *pszDataType = 
+        CPLGetXMLValue( psImageAttributes, "rasterAttributes.dataType", "" );
+    int nBitsPerSample = 
+        atoi( CPLGetXMLValue( psImageAttributes, 
+                              "rasterAttributes.bitsPerSample", "" ) );
+
+    if( nBitsPerSample == 16 && EQUAL(pszDataType,"Complex") )
+        eDataType = GDT_CInt16;
+    else if( nBitsPerSample == 16 && EQUALN(pszDataType,"Mag",3) )
+        eDataType = GDT_UInt16;
+    else
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "dataType=%s, bitsPerSample=%d: not a supported configuration.",
+                  pszDataType, nBitsPerSample );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Open each of the data files as a complex band.                  */
+/* -------------------------------------------------------------------- */
+    CPLXMLNode *psNode;
+    char *pszPath = CPLStrdup(CPLGetPath( poOpenInfo->pszFilename ));
+
+    for( psNode = psImageAttributes->psChild;
+         psNode != NULL;
+         psNode = psNode->psNext )
+    {
+        RS2RasterBand *poBand;
+        const char *pszBasename;
+
+        if( psNode->eType != CXT_Element 
+            || !EQUAL(psNode->pszValue,"fullResolutionImageData") )
+            continue;
+
+/* -------------------------------------------------------------------- */
+/*      Fetch filename.                                                 */
+/* -------------------------------------------------------------------- */
+        pszBasename = CPLGetXMLValue( psNode, "", "" );
+        if( *pszBasename == '\0' )
+            continue;
+
+/* -------------------------------------------------------------------- */
+/*      Form full filename (path of product.xml + basename).            */
+/* -------------------------------------------------------------------- */
+        char *pszFullname = 
+            CPLStrdup(CPLFormFilename( pszPath, pszBasename, NULL ));
+
+/* -------------------------------------------------------------------- */
+/*      Try and open the file.                                          */
+/* -------------------------------------------------------------------- */
+        GDALDataset *poBandFile;
+
+        poBandFile = (GDALDataset *) GDALOpen( pszFullname, GA_ReadOnly );
+        if( poBandFile == NULL )
+            continue;
+        
+/* -------------------------------------------------------------------- */
+/*      Create the band.                                                */
+/* -------------------------------------------------------------------- */
+        poBand = new RS2RasterBand( poDS, eDataType,
+                                    CPLGetXMLValue( psNode, "pole", "" ), 
+                                    poBandFile ); 
+
+        poDS->SetBand( poDS->GetRasterCount() + 1, poBand );
+
+        CPLFree( pszFullname );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Collect GCPs.                                                   */
+/* -------------------------------------------------------------------- */
+    CPLXMLNode *psGeoGrid = 
+        CPLGetXMLNode( psImageAttributes, 
+                       "geographicInformation.geolocationGrid" );
+
+    if( psGeoGrid != NULL )
+    {
+        // count gcps.
+        poDS->nGCPCount = 0;
+        
+        for( psNode = psGeoGrid->psChild; psNode != NULL;
+             psNode = psNode->psNext )
+        {
+            if( EQUAL(psNode->pszValue,"imageTiePoint") )
+                poDS->nGCPCount++ ;
+        }
+
+        poDS->pasGCPList = (GDAL_GCP *) 
+            CPLCalloc(sizeof(GDAL_GCP),poDS->nGCPCount);
+        
+        poDS->nGCPCount = 0;
+        
+        for( psNode = psGeoGrid->psChild; psNode != NULL;
+             psNode = psNode->psNext )
+        {
+            char	szID[32];
+            GDAL_GCP   *psGCP = poDS->pasGCPList + poDS->nGCPCount;
+            
+            if( !EQUAL(psNode->pszValue,"imageTiePoint") )
+                continue;
+
+            poDS->nGCPCount++ ;
+
+            sprintf( szID, "%d", poDS->nGCPCount );
+            psGCP->pszId = CPLStrdup( szID );
+            psGCP->pszInfo = CPLStrdup("");
+            psGCP->dfGCPPixel = 
+                atof(CPLGetXMLValue(psNode,"imageCoordinate.pixel","0"));
+            psGCP->dfGCPLine = 
+                atof(CPLGetXMLValue(psNode,"imageCoordinate.line","0"));
+            psGCP->dfGCPX = 
+                atof(CPLGetXMLValue(psNode,"geodeticCoordinate.longitude",""));
+            psGCP->dfGCPY = 
+                atof(CPLGetXMLValue(psNode,"geodeticCoordinate.latitude",""));
+            psGCP->dfGCPZ = 
+                atof(CPLGetXMLValue(psNode,"geodeticCoordinate.height",""));
+        }
+    }
+
+    CPLFree( pszPath );
+
+/* -------------------------------------------------------------------- */
+/*      Check for overviews.                                            */
+/* -------------------------------------------------------------------- */
+    poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                            GetGCPCount()                             */
+/************************************************************************/
+
+int RS2Dataset::GetGCPCount()
+
+{
+    return nGCPCount;
+}
+
+/************************************************************************/
+/*                          GetGCPProjection()                          */
+/************************************************************************/
+
+const char *RS2Dataset::GetGCPProjection()
+
+{
+    return pszGCPProjection;
+}
+
+/************************************************************************/
+/*                               GetGCPs()                              */
+/************************************************************************/
+
+const GDAL_GCP *RS2Dataset::GetGCPs()
+
+{
+    return pasGCPList;
+}
+
+/************************************************************************/
+/*                         GDALRegister_RS2()                          */
+/************************************************************************/
+
+void GDALRegister_RS2()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "RS2" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "RS2" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "RadarSat 2 XML Product" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_rs2.html" );
+
+        poDriver->pfnOpen = RS2Dataset::Open;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/sgi/GNUmakefile b/Utilities/GDAL/frmts/sgi/GNUmakefile
new file mode 100644
index 0000000000..c5c189ada2
--- /dev/null
+++ b/Utilities/GDAL/frmts/sgi/GNUmakefile
@@ -0,0 +1,18 @@
+
+include ../../GDALmake.opt
+
+OBJ	= sgidataset.o
+
+CPPFLAGS	:=	$(XTRA_OPT) $(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+../o/%.o:
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@
+
+all:	$(OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/sgi/makefile.vc b/Utilities/GDAL/frmts/sgi/makefile.vc
new file mode 100644
index 0000000000..7929dfee09
--- /dev/null
+++ b/Utilities/GDAL/frmts/sgi/makefile.vc
@@ -0,0 +1,14 @@
+GDAL_ROOT	=	..\..
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+
+OBJ	=    sgidataset.obj
+
+EXTRAFLAGS = -I./
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/sgi/sgidataset.cpp b/Utilities/GDAL/frmts/sgi/sgidataset.cpp
new file mode 100644
index 0000000000..7b10cff487
--- /dev/null
+++ b/Utilities/GDAL/frmts/sgi/sgidataset.cpp
@@ -0,0 +1,571 @@
+/******************************************************************************
+ * $Id: sgidataset.cpp,v 1.2 2006/02/26 14:26:10 fwarmerdam Exp $
+ *
+ * Project:  SGI Image Driver
+ * Purpose:  Implement SGI Image Support based on Paul Bourke's SGI Image code.
+ *           http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/
+ *           ftp://ftp.sgi.com/graphics/SGIIMAGESPEC
+ * Authors:  Mike Mazzella (GDAL driver)
+ *           Paul Bourke (original SGI format code)
+ *
+ ******************************************************************************
+ * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************
+ *
+ * $Log: sgidataset.cpp,v $
+ * Revision 1.2  2006/02/26 14:26:10  fwarmerdam
+ * Optimizations from Mike.
+ * http://bugzilla.remotesensing.org/show_bug.cgi?id=1046
+ *
+ * Revision 1.1  2005/12/20 16:20:31  fwarmerdam
+ * New
+ *
+ */
+
+#include "gdal_pam.h"
+#include "cpl_port.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: sgidataset.cpp,v 1.2 2006/02/26 14:26:10 fwarmerdam Exp $");
+CPL_C_START
+void	GDALRegister_SGI(void);
+CPL_C_END
+
+struct ImageRec
+{
+    GUInt16 imagic;
+    GByte type;
+    GByte bpc;
+    GUInt16 dim;
+    GUInt16 xsize;
+    GUInt16 ysize;
+    GUInt16 zsize;
+    GUInt32 min;
+    GUInt32 max;
+    char wasteBytes[4];
+    char name[80];
+    GUInt32 colorMap;
+
+    FILE* file;
+    std::string fileName;
+    unsigned char* tmp;
+    unsigned char* tmpR;
+    unsigned char* tmpG;
+    unsigned char* tmpB;
+    GUInt32 rleEnd;
+    GUInt32* rowStart;
+    GInt32* rowSize;
+
+    ImageRec()
+            : imagic(0),
+              type(0),
+              bpc(1),
+              dim(0),
+              xsize(0),
+              ysize(0),
+              zsize(0),
+              min(0),
+              max(0),
+              colorMap(0),
+              file(NULL),
+              fileName(""),
+              tmp(NULL),
+              tmpR(NULL),
+              tmpG(NULL),
+              tmpB(NULL),
+              rleEnd(0),
+              rowStart(NULL),
+              rowSize(NULL)
+        {
+            memset(wasteBytes, 0, 4);
+            memset(name, 0, 80);
+        }
+
+    void Swap()
+        {
+#ifdef CPL_LSB
+            CPL_SWAP16PTR(&imagic);
+            CPL_SWAP16PTR(&dim);
+            CPL_SWAP16PTR(&xsize);
+            CPL_SWAP16PTR(&ysize);
+            CPL_SWAP16PTR(&zsize);
+            CPL_SWAP32PTR(&min);
+            CPL_SWAP32PTR(&max);
+#endif
+        }
+};
+
+/************************************************************************/
+/*                            ConvertLong()                             */
+/************************************************************************/
+static void ConvertLong(GUInt32* array, GInt32 length) 
+{
+#ifdef CPL_LSB
+   GUInt32* ptr;
+   ptr = (GUInt32*)array;
+   while(length--)
+     CPL_SWAP32PTR(ptr++);
+#endif
+}
+
+/************************************************************************/
+/*                            ImageGetRow()                             */
+/************************************************************************/
+static void ImageGetRow(ImageRec* image, unsigned char* buf, int y, int z) 
+{
+    unsigned char *iPtr, *oPtr, pixel;
+    int count;
+
+    y = image->ysize - 1 - y;
+
+    if(int(image->type) == 1)
+    {
+        // reads row
+        VSIFSeekL(image->file, (long)image->rowStart[y+z*image->ysize], SEEK_SET);
+        if(VSIFReadL(image->tmp, 1, (GUInt32)image->rowSize[y+z*image->ysize], image->file) != (GUInt32)image->rowSize[y+z*image->ysize])
+        {
+            CPLError(CE_Failure, CPLE_OpenFailed, "file read error: row (%d) of (%s)\n", y, image->fileName.empty() ? "none" : image->fileName.c_str());
+            return;
+        }
+
+        // expands row
+        iPtr = image->tmp;
+        oPtr = buf;
+        int xsizeCount = 0;
+        for(;;)
+        {
+            pixel = *iPtr++;
+            count = (int)(pixel & 0x7F);
+            if(!count)
+            {
+                if(xsizeCount != image->xsize)
+                    CPLError(CE_Failure, CPLE_OpenFailed, "file read error: row (%d) of (%s)\n", y, image->fileName.empty() ? "none" : image->fileName.c_str());
+                return;
+            }
+            if(pixel & 0x80)
+            {
+	      memcpy(oPtr, iPtr, count);
+	      iPtr += count;
+            }
+            else
+            {
+                pixel = *iPtr++;
+		memset(oPtr, pixel, count);
+            }
+	    oPtr += count;
+	    xsizeCount += count;
+        }
+    }
+    else
+    {
+        VSIFSeekL(image->file, 512+(y*image->xsize)+(z*image->xsize*image->ysize), SEEK_SET);
+        if(VSIFReadL(buf, 1, image->xsize, image->file) != image->xsize)
+        {
+            CPLError(CE_Failure, CPLE_OpenFailed, "file read error: row (%d) of (%s)\n", y, image->fileName.empty() ? "none" : image->fileName.c_str());
+            return;
+        }
+    }
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*				SGIDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class SGIRasterBand;
+
+class SGIDataset : public GDALPamDataset
+{
+    friend class SGIRasterBand;
+
+    FILE*  fpImage;
+
+    int	   bGeoTransformValid;
+    double adfGeoTransform[6];
+
+    char** papszMetadata;
+    char** papszSubDatasets;
+
+    ImageRec image;
+
+public:
+    SGIDataset();
+    ~SGIDataset();
+
+    virtual CPLErr GetGeoTransform(double*);
+    static GDALDataset* Open(GDALOpenInfo*);
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            SGIRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class SGIRasterBand : public GDALPamRasterBand
+{
+    friend class SGIDataset;
+
+public:
+    SGIRasterBand(SGIDataset*, int);
+
+    virtual CPLErr IReadBlock(int, int, void*);
+    virtual GDALColorInterp GetColorInterpretation();
+};
+
+
+/************************************************************************/
+/*                           SGIRasterBand()                            */
+/************************************************************************/
+
+SGIRasterBand::SGIRasterBand(SGIDataset* poDS, int nBand)
+
+{
+  this->poDS = poDS;
+  this->nBand = nBand;
+  if(poDS == NULL)
+  {
+    eDataType = GDT_Byte;
+  }
+  else
+  {
+    if(int(poDS->image.bpc) == 1)
+      eDataType = GDT_Byte;
+    else
+      eDataType = GDT_Int16;
+  }
+  nBlockXSize = poDS->nRasterXSize;;
+  nBlockYSize = 1;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr SGIRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
+				 void*  pImage)
+
+{
+    SGIDataset* poGDS = (SGIDataset*) poDS;
+    
+    CPLAssert(nBlockXOff == 0);
+    if(nBlockXOff != 0)
+    {
+        printf("ERROR:  unhandled block value\n");
+        exit(0);
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Load the desired data into the working buffer.              */
+/* -------------------------------------------------------------------- */
+    ImageGetRow(&(poGDS->image), (unsigned char*)pImage, nBlockYOff, nBand-1);
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp SGIRasterBand::GetColorInterpretation()
+
+{
+    SGIDataset* poGDS = (SGIDataset*)poDS;
+
+    if(poGDS->nBands == 1)
+        return GCI_GrayIndex;
+    else if(poGDS->nBands == 2)
+    {
+        if(nBand == 1)
+            return GCI_GrayIndex;
+        else
+            return GCI_AlphaBand;
+    }
+    else if(poGDS->nBands == 3)
+    {
+        if(nBand == 1)
+            return GCI_RedBand;
+        else if(nBand == 2)
+            return GCI_GrayIndex;
+        else
+            return GCI_BlueBand;
+    }
+    else if(poGDS->nBands == 4)
+    {
+        if(nBand == 1)
+            return GCI_RedBand;
+        else if(nBand == 2)
+            return GCI_GrayIndex;
+        else if(nBand == 3)
+            return GCI_BlueBand;
+        else
+            return GCI_AlphaBand;
+    }
+    return GCI_Undefined;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             SGIDataset                               */
+/* ==================================================================== */
+/************************************************************************/
+
+
+/************************************************************************/
+/*                            SGIDataset()                              */
+/************************************************************************/
+
+SGIDataset::SGIDataset()
+  : fpImage(NULL),
+    bGeoTransformValid(FALSE),
+    papszMetadata(NULL),
+    papszSubDatasets(NULL)
+{
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+}
+
+/************************************************************************/
+/*                           ~SGIDataset()                            */
+/************************************************************************/
+
+SGIDataset::~SGIDataset()
+
+{
+    FlushCache();
+
+    if(fpImage != NULL)
+        VSIFCloseL(fpImage);
+
+    if(papszMetadata != NULL) CSLDestroy(papszMetadata);
+
+    if(image.tmp != NULL) CPLFree(image.tmp);
+    if(image.tmpR != NULL) CPLFree(image.tmpR);
+    if(image.tmpG != NULL) CPLFree(image.tmpG);
+    if(image.tmpB != NULL) CPLFree(image.tmpB);
+    if(image.rowSize != NULL) CPLFree(image.rowSize);
+    if(image.rowStart != NULL) CPLFree(image.rowStart);
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr SGIDataset::GetGeoTransform(double * padfTransform)
+
+{
+    if(bGeoTransformValid)
+    {
+        memcpy(padfTransform, adfGeoTransform, sizeof(double)*6);
+        
+        return CE_None;
+    }
+    else 
+        return GDALPamDataset::GetGeoTransform(padfTransform);
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset* SGIDataset::Open(GDALOpenInfo* poOpenInfo)
+
+{
+/* -------------------------------------------------------------------- */
+/*	First we check to see if the file has the expected header	*/
+/*	bytes.								*/    
+/* -------------------------------------------------------------------- */
+    if(poOpenInfo->nHeaderBytes < 12)
+        return NULL;
+
+    ImageRec tmpImage;
+    memcpy(&tmpImage, poOpenInfo->pabyHeader, 12);
+    tmpImage.Swap();
+
+    if(tmpImage.imagic != 474)
+        return NULL;
+
+    if(tmpImage.bpc != 1)
+    {
+        CPLError(CE_Failure, CPLE_NotSupported, 
+                 "The SGI driver only supports 1 byte channel values.\n");
+        return NULL;
+    }
+  
+    if(poOpenInfo->eAccess == GA_Update)
+    {
+        CPLError(CE_Failure, CPLE_NotSupported, 
+                 "The SGI driver does not support update access to existing"
+                 " datasets.\n");
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    SGIDataset* poDS;
+
+    poDS = new SGIDataset();
+
+/* -------------------------------------------------------------------- */
+/*      Open the file using the large file api.                         */
+/* -------------------------------------------------------------------- */
+    poDS->fpImage = VSIFOpenL(poOpenInfo->pszFilename, "rb");
+    if(poDS->fpImage == NULL)
+    {
+        CPLError(CE_Failure, CPLE_OpenFailed, "VSIFOpenL(%s) failed unexpectedly in sgidataset.cpp", poOpenInfo->pszFilename);
+        return NULL;
+    }
+    // printf("----------- %s\n", poOpenInfo->pszFilename);
+
+    poDS->eAccess = GA_ReadOnly;
+
+/* -------------------------------------------------------------------- */
+/*	Read pre-image data after ensuring the file is rewound.         */
+/* -------------------------------------------------------------------- */
+    VSIFSeekL(poDS->fpImage, 0, SEEK_SET);
+    if(VSIFReadL((void*)(&(poDS->image)), 1, 12, poDS->fpImage) != 12)
+    {
+        CPLError(CE_Failure, CPLE_OpenFailed, "file read error while reading header in sgidataset.cpp");
+        return NULL;
+    }
+    poDS->image.Swap();
+    poDS->image.file = poDS->fpImage;
+    poDS->image.fileName = poOpenInfo->pszFilename;
+
+/* -------------------------------------------------------------------- */
+/*      Capture some information from the file that is of interest.     */
+/* -------------------------------------------------------------------- */
+    poDS->nRasterXSize = poDS->image.xsize;
+    poDS->nRasterYSize = poDS->image.ysize;
+    poDS->nBands = poDS->image.zsize;
+    int numItems = (int(poDS->image.bpc) == 1) ? 256 : 65536;
+    poDS->image.tmp = (unsigned char*)CPLMalloc(poDS->image.xsize*numItems);
+    poDS->image.tmpR = (unsigned char*)CPLMalloc(poDS->image.xsize*numItems);
+    poDS->image.tmpG = (unsigned char*)CPLMalloc(poDS->image.xsize*numItems);
+    poDS->image.tmpB = (unsigned char*)CPLMalloc(poDS->image.xsize*numItems);
+    if((poDS->image.tmp == NULL) || (poDS->image.tmpR == NULL) ||
+       (poDS->image.tmpG == NULL) || (poDS->image.tmpB == NULL))
+    {
+      if(poDS->image.tmp != NULL) {CPLFree(poDS->image.tmp); poDS->image.tmp = NULL;}
+      if(poDS->image.tmpR != NULL) {CPLFree(poDS->image.tmpR); poDS->image.tmpR = NULL;}
+      if(poDS->image.tmpG != NULL) {CPLFree(poDS->image.tmpG); poDS->image.tmpG = NULL;}
+      if(poDS->image.tmpB != NULL) {CPLFree(poDS->image.tmpB); poDS->image.tmpB = NULL;}
+      CPLError(CE_Failure, CPLE_OpenFailed, "ran out of memory in sgidataset.cpp");
+      return NULL;
+    }
+    memset(poDS->image.tmp, 0, poDS->image.xsize*numItems);
+    memset(poDS->image.tmpR, 0, poDS->image.xsize*numItems);
+    memset(poDS->image.tmpG, 0, poDS->image.xsize*numItems);
+    memset(poDS->image.tmpB, 0, poDS->image.xsize*numItems);
+
+    if(int(poDS->image.type) == 1)
+    {
+        int x = poDS->image.ysize * poDS->image.zsize * sizeof(GUInt32);
+        poDS->image.rowStart = (GUInt32*)CPLMalloc(x);
+        poDS->image.rowSize = (GInt32*)CPLMalloc(x);
+        memset(poDS->image.rowStart, 0, x);
+        memset(poDS->image.rowSize, 0, x);
+        if(poDS->image.rowStart == NULL || poDS->image.rowSize == NULL)
+        {
+	  if(poDS->image.tmp != NULL) {CPLFree(poDS->image.tmp); poDS->image.tmp = NULL;}
+	  if(poDS->image.tmpR != NULL) {CPLFree(poDS->image.tmpR); poDS->image.tmpR = NULL;}
+	  if(poDS->image.tmpG != NULL) {CPLFree(poDS->image.tmpG); poDS->image.tmpG = NULL;}
+	  if(poDS->image.tmpB != NULL) {CPLFree(poDS->image.tmpB); poDS->image.tmpB = NULL;}
+	  CPLError(CE_Failure, CPLE_OpenFailed, "ran out of memory in sgidataset.cpp");
+	  return NULL;
+        }
+        poDS->image.rleEnd = 512 + (2 * x);
+        VSIFSeekL(poDS->fpImage, 512, SEEK_SET);
+        if((int) VSIFReadL(poDS->image.rowStart, 1, x, poDS->image.file) != x)
+        {
+            CPLError(CE_Failure, CPLE_OpenFailed, "file read error while reading start positions in sgidataset.cpp");
+            return NULL;
+        }
+        if((int) VSIFReadL(poDS->image.rowSize, 1, x, poDS->image.file) != x)
+        {
+            CPLError(CE_Failure, CPLE_OpenFailed, "file read error while reading row lengths in sgidataset.cpp");
+            return NULL;
+        }
+        ConvertLong(poDS->image.rowStart, x/(int)sizeof(GUInt32));
+        ConvertLong((GUInt32*)poDS->image.rowSize, x/(int)sizeof(GInt32));
+    }
+    else
+    {
+        poDS->image.rowStart = NULL;
+        poDS->image.rowSize = NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    for(int iBand = 0; iBand < poDS->nBands; iBand++)
+        poDS->SetBand(iBand+1, new SGIRasterBand(poDS, iBand+1));
+
+/* -------------------------------------------------------------------- */
+/*      Check for world file.                                           */
+/* -------------------------------------------------------------------- */
+    poDS->bGeoTransformValid = 
+        GDALReadWorldFile(poOpenInfo->pszFilename, ".wld", 
+                          poDS->adfGeoTransform);
+
+/* -------------------------------------------------------------------- */
+/*      Check for overviews.                                            */
+/* -------------------------------------------------------------------- */
+    poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription(poOpenInfo->pszFilename);
+    poDS->TryLoadXML();
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                         GDALRegister_SGI()                          */
+/************************************************************************/
+
+void GDALRegister_SGI()
+
+{
+    GDALDriver*  poDriver;
+
+    if(GDALGetDriverByName("SGI") == NULL)
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription("SGI");
+        poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, 
+                                  "SGI Image File Format 1.0");
+        poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "rgb");
+        poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/rgb");
+        poDriver->pfnOpen = SGIDataset::Open;
+        GetGDALDriverManager()->RegisterDriver(poDriver);
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/terragen/GNUmakefile b/Utilities/GDAL/frmts/terragen/GNUmakefile
new file mode 100644
index 0000000000..c86bfad7c5
--- /dev/null
+++ b/Utilities/GDAL/frmts/terragen/GNUmakefile
@@ -0,0 +1,13 @@
+
+include ../../GDALmake.opt
+
+OBJ	=	terragendataset.o
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/terragen/frmt_terragen.html b/Utilities/GDAL/frmts/terragen/frmt_terragen.html
new file mode 100644
index 0000000000..b5fa5a638e
--- /dev/null
+++ b/Utilities/GDAL/frmts/terragen/frmt_terragen.html
@@ -0,0 +1,82 @@
+<html>
+<head>
+	<title>Terragen --- Terragen Terrain File</title>
+</head>
+
+<body bgcolor="#ffffff">
+
+<h1>Terragen --- Terragen&trade; Terrain File</h1>
+
+Terragen terrain files store 16-bit elevation values with 
+optional gridspacing (but not positioning).
+The file extension for Terragen heightfields is "TER" or "TERRAIN" (which 
+in the former case is the same as Leveller, but the driver only recognizes Terragen files). The driver ID is "Terragen".
+The dataset is file-based and has only one elevation band.
+Void elevations are not supported. Pixels are considered points.
+<p>
+
+<h2>Reading</h2>
+<blockquote>
+dataset::GetProjectionRef() returns a local coordinate system 
+using meters.
+<p>
+band::GetUnitType() returns meters.
+<p>
+Elevations are Int16. You must use the band::GetScale() and 
+band::GetOffset() to convert them to meters.
+<p>&nbsp;<br>
+</blockquote>
+
+<h2>Writing</h2>
+<blockquote>
+Use the Create call. Set the MINUSERPIXELVALUE option (a float) 
+to the lowest elevation of your elevation data, and 
+MAXUSERPIXELVALUE to the highest. The units must match 
+the elevation units you will give to band::SetUnitType().
+<p>
+Call dataset::SetProjection() and dataset::SetGeoTransform()
+with your coordinate system details. Otherwise, the driver 
+will not encode physical elevations properly. Geographic 
+(degree-based) coordinate systems will be converted to 
+a local meter-based system.
+<p>
+Elevations are Float32.
+<p>&nbsp;<br>
+</blockquote>
+
+
+<h2>Roundtripping</h2>
+<blockquote>
+Errors per trip tend to be a few centimeters for elevations 
+and up to one or two meters for ground extents if 
+degree-based coordinate systems are written. Large degree-based 
+DEMs incur unavoidable distortions since the driver currently 
+only uses meters.
+<p>&nbsp;<br>
+</blockquote>
+
+<p>
+
+<h2>History</h2>
+v1.0 (Mar 26/06): Created.<br>
+v1.1 (Apr 20/06): Added Create() support and SIZE-only read fix. 
+<p>
+
+<h2>See Also:</h2>
+
+<ul>
+<li> Implemented as <tt>gdal/frmts/terragen/terragendataset.cpp</tt>.<p>
+
+<li>
+See <a href="./readme.txt">readme.txt</a> 
+for installation and support information.<p>
+
+<li> <a href="http://www.planetside.co.uk/terragen/dev/tgterrain.html">
+Terragen Terrain File Specification</a>.<p>
+
+</ul>
+
+
+</body>
+</html>
+
diff --git a/Utilities/GDAL/frmts/terragen/makefile.vc b/Utilities/GDAL/frmts/terragen/makefile.vc
new file mode 100644
index 0000000000..a12392e71d
--- /dev/null
+++ b/Utilities/GDAL/frmts/terragen/makefile.vc
@@ -0,0 +1,13 @@
+
+OBJ	=	terragendataset.obj
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/terragen/readme.txt b/Utilities/GDAL/frmts/terragen/readme.txt
new file mode 100644
index 0000000000..e06f1b33fa
--- /dev/null
+++ b/Utilities/GDAL/frmts/terragen/readme.txt
@@ -0,0 +1,14 @@
+Terragen(tm) GDAL Driver 1.1
+Copyright 2006 Daylon Graphics Ltd.
+Support: support@daylongraphics.com
+
+
+This is a GDAL driver for Terragen heightfield files.
+
+...
+
+For more information on Terragen, please visit
+the Planetside website at
+http://www.planetside.co.uk/terragen
+
+
diff --git a/Utilities/GDAL/frmts/terragen/terragendataset.cpp b/Utilities/GDAL/frmts/terragen/terragendataset.cpp
new file mode 100644
index 0000000000..cb7833b58e
--- /dev/null
+++ b/Utilities/GDAL/frmts/terragen/terragendataset.cpp
@@ -0,0 +1,1101 @@
+/******************************************************************************
+ * terragendataset.cpp,v 1.1
+ *
+ * Project:  Terragen(tm) TER Driver
+ * Purpose:  Reader for Terragen TER documents
+ * Author:   Ray Gardener, Daylon Graphics Ltd.
+ *
+ * Portions of this module derived from GDAL drivers by 
+ * Frank Warmerdam, see http://www.gdal.org
+
+ rcg	apr 19/06	Fixed bug with hf size being misread by one
+					if xpts/ypts tags not included in file.
+					Added Create() support.
+					Treat pixels as points.
+
+ *
+ ******************************************************************************
+ * Copyright (c) 2006 Daylon Graphics Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ */
+
+/*
+	Terragen format notes:
+
+	Based on official Planetside specs.
+
+	All distances along all three axes are in
+	terrain units, which are 30m by default.
+	If a SCAL chunk is present, however, it
+	can indicate something other than 30.
+	Note that uniform scaling should be used.
+
+	The offset (base height) in the ALTW chunk 
+	is in terrain units, and the scale (height scale)
+	is a normalized value using unsigned 16-bit notation.
+	The physical terrain value for a read pixel is
+		hv' = hv * scale / 65536 + offset.
+		It still needs to be scaled by SCAL to 
+		get to meters.
+
+	For writing:
+			SCAL = gridpost distance in meters
+			hv_px = hv_m / SCAL
+			span_px = span_m / SCAL
+			offset = span_px.midpoint
+			scale = span_px
+			physical hv =
+				(hv_px - offset) * 65536.0/scale
+
+
+	We tell callers that:
+
+		Elevations are Int16 when reading, 
+		and Float32 when writing. We need logical 
+		elevations when writing so that we can 
+		encode them with as much precision as possible 
+		when going down to physical 16-bit ints.
+		Implementing band::SetScale/SetOffset won't work because 
+		it requires callers to know format write details.
+		So we've added two Create() options that let the 
+		caller tell us the span's logical extent, and with 
+		those two values we can convert to physical pixels.
+
+		band::GetUnitType() returns meters.
+		band::GetScale() returns SCAL * (scale/65536)
+		band::GetOffset() returns SCAL * offset 
+		ds::GetProjectionRef() returns a local CS
+			using meters.
+		ds::GetGeoTransform() returns a scale matrix
+			having SCAL sx,sy members.
+
+		ds::SetGeoTransform() lets us establish the
+			size of ground pixels.
+		ds::SetProjection() lets us establish what
+			units ground measures are in (also needed 
+			to calc the size of ground pixels).
+		band::SetUnitType() tells us what units
+			the given Float32 elevations are in.
+		band::SetScale() is unused.
+		band::SetOffset() is unused.
+*/
+
+#include "cpl_string.h"
+#include "gdal_pam.h"
+#include "ogr_spatialref.h"
+
+// CPL_CVSID("$Id: terragendataset.cpp,v 1.1 2006/04/20 13:54:12 fwarmerdam Exp $");
+
+CPL_C_START
+void	GDALRegister_Terragen(void);
+CPL_C_END
+
+
+const double kdEarthCircumPolar = 40007849;
+const double kdEarthCircumEquat = 40075004;
+
+#define str_equal(_s1, _s2)	(0 == strcmp((_s1),(_s2)))
+#define array_size(_a)		(sizeof(_a) / sizeof(_a[0]))
+
+#ifndef min
+	#define min(a, b)	( (a) < (b) ? (a) : (b) )
+#endif
+
+static double average(double a, double b)
+{
+	return 0.5 * (a + b);
+}
+
+
+static double degrees_to_radians(double d) 
+{
+	return (d * 0.017453292);
+}
+
+static bool approx_equal(double a, double b)
+{
+	const double epsilon = 1e-5;
+	return (fabs(a-b) <= epsilon);
+}
+
+
+
+/************************************************************************/
+/* ==================================================================== */
+/*				TerragenDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class TerragenRasterBand;
+
+class TerragenDataset : public GDALPamDataset
+{
+    friend class TerragenRasterBand;
+
+
+    double		m_dScale,
+        m_dOffset,
+        m_dSCAL, // 30.0 normally, from SCAL chunk
+        m_adfTransform[6],
+        m_dGroundScale,
+        m_dMetersPerGroundUnit,
+        m_dMetersPerElevUnit,
+        m_dLogSpan[2], 
+        m_span_m[2], 
+        m_span_px[2];
+
+    FILE*			m_fp;
+    vsi_l_offset	m_nDataOffset;
+
+    GInt16		m_nHeightScale;
+    GInt16		m_nBaseHeight;
+
+    char*		m_pszFilename;
+    char*		m_pszProjection;
+    char		m_szUnits[32];
+
+    bool		m_bIsGeo;
+
+
+    int         LoadFromFile();
+
+
+public:
+    TerragenDataset();
+    ~TerragenDataset();
+    
+    static GDALDataset* Open( GDALOpenInfo* );
+    static GDALDataset* Create( const char* pszFilename,
+                                int nXSize, int nYSize, int nBands,
+                                GDALDataType eType, char** papszOptions );
+
+    virtual CPLErr 	GetGeoTransform( double* );
+    virtual const char*	GetProjectionRef(void);
+    virtual CPLErr SetProjection( const char * );
+    virtual CPLErr SetGeoTransform( double * );
+
+protected:
+    bool get(GInt16&);
+    bool get(GUInt16&);
+    bool get(float&);
+    bool put(GInt16);
+    bool put(float);
+    bool skip(size_t n) { return ( 0 == VSIFSeekL(m_fp, n, SEEK_CUR) ); }
+    bool pad(size_t n) { return this->skip(n); }
+
+    bool read_next_tag(char*);
+    bool write_next_tag(const char*);
+    bool tag_is(const char* szTag, const char*);
+
+    bool write_header(void);
+};
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            TerragenRasterBand                        */
+/* ==================================================================== */
+/************************************************************************/
+
+class TerragenRasterBand : public GDALPamRasterBand
+{
+    friend class TerragenDataset;
+
+    void*	        m_pvLine;
+    bool            m_bFirstTime;
+
+public:
+
+    TerragenRasterBand(TerragenDataset*);
+    virtual ~TerragenRasterBand()
+	{
+            if(m_pvLine != NULL)
+                CPLFree(m_pvLine);
+	}
+    
+    // Geomeasure support.
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual const char* GetUnitType();
+    virtual double GetOffset(int* pbSuccess = NULL);
+    virtual double GetScale(int* pbSuccess = NULL);
+
+    virtual CPLErr IWriteBlock( int, int, void * );
+    virtual CPLErr SetUnitType( const char* );
+};
+
+
+/************************************************************************/
+/*                         TerragenRasterBand()                         */
+/************************************************************************/
+
+TerragenRasterBand::TerragenRasterBand( TerragenDataset *poDS )
+{
+	m_bFirstTime = true;
+    this->poDS = poDS;
+    this->nBand = 1;
+
+    eDataType = poDS->GetAccess() == GA_ReadOnly 
+		? GDT_Int16
+		: GDT_Float32;
+
+    nBlockXSize = poDS->GetRasterXSize();
+    nBlockYSize = 1;
+
+	m_pvLine = CPLMalloc(sizeof(GInt16) * nBlockXSize);
+}
+
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr TerragenRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                       void* pImage )
+
+{
+    //CPLAssert( sizeof(float) == sizeof(GInt32) );
+    CPLAssert( nBlockXOff == 0  );
+    CPLAssert( pImage != NULL );
+
+    TerragenDataset& ds = *(TerragenDataset *) poDS;
+
+/* -------------------------------------------------------------------- */
+/*      Seek to scanline.                                               
+	Terragen is a bottom-top format, so we have to
+	invert the row location.
+ -------------------------------------------------------------------- */
+    const size_t rowbytes = nBlockXSize * sizeof(GInt16);
+
+    if(0 != VSIFSeekL(
+           ds.m_fp, 
+           ds.m_nDataOffset + 
+			  (ds.GetRasterYSize() -1 - nBlockYOff) * rowbytes, 
+           SEEK_SET))
+    {
+        CPLError( CE_Failure, CPLE_FileIO, 
+                  "Terragen Seek failed:%s", VSIStrerror( errno ) );
+        return CE_Failure;
+    }
+
+
+/* -------------------------------------------------------------------- */
+/*      Read the scanline into the line buffer.                        */
+/* -------------------------------------------------------------------- */
+
+    if( VSIFReadL( pImage, rowbytes, 1, ds.m_fp ) != 1 )
+    {
+        CPLError( CE_Failure, CPLE_FileIO, 
+                  "Terragen read failed:%s", VSIStrerror( errno ) );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Swap on MSB platforms.                                          */
+/* -------------------------------------------------------------------- */
+#ifdef CPL_MSB 
+    GDALSwapWords( pImage, sizeof(GInt16), nRasterXSize, sizeof(GInt16) );
+#endif    
+
+    return CE_None;
+}
+
+
+
+/************************************************************************/
+/*                            GetUnitType()                             */
+/************************************************************************/
+const char *TerragenRasterBand::GetUnitType()
+{
+    // todo: Return elevation units.
+    // For Terragen documents, it's the same as the ground units.
+    TerragenDataset *poGDS = (TerragenDataset *) poDS;
+
+    return poGDS->m_szUnits;
+}
+
+
+/************************************************************************/
+/*                              GetScale()                              */
+/************************************************************************/
+
+double TerragenRasterBand::GetScale(int* pbSuccess)
+{
+    const TerragenDataset& ds = *(TerragenDataset *) poDS;
+	if(pbSuccess != NULL)
+		*pbSuccess = TRUE;
+	return ds.m_dScale;
+}
+
+/************************************************************************/
+/*                             GetOffset()                              */
+/************************************************************************/
+
+double TerragenRasterBand::GetOffset(int* pbSuccess)
+{
+    const TerragenDataset& ds = *(TerragenDataset *) poDS;
+	if(pbSuccess != NULL)
+		*pbSuccess = TRUE;
+	return ds.m_dOffset;
+}
+
+
+
+/************************************************************************/
+/*                             IWriteBlock()                            */
+/************************************************************************/
+
+CPLErr TerragenRasterBand::IWriteBlock
+( 
+	int nBlockXOff, 
+	int nBlockYOff,
+    void* pImage
+)
+{
+    CPLAssert( nBlockXOff == 0  );
+    CPLAssert( pImage != NULL );
+    CPLAssert( m_pvLine != NULL );
+
+#define sgn(_n) ((_n) < 0 ? -1 : ((_n) > 0 ? 1 : 0) )
+#define sround(_f)	\
+		(int)((_f) + (0.5 * sgn(_f)))
+
+    const size_t pixelsize = sizeof(GInt16);
+
+
+    TerragenDataset& ds = *(TerragenDataset*)poDS;
+    if(m_bFirstTime)
+    {
+        m_bFirstTime = false;
+        ds.write_header();
+        ds.m_nDataOffset = VSIFTellL(ds.m_fp);
+    }
+    const size_t rowbytes = nBlockXSize * pixelsize;
+
+    GInt16* pLine = (GInt16*)m_pvLine;
+
+    if(0 == VSIFSeekL(
+           ds.m_fp, 
+           ds.m_nDataOffset + 
+           // Terragen is Y inverted.
+           (ds.GetRasterYSize()-1-nBlockYOff) * rowbytes, 
+           SEEK_SET))
+    {
+        // Convert each float32 to int16.
+        float* pfImage = (float*)pImage;
+        for(size_t x = 0; x < (size_t) nBlockXSize; x++)
+        {
+            float f = pfImage[x];
+            f *= ds.m_dMetersPerElevUnit;
+            f /= ds.m_dSCAL;
+            GInt16 hv = 
+                (GInt16)((f - ds.m_nBaseHeight) *
+                         65536.0 / ds.m_nHeightScale);
+            pLine[x] = hv;
+        }
+
+#ifdef CPL_MSB 
+        GDALSwapWords( m_pvLine, pixelsize, nBlockXSize, pixelsize );
+#endif    
+        if(1 == VSIFWriteL(m_pvLine, rowbytes, 1, ds.m_fp))
+            return CE_None;
+    }
+
+    return CE_Failure;
+}
+
+
+CPLErr TerragenRasterBand::SetUnitType( const char* psz )
+{
+    TerragenDataset& ds = *(TerragenDataset*)poDS;
+
+    if(EQUAL(psz, "m"))
+        ds.m_dMetersPerElevUnit = 1.0;
+    else if(EQUAL(psz, "ft"))
+        ds.m_dMetersPerElevUnit = 0.3048;
+    else if(EQUAL(psz, "sft"))
+        ds.m_dMetersPerElevUnit = 1200.0 / 3937.0;
+    else
+        return CE_Failure;
+
+    return CE_None;
+}
+
+
+
+/************************************************************************/
+/* ==================================================================== */
+/*				TerragenDataset		                                    */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                          TerragenDataset()                           */
+/************************************************************************/
+
+TerragenDataset::TerragenDataset()
+{
+	m_pszFilename = NULL;
+    m_fp = NULL;
+	m_bIsGeo = false;
+	m_pszProjection = NULL;
+	m_nHeightScale = 65536;
+	m_nBaseHeight = 0;
+	m_dMetersPerGroundUnit = 1.0;
+	m_dSCAL = 30.0;
+	m_dLogSpan[0] = m_dLogSpan[1] = 0.0;
+
+    m_adfTransform[0] = 0.0;
+    m_adfTransform[1] = m_dSCAL;
+    m_adfTransform[2] = 0.0;
+    m_adfTransform[3] = 0.0;
+    m_adfTransform[4] = 0.0;
+    m_adfTransform[5] = m_dSCAL;
+}
+
+/************************************************************************/
+/*                          ~TerragenDataset()                          */
+/************************************************************************/
+
+TerragenDataset::~TerragenDataset()
+
+{
+    FlushCache();
+
+	CPLFree(m_pszProjection);
+	CPLFree(m_pszFilename);
+
+    if( m_fp != NULL )
+        VSIFCloseL( m_fp );
+}
+
+
+/************************************************************************/
+/*                            write_header()                            */
+/************************************************************************/
+
+bool TerragenDataset::write_header()
+{
+    char szHeader[16];
+    memcpy(szHeader, "TERRAGENTERRAIN ", sizeof(szHeader));
+
+    if(1 != VSIFWriteL( (void *) szHeader, sizeof(szHeader), 1, m_fp ))
+    {
+        CPLError( CE_Failure, CPLE_FileIO, 
+                  "Couldn't write to Terragen file %s.\n"
+                  "Is file system full?",
+                  m_pszFilename );
+        VSIFCloseL( m_fp );
+
+        return false;
+    }
+
+    
+// -------------------------------------------------------------------- 
+//      Write out the heightfield dimensions, etc.
+// -------------------------------------------------------------------- 
+
+    const int nXSize = this->GetRasterXSize();
+    const int nYSize = this->GetRasterYSize();
+
+    this->write_next_tag("SIZE");
+    this->put((GInt16)(min(nXSize, nYSize)-1));
+    this->pad(sizeof(GInt16));
+
+    if(nXSize != nYSize)
+    {
+        this->write_next_tag("XPTS");
+        this->put((GInt16)nXSize); this->pad(sizeof(GInt16));
+        this->write_next_tag("YPTS");
+        this->put((GInt16)nYSize); this->pad(sizeof(GInt16));
+    }
+
+    if(m_bIsGeo)
+    {
+        /*
+          With a geographic projection (degrees),
+          m_dGroundScale will be in degrees and
+          m_dMetersPerGroundUnit is undefined.
+          So we're going to estimate a m_dMetersPerGroundUnit
+          value here (i.e., meters per degree).
+
+          We figure out the degree size of one 
+          pixel, and then the latitude degrees 
+          of the heightfield's center. The circumference of
+          the latitude's great circle lets us know how
+          wide the pixel is in meters, and we 
+          average that with the pixel's meter breadth,
+          which is based on the polar circumference.
+        */
+
+        /*const double m_dDegLongPerPixel = 
+          fabs(m_adfTransform[1]);*/
+
+        const double m_dDegLatPerPixel = 
+            fabs(m_adfTransform[5]);
+
+        /*const double m_dCenterLongitude =
+          m_adfTransform[0] + 
+          (0.5 * m_dDegLongPerPixel * (nXSize-1));*/
+
+        const double m_dCenterLatitude =
+            m_adfTransform[3] + 
+            (0.5 * m_dDegLatPerPixel * (nYSize-1));
+
+
+        const double dLatCircum = kdEarthCircumEquat 
+            * sin(degrees_to_radians(90.0 - m_dCenterLatitude));
+
+        const double dMetersPerDegLongitude = dLatCircum / 360;
+        /*const double dMetersPerPixelX = 
+          (m_dDegLongPerPixel / 360) * dLatCircum;*/
+
+        const double dMetersPerDegLatitude = 
+            kdEarthCircumPolar / 360;
+        /*const double dMetersPerPixelY = 
+          (m_dDegLatPerPixel / 360) * kdEarthCircumPolar;*/
+
+        m_dMetersPerGroundUnit = 
+            average(dMetersPerDegLongitude, dMetersPerDegLatitude);
+
+    }
+		
+    m_dSCAL = m_dGroundScale * m_dMetersPerGroundUnit;
+
+    if(m_dSCAL != 30.0)
+    {
+        const float sc = (float)m_dSCAL;
+        this->write_next_tag("SCAL");
+        this->put(sc);
+        this->put(sc);
+        this->put(sc);
+    }
+
+    if(!this->write_next_tag("ALTW"))
+    {
+        CPLError( CE_Failure, CPLE_FileIO, 
+                  "Couldn't write to Terragen file %s.\n"
+                  "Is file system full?",
+                  m_pszFilename );
+        VSIFCloseL( m_fp );
+
+        return false;
+    }
+
+    // Compute physical scales and offsets.
+    m_span_m[0] = m_dLogSpan[0] * m_dMetersPerElevUnit;
+    m_span_m[1] = m_dLogSpan[1] * m_dMetersPerElevUnit;
+
+    m_span_px[0] = m_span_m[0] / m_dSCAL;
+    m_span_px[1] = m_span_m[1] / m_dSCAL;
+
+    m_nBaseHeight = (GInt16) 
+        average(m_span_px[0], m_span_px[1]);
+
+    m_nHeightScale = (GInt16)
+        ( floor( ((int)m_span_px[1] - m_nBaseHeight + 1) * 2) );
+
+    return (this->put(m_nHeightScale) &&
+            this->put(m_nBaseHeight));
+}
+
+
+
+/************************************************************************/
+/*                                get()                                 */
+/************************************************************************/
+
+bool TerragenDataset::get(GInt16& value)
+{
+    if(1 == VSIFReadL(&value, sizeof(value), 1, m_fp))
+    {
+        CPL_LSBPTR16(&value);
+        return true;
+    }
+    return false;
+}
+
+
+bool TerragenDataset::get(GUInt16& value)
+{
+    if(1 == VSIFReadL(&value, sizeof(value), 1, m_fp))
+    {
+        CPL_LSBPTR16(&value);
+        return true;
+    }
+    return false;
+}
+
+
+bool TerragenDataset::get(float& value)
+{
+    if(1 == VSIFReadL(&value, sizeof(value), 1, m_fp))
+    {
+        CPL_LSBPTR32(&value);
+        return true;
+    }
+    return false;
+}
+
+
+/************************************************************************/
+/*                                put()                                 */
+/************************************************************************/
+
+bool TerragenDataset::put(GInt16 n)
+{
+    CPL_LSBPTR16(&n);
+    return (1 == VSIFWriteL(&n, sizeof(n), 1, m_fp));
+}
+
+
+bool TerragenDataset::put(float f)
+{
+    CPL_LSBPTR32(&f);
+    return( 1 == VSIFWriteL(&f, sizeof(f), 1, m_fp) );
+}
+
+/************************************************************************/
+/*                              tag stuff                               */
+/************************************************************************/
+
+
+bool TerragenDataset::read_next_tag(char* szTag)
+{
+	return (1 == VSIFReadL(szTag, 4, 1, m_fp));
+}
+
+
+bool TerragenDataset::write_next_tag(const char* szTag)
+{
+	return (1 == VSIFWriteL((void*)szTag, 4, 1, m_fp));
+}
+
+
+bool TerragenDataset::tag_is(const char* szTag, const char* sz)
+{
+	return (0 == memcmp(szTag, sz, 4));
+}
+
+
+
+/************************************************************************/
+/*                            LoadFromFile()                            */
+/************************************************************************/
+
+int TerragenDataset::LoadFromFile()
+{
+    GUInt16	nSize, xpts=0, ypts=0;
+    m_dSCAL = 30.0;
+    m_nDataOffset = 0;
+
+    if(0 != VSIFSeekL(m_fp, 16, SEEK_SET))
+        return 0;
+
+    char szTag[4];
+    if(!this->read_next_tag(szTag) || !tag_is(szTag, "SIZE"))
+        return 0;
+
+    if(!this->get(nSize) || !this->skip(2))
+        return 0;
+
+    // Set dimensions to SIZE chunk. If we don't 
+    // encounter XPTS/YPTS chunks, we can assume
+    // the terrain to be square.
+    xpts = ypts = nSize+1;
+
+    while(this->read_next_tag(szTag))
+    {
+        if(this->tag_is(szTag, "XPTS"))
+        {
+            this->get(xpts);
+            if(xpts < nSize || !this->skip(2))
+                return 0;
+            continue;
+        }
+
+        if(this->tag_is(szTag, "YPTS"))
+        {
+            this->get(ypts);
+            if(ypts < nSize || !this->skip(2))
+                return 0;
+            continue;
+        }
+
+        if(this->tag_is(szTag, "SCAL"))
+        {
+            float sc[3];
+            this->get(sc[0]);
+            this->get(sc[1]);
+            this->get(sc[2]);
+            m_dSCAL = sc[1];
+            continue;
+        }
+
+        if(this->tag_is(szTag, "CRAD"))
+        {
+            if(!this->skip(sizeof(float)))
+                return 0;
+            continue;
+        }
+        if(this->tag_is(szTag, "CRVM"))
+        {
+            if(!this->skip(sizeof(GUInt32)))
+                return 0;
+            continue;
+        }
+        if(this->tag_is(szTag, "ALTW"))
+        {
+            this->get(m_nHeightScale);
+            this->get(m_nBaseHeight);
+            m_nDataOffset = VSIFTellL(m_fp);
+            if(!this->skip(xpts * ypts * sizeof(GInt16)))
+                return 0;
+            continue;
+        }
+        if(this->tag_is(szTag, "EOF "))
+        {
+            break;
+        }
+    }
+
+
+    if(xpts == 0 || ypts == 0 || m_nDataOffset == 0)
+        return 0;
+
+    nRasterXSize = xpts;
+    nRasterYSize = ypts;
+
+    // todo: sanity check: do we have enough pixels?
+
+    // Cache realworld scaling and offset.
+    m_dScale = m_dSCAL / 65536 * m_nHeightScale;
+    m_dOffset = m_dSCAL * m_nBaseHeight;
+    strcpy(m_szUnits, "m");
+
+    // Make our projection to have origin at the
+    // NW corner, and groundscale to match elev scale
+    // (i.e., uniform voxels).
+    m_adfTransform[0] = 0.0;
+    m_adfTransform[1] = m_dSCAL;
+    m_adfTransform[2] = 0.0;
+    m_adfTransform[3] = 0.0;
+    m_adfTransform[4] = 0.0;
+    m_adfTransform[5] = m_dSCAL;
+
+
+/* -------------------------------------------------------------------- */
+/*      Set projection.							*/
+/* -------------------------------------------------------------------- */
+    // Terragen files as of Apr 2006 are partially georeferenced,
+    // we can declare a local coordsys that uses meters.
+    OGRSpatialReference sr;
+
+    sr.SetLocalCS("Terragen world space");
+    if(OGRERR_NONE != sr.SetLinearUnits("m", 1.0))
+        return 0;
+
+    if(OGRERR_NONE != sr.exportToWkt(&m_pszProjection))
+        return 0;
+
+    return TRUE;
+}
+
+
+/************************************************************************/
+/*                           SetProjection()                            */
+/************************************************************************/
+
+CPLErr TerragenDataset::SetProjection( const char * pszNewProjection )
+{
+    // Terragen files aren't really georeferenced, but 
+    // we should get the projection's linear units so 
+    // that we can scale elevations correctly.
+
+    //m_dSCAL = 30.0; // default
+
+    OGRSpatialReference oSRS( pszNewProjection );
+
+/* -------------------------------------------------------------------- */
+/*      Linear units.                                                   */
+/* -------------------------------------------------------------------- */
+    m_bIsGeo = oSRS.IsGeographic();
+    if(m_bIsGeo)
+    {
+        // The caller is using degrees. We need to convert 
+        // to meters, otherwise we can't derive a SCAL
+        // value to scale elevations with.
+        m_bIsGeo = true;
+    }
+    else
+    {
+        double dfLinear = oSRS.GetLinearUnits();
+
+        if( approx_equal(dfLinear, 0.3048))
+            m_dMetersPerGroundUnit = 0.3048;
+        else if( approx_equal(dfLinear, atof(SRS_UL_US_FOOT_CONV)) )
+            m_dMetersPerGroundUnit = atof(SRS_UL_US_FOOT_CONV);
+        else
+            m_dMetersPerGroundUnit = 1.0;
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char*	TerragenDataset::GetProjectionRef(void)
+{
+    if(m_pszProjection == NULL )
+        return "";
+    else
+        return m_pszProjection;
+}
+
+/************************************************************************/
+/*                          SetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr TerragenDataset::SetGeoTransform( double *padfGeoTransform )
+{
+    memcpy(m_adfTransform, padfGeoTransform, 
+           sizeof(m_adfTransform));
+
+    // Average the projection scales.
+    m_dGroundScale = 
+        average(fabs(m_adfTransform[1]), fabs(m_adfTransform[5]));
+    return CE_None;
+}
+
+
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr TerragenDataset::GetGeoTransform(double* padfTransform)
+{
+    memcpy(padfTransform, m_adfTransform, sizeof(m_adfTransform));
+    return CE_None;
+}
+
+
+/************************************************************************/
+/*                                Create()                                */
+/************************************************************************/
+GDALDataset* TerragenDataset::Create
+(
+	const char* pszFilename,
+    int nXSize, int nYSize, int nBands,
+    GDALDataType eType, char** papszOptions 
+)
+{
+    TerragenDataset* poDS = new TerragenDataset();
+
+    poDS->eAccess = GA_Update;
+	
+    poDS->m_pszFilename = CPLStrdup(pszFilename);
+
+    // -------------------------------------------------------------------- 
+    //      Verify input options.                                           
+    // -------------------------------------------------------------------- 
+    const char* pszValue = CSLFetchNameValue( 
+        papszOptions,"MINUSERPIXELVALUE");
+    if( pszValue != NULL )
+        poDS->m_dLogSpan[0] = atof( pszValue );
+
+    pszValue = CSLFetchNameValue( 
+        papszOptions,"MAXUSERPIXELVALUE");
+    if( pszValue != NULL )
+        poDS->m_dLogSpan[1] = atof( pszValue );
+
+
+    if( poDS->m_dLogSpan[1] <= poDS->m_dLogSpan[0] )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Inverted, flat, or unspecified span for Terragen file." );
+
+        delete poDS;
+        return NULL;
+    }
+
+    if( eType != GDT_Float32 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Attempt to create Terragen dataset with a non-float32\n"
+                  "data type (%s).\n",
+                  GDALGetDataTypeName(eType) );
+
+        delete poDS;
+        return NULL;
+    }
+
+
+    if( nBands != 1 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported,
+                  "Terragen driver doesn't support %d bands. Must be 1.\n",
+                  nBands );
+
+        delete poDS;
+        return NULL;
+    }
+
+
+// -------------------------------------------------------------------- 
+//      Try to create the file.                                         
+// -------------------------------------------------------------------- 
+    
+
+    poDS->m_fp = VSIFOpenL( pszFilename, "wb+" );
+
+    if( poDS->m_fp == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Attempt to create file `%s' failed.\n",
+                  pszFilename );
+        delete poDS;
+        return NULL;
+    }
+
+    poDS->nRasterXSize = nXSize;
+    poDS->nRasterYSize = nYSize;
+
+    // Don't bother writing the header here; the first 
+    // call to IWriteBlock will do that instead, since 
+    // the elevation data's location depends on the
+    // header size.
+
+
+// -------------------------------------------------------------------- 
+//      Instance a band.                                                
+// -------------------------------------------------------------------- 
+    poDS->SetBand( 1, new TerragenRasterBand( poDS ) );
+	 
+
+    //VSIFClose( poDS->m_fp );
+
+    //return (GDALDataset *) GDALOpen( pszFilename, GA_Update );
+    return (GDALDataset *) poDS;
+
+}
+
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *TerragenDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    // The file should have at least 32 header bytes
+    if( poOpenInfo->nHeaderBytes < 32 )
+        return NULL;
+
+    if( !EQUALN((const char *) poOpenInfo->pabyHeader, 
+		"TERRAGENTERRAIN ", 16) )
+        return NULL;
+
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    TerragenDataset 	*poDS;
+
+    poDS = new TerragenDataset();
+
+    // Reopen for large file access.
+    if( poOpenInfo->eAccess == GA_Update )
+        poDS->m_fp = VSIFOpenL( poOpenInfo->pszFilename, "rb+" );
+    else
+        poDS->m_fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
+
+    if( poDS->m_fp == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Failed to re-open %s within Terragen driver.\n",
+                  poOpenInfo->pszFilename );
+        return NULL;
+    }
+    poDS->eAccess = poOpenInfo->eAccess;
+
+    
+/* -------------------------------------------------------------------- */
+/*	Read the file.							*/
+/* -------------------------------------------------------------------- */
+    if( !poDS->LoadFromFile() )
+    {
+        delete poDS;
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    poDS->SetBand( 1, new TerragenRasterBand( poDS ));
+
+    poDS->SetMetadataItem( GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT );
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return( poDS );
+}
+
+/************************************************************************/
+/*                        GDALRegister_Terragen()                       */
+/************************************************************************/
+
+void GDALRegister_Terragen()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "Terragen" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "Terragen" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, 
+                                   "ter" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "Terragen heightfield" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_terragen.html" );
+
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
+"<CreationOptionList>"
+"   <Option name='MINUSERPIXELVALUE' type='float' description='Lowest logical elevation'/>"
+"   <Option name='MAXUSERPIXELVALUE' type='float' description='Highest logical elevation'/>"
+"</CreationOptionList>" );
+        
+        poDriver->pfnOpen = TerragenDataset::Open;
+        poDriver->pfnCreate = TerragenDataset::Create;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
+
diff --git a/Utilities/GDAL/frmts/vrt/GNUmakefile b/Utilities/GDAL/frmts/vrt/GNUmakefile
new file mode 100644
index 0000000000..6424adb21f
--- /dev/null
+++ b/Utilities/GDAL/frmts/vrt/GNUmakefile
@@ -0,0 +1,21 @@
+
+
+include ../../GDALmake.opt
+
+OBJ	=	vrtdataset.o vrtrasterband.o vrtdriver.o vrtsources.o \
+		vrtfilters.o vrtsourcedrasterband.o vrtrawrasterband.o \
+		vrtwarped.o vrtderivedrasterband.o
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(CPPFLAGS) -I../raw
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+install-obj:	$(O_OBJ)
+
+$(OBJ) $(O_OBJ):	vrtdataset.h
+
+install:
+	$(INSTALL_DATA) vrtdataset.h $(INST_INCLUDE)
diff --git a/Utilities/GDAL/frmts/vrt/makefile.vc b/Utilities/GDAL/frmts/vrt/makefile.vc
new file mode 100644
index 0000000000..06ba25ba34
--- /dev/null
+++ b/Utilities/GDAL/frmts/vrt/makefile.vc
@@ -0,0 +1,17 @@
+
+OBJ	=	vrtdataset.obj vrtrasterband.obj vrtdriver.obj \
+		vrtsources.obj vrtfilters.obj vrtsourcedrasterband.obj \
+		vrtrawrasterband.obj vrtderivedrasterband.obj vrtwarped.obj
+
+GDAL_ROOT	=	..\..
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+EXTRAFLAGS = 	-I..\raw
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/vrt/vrt_tutorial.dox b/Utilities/GDAL/frmts/vrt/vrt_tutorial.dox
new file mode 100755
index 0000000000..4f3771bddd
--- /dev/null
+++ b/Utilities/GDAL/frmts/vrt/vrt_tutorial.dox
@@ -0,0 +1,613 @@
+#ifndef DOXYGEN_SKIP
+/* $Id: vrt_tutorial.dox,v 1.10 2005/10/28 16:59:50 pnagy Exp $ */
+#endif /* DOXYGEN_SKIP */
+
+/*!
+
+\page gdal_vrttut GDAL Virtual Format Tutorial
+
+\section gdal_vrttut_intro Introduction
+
+The VRT driver is a format driver for GDAL that allows a virtual GDAL dataset
+to be composed from other GDAL datasets with repositioning, and algorithms
+potentially applied as well as various kinds of metadata altered or added.
+VRT descriptions of datasets can be saved in an XML format normally given the
+extension .vrt.<p>
+
+An example of a simple .vrt file referring to a 512x512 dataset with one band
+loaded from utm.tif might look like this:
+
+\code
+<VRTDataset rasterXSize="512" rasterYSize="512">
+  <GeoTransform>440720.0, 60.0, 0.0, 3751320.0, 0.0, -60.0</GeoTransform>
+  <VRTRasterBand dataType="Byte" band="1">
+    <ColorInterp>Gray</ColorInterp>
+    <SimpleSource>
+      <SourceFilename relativeToVRT="1">utm.tif</SourceFilename>
+      <SourceBand>1</SourceBand>
+      <SrcRect xOff="0" yOff="0" xSize="512" ySize="512"/>
+      <DstRect xOff="0" yOff="0" xSize="512" ySize="512"/>
+    </SimpleSource>
+  </VRTRasterBand>
+</VRTDataset>
+\endcode
+
+VRT files can be produced by translating to VRT format.  The resulting file can
+then be edited to modify mappings, add metadata or other purposes.  VRT files
+can also be produced programmatically by various means.<p>
+
+This tutorial will cover the .vrt file format (suitable for users editing 
+.vrt files), and how .vrt files may be created and manipulated programmatically
+for developers.<p>
+
+\section gdal_vrttut_format .vrt Format
+
+Virtual files stored on disk are kept in an XML format with the following
+elements.<p>
+
+<ul>
+<li> 
+<b>VRTDataset</b>: This is the root element for the whole GDAL dataset. 
+It must have the attributes rasterXSize and rasterYSize describing the width 
+and height of the dataset in pixels.  It may have SRS, GeoTransform, 
+GCPList, Metadata, and VRTRasterBand subelements.
+
+\code
+<VRTDataset rasterXSize="512" rasterYSize="512">
+\endcode
+
+<li> <b>SRS</b>: This element contains the spatial reference system (coordinate 
+system) in OGC WKT format.  Note that this must be appropriately escaped for
+XML, so items like quotes will have the ampersand escape sequences substituted.
+As as well WKT, and valid input to the SetFromUserInput() method (such as well
+known GEOGCS names, and PROJ.4 format) is also allowed in the SRS element.
+
+\code
+  <SRS>PROJCS[&quot;NAD27 / UTM zone 11N&quot;,GEOGCS[&quot;NAD27&quot;,DATUM[&quot;North_American_Datum_1927&quot;,SPHEROID[&quot;Clarke 1866&quot;,6378206.4,294.9786982139006,AUTHORITY[&quot;EPSG&quot;,&quot;7008&quot;]],AUTHORITY[&quot;EPSG&quot;,&quot;6267&quot;]],PRIMEM[&quot;Greenwich&quot;,0],UNIT[&quot;degree&quot;,0.0174532925199433],AUTHORITY[&quot;EPSG&quot;,&quot;4267&quot;]],PROJECTION[&quot;Transverse_Mercator&quot;],PARAMETER[&quot;latitude_of_origin&quot;,0],PARAMETER[&quot;central_meridian&quot;,-117],PARAMETER[&quot;scale_factor&quot;,0.9996],PARAMETER[&quot;false_easting&quot;,500000],PARAMETER[&quot;false_northing&quot;,0],UNIT[&quot;metre&quot;,1,AUTHORITY[&quot;EPSG&quot;,&quot;9001&quot;]],AUTHORITY[&quot;EPSG&quot;,&quot;26711&quot;]]</SRS>
+\endcode
+
+<li> <b>GeoTransform</b>: This element contains a six value affine 
+geotransformation for the dataset, mapping between pixel/line coordinates 
+and georeferenced coordinates.  The list of values is the data of the SRS 
+element, and the values are separated by commas.
+<P>
+The parameter order is as follows:
+    [0] top left x;
+    [1] w-e pixel resolution;
+    [2] rotation, 0 if image is "north up";
+    [3] top left y;
+    [4] rotation, 0 if image is "north up";
+    [5] n-s pixel resolution.
+
+\code
+  <GeoTransform>440720.0,  60,  0.0,  3751320.0,  0.0, -60.0</GeoTransform>
+\endcode
+
+<li> <b>Metadata</b>: This element contains a list of metadata name/value 
+pairs associated with the VRTDataset as a whole, or a VRTRasterBand.   It has
+<MDI> (metadata item) subelements which have a "key" attribute and the value
+as the data of the element.  
+
+\code
+  <Metadata>
+    <MDI key="md_key">Metadata value</MDI>
+  </Metadata>
+\endcode
+
+<li> <b>VRTRasterBand</b>: This represents one band of a dataset.  It will
+have a dataType attribute with the type of the pixel data associated with
+this band (use names Byte, UInt16, Int16, UInt32, Int32, Float32, Float64, 
+CInt16, CInt32, CFloat32 or CFloat64) and the band this element represents
+(1 based).  This element may have Metadata, ColorInterp, NoDataValue, 
+ColorTable, and Description subelements as well as the various kinds of 
+source elements such as SimpleSource.  A raster band may have many "sources"
+indicating where the actual raster data should be fetched from, and how it
+should be mapped into the raster bands pixel space. 
+
+<li> <b>ColorInterp</b>: The data of this element should be the name of
+a color interpretation type.  One of Gray, Palette, Red, Green, Blue, Alpha,
+Hue, Saturation, Lightness, Cyan, Magenta, Yellow, Black, or Unknown.<p>
+
+\code
+  <ColorInterp>Gray</ColorInterp>: 
+\endcode
+
+<li> <b>NoDataValue</b>: If this element exists a raster band has a 
+nodata value associated with, of the value given as data in the element.
+
+\code
+  <NoDataValue>-100.0</NoDataValue>
+\endcode
+
+<li> <b>ColorTable</b>: This element is parent to a set of Entry 
+elements defining the entries in a color table.  Currently only RGBA
+color tables are supported with c1 being red, c2 being green, c3 being
+blue and c4 being alpha.  The entries are ordered and will be assumed
+to start from color table entry 0. 
+
+\code
+    <ColorTable>
+      <Entry c1="0" c2="0" c3="0" c4="255"/>
+      <Entry c1="145" c2="78" c3="224" c4="255"/>
+    </ColorTable>
+\endcode
+
+<li> <b>Description</b>: This element contains the optional description
+of a raster band as it's text value.
+
+\code
+  <Description>Crop Classification Layer</Description>
+\endcode
+
+<li> <b>UnitType</b>: This optional element contains the vertical units for
+elevation band data.  One of "m" for meters or "ft" for feet. Default 
+assumption is meters.<p>
+
+\code
+  <UnitType>ft</UnitType>
+\endcode
+
+<li> <b>Offset</b>: This optional element contains the offset that should be
+applied when computing "real" pixel values from scaled pixel values on 
+a raster band.   The default is 0.0.<p>
+
+\code
+  <Offset>0.0</Offset>
+\endcode
+
+<li> <b>Scale</b>: This optional element contains the scale that should be
+applied when computing "real" pixel values from scaled pixel values on a
+raster band.   The default is 1.0.<p>
+
+\code
+  <Scale>0.0</Scale>
+\endcode
+
+<li> <b>CategoryNames</b>: This optional element contains a list of Category
+subelements with the names of the categories for classified raster band. <p>
+
+\code
+  <CategoryNames>
+    <Category>Missing</Category>
+    <Category>Non-Crop</Category>
+    <Category>Wheat</Category>
+    <Category>Corn</Category>
+    <Category>Soybeans</Category>
+  </CategoryNames>
+\endcode
+
+<li> <b>SimpleSource</b>: The SimpleSource indicates that raster data
+should be read from a separate dataset, indicating the dataset, and band to be
+read from, and how the data should map into this bands raster space.  
+The SimpleSource may have the SourceFilename, SourceBand, SrcRect, and DstRect
+subelements.  The SrcRect element will indicate what rectangle on the indicated
+source file should be read, and the DstRect element indicates how that 
+rectangle of source data should be mapped into the VRTRasterBands space.  
+
+The relativeToVRT attribute on the SourceFilename indicates whether the
+filename should be interpreted as relative to the .vrt file (value is 1)
+or not relative to the .vrt file (value is 0).  The default is 0.
+
+\code
+    <SimpleSource>
+      <SourceFilename relativeToVRT="1">utm.tif</SourceFilename>
+      <SourceBand>1</SourceBand>
+      <SrcRect xOff="0" yOff="0" xSize="512" ySize="512"/>
+      <DstRect xOff="0" yOff="0" xSize="512" ySize="512"/>
+    </SimpleSource>
+\endcode
+
+<li> <b>KernelFilteredSource</b>: This is a pixel source derived from the
+Simple Source (so it shares the SourceFilename, SourceBand, SrcRect and 
+DestRect elements, but it also passes the data through a simple filtering 
+kernel specified with the Kernel element.  The Kernel element should have
+two child elements, Size and Coefs and optionally the boolean attribute
+normalized (defaults to false=0).  The size must always be an odd number,
+and the Coefs must have Size * Size entries separated by spaces.
+
+\code
+    <KernelFilteredSource>
+      <SourceFilename>/debian/home/warmerda/openev/utm.tif</SourceFilename>
+      <SourceBand>1</SourceBand>
+      <Kernel normalized="1">
+        <Size>3</Size>
+        <Coefs>0.11111111 0.11111111 0.11111111 0.11111111 0.11111111 0.11111111 0.11111111 0.11111111 0.11111111</Coefs>
+      </Kernel>
+    </KernelFilteredSource>
+\endcode
+
+</ul>
+
+\section gdal_vrttut_vrt .vrt Descriptions for Raw Files
+
+So far we have described how to derive new virtual datasets from existing
+files supports by GDAL.  However, it is also common to need to utilize 
+raw binary raster files for which the regular layout of the data is known
+but for which no format specific driver exists.  This can be accomplished
+by writing a .vrt file describing the raw file.  
+
+For example, the following .vrt describes a raw raster file containing
+floating point complex pixels in a file called l2p3hhsso.img.  The image
+data starts from the first byte (ImageOffset=0).  The byte offset between
+pixels is 8 (PixelOffset=8), the size of a CFloat32.  The byte offset
+from the start of one line to the start of the next is 9376 bytes 
+(LineOffset=9376) which is the width (1172) times the size of a pixel (8). 
+
+\code
+<VRTDataset rasterXSize="1172" rasterYSize="1864">
+  <VRTRasterBand dataType="CFloat32" band="1" subClass="VRTRawRasterBand">
+    <SourceFilename relativetoVRT="1">l2p3hhsso.img</SourceFilename>
+    <ImageOffset>0</ImageOffset>
+    <PixelOffset>8</PixelOffset>
+    <LineOffset>9376</LineOffset>
+    <ByteOrder>MSB</ByteOrder>
+  </VRTRasterBand>
+</VRTDataset>
+\endcode
+
+Some things to note are that the VRTRasterBand has a subClass specifier
+of "VRTRawRasterBand".  Also, the VRTRasterBand contains a number of 
+previously unseen elements but no "source" information.  VRTRawRasterBands
+may never have sources (ie. SimpleSource), but should contain the following
+elements in addition to all the normal "metadata" elements previously 
+described which are still supported. 
+
+<ul>
+
+<li> <b>SourceFilename</b>: The name of the raw file containing the
+data for this band.  The relativeToVRT attribute can be used to indicate
+if the SourceFilename is relative to the .vrt file (1) or not (0).
+
+<li> <b>ImageOffset</b>: The offset in bytes to the beginning of the first
+pixel of data of this image band.   Defaults to zero. 
+
+<li> <b>PixelOffset</b>: The offset in bytes from the beginning of one
+pixel and the next on the same line.  In packed single band data this will
+be the size of the <b>dataType</b> in bytes.
+
+<li> <b>LineOffset</b>: The offset in bytes from the beginning of one
+scanline of data and the next scanline of data.  In packed single band
+data this will be PixelOffset * rasterXSize. 
+
+<li> <b>ByteOrder</b>: Defines the byte order of the data on disk.  
+Either LSB (Least Significant Byte first) such as the natural byte order
+on Intel x86 systems or MSB (Most Significant Byte first) such as the natural
+byte order on Motorola or Sparc systems.  Defaults to being the local machine
+order. 
+
+</ul>
+
+A few other notes:
+
+<ul>
+
+<li> The image data on disk is assumed to be of the same data type as
+the band <b>dataType</b> of the VRTRawRasterBand. 
+
+<li> All the non-source attributes of the VRTRasterBand are supported, 
+including color tables, metadata, nodata values, and color interpretation.
+
+<li> The VRTRawRasterBand supports in place update of the raster, whereas
+the source based VRTRasterBand is always read-only.
+
+<li> The OpenEV tool includes a File menu option to input parameters
+describing a raw raster file in a GUI and create the corresponding .vrt
+file. 
+
+<li> Multiple bands in the one .vrt file can come from the same raw file. 
+Just ensure that the ImageOffset, PixelOffset, and LineOffset definition
+for each band is appropriate for the pixels of that particular band.  
+
+</ul>
+
+Another example, in this case a 400x300 RGB pixel interleaved image. 
+
+\code
+<VRTDataset rasterXSize="400" rasterYSize="300">
+  <VRTRasterBand dataType="Byte" band="1" subClass="VRTRawRasterBand">
+    <ColorInterp>Red</ColorInterp>
+    <SourceFilename relativetoVRT="1">rgb.raw</SourceFilename>
+    <ImageOffset>0</ImageOffset>
+    <PixelOffset>3</PixelOffset>
+    <LineOffset>1200</LineOffset>
+  </VRTRasterBand>
+  <VRTRasterBand dataType="Byte" band="2" subClass="VRTRawRasterBand">
+    <ColorInterp>Green</ColorInterp>
+    <SourceFilename relativetoVRT="1">rgb.raw</SourceFilename>
+    <ImageOffset>1</ImageOffset>
+    <PixelOffset>3</PixelOffset>
+    <LineOffset>1200</LineOffset>
+  </VRTRasterBand>
+  <VRTRasterBand dataType="Byte" band="3" subClass="VRTRawRasterBand">
+    <ColorInterp>Blue</ColorInterp>
+    <SourceFilename relativetoVRT="1">rgb.raw</SourceFilename>
+    <ImageOffset>2</ImageOffset>
+    <PixelOffset>3</PixelOffset>
+    <LineOffset>1200</LineOffset>
+  </VRTRasterBand>
+</VRTDataset>
+\endcode
+
+\section gdal_vrttut_creation Programatic Creation of VRT Datasets
+
+The VRT driver supports several methods of creating VRT datasets.  As of
+GDAL 1.2.0 the vrtdataset.h include file should be installed with the core
+GDAL include files, allowing direct access to the VRT classes.  However, 
+even without that most capabilities remain available through standard GDAL
+interfaces.<p>
+
+To create a VRT dataset that is a clone of an existing dataset use the
+CreateCopy() method.  For example to clone utm.tif into a wrk.vrt file in
+C++ the following could be used:
+
+\code
+  GDALDriver *poDriver = (GDALDriver *) GDALGetDriverByName( "VRT" );
+  GDALDataset *poSrcDS, *poVRTDS;
+
+  poSrcDS = (GDALDataset *) GDALOpenShared( "utm.tif", GA_ReadOnly );
+
+  poVRTDS = 
+    poDriver->CreateCopy( "wrk.vrt", poSrcDS, FALSE, NULL, NULL, NULL );
+  delete poVRTDS;
+  delete poSrcDS;
+\endcode
+
+To create a virtual copy of a dataset with some attributes added or changed
+such as metadata or coordinate system that are often hard to change on other
+formats, you might do the following.  In this case, the virtual dataset is
+created "in memory" only by virtual of creating it with an empty filename, and
+then used as a modified source to pass to a CreateCopy() written out in TIFF
+format. 
+
+\code
+  poVRTDS = poDriver->CreateCopy( "", poSrcDS, FALSE, NULL, NULL, NULL );
+
+  poVRTDS->SetMetadataItem( "SourceAgency", "United States Geological Survey");
+  poVRTDS->SetMetadataItem( "SourceDate", "July 21, 2003" );
+
+  poVRTDS->GetRasterBand( 1 )->SetNoDataValue( -999.0 );
+
+  GDALDriver *poTIFFDriver = (GDALDriver *) GDALGetDriverByName( "GTiff" );
+  GDALDataset *poTiffDS;
+
+  poTiffDS = 
+    poTIFFDriver->CreateCopy( "wrk.tif", poVRTDS, FALSE, NULL, NULL, NULL );
+  delete poTiffDS;
+\endcode
+
+In this example a virtual dataset is created with the Create() method, and 
+adding bands and sources programmatically, but still via the "generic" API. 
+A special attribute of VRT datasets is that sources can be added to the bands
+by passing the XML describing the source into SetMetadata() on the special
+domain target "new_vrt_sources".  The domain target "vrt_sources" may also be
+used, in which case any existing sources will be discarded before adding the
+new ones.  In this example we construct a simple averaging filter source
+instead of using the simple source. 
+
+\code
+  // construct XML for simple 3x3 average filter kernel source.
+  const char *pszFilterSourceXML  =
+"<KernelFilteredSource>"
+"  <SourceFilename>utm.tif</SourceFilename>1<SourceBand>1</SourceBand>"
+"  <Kernel>"
+"    <Size>3</Size>"
+"    <Coefs>0.111 0.111 0.111 0.111 0.111 0.111 0.111 0.111 0.111</Coefs>"
+"  </Kernel>"
+"</KernelFilteredSource>";
+
+  // Create the virtual dataset. 
+  poVRTDS = poDriver->Create( "", 512, 512, 1, GDT_Byte, NULL );
+  poVRTDS->GetRasterBand(1)->SetMetadataItem("source_0",pszFilterSourceXML",
+                                             "new_vrt_sources");
+\endcode
+
+A more general form of this that will produce a 3x3 average filtered clone
+of any input datasource might look like the following.  In this case we
+deliberately set the filtered datasource as in the "vrt_sources" domain 
+to override the SimpleSource created by the CreateCopy() method.  The fact
+that we used CreateCopy() ensures that all the other metadata, georeferencing
+and so forth is preserved from the source dataset ... the only thing we are
+changing is the data source for each band.
+
+\code
+  int   nBand;
+  GDALDriver *poDriver = (GDALDriver *) GDALGetDriverByName( "VRT" );
+  GDALDataset *poSrcDS, *poVRTDS;
+
+  poSrcDS = (GDALDataset *) GDALOpenShared( pszSourceFilename, GA_ReadOnly );
+
+  poVRTDS = poDriver->CreateCopy( "", poSrcDS, FALSE, NULL, NULL, NULL );
+
+  for( nBand = 1; nBand <= poVRTDS->GetRasterCount(); nBand++ )
+  {
+      char szFilterSourceXML[10000];
+
+      GDALRasterBand *poBand = poVRTDS->GetRasterBand( nBand );
+
+      sprintf( szFilterSourceXML, 
+	"<KernelFilteredSource>"
+	"  <SourceFilename>%s</SourceFilename>1<SourceBand>%d</SourceBand>"
+	"  <Kernel>"
+	"    <Size>3</Size>"
+	"    <Coefs>0.111 0.111 0.111 0.111 0.111 0.111 0.111 0.111 0.111</Coefs>"
+	"  </Kernel>"
+	"</KernelFilteredSource>", 
+	pszSourceFilename, nBand );
+	
+      poBand->SetMetadataItem( "source_0", szFilterSourceXML, "vrt_sources" );
+  }
+\endcode
+
+<h2>Using Derived Bands</h2>
+
+A specialized type of band is a 'derived' band which derives its pixel
+information from its source bands.  With this type of band you must also
+specify a pixel function, which has the responsibility of generating the
+output raster.  Pixel functions are created by an application and then
+registered with GDAL using a unique key.
+
+Using derived bands you can create VRT datasets that manipulate bands on
+the fly without having to create new band files on disk.  For example, you
+might want to generate a band using four source bands from a nine band input
+dataset (x0, x3, x4, and x8):
+
+\code
+  band_value = sqrt((x3*x3+x4*x4)/(x0*x8));
+\endcode
+
+You could write the pixel function to compute this value and then register
+it with GDAL with the name "MyFirstFunction".  Then, the following VRT XML
+could be used to display this derived band:
+
+\code
+<VRTDataset rasterXSize="1000" rasterYSize="1000">
+  <VRTRasterBand dataType="Float32" band="1" subClass="VRTDerivedRasterBand">>
+    <Description>Magnitude</Description>
+    <PixelFunctionType>MyFirstFunction</PixelFunctionType>
+    <SimpleSource>
+      <SourceFilename relativeToVRT="1">nine_band.dat</SourceFilename>
+      <SourceBand>1</SourceBand>
+      <SrcRect xOff="0" yOff="0" xSize="1000" ySize="1000"/>
+      <DstRect xOff="0" yOff="0" xSize="1000" ySize="1000"/>
+    </SimpleSource>
+    <SimpleSource>
+      <SourceFilename relativeToVRT="1">nine_band.dat</SourceFilename>
+      <SourceBand>4</SourceBand>
+      <SrcRect xOff="0" yOff="0" xSize="1000" ySize="1000"/>
+      <DstRect xOff="0" yOff="0" xSize="1000" ySize="1000"/>
+    </SimpleSource>
+    <SimpleSource>
+      <SourceFilename relativeToVRT="1">nine_band.dat</SourceFilename>
+      <SourceBand>5</SourceBand>
+      <SrcRect xOff="0" yOff="0" xSize="1000" ySize="1000"/>
+      <DstRect xOff="0" yOff="0" xSize="1000" ySize="1000"/>
+    </SimpleSource>
+    <SimpleSource>
+      <SourceFilename relativeToVRT="1">nine_band.dat</SourceFilename>
+      <SourceBand>9</SourceBand>
+      <SrcRect xOff="0" yOff="0" xSize="1000" ySize="1000"/>
+      <DstRect xOff="0" yOff="0" xSize="1000" ySize="1000"/>
+    </SimpleSource>
+  </VRTRasterBand>
+</VRTDataset>
+\endcode
+
+In addition to the subclass specification (VRTDerivedRasterBand) and
+the PixelFunctionType value, there is another new parameter that can come
+in handy: SourceTransferType.  Typically the source rasters are obtained
+using the data type of the derived band.  There might be times,
+however, when you want the pixel function to have access to
+higher resolution source data than the data type being generated.
+For example, you might have a derived band of type "Float", which takes
+a single source of type "CFloat32" or "CFloat64", and returns the imaginary
+portion.  To accomplish this, set the SourceTransferType to "CFloat64".
+Otherwise the source would be converted to "Float" prior to
+calling the pixel function, and the imaginary portion would be lost.
+
+\code
+<VRTDataset rasterXSize="1000" rasterYSize="1000">
+  <VRTRasterBand dataType="Float32" band="1" subClass="VRTDerivedRasterBand">>
+    <Description>Magnitude</Description>
+    <PixelFunctionType>MyFirstFunction</PixelFunctionType>
+    <SourceTransferType>"CFloat64"</SourceTransferType>
+    ...
+\endcode
+
+<h3>Writing Pixel Functions</h3>
+
+To register this function with GDAL (prior to accessing any VRT datasets
+with derived bands that use this function), an application calls
+GDALAddDerivedBandPixelFunc with a key and a GDALDerivedPixelFunc:
+ 
+\code
+    GDALAddDerivedBandPixelFunc("MyFirstFunction", TestFunction);
+\endcode
+
+A good time to do this is at the beginning of an application when the
+GDAL drivers are registered.
+
+GDALDerivedPixelFunc is defined with a signature similar to IRasterIO:
+
+@param papoSources A pointer to packed rasters; one per source.  The
+datatype of all will be the same, specified in the eSrcType parameter.
+
+@param nSources The number of source rasters.
+
+@param pData The buffer into which the data should be read, or from which
+it should be written.  This buffer must contain at least nBufXSize *
+nBufYSize words of type eBufType.  It is organized in left to right,
+top to bottom pixel order.  Spacing is controlled by the nPixelSpace,
+and nLineSpace parameters.
+
+@param nBufXSize The width of the buffer image into which the desired
+region is to be read, or from which it is to be written.
+
+@param nBufYSize The height of the buffer image into which the desired
+region is to be read, or from which it is to be written.
+
+@param eSrcType The type of the pixel values in the papoSources raster
+array.
+
+@param eBufType The type of the pixel values that the pixel function must
+generate in the pData data buffer.
+
+@param nPixelSpace The byte offset from the start of one pixel value in
+pData to the start of the next pixel value within a scanline.  If
+defaulted (0) the size of the datatype eBufType is used.
+
+@param nLineSpace The byte offset from the start of one scanline in
+pData to the start of the next.
+
+@return CE_Failure on failure, otherwise CE_None.
+
+\code
+typedef CPLErr
+(*GDALDerivedPixelFunc)(void **papoSources, int nSources, void *pData,
+			int nXSize, int nYSize,
+			GDALDataType eSrcType, GDALDataType eBufType,
+                        int nPixelSpace, int nLineSpace);
+\endcode
+
+The following is an implementation of the pixel function:
+
+\code
+#include "gdal.h"
+
+CPLErr TestFunction(void **papoSources, int nSources, void *pData,
+		    int nXSize, int nYSize,
+		    GDALDataType eSrcType, GDALDataType eBufType,
+		    int nPixelSpace, int nLineSpace)
+{
+
+    int ii, iLine, iCol;
+    double pix_val;
+    double x0, x3, x4, x8;
+
+    /* ---- Init ---- */
+    if (nSources != 4) return CE_Failure;
+
+    /* ---- Set pixels ---- */
+    for( iLine = 0; iLine < nYSize; iLine++ ) {
+	for( iCol = 0; iCol < nXSize; iCol++ ) {
+	    ii = iLine * nXSize + iCol;
+
+            /* Source raster pixels may be obtained with SRCVAL macro */
+            x0 = SRCVAL(papoSources[0], eSrcType, ii);
+            x3 = SRCVAL(papoSources[1], eSrcType, ii);
+            x4 = SRCVAL(papoSources[2], eSrcType, ii);
+            x8 = SRCVAL(papoSources[3], eSrcType, ii);
+
+	    pix_val = sqrt((x3*x3+x4*x4)/(x0*x8));
+
+	    GDALCopyWords(&pix_val, GDT_Float64, 0,
+			  ((GByte *)pData) + nLineSpace * iLine +
+			  iCol * nPixelSpace, eBufType, nPixelSpace, 1);
+	}
+    }
+
+    /* ---- Return success ---- */
+    return CE_None;
+}
+\endcode
+
+*/
diff --git a/Utilities/GDAL/frmts/vrt/vrtdataset.cpp b/Utilities/GDAL/frmts/vrt/vrtdataset.cpp
new file mode 100644
index 0000000000..9232675534
--- /dev/null
+++ b/Utilities/GDAL/frmts/vrt/vrtdataset.cpp
@@ -0,0 +1,946 @@
+/******************************************************************************
+ * $Id: vrtdataset.cpp,v 1.25 2006/02/10 15:02:15 fwarmerdam Exp $
+ *
+ * Project:  Virtual GDAL Datasets
+ * Purpose:  Implementation of VRTDataset
+ * Author:   Frank Warmerdam <warmerdam@pobox.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: vrtdataset.cpp,v $
+ * Revision 1.25  2006/02/10 15:02:15  fwarmerdam
+ * Write via the large file API.
+ *
+ * Revision 1.24  2006/02/08 06:12:07  fwarmerdam
+ * Override SetMetadata methods so that metadata can be preserved.
+ * Support saving histograms in VRT per bug 1060.
+ *
+ * Revision 1.23  2005/10/28 16:59:50  pnagy
+ * Added VRTDerivedBand support
+ *
+ * Revision 1.22  2005/06/28 14:50:44  fwarmerdam
+ * avoid destroy psTree in case of failure - done at higher level
+ *
+ * Revision 1.21  2005/05/05 13:56:34  fwarmerdam
+ * moved metadata handling to PAM
+ *
+ * Revision 1.20  2005/04/11 17:08:36  fwarmerdam
+ * Fixed leak of xml tree when XMLInit() fails.
+ *
+ * Revision 1.19  2004/10/03 16:52:45  fwarmerdam
+ * ensure overviews initialized in Create()
+ *
+ * Revision 1.18  2004/08/11 20:34:52  warmerda
+ * Allow "user input" for projection information in .vrt file.
+ *
+ * Revision 1.17  2004/08/11 18:43:50  warmerda
+ * restructure init to support derived class, add pszVRTPath to serialize
+ *
+ * Revision 1.16  2004/07/30 21:51:29  warmerda
+ * added support for VRTRawRasterBand
+ *
+ * Revision 1.15  2004/07/27 21:59:48  warmerda
+ * Enable .ovr support.
+ *
+ * Revision 1.14  2004/03/16 18:34:35  warmerda
+ * added support for relativeToVRT attribute on SourceFilename
+ *
+ * Revision 1.13  2003/07/17 20:31:12  warmerda
+ * moved out driver and VRTCreateCopy() code
+ *
+ * Revision 1.12  2003/06/10 19:58:35  warmerda
+ * added support for AddFuncSource in AddBand() method for Imagine
+ *
+ * Revision 1.11  2002/12/05 22:17:19  warmerda
+ * added support for opening directly from XML
+ *
+ * Revision 1.10  2002/11/30 16:55:49  warmerda
+ * added OpenXML method
+ *
+ * Revision 1.9  2002/11/24 04:27:52  warmerda
+ * CopyCreate() nows saves source image directly if it is a VRTDataset
+ *
+ * Revision 1.8  2002/11/23 18:54:17  warmerda
+ * added CREATIONDATATYPES metadata for drivers
+ *
+ * Revision 1.7  2002/09/04 06:50:37  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.6  2002/06/12 21:12:25  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.5  2002/05/29 16:40:18  warmerda
+ * dont provide default value for method argumentsvrtdataset.cpp
+ *
+ * Revision 1.4  2002/05/29 16:06:05  warmerda
+ * complete detailed band metadata
+ *
+ * Revision 1.3  2002/04/19 19:43:38  warmerda
+ * added CreateCopy method
+ *
+ * Revision 1.2  2001/11/18 15:46:45  warmerda
+ * added SRS and GeoTransform
+ *
+ * Revision 1.1  2001/11/16 21:14:31  warmerda
+ * New
+ *
+ */
+
+#include "vrtdataset.h"
+#include "cpl_string.h"
+#include "cpl_minixml.h"
+#include "ogr_spatialref.h"
+
+CPL_CVSID("$Id: vrtdataset.cpp,v 1.25 2006/02/10 15:02:15 fwarmerdam Exp $");
+
+/************************************************************************/
+/*                            VRTDataset()                             */
+/************************************************************************/
+
+VRTDataset::VRTDataset( int nXSize, int nYSize )
+
+{
+    nRasterXSize = nXSize;
+    nRasterYSize = nYSize;
+    pszProjection = NULL;
+
+    bNeedsFlush = FALSE;
+
+    bGeoTransformSet = FALSE;
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+
+    nGCPCount = 0;
+    pasGCPList = NULL;
+    pszGCPProjection = CPLStrdup("");
+
+    pszVRTPath = NULL;
+    
+    GDALRegister_VRT();
+    poDriver = (GDALDriver *) GDALGetDriverByName( "VRT" );
+}
+
+/************************************************************************/
+/*                            ~VRTDataset()                            */
+/************************************************************************/
+
+VRTDataset::~VRTDataset()
+
+{
+    FlushCache();
+    CPLFree( pszProjection );
+
+    CPLFree( pszGCPProjection );
+    if( nGCPCount > 0 )
+    {
+        GDALDeinitGCPs( nGCPCount, pasGCPList );
+        CPLFree( pasGCPList );
+    }
+    CPLFree( pszVRTPath );
+}
+
+/************************************************************************/
+/*                             FlushCache()                             */
+/************************************************************************/
+
+void VRTDataset::FlushCache()
+
+{
+    GDALDataset::FlushCache();
+
+    if( !bNeedsFlush )
+        return;
+
+    bNeedsFlush = FALSE;
+
+    // We don't write to disk if there is no filename.  This is a 
+    // memory only dataset.
+    if( strlen(GetDescription()) == 0 
+        || EQUALN(GetDescription(),"<VRTDataset",11) )
+        return;
+
+    /* -------------------------------------------------------------------- */
+    /*      Create the output file.                                         */
+    /* -------------------------------------------------------------------- */
+    FILE *fpVRT;
+
+    fpVRT = VSIFOpenL( GetDescription(), "w" );
+    if( fpVRT == NULL )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Failed to write .vrt file in FlushCache()." );
+        return;
+    }
+
+    /* -------------------------------------------------------------------- */
+    /*      Convert tree to a single block of XML text.                     */
+    /* -------------------------------------------------------------------- */
+    char *pszVRTPath = CPLStrdup(CPLGetPath(GetDescription()));
+    CPLXMLNode *psDSTree = SerializeToXML( pszVRTPath );
+    char *pszXML;
+
+    pszXML = CPLSerializeXMLTree( psDSTree );
+
+    CPLDestroyXMLNode( psDSTree );
+
+    CPLFree( pszVRTPath );
+
+    /* -------------------------------------------------------------------- */
+    /*      Write to disk.                                                  */
+    /* -------------------------------------------------------------------- */
+    VSIFWriteL( pszXML, 1, strlen(pszXML), fpVRT );
+    VSIFCloseL( fpVRT );
+
+    CPLFree( pszXML );
+}
+
+/************************************************************************/
+/*                           SerializeToXML()                           */
+/************************************************************************/
+
+CPLXMLNode *VRTDataset::SerializeToXML( const char *pszVRTPath )
+
+{
+    /* -------------------------------------------------------------------- */
+    /*      Setup root node and attributes.                                 */
+    /* -------------------------------------------------------------------- */
+    CPLXMLNode *psDSTree, *psMD;
+    char       szNumber[128];
+
+    psDSTree = CPLCreateXMLNode( NULL, CXT_Element, "VRTDataset" );
+
+    sprintf( szNumber, "%d", GetRasterXSize() );
+    CPLSetXMLValue( psDSTree, "#rasterXSize", szNumber );
+
+    sprintf( szNumber, "%d", GetRasterYSize() );
+    CPLSetXMLValue( psDSTree, "#rasterYSize", szNumber );
+
+ /* -------------------------------------------------------------------- */
+ /*      SRS                                                             */
+ /* -------------------------------------------------------------------- */
+    if( pszProjection != NULL && strlen(pszProjection) > 0 )
+        CPLSetXMLValue( psDSTree, "SRS", pszProjection );
+
+ /* -------------------------------------------------------------------- */
+ /*      Geotransform.                                                   */
+ /* -------------------------------------------------------------------- */
+    if( bGeoTransformSet )
+    {
+        CPLSetXMLValue( psDSTree, "GeoTransform", 
+                        CPLSPrintf( "%24.16e,%24.16e,%24.16e,%24.16e,%24.16e,%24.16e",
+                                    adfGeoTransform[0],
+                                    adfGeoTransform[1],
+                                    adfGeoTransform[2],
+                                    adfGeoTransform[3],
+                                    adfGeoTransform[4],
+                                    adfGeoTransform[5] ) );
+    }
+
+    /* -------------------------------------------------------------------- */
+    /*      Metadata                                                        */
+    /* -------------------------------------------------------------------- */
+    psMD = PamSerializeMetadata( this );
+    if( psMD != NULL )
+        CPLAddXMLChild( psDSTree, psMD );
+
+ /* -------------------------------------------------------------------- */
+ /*      GCPs                                                            */
+ /* -------------------------------------------------------------------- */
+    if( nGCPCount > 0 )
+    {
+        CPLXMLNode *psGCPList = CPLCreateXMLNode( psDSTree, CXT_Element, 
+                                                  "GCPList" );
+
+        if( pszGCPProjection != NULL && strlen(pszGCPProjection) > 0 )
+            CPLSetXMLValue( psGCPList, "#Projection", pszGCPProjection );
+
+        for( int iGCP = 0; iGCP < nGCPCount; iGCP++ )
+        {
+            CPLXMLNode *psXMLGCP;
+            GDAL_GCP *psGCP = pasGCPList + iGCP;
+
+            psXMLGCP = CPLCreateXMLNode( psGCPList, CXT_Element, "GCP" );
+
+            CPLSetXMLValue( psXMLGCP, "#Id", psGCP->pszId );
+
+            if( psGCP->pszInfo != NULL && strlen(psGCP->pszInfo) > 0 )
+                CPLSetXMLValue( psXMLGCP, "Info", psGCP->pszInfo );
+
+            CPLSetXMLValue( psXMLGCP, "#Pixel", 
+                            CPLSPrintf( "%.4f", psGCP->dfGCPPixel ) );
+
+            CPLSetXMLValue( psXMLGCP, "#Line", 
+                            CPLSPrintf( "%.4f", psGCP->dfGCPLine ) );
+
+            CPLSetXMLValue( psXMLGCP, "#X", 
+                            CPLSPrintf( "%.12E", psGCP->dfGCPX ) );
+
+            CPLSetXMLValue( psXMLGCP, "#Y", 
+                            CPLSPrintf( "%.12E", psGCP->dfGCPY ) );
+
+            if( psGCP->dfGCPZ != 0.0 )
+                CPLSetXMLValue( psXMLGCP, "#GCPZ", 
+                                CPLSPrintf( "%.12E", psGCP->dfGCPZ ) );
+        }
+    }
+
+    /* -------------------------------------------------------------------- */
+    /*      Serialize bands.                                                */
+    /* -------------------------------------------------------------------- */
+    for( int iBand = 0; iBand < nBands; iBand++ )
+    {
+        CPLXMLNode *psBandTree = 
+            ((VRTRasterBand *) papoBands[iBand])->SerializeToXML(pszVRTPath);
+
+        if( psBandTree != NULL )
+            CPLAddXMLChild( psDSTree, psBandTree );
+    }
+
+    return psDSTree;
+}
+
+/************************************************************************/
+/*                              XMLInit()                               */
+/************************************************************************/
+
+CPLErr VRTDataset::XMLInit( CPLXMLNode *psTree, const char *pszVRTPath )
+
+{
+    if( pszVRTPath != NULL )
+        this->pszVRTPath = CPLStrdup(pszVRTPath);
+
+/* -------------------------------------------------------------------- */
+/*      Check for an SRS node.                                          */
+/* -------------------------------------------------------------------- */
+    if( strlen(CPLGetXMLValue(psTree, "SRS", "")) > 0 )
+    {
+        OGRSpatialReference oSRS;
+
+        CPLFree( pszProjection );
+        pszProjection = NULL;
+
+        if( oSRS.SetFromUserInput( CPLGetXMLValue(psTree, "SRS", "") )
+            == OGRERR_NONE )
+            oSRS.exportToWkt( &pszProjection );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for a GeoTransform node.                                  */
+/* -------------------------------------------------------------------- */
+    if( strlen(CPLGetXMLValue(psTree, "GeoTransform", "")) > 0 )
+    {
+        const char *pszGT = CPLGetXMLValue(psTree, "GeoTransform", "");
+        char	**papszTokens;
+
+        papszTokens = CSLTokenizeStringComplex( pszGT, ",", FALSE, FALSE );
+        if( CSLCount(papszTokens) != 6 )
+        {
+            CPLError( CE_Warning, CPLE_AppDefined,
+                      "GeoTransform node does not have expected six values.");
+        }
+        else
+        {
+            for( int iTA = 0; iTA < 6; iTA++ )
+                adfGeoTransform[iTA] = atof(papszTokens[iTA]);
+            bGeoTransformSet = TRUE;
+        }
+
+        CSLDestroy( papszTokens );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Check for GCPs.                                                 */
+/* -------------------------------------------------------------------- */
+    CPLXMLNode *psGCPList = CPLGetXMLNode( psTree, "GCPList" );
+
+    if( psGCPList != NULL )
+    {
+        CPLXMLNode *psXMLGCP;
+        OGRSpatialReference oSRS;
+        const char *pszRawProj = CPLGetXMLValue(psGCPList, "Projection", "");
+
+        CPLFree( pszGCPProjection );
+
+        if( strlen(pszRawProj) > 0 
+            && oSRS.SetFromUserInput( pszRawProj ) == OGRERR_NONE )
+            oSRS.exportToWkt( &pszGCPProjection );
+        else
+            pszGCPProjection = CPLStrdup("");
+
+        // Count GCPs.
+        int  nGCPMax = 0;
+         
+        for( psXMLGCP = psGCPList->psChild; psXMLGCP != NULL; 
+             psXMLGCP = psXMLGCP->psNext )
+            nGCPMax++;
+         
+        pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPMax);
+         
+        for( psXMLGCP = psGCPList->psChild; psXMLGCP != NULL; 
+             psXMLGCP = psXMLGCP->psNext )
+        {
+            GDAL_GCP *psGCP = pasGCPList + nGCPCount;
+
+            if( !EQUAL(psXMLGCP->pszValue,"GCP") || 
+                psXMLGCP->eType != CXT_Element )
+                continue;
+             
+            GDALInitGCPs( 1, psGCP );
+             
+            CPLFree( psGCP->pszId );
+            psGCP->pszId = CPLStrdup(CPLGetXMLValue(psXMLGCP,"Id",""));
+             
+            CPLFree( psGCP->pszInfo );
+            psGCP->pszInfo = CPLStrdup(CPLGetXMLValue(psXMLGCP,"Info",""));
+             
+            psGCP->dfGCPPixel = atof(CPLGetXMLValue(psXMLGCP,"Pixel","0.0"));
+            psGCP->dfGCPLine = atof(CPLGetXMLValue(psXMLGCP,"Line","0.0"));
+             
+            psGCP->dfGCPX = atof(CPLGetXMLValue(psXMLGCP,"X","0.0"));
+            psGCP->dfGCPY = atof(CPLGetXMLValue(psXMLGCP,"Y","0.0"));
+            psGCP->dfGCPZ = atof(CPLGetXMLValue(psXMLGCP,"Z","0.0"));
+
+            nGCPCount++;
+        }
+    }
+     
+/* -------------------------------------------------------------------- */
+/*      Apply any dataset level metadata.                               */
+/* -------------------------------------------------------------------- */
+    PamApplyMetadata( psTree, this );
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    int		nBands = 0;
+    CPLXMLNode *psChild;
+
+    for( psChild=psTree->psChild; psChild != NULL; psChild=psChild->psNext )
+    {
+        if( psChild->eType == CXT_Element
+            && EQUAL(psChild->pszValue,"VRTRasterBand") )
+        {
+            VRTRasterBand  *poBand = NULL;
+            const char *pszSubclass = CPLGetXMLValue( psChild, "subclass", 
+                                                      "VRTSourcedRasterBand" );
+
+            if( EQUAL(pszSubclass,"VRTSourcedRasterBand") )
+                poBand = new VRTSourcedRasterBand( this, nBands+1 );
+            else if( EQUAL(pszSubclass, "VRTDerivedRasterBand") )
+                poBand = new VRTDerivedRasterBand( this, nBands+1 );
+            else if( EQUAL(pszSubclass, "VRTRawRasterBand") )
+                poBand = new VRTRawRasterBand( this, nBands+1 );
+            else if( EQUAL(pszSubclass, "VRTWarpedRasterBand") )
+                poBand = new VRTWarpedRasterBand( this, nBands+1 );
+            else
+                CPLError( CE_Failure, CPLE_AppDefined,
+                          "VRTRasterBand of unrecognised subclass '%s'.",
+                          pszSubclass );
+
+            if( poBand != NULL 
+                && poBand->XMLInit( psChild, pszVRTPath ) == CE_None )
+            {
+                SetBand( ++nBands, poBand );
+            }
+            else
+            {
+                if( poBand )
+                    delete poBand; 
+                return CE_Failure;
+            }
+        }
+    }
+    
+    return CE_None;
+}
+
+/************************************************************************/
+/*                            GetGCPCount()                             */
+/************************************************************************/
+
+int VRTDataset::GetGCPCount()
+
+{
+    return nGCPCount;
+}
+
+/************************************************************************/
+/*                          GetGCPProjection()                          */
+/************************************************************************/
+
+const char *VRTDataset::GetGCPProjection()
+
+{
+    return pszGCPProjection;
+}
+
+/************************************************************************/
+/*                               GetGCPs()                              */
+/************************************************************************/
+
+const GDAL_GCP *VRTDataset::GetGCPs()
+
+{
+    return pasGCPList;
+}
+
+/************************************************************************/
+/*                              SetGCPs()                               */
+/************************************************************************/
+
+CPLErr VRTDataset::SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList,
+                            const char *pszGCPProjection )
+
+{
+    CPLFree( this->pszGCPProjection );
+    if( this->nGCPCount > 0 )
+    {
+        GDALDeinitGCPs( this->nGCPCount, this->pasGCPList );
+        CPLFree( this->pasGCPList );
+    }
+
+    this->pszGCPProjection = CPLStrdup(pszGCPProjection);
+
+    this->nGCPCount = nGCPCount;
+
+    this->pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPList );
+
+    this->bNeedsFlush = TRUE;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           SetProjection()                            */
+/************************************************************************/
+
+CPLErr VRTDataset::SetProjection( const char *pszWKT )
+
+{
+    CPLFree( pszProjection );
+    pszProjection = NULL;
+
+    if( pszWKT != NULL )
+        pszProjection = CPLStrdup(pszWKT);
+
+    bNeedsFlush = TRUE;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *VRTDataset::GetProjectionRef()
+
+{
+    if( pszProjection == NULL )
+        return "";
+    else
+        return pszProjection;
+}
+
+/************************************************************************/
+/*                          SetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr VRTDataset::SetGeoTransform( double *padfGeoTransformIn )
+
+{
+    memcpy( adfGeoTransform, padfGeoTransformIn, sizeof(double) * 6 );
+    bGeoTransformSet = TRUE;
+
+    bNeedsFlush = TRUE;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr VRTDataset::GetGeoTransform( double * padfGeoTransform )
+
+{
+    memcpy( padfGeoTransform, adfGeoTransform, sizeof(double) * 6 );
+
+    if( bGeoTransformSet )
+        return CE_None;
+    else
+        return CE_Failure;
+}
+
+/************************************************************************/
+/*                            SetMetadata()                             */
+/************************************************************************/
+
+CPLErr VRTDataset::SetMetadata( char **papszMetadata, 
+                                   const char *pszDomain )
+
+{
+    SetNeedsFlush();
+
+    return GDALDataset::SetMetadata( papszMetadata, pszDomain );
+}
+
+/************************************************************************/
+/*                          SetMetadataItem()                           */
+/************************************************************************/
+
+CPLErr VRTDataset::SetMetadataItem( const char *pszName, 
+                                    const char *pszValue, 
+                                    const char *pszDomain )
+
+{
+    SetNeedsFlush();
+
+    return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain );
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *VRTDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    char *pszVRTPath = NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Does this appear to be a virtual dataset definition XML         */
+/*      file?                                                           */
+/* -------------------------------------------------------------------- */
+    if( (poOpenInfo->nHeaderBytes < 20 
+         || !EQUALN((const char *)poOpenInfo->pabyHeader,"<VRTDataset",11))
+        && !EQUALN(poOpenInfo->pszFilename,"<VRTDataset",11) )
+        return NULL;
+
+/* -------------------------------------------------------------------- */
+/*	Try to read the whole file into memory.				*/
+/* -------------------------------------------------------------------- */
+    char        *pszXML;
+
+    if( poOpenInfo->fp != NULL )
+    {
+        unsigned int nLength;
+     
+        VSIFSeek( poOpenInfo->fp, 0, SEEK_END );
+        nLength = VSIFTell( poOpenInfo->fp );
+        VSIFSeek( poOpenInfo->fp, 0, SEEK_SET );
+        
+        nLength = MAX(0,nLength);
+        pszXML = (char *) VSIMalloc(nLength+1);
+        
+        if( pszXML == NULL )
+        {
+            CPLError( CE_Failure, CPLE_OutOfMemory, 
+                      "Failed to allocate %d byte buffer to hold VRT xml file.",
+                      nLength );
+            return NULL;
+        }
+        
+        if( VSIFRead( pszXML, 1, nLength, poOpenInfo->fp ) != nLength )
+        {
+            CPLFree( pszXML );
+            CPLError( CE_Failure, CPLE_FileIO,
+                      "Failed to read %d bytes from VRT xml file.",
+                      nLength );
+            return NULL;
+        }
+        
+        pszXML[nLength] = '\0';
+        pszVRTPath = CPLStrdup(CPLGetPath(poOpenInfo->pszFilename));
+    }
+/* -------------------------------------------------------------------- */
+/*      Or use the filename as the XML input.                           */
+/* -------------------------------------------------------------------- */
+    else
+    {
+        pszXML = CPLStrdup( poOpenInfo->pszFilename );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Turn the XML representation into a VRTDataset.                  */
+/* -------------------------------------------------------------------- */
+    VRTDataset *poDS = (VRTDataset *) OpenXML( pszXML, pszVRTPath );
+
+    if( poDS != NULL )
+        poDS->bNeedsFlush = FALSE;
+
+    CPLFree( pszXML );
+    CPLFree( pszVRTPath );
+
+/* -------------------------------------------------------------------- */
+/*      Open overviews.                                                 */
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->fp != NULL && poDS != NULL )
+        poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                              OpenXML()                               */
+/*                                                                      */
+/*      Create an open VRTDataset from a supplied XML representation    */
+/*      of the dataset.                                                 */
+/************************************************************************/
+
+GDALDataset *VRTDataset::OpenXML( const char *pszXML, const char *pszVRTPath )
+
+{
+ /* -------------------------------------------------------------------- */
+ /*      Parse the XML.                                                  */
+ /* -------------------------------------------------------------------- */
+    CPLXMLNode	*psTree;
+
+    psTree = CPLParseXMLString( pszXML );
+
+    if( psTree == NULL )
+        return NULL;
+
+    if( CPLGetXMLNode( psTree, "rasterXSize" ) == NULL
+        || CPLGetXMLNode( psTree, "rasterYSize" ) == NULL
+        || CPLGetXMLNode( psTree, "VRTRasterBand" ) == NULL )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Missing one of rasterXSize, rasterYSize or bands on"
+                  " VRTDataset." );
+        CPLDestroyXMLNode( psTree );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create the new virtual dataset object.                          */
+/* -------------------------------------------------------------------- */
+    VRTDataset *poDS;
+    int nXSize = atoi(CPLGetXMLValue(psTree,"rasterXSize","0"));
+    int nYSize = atoi(CPLGetXMLValue(psTree,"rasterYSize","0"));
+
+    if( strstr(pszXML,"VRTWarpedDataset") != NULL )
+        poDS = new VRTWarpedDataset( nXSize, nYSize );
+    else
+        poDS = new VRTDataset( nXSize, nYSize );
+
+    if( poDS->XMLInit( psTree, pszVRTPath ) != CE_None )
+    {
+        delete poDS;
+        poDS = NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try to return a regular handle on the file.                     */
+/* -------------------------------------------------------------------- */
+    CPLDestroyXMLNode( psTree );
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                              AddBand()                               */
+/************************************************************************/
+
+CPLErr VRTDataset::AddBand( GDALDataType eType, char **papszOptions )
+
+{
+    int i;
+
+    const char *pszSubClass = CSLFetchNameValue(papszOptions, "subclass");
+
+    bNeedsFlush = 1;
+
+/* ==================================================================== */
+/*      Handle a new raw band.                                          */
+/* ==================================================================== */
+    if( pszSubClass != NULL && EQUAL(pszSubClass,"VRTRawRasterBand") )
+    {
+        int nWordDataSize = GDALGetDataTypeSize( eType ) / 8;
+        vsi_l_offset nImageOffset = 0;
+        int nPixelOffset = nWordDataSize;
+        int nLineOffset = nWordDataSize * GetRasterXSize();
+        const char *pszFilename;
+        const char *pszByteOrder = NULL;
+        int bRelativeToVRT = FALSE;
+
+/* -------------------------------------------------------------------- */
+/*      Collect required information.                                   */
+/* -------------------------------------------------------------------- */
+        if( CSLFetchNameValue(papszOptions, "ImageOffset") != NULL )
+            nImageOffset = atoi(CSLFetchNameValue(papszOptions, "ImageOffset"));
+
+        if( CSLFetchNameValue(papszOptions, "PixelOffset") != NULL )
+            nPixelOffset = atoi(CSLFetchNameValue(papszOptions,"PixelOffset"));
+
+        if( CSLFetchNameValue(papszOptions, "LineOffset") != NULL )
+            nLineOffset = atoi(CSLFetchNameValue(papszOptions, "LineOffset"));
+
+        if( CSLFetchNameValue(papszOptions, "ByteOrder") != NULL )
+            pszByteOrder = CSLFetchNameValue(papszOptions, "ByteOrder");
+
+        if( CSLFetchNameValue(papszOptions, "SourceFilename") != NULL )
+            pszFilename = CSLFetchNameValue(papszOptions, "SourceFilename");
+        else
+        {
+            CPLError( CE_Failure, CPLE_AppDefined, 
+                      "AddBand() requires a SourceFilename option for VRTRawRasterBands." );
+            return CE_Failure;
+        }
+        
+        bRelativeToVRT = 
+            CSLFetchBoolean( papszOptions, "RelativeToVRT", FALSE );
+
+/* -------------------------------------------------------------------- */
+/*      Create and initialize the band.                                 */
+/* -------------------------------------------------------------------- */
+        CPLErr eErr;
+
+        VRTRawRasterBand *poBand = 
+            new VRTRawRasterBand( this, GetRasterCount() + 1, eType );
+
+        eErr = 
+            poBand->SetRawLink( pszFilename, NULL, FALSE, 
+                                nImageOffset, nPixelOffset, nLineOffset, 
+                                pszByteOrder );
+        if( eErr != CE_None )
+        {
+            delete poBand;
+            return eErr;
+        }
+
+        SetBand( GetRasterCount() + 1, poBand );
+
+        return CE_None;
+    }
+
+/* ==================================================================== */
+/*      Handle a new "sourced" band.                                    */
+/* ==================================================================== */
+    else
+    {
+        VRTSourcedRasterBand *poBand;
+
+	/* ---- Check for our sourced band 'derived' subclass ---- */
+        if(pszSubClass != NULL && EQUAL(pszSubClass,"VRTDerivedRasterBand")) {
+	    poBand = new VRTDerivedRasterBand
+		(this, GetRasterCount() + 1, eType, 
+		 GetRasterXSize(), GetRasterYSize());
+	}
+	else {
+
+	    /* ---- Standard sourced band ---- */
+	    poBand = new VRTSourcedRasterBand
+		(this, GetRasterCount() + 1, eType, 
+		 GetRasterXSize(), GetRasterYSize());
+	}
+
+        SetBand( GetRasterCount() + 1, poBand );
+
+        for( i=0; papszOptions != NULL && papszOptions[i] != NULL; i++ )
+        {
+            if( EQUALN(papszOptions[i],"AddFuncSource=", 14) )
+            {
+                VRTImageReadFunc pfnReadFunc = NULL;
+                void             *pCBData = NULL;
+                double           dfNoDataValue = VRT_NODATA_UNSET;
+
+                char **papszTokens = CSLTokenizeStringComplex( papszOptions[i]+14,
+                                                               ",", TRUE, FALSE );
+
+                if( CSLCount(papszTokens) < 1 )
+                {
+                    CPLError( CE_Failure, CPLE_AppDefined, 
+                              "AddFuncSource() ... required argument missing." );
+                }
+
+                sscanf( papszTokens[0], "%p", &pfnReadFunc );
+                if( CSLCount(papszTokens) > 1 )
+                    sscanf( papszTokens[1], "%p", &pCBData );
+                if( CSLCount(papszTokens) > 2 )
+                    dfNoDataValue = atof( papszTokens[2] );
+
+                poBand->AddFuncSource( pfnReadFunc, pCBData, dfNoDataValue );
+            }
+        }
+
+        return CE_None;
+    }
+}
+
+/************************************************************************/
+/*                             VRTCreate()                              */
+/************************************************************************/
+
+GDALDataset *
+VRTDataset::Create( const char * pszName,
+                    int nXSize, int nYSize, int nBands,
+                    GDALDataType eType, char ** papszOptions )
+
+{
+    VRTDataset *poDS;
+    int        iBand;
+
+    (void) papszOptions;
+
+    if( EQUALN(pszName,"<VRTDataset",11) )
+    {
+        GDALDataset *poDS = OpenXML( pszName, NULL );
+        poDS->SetDescription( "<FromXML>" );
+        return poDS;
+    }
+    else
+    {
+        const char *pszSubclass = CSLFetchNameValue( papszOptions,
+                                                     "SUBCLASS" );
+
+        if( pszSubclass == NULL || EQUAL(pszSubclass,"VRTDataset") )
+            poDS = new VRTDataset( nXSize, nYSize );
+        else if( EQUAL(pszSubclass,"VRTWarpedDataset") )
+        {
+            poDS = new VRTWarpedDataset( nXSize, nYSize );
+        }
+        else
+        {
+            CPLError( CE_Failure, CPLE_AppDefined, 
+                      "SUBCLASS=%s not recognised.", 
+                      pszSubclass );
+            return NULL;
+        }
+
+        poDS->SetDescription( pszName );
+        
+        for( iBand = 0; iBand < nBands; iBand++ )
+            poDS->AddBand( eType, NULL );
+        
+        poDS->bNeedsFlush = 1;
+
+        poDS->oOvManager.Initialize( poDS, pszName );
+        
+        return poDS;
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/vrt/vrtdataset.h b/Utilities/GDAL/frmts/vrt/vrtdataset.h
new file mode 100644
index 0000000000..9e33c79674
--- /dev/null
+++ b/Utilities/GDAL/frmts/vrt/vrtdataset.h
@@ -0,0 +1,666 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  Virtual GDAL Datasets
+ * Purpose:  Declaration of virtual gdal dataset classes.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: vrtdataset.h,v $
+ * Revision 1.22  2006/02/08 06:12:08  fwarmerdam
+ * Override SetMetadata methods so that metadata can be preserved.
+ * Support saving histograms in VRT per bug 1060.
+ *
+ * Revision 1.21  2005/10/28 16:59:51  pnagy
+ * Added VRTDerivedBand support
+ *
+ * Revision 1.20  2005/05/05 13:56:53  fwarmerdam
+ * include gdal_pam.h
+ *
+ * Revision 1.19  2005/04/07 17:27:33  fwarmerdam
+ * Default path in OpenXML to NULL.
+ *
+ * Revision 1.18  2004/08/12 08:24:26  warmerda
+ * added overview support
+ *
+ * Revision 1.17  2004/08/11 18:51:48  warmerda
+ * added warped dataset support
+ *
+ * Revision 1.16  2004/07/30 21:51:00  warmerda
+ * added support for VRTRawRasterBand
+ *
+ * Revision 1.15  2004/07/28 17:47:33  warmerda
+ * Disable VRTWarpedRasterBand for now.
+ *
+ * Revision 1.14  2004/07/28 16:56:02  warmerda
+ * added VRTSourcedRasterBand
+ *
+ * Revision 1.13  2004/04/15 18:54:38  warmerda
+ * added UnitType, Offset, Scale and CategoryNames support
+ *
+ * Revision 1.12  2004/03/16 18:34:35  warmerda
+ * added support for relativeToVRT attribute on SourceFilename
+ *
+ * Revision 1.11  2003/09/11 23:00:04  aamici
+ * add class constructors and destructors where needed in order to
+ * let the mingw/cygwin binutils produce sensible partially linked objet files
+ * with 'ld -r'.
+ *
+ * Revision 1.10  2003/08/07 17:11:21  warmerda
+ * added normalized flag for kernel based filters
+ *
+ * Revision 1.9  2003/07/17 20:30:24  warmerda
+ * Added custom VRTDriver and moved all the sources class declarations in here.
+ *
+ * Revision 1.8  2003/06/10 19:59:33  warmerda
+ * added new Func based source type for passthrough to a callback
+ *
+ * Revision 1.7  2003/03/13 20:38:30  dron
+ * bNoDataValueSet added to VRTRasterBand class.
+ *
+ * Revision 1.6  2002/11/30 16:55:49  warmerda
+ * added OpenXML method
+ *
+ * Revision 1.5  2002/11/24 04:29:02  warmerda
+ * Substantially rewrote VRTSimpleSource.  Now VRTSource is base class, and
+ * sources do their own SerializeToXML(), and XMLInit().  New VRTComplexSource
+ * supports scaling and nodata values.
+ *
+ * Revision 1.4  2002/05/29 18:13:44  warmerda
+ * added nodata handling for averager
+ *
+ * Revision 1.3  2002/05/29 16:06:05  warmerda
+ * complete detailed band metadata
+ *
+ * Revision 1.2  2001/11/18 15:46:45  warmerda
+ * added SRS and GeoTransform
+ *
+ * Revision 1.1  2001/11/16 21:14:31  warmerda
+ * New
+ *
+ */
+
+#ifndef VIRTUALDATASET_H_INCLUDED
+#define VIRTUALDATASET_H_INCLUDED
+
+#include "gdal_priv.h"
+#include "gdal_pam.h"
+#include "cpl_minixml.h"
+
+CPL_C_START
+void	GDALRegister_VRT(void);
+typedef CPLErr
+(*VRTImageReadFunc)( void *hCBData,
+                     int nXOff, int nYOff, int nXSize, int nYSize,
+                     void *pData );
+CPL_C_END
+
+int VRTApplyMetadata( CPLXMLNode *, GDALMajorObject * );
+CPLXMLNode *VRTSerializeMetadata( GDALMajorObject * );
+
+/************************************************************************/
+/*                              VRTSource                               */
+/************************************************************************/
+
+class VRTSource 
+{
+public:
+    virtual ~VRTSource();
+
+    virtual CPLErr  RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, 
+                              void *pData, int nBufXSize, int nBufYSize, 
+                              GDALDataType eBufType, 
+                              int nPixelSpace, int nLineSpace ) = 0;
+
+    virtual CPLErr  XMLInit( CPLXMLNode *psTree, const char * ) = 0;
+    virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath ) = 0;
+};
+
+typedef VRTSource *(*VRTSourceParser)(CPLXMLNode *, const char *);
+
+VRTSource *VRTParseCoreSources( CPLXMLNode *psTree, const char * );
+VRTSource *VRTParseFilterSources( CPLXMLNode *psTree, const char * );
+
+/************************************************************************/
+/*                              VRTDataset                              */
+/************************************************************************/
+
+class CPL_DLL VRTDataset : public GDALDataset
+{
+    char           *pszProjection;
+
+    int            bGeoTransformSet;
+    double         adfGeoTransform[6];
+
+    int           nGCPCount;
+    GDAL_GCP      *pasGCPList;
+    char          *pszGCPProjection;
+
+    int            bNeedsFlush;
+    
+    char          *pszVRTPath;
+
+  public:
+                 VRTDataset(int nXSize, int nYSize);
+                ~VRTDataset();
+
+    void          SetNeedsFlush() { bNeedsFlush = TRUE; }
+    virtual void  FlushCache();
+
+    virtual const char *GetProjectionRef(void);
+    virtual CPLErr SetProjection( const char * );
+    virtual CPLErr GetGeoTransform( double * );
+    virtual CPLErr SetGeoTransform( double * );
+
+    virtual CPLErr SetMetadata( char **papszMD, const char *pszDomain = "" );
+    virtual CPLErr SetMetadataItem( const char *pszName, const char *pszValue,
+                                    const char *pszDomain = "" );
+
+    virtual int    GetGCPCount();
+    virtual const char *GetGCPProjection();
+    virtual const GDAL_GCP *GetGCPs();
+    virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList,
+                            const char *pszGCPProjection );
+
+    virtual CPLErr AddBand( GDALDataType eType, 
+                            char **papszOptions=NULL );
+
+    virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath);
+    virtual CPLErr      XMLInit( CPLXMLNode *, const char * );
+ 
+    static GDALDataset *Open( GDALOpenInfo * );
+    static GDALDataset *OpenXML( const char *, const char * = NULL );
+    static GDALDataset *Create( const char * pszName,
+                                int nXSize, int nYSize, int nBands,
+                                GDALDataType eType, char ** papszOptions );
+};
+
+/************************************************************************/
+/*                           VRTWarpedDataset                           */
+/************************************************************************/
+
+class GDALWarpOperation;
+class VRTWarpedRasterBand;
+
+class CPL_DLL VRTWarpedDataset : public VRTDataset
+{
+    int               nBlockXSize;
+    int               nBlockYSize;
+    GDALWarpOperation *poWarper;
+
+public:
+    int               nOverviewCount;
+    VRTWarpedDataset  **papoOverviews;
+
+public:
+                      VRTWarpedDataset( int nXSize, int nYSize );
+                     ~VRTWarpedDataset();
+
+    CPLErr            Initialize( /* GDALWarpOptions */ void * );
+
+    virtual CPLErr IBuildOverviews( const char *, int, int *,
+                                    int, int *, GDALProgressFunc, void * );
+    
+    virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath );
+    virtual CPLErr    XMLInit( CPLXMLNode *, const char * );
+
+    virtual CPLErr AddBand( GDALDataType eType, 
+                            char **papszOptions=NULL );
+    
+    CPLErr            ProcessBlock( int iBlockX, int iBlockY );
+
+    void              GetBlockSize( int *, int * );
+};
+
+/************************************************************************/
+/*                            VRTRasterBand                             */
+/*                                                                      */
+/*      Provides support for all the various kinds of metadata but      */
+/*      no raster access.  That is handled by derived classes.          */
+/************************************************************************/
+
+class CPL_DLL VRTRasterBand : public GDALRasterBand
+{
+  protected:
+    int            bNoDataValueSet;
+    double         dfNoDataValue;
+
+    GDALColorTable *poColorTable;
+
+    GDALColorInterp eColorInterp;
+
+    char           *pszUnitType;
+    char           **papszCategoryNames;
+    
+    double         dfOffset;
+    double         dfScale;
+
+    CPLXMLNode    *psSavedHistograms;
+
+    void           Initialize( int nXSize, int nYSize );
+
+  public:
+
+    		   VRTRasterBand();
+    virtual        ~VRTRasterBand();
+
+    virtual CPLErr         XMLInit( CPLXMLNode *, const char * );
+    virtual CPLXMLNode *   SerializeToXML( const char *pszVRTPath );
+
+#define VRT_NODATA_UNSET -1234.56
+
+    virtual CPLErr SetNoDataValue( double );
+    virtual double GetNoDataValue( int *pbSuccess = NULL );
+
+    virtual CPLErr SetColorTable( GDALColorTable * ); 
+    virtual GDALColorTable *GetColorTable();
+
+    virtual CPLErr SetColorInterpretation( GDALColorInterp );
+    virtual GDALColorInterp GetColorInterpretation();
+
+    virtual const char *GetUnitType();
+    CPLErr SetUnitType( const char * ); 
+
+    virtual char **GetCategoryNames();
+    virtual CPLErr SetCategoryNames( char ** );
+
+    virtual CPLErr SetMetadata( char **papszMD, const char *pszDomain = "" );
+    virtual CPLErr SetMetadataItem( const char *pszName, const char *pszValue,
+                                    const char *pszDomain = "" );
+
+    virtual double GetOffset( int *pbSuccess = NULL );
+    CPLErr SetOffset( double );
+    virtual double GetScale( int *pbSuccess = NULL );
+    CPLErr SetScale( double );
+    
+    virtual CPLErr  GetHistogram( double dfMin, double dfMax,
+                          int nBuckets, int * panHistogram,
+                          int bIncludeOutOfRange, int bApproxOK,
+                          GDALProgressFunc, void *pProgressData );
+
+    virtual CPLErr GetDefaultHistogram( double *pdfMin, double *pdfMax,
+                                        int *pnBuckets, int ** ppanHistogram,
+                                        int bForce,
+                                        GDALProgressFunc, void *pProgressData);
+
+    virtual CPLErr SetDefaultHistogram( double dfMin, double dfMax,
+                                        int nBuckets, int *panHistogram );
+
+    CPLErr         CopyCommonInfoFrom( GDALRasterBand * );
+};
+
+/************************************************************************/
+/*                         VRTSourcedRasterBand                         */
+/************************************************************************/
+
+class CPL_DLL VRTSourcedRasterBand : public VRTRasterBand
+{
+
+    void           Initialize( int nXSize, int nYSize );
+
+  public:
+    int		   nSources;
+    VRTSource    **papoSources;
+    int            bEqualAreas;
+
+    		   VRTSourcedRasterBand( GDALDataset *poDS, int nBand );
+                   VRTSourcedRasterBand( GDALDataType eType, 
+                                         int nXSize, int nYSize );
+                   VRTSourcedRasterBand( GDALDataset *poDS, int nBand, 
+                                         GDALDataType eType, 
+                                         int nXSize, int nYSize );
+    virtual        ~VRTSourcedRasterBand();
+
+    virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
+                              void *, int, int, GDALDataType,
+                              int, int );
+
+    virtual char      **GetMetadata( const char * pszDomain = "" );
+    virtual CPLErr      SetMetadata( char ** papszMetadata,
+                                     const char * pszDomain = "" );
+
+    virtual CPLErr         XMLInit( CPLXMLNode *, const char * );
+    virtual CPLXMLNode *   SerializeToXML( const char *pszVRTPath );
+
+    CPLErr         AddSource( VRTSource * );
+    CPLErr         AddSimpleSource( GDALRasterBand *poSrcBand, 
+                                    int nSrcXOff=-1, int nSrcYOff=-1, 
+                                    int nSrcXSize=-1, int nSrcYSize=-1, 
+                                    int nDstXOff=-1, int nDstYOff=-1, 
+                                    int nDstXSize=-1, int nDstYSize=-1,
+                                    const char *pszResampling = "near",
+                                    double dfNoDataValue = VRT_NODATA_UNSET);
+    CPLErr         AddComplexSource( GDALRasterBand *poSrcBand, 
+                                     int nSrcXOff=-1, int nSrcYOff=-1, 
+                                     int nSrcXSize=-1, int nSrcYSize=-1, 
+                                     int nDstXOff=-1, int nDstYOff=-1, 
+                                     int nDstXSize=-1, int nDstYSize=-1,
+                                     double dfScaleOff=0.0, 
+                                     double dfScaleRatio=1.0,
+                                     double dfNoDataValue = VRT_NODATA_UNSET);
+
+    CPLErr         AddFuncSource( VRTImageReadFunc pfnReadFunc, void *hCBData,
+                                  double dfNoDataValue = VRT_NODATA_UNSET );
+
+
+    virtual CPLErr IReadBlock( int, int, void * );
+};
+
+/************************************************************************/
+/*                         VRTWarpedRasterBand                          */
+/************************************************************************/
+
+class CPL_DLL VRTWarpedRasterBand : public VRTRasterBand
+{
+  public:
+                   VRTWarpedRasterBand( GDALDataset *poDS, int nBand,
+                                     GDALDataType eType = GDT_Unknown );
+    virtual        ~VRTWarpedRasterBand();
+
+    virtual CPLErr         XMLInit( CPLXMLNode *, const char * );
+    virtual CPLXMLNode *   SerializeToXML( const char *pszVRTPath );
+
+    virtual CPLErr IReadBlock( int, int, void * );
+
+    virtual int GetOverviewCount();
+    virtual GDALRasterBand *GetOverview(int);
+};
+
+/************************************************************************/
+/*                         VRTDerivedRasterBand                         */
+/************************************************************************/
+
+class CPL_DLL VRTDerivedRasterBand : public VRTSourcedRasterBand
+{
+
+ public:
+    char *pszFuncName;
+    GDALDataType eSourceTransferType;
+
+    VRTDerivedRasterBand(GDALDataset *poDS, int nBand);
+    VRTDerivedRasterBand(GDALDataset *poDS, int nBand, 
+			 GDALDataType eType, int nXSize, int nYSize);
+    virtual        ~VRTDerivedRasterBand();
+
+    virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
+                              void *, int, int, GDALDataType,
+                              int, int );
+
+    static CPLErr AddPixelFunction
+	(const char *pszFuncName, GDALDerivedPixelFunc pfnPixelFunc);
+    static GDALDerivedPixelFunc GetPixelFunction(const char *pszFuncName);
+
+    void SetPixelFunctionName(const char *pszFuncName);
+    void SetSourceTransferType(GDALDataType eDataType);
+
+    virtual CPLErr         XMLInit( CPLXMLNode *, const char * );
+    virtual CPLXMLNode *   SerializeToXML( const char *pszVRTPath );
+
+};
+
+/************************************************************************/
+/*                           VRTRawRasterBand                           */
+/************************************************************************/
+
+class RawRasterBand;
+
+class CPL_DLL VRTRawRasterBand : public VRTRasterBand
+{
+    RawRasterBand  *poRawRaster;
+
+    char           *pszSourceFilename;
+    int            bRelativeToVRT;
+
+  public:
+                   VRTRawRasterBand( GDALDataset *poDS, int nBand,
+                                     GDALDataType eType = GDT_Unknown );
+    virtual        ~VRTRawRasterBand();
+
+    virtual CPLErr         XMLInit( CPLXMLNode *, const char * );
+    virtual CPLXMLNode *   SerializeToXML( const char *pszVRTPath );
+
+    virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
+                              void *, int, int, GDALDataType,
+                              int, int );
+
+    virtual CPLErr IReadBlock( int, int, void * );
+    virtual CPLErr IWriteBlock( int, int, void * );
+
+    CPLErr         SetRawLink( const char *pszFilename, 
+                               const char *pszVRTPath,
+                               int bRelativeToVRT, 
+                               vsi_l_offset nImageOffset, 
+                               int nPixelOffset, int nLineOffset, 
+                               const char *pszByteOrder );
+
+    void           ClearRawLink();
+
+};
+
+/************************************************************************/
+/*                              VRTDriver                               */
+/************************************************************************/
+
+class VRTDriver : public GDALDriver
+{
+  public:
+                 VRTDriver();
+                 ~VRTDriver();
+
+    char         **papszSourceParsers;
+
+    virtual char      **GetMetadata( const char * pszDomain = "" );
+    virtual CPLErr      SetMetadata( char ** papszMetadata,
+                                     const char * pszDomain = "" );
+
+    VRTSource   *ParseSource( CPLXMLNode *psSrc, const char *pszVRTPath );
+    void         AddSourceParser( const char *pszElementName, 
+                                  VRTSourceParser pfnParser );
+};
+
+/************************************************************************/
+/*                           VRTSimpleSource                            */
+/************************************************************************/
+
+class VRTSimpleSource : public VRTSource
+{
+protected:
+    GDALRasterBand      *poRasterBand;
+
+    int                 nSrcXOff;
+    int                 nSrcYOff;
+    int                 nSrcXSize;
+    int                 nSrcYSize;
+
+    int                 nDstXOff;
+    int                 nDstYOff;
+    int                 nDstXSize;
+    int                 nDstYSize;
+
+    int                 bNoDataSet;
+    double              dfNoDataValue;
+
+public:
+            VRTSimpleSource();
+    virtual ~VRTSimpleSource();
+
+    virtual CPLErr  XMLInit( CPLXMLNode *psTree, const char * );
+    virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath );
+
+    void           SetSrcBand( GDALRasterBand * );
+    void           SetSrcWindow( int, int, int, int );
+    void           SetDstWindow( int, int, int, int );
+    void           SetNoDataValue( double dfNoDataValue );
+
+    int            GetSrcDstWindow( int, int, int, int, int, int, 
+                                    int *, int *, int *, int *,
+                                    int *, int *, int *, int * );
+
+    virtual CPLErr  RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, 
+                              void *pData, int nBufXSize, int nBufYSize, 
+                              GDALDataType eBufType, 
+                              int nPixelSpace, int nLineSpace );
+
+    void            DstToSrc( double dfX, double dfY,
+                              double &dfXOut, double &dfYOut );
+    void            SrcToDst( double dfX, double dfY,
+                              double &dfXOut, double &dfYOut );
+
+};
+
+/************************************************************************/
+/*                          VRTAveragedSource                           */
+/************************************************************************/
+
+class VRTAveragedSource : public VRTSimpleSource
+{
+public:
+                    VRTAveragedSource();
+    virtual CPLErr  RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, 
+                              void *pData, int nBufXSize, int nBufYSize, 
+                              GDALDataType eBufType, 
+                              int nPixelSpace, int nLineSpace );
+    virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath );
+};
+
+/************************************************************************/
+/*                           VRTComplexSource                           */
+/************************************************************************/
+
+class VRTComplexSource : public VRTSimpleSource
+{
+public:
+                   VRTComplexSource();
+    virtual        ~VRTComplexSource();
+
+    virtual CPLErr RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, 
+                             void *pData, int nBufXSize, int nBufYSize, 
+                             GDALDataType eBufType, 
+                             int nPixelSpace, int nLineSpace );
+    virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath );
+    virtual CPLErr XMLInit( CPLXMLNode *, const char * );
+
+    int		   bDoScaling;
+    double	   dfScaleOff;
+    double         dfScaleRatio;
+
+};
+
+/************************************************************************/
+/*                           VRTFilteredSource                          */
+/************************************************************************/
+
+class VRTFilteredSource : public VRTComplexSource
+{
+private:
+    int          IsTypeSupported( GDALDataType eType );
+
+protected:
+    int          nSupportedTypesCount;
+    GDALDataType aeSupportedTypes[20];
+
+    int          nExtraEdgePixels;
+
+public:
+            VRTFilteredSource();
+    virtual ~VRTFilteredSource();
+
+    void    SetExtraEdgePixels( int );
+    void    SetFilteringDataTypesSupported( int, GDALDataType * );
+
+    virtual CPLErr  FilterData( int nXSize, int nYSize, GDALDataType eType, 
+                                GByte *pabySrcData, GByte *pabyDstData ) = 0;
+
+    virtual CPLErr  RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, 
+                              void *pData, int nBufXSize, int nBufYSize, 
+                              GDALDataType eBufType, 
+                              int nPixelSpace, int nLineSpace );
+};
+
+/************************************************************************/
+/*                       VRTKernelFilteredSource                        */
+/************************************************************************/
+
+class VRTKernelFilteredSource : public VRTFilteredSource
+{
+protected:
+    int     nKernelSize;
+
+    double  *padfKernelCoefs;
+
+    int     bNormalized;
+
+public:
+            VRTKernelFilteredSource();
+    virtual ~VRTKernelFilteredSource();
+
+    virtual CPLErr  XMLInit( CPLXMLNode *psTree, const char * );
+    virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath );
+
+    virtual CPLErr  FilterData( int nXSize, int nYSize, GDALDataType eType, 
+                                GByte *pabySrcData, GByte *pabyDstData );
+
+    CPLErr          SetKernel( int nKernelSize, double *padfCoefs );
+    void            SetNormalized( int );
+};
+
+/************************************************************************/
+/*                       VRTAverageFilteredSource                       */
+/************************************************************************/
+
+class VRTAverageFilteredSource : public VRTKernelFilteredSource
+{
+public:
+            VRTAverageFilteredSource( int nKernelSize );
+    virtual ~VRTAverageFilteredSource();
+
+    virtual CPLErr  XMLInit( CPLXMLNode *psTree, const char * );
+    virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath );
+};
+
+/************************************************************************/
+/*                            VRTFuncSource                             */
+/************************************************************************/
+class VRTFuncSource : public VRTSource
+{
+public:
+            VRTFuncSource();
+    virtual ~VRTFuncSource();
+
+    virtual CPLErr  XMLInit( CPLXMLNode *, const char *) { return CE_Failure; }
+    virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath );
+
+    virtual CPLErr  RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, 
+                              void *pData, int nBufXSize, int nBufYSize, 
+                              GDALDataType eBufType, 
+                              int nPixelSpace, int nLineSpace );
+
+    VRTImageReadFunc    pfnReadFunc;
+    void               *pCBData;
+    GDALDataType        eType;
+    
+    float               fNoDataValue;
+};
+
+#endif /* ndef VIRTUALDATASET_H_INCLUDED */
diff --git a/Utilities/GDAL/frmts/vrt/vrtderivedrasterband.cpp b/Utilities/GDAL/frmts/vrt/vrtderivedrasterband.cpp
new file mode 100644
index 0000000000..5a94368b28
--- /dev/null
+++ b/Utilities/GDAL/frmts/vrt/vrtderivedrasterband.cpp
@@ -0,0 +1,453 @@
+/******************************************************************************
+ *
+ * Project:  Virtual GDAL Datasets
+ * Purpose:  Implementation of a sourced raster band that derives its raster
+ *           by applying an algorithm (GDALDerivedPixelFunc) to the sources.
+ * Author:   Pete Nagy
+ *
+ ******************************************************************************
+ * Copyright (c) 2005 Vexcel Corp.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#include "vrtdataset.h"
+#include "cpl_minixml.h"
+#include "cpl_string.h"
+
+/************************************************************************/
+/* ==================================================================== */
+/*                          VRTDerivedRasterBand                        */
+/* ==================================================================== */
+/************************************************************************/
+
+static int nFunctions = 0;
+static GDALDerivedPixelFunc *papfnPixelFunctions = NULL;
+static char **papszNames = NULL;
+
+/************************************************************************/
+/*                        VRTDerivedRasterBand()                        */
+/************************************************************************/
+
+VRTDerivedRasterBand::VRTDerivedRasterBand(GDALDataset *poDS, int nBand)
+    : VRTSourcedRasterBand( poDS, nBand )
+
+{
+    this->pszFuncName = NULL;
+    this->eSourceTransferType = GDT_Unknown;
+}
+
+/************************************************************************/
+/*                        VRTDerivedRasterBand()                        */
+/************************************************************************/
+
+VRTDerivedRasterBand::VRTDerivedRasterBand(GDALDataset *poDS, int nBand,
+					   GDALDataType eType, 
+					   int nXSize, int nYSize)
+    : VRTSourcedRasterBand(poDS, nBand, eType, nXSize, nYSize)
+
+{
+    this->pszFuncName = NULL;
+    this->eSourceTransferType = GDT_Unknown;
+}
+
+/************************************************************************/
+/*                       ~VRTDerivedRasterBand()                        */
+/************************************************************************/
+
+VRTDerivedRasterBand::~VRTDerivedRasterBand()
+
+{
+    if (this->pszFuncName != NULL) {
+        CPLFree(this->pszFuncName);
+        this->pszFuncName = NULL;
+    }
+}
+
+/************************************************************************/
+/*                           AddPixelFunction()                         */
+/************************************************************************/
+
+/**
+ * This adds a pixel function to the global list of available pixel
+ * functions for derived bands.  Pixel functions must be registered
+ * in this way before a derived band tries to access data.
+ *
+ * Derived bands are stored with only the name of the pixel function 
+ * that it will apply, and if a pixel function matching the name is not
+ * found the IRasterIO() call will do nothing.
+ *
+ * @param pszFuncName Name used to access pixel function
+ * @param pfnNewFunction Pixel function associated with name.  An
+ *  existing pixel function registered with the same name will be
+ *  replaced with the new one.
+ *
+ * @return CE_None, invalid (NULL) parameters are currently ignored.
+ */
+CPLErr CPL_STDCALL GDALAddDerivedBandPixelFunc
+(const char *pszFuncName, GDALDerivedPixelFunc pfnNewFunction)
+{
+    int ii;
+
+    /* ---- Init ---- */
+    if ((pszFuncName == NULL) || (pfnNewFunction == NULL)) {
+	return CE_None;
+    }
+
+    /* ---- Check for match with fn already on list, and replace ---- */
+    for (ii = 0; ii < nFunctions; ii++) {
+	if (strcmp(pszFuncName, papszNames[ii]) == 0) {
+	    papfnPixelFunctions[ii] = pfnNewFunction;
+	    return CE_None;
+	}
+    }
+
+    /* ---- Increment pixel function counter and add name/fn to lists ---- */
+    nFunctions++;
+
+    papfnPixelFunctions = (GDALDerivedPixelFunc *) 
+        CPLRealloc(papfnPixelFunctions, sizeof(void*) * nFunctions);
+    papfnPixelFunctions[nFunctions - 1] = pfnNewFunction;
+
+    papszNames = (char **)
+	CPLRealloc(papszNames, sizeof(void*) * nFunctions);
+    papszNames[nFunctions - 1] = (char *)pszFuncName;
+
+    return CE_None;
+}
+/**
+ * This adds a pixel function to the global list of available pixel
+ * functions for derived bands.
+ *
+ * This is the same as the c function GDALAddDerivedBandPixelFunc()
+ *
+ * @param pszFuncName Name used to access pixel function
+ * @param pfnNewFunction Pixel function associated with name.  An
+ *  existing pixel function registered with the same name will be
+ *  replaced with the new one.
+ *
+ * @return CE_None, invalid (NULL) parameters are currently ignored.
+ */
+CPLErr VRTDerivedRasterBand::AddPixelFunction
+(const char *pszFuncName, GDALDerivedPixelFunc pfnNewFunction)
+{
+    return GDALAddDerivedBandPixelFunc(pszFuncName, pfnNewFunction);
+}
+
+/************************************************************************/
+/*                           GetPixelFunction()                         */
+/************************************************************************/
+
+/**
+ * Get a pixel function previously registered using the global
+ * AddPixelFunction.
+ *
+ * @param pszFuncName The name associated with the pixel function.
+ *
+ * @return A derived band pixel function, or NULL if none have been
+ * registered for pszFuncName.
+ */
+GDALDerivedPixelFunc VRTDerivedRasterBand::GetPixelFunction
+(const char *pszFuncName)
+{
+    int ii;
+
+    /* ---- Init ---- */
+    if ((pszFuncName == NULL) || (pszFuncName[0] == '\0')) return NULL;
+
+    /* ---- Check for match with fn added with AddPixelFunction ---- */
+    for (ii = 0; ii < nFunctions; ii++) {
+	if (strcmp(pszFuncName, papszNames[ii]) == 0) {
+	    return papfnPixelFunctions[ii];
+	}
+    }
+
+    return NULL;
+}
+
+/************************************************************************/
+/*                         SetPixelFunctionName()                       */
+/************************************************************************/
+
+/**
+ * Set the pixel function name to be applied to this derived band.  The
+ * name should match a pixel function registered using AddPixelFunction.
+ *
+ * @param pszFuncName Name of pixel function to be applied to this derived
+ * band.
+ */
+void VRTDerivedRasterBand::SetPixelFunctionName(const char *pszFuncName)
+{
+    this->pszFuncName = CPLStrdup( pszFuncName );
+}
+
+/************************************************************************/
+/*                         SetSourceTransferType()                      */
+/************************************************************************/
+
+/**
+ * Set the transfer type to be used to obtain pixel information from
+ * all of the sources.  If unset, the transfer type used will be the
+ * same as the derived band data type.  This makes it possible, for
+ * example, to pass CFloat32 source pixels to the pixel function, even
+ * if the pixel function generates a raster for a derived band that
+ * is of type Byte.
+ *
+ * @param eDataType Data type to use to obtain pixel information from
+ * the sources to be passed to the derived band pixel function.
+ */
+void VRTDerivedRasterBand::SetSourceTransferType(GDALDataType eDataType)
+{
+    this->eSourceTransferType = eDataType;
+}
+
+/************************************************************************/
+/*                             IRasterIO()                              */
+/************************************************************************/
+
+/**
+ * Read/write a region of image data for this band.
+ *
+ * Each of the sources for this derived band will be read and passed to
+ * the derived band pixel function.  The pixel function is responsible
+ * for applying whatever algorithm is necessary to generate this band's
+ * pixels from the sources.
+ *
+ * The sources will be read using the transfer type specified for sources
+ * using SetSourceTransferType().  If no transfer type has been set for
+ * this derived band, the band's data type will be used as the transfer type.
+ *
+ * @see gdalrasterband
+ *
+ * @param eRWFlag Either GF_Read to read a region of data, or GT_Write to
+ * write a region of data.
+ *
+ * @param nXOff The pixel offset to the top left corner of the region
+ * of the band to be accessed.  This would be zero to start from the left side.
+ *
+ * @param nYOff The line offset to the top left corner of the region
+ * of the band to be accessed.  This would be zero to start from the top.
+ *
+ * @param nXSize The width of the region of the band to be accessed in pixels.
+ *
+ * @param nYSize The height of the region of the band to be accessed in lines.
+ *
+ * @param pData The buffer into which the data should be read, or from which
+ * it should be written.  This buffer must contain at least nBufXSize *
+ * nBufYSize words of type eBufType.  It is organized in left to right,
+ * top to bottom pixel order.  Spacing is controlled by the nPixelSpace,
+ * and nLineSpace parameters.
+ *
+ * @param nBufXSize The width of the buffer image into which the desired
+ * region is to be read, or from which it is to be written.
+ *
+ * @param nBufYSize The height of the buffer image into which the desired
+ * region is to be read, or from which it is to be written.
+ *
+ * @param eBufType The type of the pixel values in the pData data buffer.  The
+ * pixel values will automatically be translated to/from the GDALRasterBand
+ * data type as needed.
+ *
+ * @param nPixelSpace The byte offset from the start of one pixel value in
+ * pData to the start of the next pixel value within a scanline.  If defaulted
+ * (0) the size of the datatype eBufType is used.
+ *
+ * @param nLineSpace The byte offset from the start of one scanline in
+ * pData to the start of the next.  If defaulted the size of the datatype
+ * eBufType * nBufXSize is used.
+ *
+ * @return CE_Failure if the access fails, otherwise CE_None.
+ */
+CPLErr VRTDerivedRasterBand::IRasterIO(GDALRWFlag eRWFlag,
+				       int nXOff, int nYOff, int nXSize, 
+				       int nYSize, void * pData, int nBufXSize,
+				       int nBufYSize, GDALDataType eBufType,
+				       int nPixelSpace, int nLineSpace )
+{
+    GDALDerivedPixelFunc pfnPixelFunc;
+    void **pBuffers;
+    CPLErr eErr = CE_None;
+    int iSource, ii, typesize, sourcesize;
+    GDALDataType eSrcType;
+
+    if( eRWFlag == GF_Write )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Writing through VRTSourcedRasterBand is not supported." );
+        return CE_Failure;
+    }
+
+    typesize = GDALGetDataTypeSize(eBufType) / 8;
+    if (GDALGetDataTypeSize(eBufType) % 8 > 0) typesize++;
+    eSrcType = this->eSourceTransferType;
+    if ((eSrcType == GDT_Unknown) || (eSrcType >= GDT_TypeCount)) {
+	eSrcType = eBufType;
+    }
+    sourcesize = GDALGetDataTypeSize(eSrcType) / 8;
+
+/* -------------------------------------------------------------------- */
+/*      Initialize the buffer to some background value. Use the         */
+/*      nodata value if available.                                      */
+/* -------------------------------------------------------------------- */
+    if ( nPixelSpace == typesize &&
+         (!bNoDataValueSet || dfNoDataValue == 0) ) {
+        memset( pData, 0, nBufXSize * nBufYSize * nPixelSpace );
+    }
+    else if ( !bEqualAreas || bNoDataValueSet )
+    {
+        double dfWriteValue = 0.0;
+        int    iLine;
+
+        if( bNoDataValueSet )
+            dfWriteValue = dfNoDataValue;
+
+        for( iLine = 0; iLine < nBufYSize; iLine++ )
+        {
+            GDALCopyWords( &dfWriteValue, GDT_Float64, 0, 
+                           ((GByte *)pData) + nLineSpace * iLine, 
+                           eBufType, nPixelSpace, nBufXSize );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Do we have overviews that would be appropriate to satisfy       */
+/*      this request?                                                   */
+/* -------------------------------------------------------------------- */
+    if( (nBufXSize < nXSize || nBufYSize < nYSize)
+        && GetOverviewCount() > 0 )
+    {
+        if( OverviewRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, 
+                              pData, nBufXSize, nBufYSize, 
+                              eBufType, nPixelSpace, nLineSpace ) == CE_None )
+            return CE_None;
+    }
+
+    /* ---- Get pixel function for band ---- */
+    pfnPixelFunc = VRTDerivedRasterBand::GetPixelFunction(this->pszFuncName);
+    if (pfnPixelFunc == NULL) {
+	CPLError( CE_Fatal, CPLE_IllegalArg, 
+		  "VRTDerivedRasterBand::IRasterIO:" \
+		  "Derived band pixel function '%s' not registered.\n",
+		  this->pszFuncName);
+	return CE_Failure;
+    }
+
+    /* TODO: It would be nice to use a MallocBlock function for each
+       individual buffer that would recycle blocks of memory from a
+       cache by reassigning blocks that are nearly the same size.
+       A corresponding FreeBlock might only truly free if the total size
+       of freed blocks gets to be too great of a percentage of the size
+       of the allocated blocks. */
+
+    /* ---- Get buffers for each source ---- */
+    pBuffers = (void **) CPLMalloc(sizeof(void *) * nSources);
+    for (iSource = 0; iSource < nSources; iSource++) {
+        pBuffers[iSource] = (void *) 
+	    malloc(sourcesize * nBufXSize * nBufYSize);
+        if (pBuffers[iSource] == NULL) {
+            for (ii = 0; ii < iSource; ii++) {
+                free(pBuffers[iSource]);
+	    }
+	    CPLError( CE_Fatal, CPLE_OutOfMemory, 
+		      "VRTDerivedRasterBand::IRasterIO:" \
+		      "Out of memory allocating %d bytes.\n",
+		      nPixelSpace * nBufXSize * nBufYSize);
+	    return CE_Failure;
+	}
+    }
+
+    /* ---- Load values for sources into packed buffers ---- */
+    for(iSource = 0; iSource < nSources; iSource++) {
+        eErr = ((VRTSource *)papoSources[iSource])->RasterIO
+	    (nXOff, nYOff, nXSize, nYSize, 
+	     pBuffers[iSource], nBufXSize, nBufYSize, 
+	     eSrcType, 0, 0);
+    }
+
+    /* ---- Apply pixel function ---- */
+    if (eErr == CE_None) {
+	eErr = pfnPixelFunc((void **)pBuffers, nSources,
+			    pData, nBufXSize, nBufYSize,
+			    eSrcType, eBufType, nPixelSpace, nLineSpace);
+    }
+
+    /* ---- Release buffers ---- */
+    for (iSource = 0; iSource < nSources; iSource++) {
+        free(pBuffers[iSource]);
+    }
+    CPLFree(pBuffers);
+
+    return eErr;
+}
+
+/************************************************************************/
+/*                              XMLInit()                               */
+/************************************************************************/
+
+CPLErr VRTDerivedRasterBand::XMLInit(CPLXMLNode *psTree, 
+				     const char *pszVRTPath)
+
+{
+    CPLErr eErr;
+    const char *pszTypeName;
+
+    eErr = VRTSourcedRasterBand::XMLInit( psTree, pszVRTPath );
+    if( eErr != CE_None )
+        return eErr;
+
+    /* ---- Read derived pixel function type ---- */
+    this->SetPixelFunctionName
+	(CPLGetXMLValue(psTree, "PixelFunctionType", NULL));
+
+    /* ---- Read optional source transfer data type ---- */
+    pszTypeName = CPLGetXMLValue(psTree, "SourceTransferType", NULL);
+    if (pszTypeName != NULL) {
+	this->eSourceTransferType = GDALGetDataTypeByName(pszTypeName);
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           SerializeToXML()                           */
+/************************************************************************/
+
+CPLXMLNode *VRTDerivedRasterBand::SerializeToXML(const char *pszVRTPath)
+{
+    CPLXMLNode *psTree;
+
+    psTree = VRTSourcedRasterBand::SerializeToXML( pszVRTPath );
+
+/* -------------------------------------------------------------------- */
+/*      Set subclass.                                                   */
+/* -------------------------------------------------------------------- */
+    CPLCreateXMLNode( 
+        CPLCreateXMLNode( psTree, CXT_Attribute, "subClass" ), 
+        CXT_Text, "VRTDerivedRasterBand" );
+
+    /* ---- Encode DerivedBand-specific fields ---- */
+    if( strlen(this->pszFuncName) > 0 )
+        CPLSetXMLValue(psTree, "PixelFunctionType", this->pszFuncName);
+    if( this->eSourceTransferType != GDT_Unknown)
+        CPLSetXMLValue(psTree, "SourceTransferType", 
+		       GDALGetDataTypeName(this->eSourceTransferType));
+
+    return psTree;
+}
+
diff --git a/Utilities/GDAL/frmts/vrt/vrtdriver.cpp b/Utilities/GDAL/frmts/vrt/vrtdriver.cpp
new file mode 100644
index 0000000000..87f5a41572
--- /dev/null
+++ b/Utilities/GDAL/frmts/vrt/vrtdriver.cpp
@@ -0,0 +1,309 @@
+/******************************************************************************
+ * $Id: vrtdriver.cpp,v 1.7 2004/08/11 18:45:56 warmerda Exp $
+ *
+ * Project:  Virtual GDAL Datasets
+ * Purpose:  Implementation of VRTDriver
+ * Author:   Frank Warmerdam <warmerdam@pobox.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: vrtdriver.cpp,v $
+ * Revision 1.7  2004/08/11 18:45:56  warmerda
+ * Use CopyCommonInfoFrom method
+ *
+ * Revision 1.6  2004/07/28 16:56:36  warmerda
+ * updated to use VRTSourcedRasterBand
+ *
+ * Revision 1.5  2004/04/15 18:54:38  warmerda
+ * added UnitType, Offset, Scale and CategoryNames support
+ *
+ * Revision 1.4  2004/04/02 17:20:06  warmerda
+ * Added defaulte extension, and point help topic to VRT tutorial.
+ *
+ * Revision 1.3  2004/03/16 18:34:35  warmerda
+ * added support for relativeToVRT attribute on SourceFilename
+ *
+ * Revision 1.2  2003/07/27 11:16:06  dron
+ * Check NULL pointer in GetMetadata()/SetMetadata().
+ *
+ * Revision 1.1  2003/07/17 20:27:18  warmerda
+ * New
+ *
+ */
+
+#include "vrtdataset.h"
+#include "cpl_minixml.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: vrtdriver.cpp,v 1.7 2004/08/11 18:45:56 warmerda Exp $");
+
+/************************************************************************/
+/*                             VRTDriver()                              */
+/************************************************************************/
+
+VRTDriver::VRTDriver()
+
+{
+    papszSourceParsers = NULL;
+}
+
+/************************************************************************/
+/*                             ~VRTDriver()                             */
+/************************************************************************/
+
+VRTDriver::~VRTDriver()
+
+{
+    CSLDestroy( papszSourceParsers );
+}
+
+/************************************************************************/
+/*                            GetMetadata()                             */
+/************************************************************************/
+
+char **VRTDriver::GetMetadata( const char *pszDomain )
+
+{
+    if( pszDomain && EQUAL(pszDomain,"SourceParsers") )
+        return papszSourceParsers;
+    else
+        return GDALDriver::GetMetadata( pszDomain );
+}
+
+/************************************************************************/
+/*                            SetMetadata()                             */
+/************************************************************************/
+
+CPLErr VRTDriver::SetMetadata( char **papszMetadata, const char *pszDomain )
+
+{
+    if( pszDomain && EQUAL(pszDomain,"SourceParsers") )
+    {
+        CSLDestroy( papszSourceParsers );
+        papszSourceParsers = CSLDuplicate( papszMetadata );
+        return CE_None;
+    }
+    else
+        return GDALDriver::SetMetadata( papszMetadata, pszDomain );
+}
+
+/************************************************************************/
+/*                          AddSourceParser()                           */
+/************************************************************************/
+
+void VRTDriver::AddSourceParser( const char *pszElementName, 
+                                 VRTSourceParser pfnParser )
+
+{
+    char szPtrValue[128];
+
+    sprintf( szPtrValue, "%p", pfnParser );
+    papszSourceParsers = CSLSetNameValue( papszSourceParsers, 
+                                          pszElementName, szPtrValue );
+}
+
+/************************************************************************/
+/*                            ParseSource()                             */
+/************************************************************************/
+
+VRTSource *VRTDriver::ParseSource( CPLXMLNode *psSrc, const char *pszVRTPath )
+
+{
+    const char *pszParserFunc;
+
+    if( psSrc == NULL || psSrc->eType != CXT_Element )
+        return NULL;
+
+    pszParserFunc = CSLFetchNameValue( papszSourceParsers, psSrc->pszValue );
+    if( pszParserFunc == NULL )
+        return NULL;
+
+    VRTSourceParser pfnParser = NULL;
+
+    sscanf( pszParserFunc, "%p", &pfnParser );
+
+    if( pfnParser == NULL )
+        return NULL;
+    else
+        return pfnParser( psSrc, pszVRTPath );
+}
+
+/************************************************************************/
+/*                           VRTCreateCopy()                            */
+/************************************************************************/
+
+static GDALDataset *
+VRTCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, 
+               int bStrict, char ** papszOptions, 
+               GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    VRTDataset *poVRTDS;
+
+    (void) bStrict;
+    (void) papszOptions;
+
+/* -------------------------------------------------------------------- */
+/*      If the source dataset is a virtual dataset then just write      */
+/*      it to disk as a special case to avoid extra layers of           */
+/*      indirection.                                                    */
+/* -------------------------------------------------------------------- */
+    if( EQUAL(poSrcDS->GetDriver()->GetDescription(),"VRT") )
+    {
+        FILE *fpVRT;
+
+        fpVRT = VSIFOpen( pszFilename, "w" );
+
+    /* -------------------------------------------------------------------- */
+    /*      Convert tree to a single block of XML text.                     */
+    /* -------------------------------------------------------------------- */
+        char *pszVRTPath = CPLStrdup(CPLGetPath(pszFilename));
+        CPLXMLNode *psDSTree = ((VRTDataset *) poSrcDS)->SerializeToXML( pszVRTPath );
+        char *pszXML;
+        
+        pszXML = CPLSerializeXMLTree( psDSTree );
+        
+        CPLDestroyXMLNode( psDSTree );
+
+        CPLFree( pszVRTPath );
+        
+    /* -------------------------------------------------------------------- */
+    /*      Write to disk.                                                  */
+    /* -------------------------------------------------------------------- */
+        VSIFWrite( pszXML, 1, strlen(pszXML), fpVRT );
+        VSIFClose( fpVRT );
+
+        CPLFree( pszXML );
+        
+        return (GDALDataset *) GDALOpen( pszFilename, GA_Update );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create the virtual dataset.                                     */
+/* -------------------------------------------------------------------- */
+    poVRTDS = (VRTDataset *) 
+        VRTDataset::Create( pszFilename, 
+                            poSrcDS->GetRasterXSize(),
+                            poSrcDS->GetRasterYSize(),
+                            0, GDT_Byte, NULL );
+
+/* -------------------------------------------------------------------- */
+/*      Do we have a geotransform?                                      */
+/* -------------------------------------------------------------------- */
+    double adfGeoTransform[6];
+
+    if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None )
+    {
+        poVRTDS->SetGeoTransform( adfGeoTransform );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Copy projection                                                 */
+/* -------------------------------------------------------------------- */
+    poVRTDS->SetProjection( poSrcDS->GetProjectionRef() );
+
+/* -------------------------------------------------------------------- */
+/*      Emit dataset level metadata.                                    */
+/* -------------------------------------------------------------------- */
+    poVRTDS->SetMetadata( poSrcDS->GetMetadata() );
+
+/* -------------------------------------------------------------------- */
+/*      GCPs                                                            */
+/* -------------------------------------------------------------------- */
+    if( poSrcDS->GetGCPCount() > 0 )
+    {
+        poVRTDS->SetGCPs( poSrcDS->GetGCPCount(), 
+                          poSrcDS->GetGCPs(),
+                          poSrcDS->GetGCPProjection() );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Loop over all the bands.					*/
+/* -------------------------------------------------------------------- */
+    for( int iBand = 0; iBand < poSrcDS->GetRasterCount(); iBand++ )
+    {
+        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 );
+
+/* -------------------------------------------------------------------- */
+/*      Create the band with the appropriate band type.                 */
+/* -------------------------------------------------------------------- */
+        poVRTDS->AddBand( poSrcBand->GetRasterDataType(), NULL );
+
+        VRTSourcedRasterBand *poVRTBand = 
+            (VRTSourcedRasterBand *) poVRTDS->GetRasterBand( iBand+1 );
+
+/* -------------------------------------------------------------------- */
+/*      Setup source mapping.                                           */
+/* -------------------------------------------------------------------- */
+        poVRTBand->AddSimpleSource( poSrcBand );
+
+/* -------------------------------------------------------------------- */
+/*      Emit various band level metadata.                               */
+/* -------------------------------------------------------------------- */
+        poVRTBand->CopyCommonInfoFrom( poSrcBand );
+    }
+
+    poVRTDS->FlushCache();
+
+    return poVRTDS;
+}
+
+/************************************************************************/
+/*                          GDALRegister_VRT()                          */
+/************************************************************************/
+
+void GDALRegister_VRT()
+
+{
+    VRTDriver	*poDriver;
+
+    if( GDALGetDriverByName( "VRT" ) == NULL )
+    {
+        poDriver = new VRTDriver();
+        
+        poDriver->SetDescription( "VRT" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "Virtual Raster" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "vrt" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "gdal_vrttut.html" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte Int16 UInt16 Int32 UInt32 Float32 Float64 CInt16 CInt32 CFloat32 CFloat64" );
+        
+        poDriver->pfnOpen = VRTDataset::Open;
+        poDriver->pfnCreateCopy = VRTCreateCopy;
+        poDriver->pfnCreate = VRTDataset::Create;
+
+        poDriver->AddSourceParser( "SimpleSource", 
+                                   VRTParseCoreSources );
+        poDriver->AddSourceParser( "ComplexSource", 
+                                   VRTParseCoreSources );
+        poDriver->AddSourceParser( "AveragedSource", 
+                                   VRTParseCoreSources );
+
+        poDriver->AddSourceParser( "KernelFilteredSource", 
+                                   VRTParseFilterSources );
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
diff --git a/Utilities/GDAL/frmts/vrt/vrtfilters.cpp b/Utilities/GDAL/frmts/vrt/vrtfilters.cpp
new file mode 100644
index 0000000000..bb5b8eaab8
--- /dev/null
+++ b/Utilities/GDAL/frmts/vrt/vrtfilters.cpp
@@ -0,0 +1,629 @@
+/******************************************************************************
+ * $Id: vrtfilters.cpp,v 1.6 2006/04/03 18:08:42 fwarmerdam Exp $
+ *
+ * Project:  Virtual GDAL Datasets
+ * Purpose:  Implementation of some filter types.
+ * Author:   Frank Warmerdam <warmerdam@pobox.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: vrtfilters.cpp,v $
+ * Revision 1.6  2006/04/03 18:08:42  fwarmerdam
+ * Preinitialize working buffer for filtering to zero.
+ *
+ * Revision 1.5  2004/08/11 18:46:45  warmerda
+ * pass pszVRTPath through serialize methods
+ *
+ * Revision 1.4  2004/03/16 18:34:35  warmerda
+ * added support for relativeToVRT attribute on SourceFilename
+ *
+ * Revision 1.3  2003/09/11 19:53:32  warmerda
+ * avoid type casting warnings
+ *
+ * Revision 1.2  2003/08/07 17:11:21  warmerda
+ * added normalized flag for kernel based filters
+ *
+ * Revision 1.1  2003/07/17 20:27:18  warmerda
+ * New
+ *
+ */
+
+#include "vrtdataset.h"
+#include "cpl_minixml.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: vrtfilters.cpp,v 1.6 2006/04/03 18:08:42 fwarmerdam Exp $");
+
+/************************************************************************/
+/* ==================================================================== */
+/*                          VRTFilteredSource                           */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                         VRTFilteredSource()                          */
+/************************************************************************/
+
+VRTFilteredSource::VRTFilteredSource()
+
+{
+    nExtraEdgePixels = 0;
+    nSupportedTypesCount = 1;
+    aeSupportedTypes[0] = GDT_Float32;
+}
+
+/************************************************************************/
+/*                         ~VRTFilteredSource()                         */
+/************************************************************************/
+
+VRTFilteredSource::~VRTFilteredSource()
+
+{
+}
+
+/************************************************************************/
+/*                         SetExtraEdgePixels()                         */
+/************************************************************************/
+
+void VRTFilteredSource::SetExtraEdgePixels( int nEdgePixels )
+
+{
+    nExtraEdgePixels = nEdgePixels;
+}
+
+/************************************************************************/
+/*                   SetFilteringDataTypesSupported()                   */
+/************************************************************************/
+
+void VRTFilteredSource::SetFilteringDataTypesSupported( int nTypeCount, 
+                                                        GDALDataType *paeTypes)
+
+{
+    if( nTypeCount > 
+        (int) sizeof(sizeof(aeSupportedTypes)/sizeof(GDALDataType)) )
+    {
+        CPLAssert( FALSE );
+        nTypeCount = (int) 
+            sizeof(sizeof(aeSupportedTypes)/sizeof(GDALDataType));
+    }
+
+    nSupportedTypesCount = nTypeCount;
+    memcpy( aeSupportedTypes, paeTypes, sizeof(GDALDataType) * nTypeCount );
+}
+
+/************************************************************************/
+/*                          IsTypeSupported()                           */
+/************************************************************************/
+
+int VRTFilteredSource::IsTypeSupported( GDALDataType eTestType )
+
+{
+    int i;
+
+    for( i = 0; i < nSupportedTypesCount; i++ )
+    {
+        if( eTestType == aeSupportedTypes[i] )
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+/************************************************************************/
+/*                              RasterIO()                              */
+/************************************************************************/
+
+CPLErr
+VRTFilteredSource::RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, 
+                             void *pData, int nBufXSize, int nBufYSize, 
+                             GDALDataType eBufType, 
+                             int nPixelSpace, int nLineSpace )
+
+{
+/* -------------------------------------------------------------------- */
+/*      For now we don't support filtered access to non-full            */
+/*      resolution requests. Just collect the data directly without     */
+/*      any operator.                                                   */
+/* -------------------------------------------------------------------- */
+    if( nBufXSize != nXSize || nBufYSize != nYSize )
+    {
+        return VRTComplexSource::RasterIO( nXOff, nYOff, nXSize, nYSize, 
+                                           pData, nBufXSize, nBufYSize, 
+                                           eBufType, nPixelSpace, nLineSpace );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Determine the data type we want to request.  We try to match    */
+/*      the source or destination request, and if both those fail we    */
+/*      fallback to the first supported type at least as expressive     */
+/*      as the request.                                                 */
+/* -------------------------------------------------------------------- */
+    GDALDataType eOperDataType = GDT_Unknown;
+    int i;
+    
+    if( IsTypeSupported( eBufType ) )
+        eOperDataType = eBufType;
+
+    if( eOperDataType == GDT_Unknown 
+        && IsTypeSupported( poRasterBand->GetRasterDataType() ) )
+        eOperDataType = poRasterBand->GetRasterDataType();
+
+    if( eOperDataType == GDT_Unknown )
+    {
+        for( i = 0; i < nSupportedTypesCount; i++ )
+        {
+            if( GDALDataTypeUnion( aeSupportedTypes[i], eBufType ) 
+                == aeSupportedTypes[i] )
+            {
+                eOperDataType = aeSupportedTypes[i];
+            }
+        }
+    }
+
+    if( eOperDataType == GDT_Unknown )
+    {
+        eOperDataType = aeSupportedTypes[0];
+
+        for( i = 1; i < nSupportedTypesCount; i++ )
+        {
+            if( GDALGetDataTypeSize( aeSupportedTypes[i] ) 
+                > GDALGetDataTypeSize( eOperDataType ) )
+            {
+                eOperDataType = aeSupportedTypes[i];
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Allocate the buffer of data into which our imagery will be      */
+/*      read, with the extra edge pixels as well. This will be the      */
+/*      source data fed into the filter.                                */
+/* -------------------------------------------------------------------- */
+    int nPixelOffset, nLineOffset;
+    int nExtraXSize = nBufXSize + 2 * nExtraEdgePixels;
+    int nExtraYSize = nBufYSize + 2 * nExtraEdgePixels;
+    GByte *pabyWorkData;
+
+    pabyWorkData = (GByte *) 
+        VSICalloc( nExtraXSize * nExtraYSize,
+                   (GDALGetDataTypeSize(eOperDataType) / 8) );
+    
+    if( pabyWorkData == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OutOfMemory, 
+                  "Work buffer allocation failed." );
+        return CE_Failure;
+    }
+
+    nPixelOffset = GDALGetDataTypeSize( eOperDataType ) / 8;
+    nLineOffset = nPixelOffset * nExtraXSize;
+
+/* -------------------------------------------------------------------- */
+/*      Allocate the output buffer if the passed in output buffer is    */
+/*      not of the same type as our working format, or if the passed    */
+/*      in buffer has an unusual organization.                          */
+/* -------------------------------------------------------------------- */
+    GByte *pabyOutData;
+
+    if( nPixelSpace != nPixelOffset || nLineSpace != nLineOffset
+        || eOperDataType != eBufType )
+    {
+        pabyOutData = (GByte *) 
+            VSIMalloc( nBufXSize * nBufYSize * nPixelOffset );
+
+        if( pabyOutData == NULL )
+        {
+            CPLError( CE_Failure, CPLE_OutOfMemory, 
+                      "Work buffer allocation failed." );
+            return CE_Failure;
+        }
+    }
+    else
+        pabyOutData = (GByte *) pData;
+
+/* -------------------------------------------------------------------- */
+/*      Figure out the extended window that we want to load.  Note      */
+/*      that we keep track of the file window as well as the amount     */
+/*      we will need to edge fill past the edge of the source dataset.  */
+/* -------------------------------------------------------------------- */
+    int nTopFill=0, nLeftFill=0, nRightFill=0, nBottomFill=0;
+    int nFileXOff, nFileYOff, nFileXSize, nFileYSize;
+
+    nFileXOff = nXOff - nExtraEdgePixels;
+    nFileYOff = nYOff - nExtraEdgePixels;
+    nFileXSize = nExtraXSize;
+    nFileYSize = nExtraYSize;
+
+    if( nFileXOff < 0 )
+    {
+        nLeftFill = -nFileXOff;
+        nFileXOff = 0;
+        nFileXSize -= nLeftFill;
+    }
+
+    if( nFileYOff < 0 )
+    {
+        nTopFill = -nFileYOff;
+        nFileYOff = 0;
+        nFileYSize -= nTopFill;
+    }
+
+    if( nFileXOff + nFileXSize > poRasterBand->GetXSize() )
+    {
+        nRightFill = nFileXOff + nFileXSize - poRasterBand->GetXSize();
+        nFileXSize -= nRightFill;
+    }
+
+    if( nFileYOff + nFileYSize > poRasterBand->GetYSize() )
+    {
+        nBottomFill = nFileYOff + nFileYSize - poRasterBand->GetYSize();
+        nFileYSize -= nBottomFill;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Load the data.                                                  */
+/* -------------------------------------------------------------------- */
+    CPLErr eErr;
+
+    eErr = 
+      VRTComplexSource::RasterIO( nFileXOff, nFileYOff, nFileXSize, nFileYSize,
+                                  pabyWorkData 
+                                  + nLineOffset * nTopFill
+                                  + nPixelOffset * nLeftFill,
+                                  nFileXSize, nFileYSize, eOperDataType, 
+                                  nPixelOffset, nLineOffset );
+
+    if( eErr != CE_None )
+    {
+        if( pabyWorkData != pData )
+            VSIFree( pabyWorkData );
+
+        return eErr;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Fill in missing areas.  Note that we replicate the edge         */
+/*      valid values out.  We don't using "mirroring" which might be    */
+/*      more suitable for some times of filters.  We also don't mark    */
+/*      these pixels as "nodata" though perhaps we should.              */
+/* -------------------------------------------------------------------- */
+    if( nLeftFill != 0 || nRightFill != 0 )
+    {
+        for( i = nTopFill; i < nExtraYSize - nBottomFill; i++ )
+        {
+            if( nLeftFill != 0 )
+                GDALCopyWords( pabyWorkData + nPixelOffset * nLeftFill
+                               + i * nLineOffset, eOperDataType, 0, 
+                               pabyWorkData + i * nLineOffset, eOperDataType, 
+                               nPixelOffset, nLeftFill );
+
+            if( nRightFill != 0 )
+                GDALCopyWords( pabyWorkData + i * nLineOffset
+                               + nPixelOffset * (nExtraXSize - nRightFill - 1),
+                               eOperDataType, 0, 
+                               pabyWorkData + i * nLineOffset
+                               + nPixelOffset * (nExtraXSize - nRightFill),
+                               eOperDataType, nPixelOffset, nRightFill );
+        }
+    }
+
+    for( i = 0; i < nTopFill; i++ )
+    {
+        memcpy( pabyWorkData + i * nLineOffset, 
+                pabyWorkData + nTopFill * nLineOffset, 
+                nLineOffset );
+    }
+
+    for( i = nExtraYSize - nBottomFill; i < nExtraYSize; i++ )
+    {
+        memcpy( pabyWorkData + i * nLineOffset, 
+                pabyWorkData + (nExtraYSize - nBottomFill - 1) * nLineOffset, 
+                nLineOffset );
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Filter the data.                                                */
+/* -------------------------------------------------------------------- */
+    eErr = FilterData( nBufXSize, nBufYSize, eOperDataType, 
+                       pabyWorkData, pabyOutData );
+
+    VSIFree( pabyWorkData );
+    if( eErr != CE_None )
+    {
+        if( pabyOutData != pData )
+            VSIFree( pabyOutData );
+
+        return eErr;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Copy from work buffer to target buffer.                         */
+/* -------------------------------------------------------------------- */
+    if( pabyOutData != pData )
+    {
+        for( i = 0; i < nBufYSize; i++ )
+        {
+            GDALCopyWords( pabyOutData + i * (nPixelOffset * nBufXSize),
+                           eOperDataType, nPixelOffset,
+                           ((GByte *) pData) + i * nLineSpace, 
+                           eBufType, nPixelSpace, nBufXSize );
+        }
+
+        VSIFree( pabyOutData );
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                       VRTKernelFilteredSource                        */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                      VRTKernelFilteredSource()                       */
+/************************************************************************/
+
+VRTKernelFilteredSource::VRTKernelFilteredSource()
+
+{
+    GDALDataType aeSupTypes[] = { GDT_Float32 };
+    padfKernelCoefs = NULL;
+    nKernelSize = 0;
+    bNormalized = FALSE;
+
+    SetFilteringDataTypesSupported( 1, aeSupTypes );
+}
+
+/************************************************************************/
+/*                      ~VRTKernelFilteredSource()                      */
+/************************************************************************/
+
+VRTKernelFilteredSource::~VRTKernelFilteredSource() 
+
+{
+    CPLFree( padfKernelCoefs );
+}
+
+/************************************************************************/
+/*                           SetNormalized()                            */
+/************************************************************************/
+
+void VRTKernelFilteredSource::SetNormalized( int bNormalizedIn )
+
+{
+    bNormalized = bNormalizedIn;
+}
+
+/************************************************************************/
+/*                             SetKernel()                              */
+/************************************************************************/
+
+CPLErr VRTKernelFilteredSource::SetKernel( int nNewKernelSize, 
+                                           double *padfNewCoefs )
+
+{
+    if( nNewKernelSize < 1 || (nNewKernelSize % 2) != 1 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Illegal filtering kernel size %d, must be odd positive number.", 
+                  nNewKernelSize );
+        return CE_Failure;
+    }
+
+    CPLFree( padfKernelCoefs );
+    nKernelSize = nNewKernelSize;
+
+    padfKernelCoefs = (double *) 
+        CPLMalloc(sizeof(double) * nKernelSize * nKernelSize );
+    memcpy( padfKernelCoefs, padfNewCoefs, 
+            sizeof(double) * nKernelSize * nKernelSize );
+
+    SetExtraEdgePixels( (nNewKernelSize - 1) / 2 );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                             FilterData()                             */
+/************************************************************************/
+
+CPLErr VRTKernelFilteredSource::
+FilterData( int nXSize, int nYSize, GDALDataType eType, 
+            GByte *pabySrcData, GByte *pabyDstData )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Validate data type.                                             */
+/* -------------------------------------------------------------------- */
+    if( eType != GDT_Float32 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Unsupported data type (%s) in VRTKernelFilteredSource::FilterData()", 
+                  GDALGetDataTypeName( eType ) );
+        return CE_Failure; 
+    }
+
+    CPLAssert( nExtraEdgePixels*2 + 1 == nKernelSize );
+
+/* -------------------------------------------------------------------- */
+/*      Float32 case.                                                   */
+/* -------------------------------------------------------------------- */
+    if( eType == GDT_Float32 )
+    {
+        int iX, iY; 
+
+        for( iY = 0; iY < nYSize; iY++ )
+        {
+            for( iX = 0; iX < nXSize; iX++ )
+            {
+                int    iYY, iKern = 0;
+                double dfSum = 0.0, dfKernSum = 0.0;
+                float  fResult;
+
+                for( iYY = 0; iYY < nKernelSize; iYY++ )
+                {
+                    int i;
+                    float *pafData = ((float *)pabySrcData) 
+                        + (iY+iYY) * (nXSize+2*nExtraEdgePixels) + iX;
+
+                    for( i = 0; i < nKernelSize; i++, pafData++, iKern++ )
+                    {
+                        dfSum += *pafData * padfKernelCoefs[iKern];
+                        dfKernSum += padfKernelCoefs[iKern];
+                    }
+                }
+
+                if( bNormalized )
+                {
+                    if( dfKernSum != 0.0 )
+                        fResult = (float) (dfSum / dfKernSum);
+                    else
+                        fResult = 0.0;
+                }
+                else
+                    fResult = (float) dfSum;
+
+                ((float *) pabyDstData)[iX + iY * nXSize] = fResult;
+            }
+        }
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                              XMLInit()                               */
+/************************************************************************/
+
+CPLErr VRTKernelFilteredSource::XMLInit( CPLXMLNode *psTree, 
+                                         const char *pszVRTPath )
+
+{
+    CPLErr eErr = VRTFilteredSource::XMLInit( psTree, pszVRTPath );
+    int    nNewKernelSize, i, nCoefs;
+    double *padfNewCoefs;
+
+    if( eErr != CE_None )
+        return eErr;
+
+    nNewKernelSize = atoi(CPLGetXMLValue(psTree,"Kernel.Size","0"));
+
+    if( nNewKernelSize == 0 )
+        return CE_None;
+
+    char **papszCoefItems = 
+        CSLTokenizeString( CPLGetXMLValue(psTree,"Kernel.Coefs","") );
+
+    nCoefs = CSLCount(papszCoefItems);
+
+    if( nCoefs != nNewKernelSize * nNewKernelSize )
+    {
+        CSLDestroy( papszCoefItems );
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Got wrong number of filter kernel coefficients (%s)", 
+                  CPLGetXMLValue(psTree,"Kernel.Coefs","") );
+        return CE_Failure;
+    }
+
+    padfNewCoefs = (double *) CPLMalloc(sizeof(double) * nCoefs);
+
+    for( i = 0; i < nCoefs; i++ )
+        padfNewCoefs[i] = atof(papszCoefItems[i]);
+
+    eErr = SetKernel( nNewKernelSize, padfNewCoefs );
+
+    CPLFree( padfNewCoefs );
+    CSLDestroy( papszCoefItems );
+
+    SetNormalized( atoi(CPLGetXMLValue(psTree,"Kernel.normalized","0")) );
+
+    return eErr;
+}
+
+/************************************************************************/
+/*                           SerializeToXML()                           */
+/************************************************************************/
+
+CPLXMLNode *VRTKernelFilteredSource::SerializeToXML( const char *pszVRTPath )
+
+{
+    CPLXMLNode *psSrc = VRTFilteredSource::SerializeToXML( pszVRTPath );
+    CPLXMLNode *psKernel;
+    char *pszKernelCoefs;
+    int iCoef, nCoefCount = nKernelSize * nKernelSize;
+
+    if( psSrc == NULL )
+        return NULL;
+
+    CPLFree( psSrc->pszValue );
+    psSrc->pszValue = CPLStrdup("KernelFilteredSource" );
+
+    psKernel = CPLCreateXMLNode( psSrc, CXT_Element, "Kernel" );
+
+    if( bNormalized )
+        CPLCreateXMLNode( 
+            CPLCreateXMLNode( psKernel, CXT_Attribute, "normalized" ), 
+            CXT_Text, "1" );
+    else
+        CPLCreateXMLNode( 
+            CPLCreateXMLNode( psKernel, CXT_Attribute, "normalized" ), 
+            CXT_Text, "0" );
+
+    pszKernelCoefs = (char *) CPLMalloc(nCoefCount * 32);
+
+    strcpy( pszKernelCoefs, "" );
+    for( iCoef = 0; iCoef < nCoefCount; iCoef++ )
+        sprintf( pszKernelCoefs + strlen(pszKernelCoefs), 
+                 "%.8g ", padfKernelCoefs[iCoef] );
+    
+    CPLSetXMLValue( psKernel, "Size", CPLSPrintf( "%d", nKernelSize ) );
+    CPLSetXMLValue( psKernel, "Coefs", pszKernelCoefs );
+
+    CPLFree( pszKernelCoefs );
+
+    return psSrc;
+}
+
+/************************************************************************/
+/*                       VRTParseFilterSources()                        */
+/************************************************************************/
+
+VRTSource *VRTParseFilterSources( CPLXMLNode *psChild, const char *pszVRTPath )
+
+{
+    VRTSource *poSrc;
+
+    if( EQUAL(psChild->pszValue,"KernelFilteredSource") )
+    {
+        poSrc = new VRTKernelFilteredSource();
+        if( poSrc->XMLInit( psChild, pszVRTPath ) == CE_None )
+            return poSrc;
+        else
+            delete poSrc;
+    }
+
+    return NULL;
+}
+
diff --git a/Utilities/GDAL/frmts/vrt/vrtrasterband.cpp b/Utilities/GDAL/frmts/vrt/vrtrasterband.cpp
new file mode 100644
index 0000000000..9b57c7223d
--- /dev/null
+++ b/Utilities/GDAL/frmts/vrt/vrtrasterband.cpp
@@ -0,0 +1,811 @@
+/******************************************************************************
+ * $Id: vrtrasterband.cpp,v 1.22 2006/02/08 06:12:08 fwarmerdam Exp $
+ *
+ * Project:  Virtual GDAL Datasets
+ * Purpose:  Implementation of VRTRasterBand
+ * Author:   Frank Warmerdam <warmerdam@pobox.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: vrtrasterband.cpp,v $
+ * Revision 1.22  2006/02/08 06:12:08  fwarmerdam
+ * Override SetMetadata methods so that metadata can be preserved.
+ * Support saving histograms in VRT per bug 1060.
+ *
+ * Revision 1.21  2005/05/05 13:56:14  fwarmerdam
+ * moved metadata handling to PAM
+ *
+ * Revision 1.20  2004/08/11 18:46:45  warmerda
+ * pass pszVRTPath through serialize methods
+ *
+ * Revision 1.19  2004/07/28 16:55:08  warmerda
+ * split alot of functionality out into VRTSourcedRasterBand
+ *
+ * Revision 1.18  2004/04/15 18:54:38  warmerda
+ * added UnitType, Offset, Scale and CategoryNames support
+ *
+ * Revision 1.17  2004/03/16 18:34:35  warmerda
+ * added support for relativeToVRT attribute on SourceFilename
+ *
+ * Revision 1.16  2003/12/10 15:44:42  warmerda
+ * Fix problem with NULL pszDomain values.
+ *
+ * Revision 1.15  2003/07/17 20:29:15  warmerda
+ * Moved all the sources implementations out.
+ * Improved error checking and returning when initializing from XML.
+ * Added vrt_sources (and new_vrt_sources) metadata domains to permit
+ * management of sources via XML passed in/out of the metadata methods.
+ *
+ * Revision 1.14  2003/06/10 19:59:33  warmerda
+ * added new Func based source type for passthrough to a callback
+ *
+ * Revision 1.13  2003/03/14 10:31:22  dron
+ * Check return value of the VRTSimpleSource::XMLInit() in VRTRasterBand::XMLInit().
+ *
+ * Revision 1.12  2003/03/13 20:39:25  dron
+ * Improved block initialization logic in IRasterIO().
+ *
+ * Revision 1.11  2003/01/15 21:30:48  warmerda
+ * some rounding tweaks when adjusting output size in GetSrcDstWindow
+ *
+ * Revision 1.10  2002/12/13 15:07:07  warmerda
+ * fixed bug in IReadBlock
+ *
+ * Revision 1.9  2002/12/04 03:12:57  warmerda
+ * Fixed some bugs in last change.
+ *
+ * Revision 1.8  2002/12/03 15:21:19  warmerda
+ * finished up window computations for odd cases
+ *
+ * Revision 1.7  2002/11/24 04:29:02  warmerda
+ * Substantially rewrote VRTSimpleSource.  Now VRTSource is base class, and
+ * sources do their own SerializeToXML(), and XMLInit().  New VRTComplexSource
+ * supports scaling and nodata values.
+ *
+ * Revision 1.6  2002/07/15 18:51:46  warmerda
+ * ensure even unshared datasets are dereferenced properly
+ *
+ * Revision 1.5  2002/05/29 18:13:44  warmerda
+ * added nodata handling for averager
+ *
+ * Revision 1.4  2002/05/29 16:06:05  warmerda
+ * complete detailed band metadata
+ *
+ * Revision 1.3  2001/12/06 19:53:58  warmerda
+ * added write check to rasterio
+ *
+ * Revision 1.2  2001/11/16 21:38:07  warmerda
+ * try to work even if bModified
+ *
+ * Revision 1.1  2001/11/16 21:14:31  warmerda
+ * New
+ *
+ */
+
+#include "vrtdataset.h"
+#include "cpl_minixml.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: vrtrasterband.cpp,v 1.22 2006/02/08 06:12:08 fwarmerdam Exp $");
+
+/************************************************************************/
+/* ==================================================================== */
+/*                          VRTRasterBand                               */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                           VRTRasterBand()                            */
+/************************************************************************/
+
+VRTRasterBand::VRTRasterBand()
+
+{
+    Initialize( 0, 0 );
+}
+
+/************************************************************************/
+/*                             Initialize()                             */
+/************************************************************************/
+
+void VRTRasterBand::Initialize( int nXSize, int nYSize )
+
+{
+    poDS = NULL;
+    nBand = 0;
+    eAccess = GA_ReadOnly;
+    eDataType = GDT_Byte;
+
+    nRasterXSize = nXSize;
+    nRasterYSize = nYSize;
+    
+    nBlockXSize = MIN(128,nXSize);
+    nBlockYSize = MIN(128,nYSize);
+
+    bNoDataValueSet = FALSE;
+    dfNoDataValue = -10000.0;
+    poColorTable = NULL;
+    eColorInterp = GCI_Undefined;
+
+    pszUnitType = NULL;
+    papszCategoryNames = NULL;
+    dfOffset = 0.0;
+    dfScale = 1.0;
+
+    psSavedHistograms = NULL;
+}
+
+/************************************************************************/
+/*                           ~VRTRasterBand()                           */
+/************************************************************************/
+
+VRTRasterBand::~VRTRasterBand()
+
+{
+    CPLFree( pszUnitType );
+
+    if( poColorTable != NULL )
+        delete poColorTable;
+
+    CSLDestroy( papszCategoryNames );
+}
+
+/************************************************************************/
+/*                         CopyCommonInfoFrom()                         */
+/*                                                                      */
+/*      Copy common metadata, pixel descriptions, and color             */
+/*      interpretation from the provided source band.                   */
+/************************************************************************/
+
+CPLErr VRTRasterBand::CopyCommonInfoFrom( GDALRasterBand * poSrcBand )
+
+{
+    int bSuccess;
+    double dfNoData;
+
+    SetMetadata( poSrcBand->GetMetadata() );
+    SetColorTable( poSrcBand->GetColorTable() );
+    SetColorInterpretation(poSrcBand->GetColorInterpretation());
+    if( strlen(poSrcBand->GetDescription()) > 0 )
+        SetDescription( poSrcBand->GetDescription() );
+    dfNoData = poSrcBand->GetNoDataValue( &bSuccess );
+    if( bSuccess )
+        SetNoDataValue( dfNoData );
+
+    SetOffset( poSrcBand->GetOffset() );
+    SetScale( poSrcBand->GetScale() );
+    SetCategoryNames( poSrcBand->GetCategoryNames() );
+    if( !EQUAL(poSrcBand->GetUnitType(),"") )
+        SetUnitType( poSrcBand->GetUnitType() );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                            SetMetadata()                             */
+/************************************************************************/
+
+CPLErr VRTRasterBand::SetMetadata( char **papszMetadata, 
+                                   const char *pszDomain )
+
+{
+    ((VRTDataset *) poDS)->SetNeedsFlush();
+
+    return GDALRasterBand::SetMetadata( papszMetadata, pszDomain );
+}
+
+/************************************************************************/
+/*                          SetMetadataItem()                           */
+/************************************************************************/
+
+CPLErr VRTRasterBand::SetMetadataItem( const char *pszName, 
+                                       const char *pszValue, 
+                                       const char *pszDomain )
+
+{
+    ((VRTDataset *) poDS)->SetNeedsFlush();
+
+    return GDALRasterBand::SetMetadataItem( pszName, pszValue, pszDomain );
+}
+
+/************************************************************************/
+/*                            GetUnitType()                             */
+/************************************************************************/
+
+const char *VRTRasterBand::GetUnitType()
+
+{
+    if( pszUnitType == NULL )
+        return "";
+    else
+        return pszUnitType;
+}
+
+/************************************************************************/
+/*                            SetUnitType()                             */
+/************************************************************************/
+
+CPLErr VRTRasterBand::SetUnitType( const char *pszNewValue )
+
+{
+    CPLFree( pszUnitType );
+    
+    if( pszNewValue == NULL )
+        pszUnitType = NULL;
+    else
+        pszUnitType = CPLStrdup(pszNewValue);
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                             GetOffset()                              */
+/************************************************************************/
+
+double VRTRasterBand::GetOffset( int *pbSuccess )
+
+{
+    if( pbSuccess != NULL )
+        *pbSuccess = TRUE;
+
+    return dfOffset;
+}
+
+/************************************************************************/
+/*                             SetOffset()                              */
+/************************************************************************/
+
+CPLErr VRTRasterBand::SetOffset( double dfNewOffset )
+
+{
+    dfOffset = dfNewOffset;
+    return CE_None;
+}
+
+/************************************************************************/
+/*                              GetScale()                              */
+/************************************************************************/
+
+double VRTRasterBand::GetScale( int *pbSuccess )
+
+{
+    if( pbSuccess != NULL )
+        *pbSuccess = TRUE;
+
+    return dfScale;
+}
+
+/************************************************************************/
+/*                              SetScale()                              */
+/************************************************************************/
+
+CPLErr VRTRasterBand::SetScale( double dfNewScale )
+
+{
+    dfScale = dfNewScale;
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          GetCategoryNames()                          */
+/************************************************************************/
+
+char **VRTRasterBand::GetCategoryNames()
+
+{
+    return papszCategoryNames;
+}
+
+/************************************************************************/
+/*                          SetCategoryNames()                          */
+/************************************************************************/
+
+CPLErr VRTRasterBand::SetCategoryNames( char ** papszNewNames )
+
+{
+    CSLDestroy( papszCategoryNames );
+    papszCategoryNames = CSLDuplicate( papszNewNames );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                              XMLInit()                               */
+/************************************************************************/
+
+CPLErr VRTRasterBand::XMLInit( CPLXMLNode * psTree, 
+                               const char *pszVRTPath )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Validate a bit.                                                 */
+/* -------------------------------------------------------------------- */
+    if( psTree == NULL || psTree->eType != CXT_Element
+        || !EQUAL(psTree->pszValue,"VRTRasterBand") )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Invalid node passed to VRTRasterBand::XMLInit()." );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Set the band if provided as an attribute.                       */
+/* -------------------------------------------------------------------- */
+    if( CPLGetXMLValue( psTree, "band", NULL) != NULL )
+        nBand = atoi(CPLGetXMLValue( psTree, "band", "0"));
+
+/* -------------------------------------------------------------------- */
+/*      Set the band if provided as an attribute.                       */
+/* -------------------------------------------------------------------- */
+    const char *pszDataType = CPLGetXMLValue( psTree, "dataType", NULL);
+    if( pszDataType != NULL )
+    {
+        for( int iType = 0; iType < GDT_TypeCount; iType++ )
+        {
+            const char *pszThisName = GDALGetDataTypeName((GDALDataType)iType);
+
+            if( pszThisName != NULL && EQUAL(pszDataType,pszThisName) )
+            {
+                eDataType = (GDALDataType) iType;
+                break;
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Apply any band level metadata.                                  */
+/* -------------------------------------------------------------------- */
+    PamApplyMetadata( psTree, this );
+
+/* -------------------------------------------------------------------- */
+/*      Collect various other items of metadata.                        */
+/* -------------------------------------------------------------------- */
+    SetDescription( CPLGetXMLValue( psTree, "Description", "" ) );
+    
+    if( CPLGetXMLValue( psTree, "NoDataValue", NULL ) != NULL )
+        SetNoDataValue( atof(CPLGetXMLValue( psTree, "NoDataValue", "0" )) );
+
+    SetUnitType( CPLGetXMLValue( psTree, "UnitType", NULL ) );
+
+    SetOffset( atof(CPLGetXMLValue( psTree, "Offset", "0.0" )) );
+    SetScale( atof(CPLGetXMLValue( psTree, "Scale", "1.0" )) );
+
+    if( CPLGetXMLValue( psTree, "ColorInterp", NULL ) != NULL )
+    {
+        const char *pszInterp = CPLGetXMLValue( psTree, "ColorInterp", NULL );
+        int        iInterp;
+        
+        for( iInterp = 0; iInterp < 13; iInterp++ )
+        {
+            const char *pszCandidate 
+                = GDALGetColorInterpretationName( (GDALColorInterp) iInterp );
+
+            if( pszCandidate != NULL && EQUAL(pszCandidate,pszInterp) )
+            {
+                SetColorInterpretation( (GDALColorInterp) iInterp );
+                break;
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Category names.                                                 */
+/* -------------------------------------------------------------------- */
+    if( CPLGetXMLNode( psTree, "CategoryNames" ) != NULL )
+    {
+        CPLXMLNode *psEntry;
+
+        CSLDestroy( papszCategoryNames );
+        papszCategoryNames = NULL;
+
+        for( psEntry = CPLGetXMLNode( psTree, "CategoryNames" )->psChild;
+             psEntry != NULL; psEntry = psEntry->psNext )
+        {
+            if( psEntry->eType != CXT_Element 
+                || !EQUAL(psEntry->pszValue,"Category") 
+                || psEntry->psChild == NULL 
+                || psEntry->psChild->eType != CXT_Text )
+                continue;
+            
+            papszCategoryNames = CSLAddString( papszCategoryNames, 
+                                               psEntry->psChild->pszValue );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Collect a color table.                                          */
+/* -------------------------------------------------------------------- */
+    if( CPLGetXMLNode( psTree, "ColorTable" ) != NULL )
+    {
+        CPLXMLNode *psEntry;
+        GDALColorTable oTable;
+        int        iEntry = 0;
+
+        for( psEntry = CPLGetXMLNode( psTree, "ColorTable" )->psChild;
+             psEntry != NULL; psEntry = psEntry->psNext )
+        {
+            GDALColorEntry sCEntry;
+
+            sCEntry.c1 = (short) atoi(CPLGetXMLValue( psEntry, "c1", "0" ));
+            sCEntry.c2 = (short) atoi(CPLGetXMLValue( psEntry, "c2", "0" ));
+            sCEntry.c3 = (short) atoi(CPLGetXMLValue( psEntry, "c3", "0" ));
+            sCEntry.c4 = (short) atoi(CPLGetXMLValue( psEntry, "c4", "255" ));
+
+            oTable.SetColorEntry( iEntry++, &sCEntry );
+        }
+        
+        SetColorTable( &oTable );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Histograms                                                      */
+/* -------------------------------------------------------------------- */
+    CPLXMLNode *psHist = CPLGetXMLNode( psTree, "Histograms" );
+    if( psHist != NULL )
+    {
+        CPLXMLNode *psNext = psHist->psNext;
+        psHist->psNext = NULL;
+
+        psSavedHistograms = CPLCloneXMLTree( psHist );
+        psHist->psNext = psNext;
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           SerializeToXML()                           */
+/************************************************************************/
+
+CPLXMLNode *VRTRasterBand::SerializeToXML( const char *pszVRTPath )
+
+{
+    CPLXMLNode *psTree;
+
+    psTree = CPLCreateXMLNode( NULL, CXT_Element, "VRTRasterBand" );
+
+/* -------------------------------------------------------------------- */
+/*      Various kinds of metadata.                                      */
+/* -------------------------------------------------------------------- */
+    CPLXMLNode *psMD;
+
+    CPLSetXMLValue( psTree, "#dataType", 
+                    GDALGetDataTypeName( GetRasterDataType() ) );
+
+    if( nBand > 0 )
+        CPLSetXMLValue( psTree, "#band", CPLSPrintf( "%d", GetBand() ) );
+
+    psMD = PamSerializeMetadata( this );
+    if( psMD != NULL )
+        CPLAddXMLChild( psTree, psMD );
+
+    if( strlen(GetDescription()) > 0 )
+        CPLSetXMLValue( psTree, "Description", GetDescription() );
+
+    if( bNoDataValueSet )
+        CPLSetXMLValue( psTree, "NoDataValue", 
+                        CPLSPrintf( "%.14E", dfNoDataValue ) );
+
+    if( pszUnitType != NULL )
+        CPLSetXMLValue( psTree, "UnitType", pszUnitType );
+
+    if( dfOffset != 0.0 )
+        CPLSetXMLValue( psTree, "Offset", 
+                        CPLSPrintf( "%.16g", dfOffset ) );
+
+    if( dfScale != 1.0 )
+        CPLSetXMLValue( psTree, "Scale", 
+                        CPLSPrintf( "%.16g", dfScale ) );
+
+    if( eColorInterp != GCI_Undefined )
+        CPLSetXMLValue( psTree, "ColorInterp", 
+                        GDALGetColorInterpretationName( eColorInterp ) );
+
+/* -------------------------------------------------------------------- */
+/*      Category names.                                                 */
+/* -------------------------------------------------------------------- */
+    if( papszCategoryNames != NULL )
+    {
+        CPLXMLNode *psCT_XML = CPLCreateXMLNode( psTree, CXT_Element, 
+                                                 "CategoryNames" );
+
+        for( int iEntry=0; papszCategoryNames[iEntry] != NULL; iEntry++ )
+        {
+            CPLCreateXMLElementAndValue( psCT_XML, "Category", 
+                                         papszCategoryNames[iEntry] );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Histograms.                                                     */
+/* -------------------------------------------------------------------- */
+    if( psSavedHistograms != NULL )
+        CPLAddXMLChild( psTree, CPLCloneXMLTree( psSavedHistograms ) );
+
+/* -------------------------------------------------------------------- */
+/*      Color Table.                                                    */
+/* -------------------------------------------------------------------- */
+    if( poColorTable != NULL )
+    {
+        CPLXMLNode *psCT_XML = CPLCreateXMLNode( psTree, CXT_Element, 
+                                                 "ColorTable" );
+
+        for( int iEntry=0; iEntry < poColorTable->GetColorEntryCount(); 
+             iEntry++ )
+        {
+            GDALColorEntry sEntry;
+            CPLXMLNode *psEntry_XML = CPLCreateXMLNode( psCT_XML, CXT_Element, 
+                                                        "Entry" );
+
+            poColorTable->GetColorEntryAsRGB( iEntry, &sEntry );
+            
+            CPLSetXMLValue( psEntry_XML, "#c1", CPLSPrintf("%d",sEntry.c1) );
+            CPLSetXMLValue( psEntry_XML, "#c2", CPLSPrintf("%d",sEntry.c2) );
+            CPLSetXMLValue( psEntry_XML, "#c3", CPLSPrintf("%d",sEntry.c3) );
+            CPLSetXMLValue( psEntry_XML, "#c4", CPLSPrintf("%d",sEntry.c4) );
+        }
+    }
+
+    return psTree;
+}
+
+/************************************************************************/
+/*                           SetNoDataValue()                           */
+/************************************************************************/
+
+CPLErr VRTRasterBand::SetNoDataValue( double dfNewValue )
+
+{
+    bNoDataValueSet = TRUE;
+    dfNoDataValue = dfNewValue;
+    
+    ((VRTDataset *)poDS)->SetNeedsFlush();
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           GetNoDataValue()                           */
+/************************************************************************/
+
+double VRTRasterBand::GetNoDataValue( int *pbSuccess )
+
+{
+    if( pbSuccess )
+        *pbSuccess = bNoDataValueSet;
+
+    return dfNoDataValue;
+}
+
+/************************************************************************/
+/*                           SetColorTable()                            */
+/************************************************************************/
+
+CPLErr VRTRasterBand::SetColorTable( GDALColorTable *poTableIn )
+
+{
+    if( poColorTable != NULL )
+    {
+        delete poColorTable;
+        poColorTable = NULL;
+    }
+
+    if( poTableIn )
+    {
+        poColorTable = poTableIn->Clone();
+        eColorInterp = GCI_PaletteIndex;
+    }
+
+    ((VRTDataset *)poDS)->SetNeedsFlush();
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *VRTRasterBand::GetColorTable()
+
+{
+    return poColorTable;
+}
+
+/************************************************************************/
+/*                       SetColorInterpretation()                       */
+/************************************************************************/
+
+CPLErr VRTRasterBand::SetColorInterpretation( GDALColorInterp eInterpIn )
+
+{
+    ((VRTDataset *)poDS)->SetNeedsFlush();
+
+    eColorInterp = eInterpIn;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp VRTRasterBand::GetColorInterpretation()
+
+{
+    return eColorInterp;
+}
+
+/************************************************************************/
+/*                            GetHistogram()                            */
+/************************************************************************/
+
+CPLErr VRTRasterBand::GetHistogram( double dfMin, double dfMax,
+                                    int nBuckets, int * panHistogram,
+                                    int bIncludeOutOfRange, int bApproxOK,
+                                    GDALProgressFunc pfnProgress, 
+                                    void *pProgressData )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Check if we have a matching histogram.                          */
+/* -------------------------------------------------------------------- */
+    CPLXMLNode *psHistItem;
+
+    psHistItem = PamFindMatchingHistogram( psSavedHistograms, 
+                                           dfMin, dfMax, nBuckets, 
+                                           bIncludeOutOfRange, bApproxOK );
+    if( psHistItem != NULL )
+    {
+        int *panTempHist = NULL;
+
+        if( PamParseHistogram( psHistItem, &dfMin, &dfMax, &nBuckets, 
+                               &panTempHist,
+                               &bIncludeOutOfRange, &bApproxOK ) )
+        {
+            memcpy( panHistogram, panTempHist, sizeof(int) * nBuckets );
+            CPLFree( panTempHist );
+            return CE_None;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      We don't have an existing histogram matching the request, so    */
+/*      generate one manually.                                          */
+/* -------------------------------------------------------------------- */
+    CPLErr eErr;
+
+    eErr = GDALRasterBand::GetHistogram( dfMin, dfMax, 
+                                         nBuckets, panHistogram, 
+                                         bIncludeOutOfRange, bApproxOK,
+                                         pfnProgress, pProgressData );
+
+/* -------------------------------------------------------------------- */
+/*      Save an XML description of this histogram.                      */
+/* -------------------------------------------------------------------- */
+    if( eErr == CE_None )
+    {
+        CPLXMLNode *psXMLHist;
+
+        psXMLHist = PamHistogramToXMLTree( dfMin, dfMax, nBuckets, 
+                                           panHistogram, 
+                                           bIncludeOutOfRange, bApproxOK );
+        if( psXMLHist != NULL )
+        {
+            ((VRTDataset *) poDS)->SetNeedsFlush();
+
+            if( psSavedHistograms == NULL )
+                psSavedHistograms = CPLCreateXMLNode( NULL, CXT_Element,
+                                                      "Histograms" );
+            
+            CPLAddXMLChild( psSavedHistograms, psXMLHist );
+        }
+    }
+
+    return eErr;
+}
+
+/************************************************************************/
+/*                        SetDefaultHistogram()                         */
+/************************************************************************/
+
+CPLErr VRTRasterBand::SetDefaultHistogram( double dfMin, double dfMax, 
+                                           int nBuckets, int *panHistogram)
+
+{
+    CPLXMLNode *psNode;
+
+/* -------------------------------------------------------------------- */
+/*      Do we have a matching histogram we should replace?              */
+/* -------------------------------------------------------------------- */
+    psNode = PamFindMatchingHistogram( psSavedHistograms, 
+                                       dfMin, dfMax, nBuckets,
+                                       TRUE, TRUE );
+    if( psNode != NULL )
+    {
+        /* blow this one away */
+        CPLRemoveXMLChild( psSavedHistograms, psNode );
+        CPLDestroyXMLNode( psNode );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Translate into a histogram XML tree.                            */
+/* -------------------------------------------------------------------- */
+    CPLXMLNode *psHistItem;
+
+    psHistItem = PamHistogramToXMLTree( dfMin, dfMax, nBuckets, 
+                                        panHistogram, TRUE, FALSE );
+
+/* -------------------------------------------------------------------- */
+/*      Insert our new default histogram at the front of the            */
+/*      histogram list so that it will be the default histogram.        */
+/* -------------------------------------------------------------------- */
+    ((VRTDataset *) poDS)->SetNeedsFlush();
+
+    if( psSavedHistograms == NULL )
+        psSavedHistograms = CPLCreateXMLNode( NULL, CXT_Element,
+                                              "Histograms" );
+            
+    psHistItem->psNext = psSavedHistograms->psChild;
+    psSavedHistograms->psChild = psHistItem;
+    
+    return CE_None;
+}
+
+/************************************************************************/
+/*                        GetDefaultHistogram()                         */
+/************************************************************************/
+
+CPLErr 
+VRTRasterBand::GetDefaultHistogram( double *pdfMin, double *pdfMax, 
+                                    int *pnBuckets, int **ppanHistogram, 
+                                    int bForce,
+                                    GDALProgressFunc pfnProgress, 
+                                    void *pProgressData )
+    
+{
+    if( psSavedHistograms != NULL )
+    {
+        CPLXMLNode *psXMLHist;
+
+        for( psXMLHist = psSavedHistograms->psChild;
+             psXMLHist != NULL; psXMLHist = psXMLHist->psNext )
+        {
+            int bApprox, bIncludeOutOfRange;
+
+            if( psXMLHist->eType != CXT_Element
+                || !EQUAL(psXMLHist->pszValue,"HistItem") )
+                continue;
+
+            if( PamParseHistogram( psXMLHist, pdfMin, pdfMax, pnBuckets, 
+                                   ppanHistogram, &bIncludeOutOfRange,
+                                   &bApprox ) )
+                return CE_None;
+            else
+                return CE_Failure;
+        }
+    }
+
+    return GDALRasterBand::GetDefaultHistogram( pdfMin, pdfMax, pnBuckets, 
+                                                ppanHistogram, bForce, 
+                                                pfnProgress,pProgressData);
+}
diff --git a/Utilities/GDAL/frmts/vrt/vrtrawrasterband.cpp b/Utilities/GDAL/frmts/vrt/vrtrawrasterband.cpp
new file mode 100644
index 0000000000..f48b93a206
--- /dev/null
+++ b/Utilities/GDAL/frmts/vrt/vrtrawrasterband.cpp
@@ -0,0 +1,414 @@
+/******************************************************************************
+ * $Id: vrtrawrasterband.cpp,v 1.4 2006/01/06 22:21:05 gwalter Exp $
+ *
+ * Project:  Virtual GDAL Datasets
+ * Purpose:  Implementation of VRTRawRasterBand
+ * Author:   Frank Warmerdam <warmerdam@pobox.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: vrtrawrasterband.cpp,v $
+ * Revision 1.4  2006/01/06 22:21:05  gwalter
+ * Make sure overviews are read for raw raster bands if available.
+ *
+ * Revision 1.3  2004/09/29 13:25:12  fwarmerdam
+ * Don't use relative path ... handle in makefile.
+ *
+ * Revision 1.2  2004/08/11 18:46:45  warmerda
+ * pass pszVRTPath through serialize methods
+ *
+ * Revision 1.1  2004/07/30 21:50:24  warmerda
+ * New
+ *
+ */
+
+#include "vrtdataset.h"
+#include "cpl_minixml.h"
+#include "cpl_string.h"
+#include "rawdataset.h"
+
+CPL_CVSID("$Id: vrtrawrasterband.cpp,v 1.4 2006/01/06 22:21:05 gwalter Exp $");
+
+/************************************************************************/
+/* ==================================================================== */
+/*                          VRTRawRasterBand                            */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                          VRTRawRasterBand()                          */
+/************************************************************************/
+
+VRTRawRasterBand::VRTRawRasterBand( GDALDataset *poDS, int nBand,
+                                    GDALDataType eType )
+
+{
+    Initialize( poDS->GetRasterXSize(), poDS->GetRasterYSize() );
+
+    this->poDS = poDS;
+    this->nBand = nBand;
+
+    if( eType != GDT_Unknown )
+        this->eDataType = eType;
+
+    poRawRaster = NULL;
+    pszSourceFilename = NULL;
+}
+
+/************************************************************************/
+/*                         ~VRTRawRasterBand()                          */
+/************************************************************************/
+
+VRTRawRasterBand::~VRTRawRasterBand()
+
+{
+    FlushCache();
+    ClearRawLink();
+}
+
+/************************************************************************/
+/*                             IRasterIO()                              */
+/************************************************************************/
+
+CPLErr VRTRawRasterBand::IRasterIO( GDALRWFlag eRWFlag,
+                                 int nXOff, int nYOff, int nXSize, int nYSize,
+                                 void * pData, int nBufXSize, int nBufYSize,
+                                 GDALDataType eBufType,
+                                 int nPixelSpace, int nLineSpace )
+
+{
+    if( poRawRaster == NULL )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "No raw raster band configured on VRTRawRasterBand." );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Do we have overviews that would be appropriate to satisfy       */
+/*      this request?                                                   */
+/* -------------------------------------------------------------------- */
+    if( (nBufXSize < nXSize || nBufYSize < nYSize)
+        && GetOverviewCount() > 0 )
+    {
+        if( OverviewRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, 
+                              pData, nBufXSize, nBufYSize, 
+                              eBufType, nPixelSpace, nLineSpace ) == CE_None )
+            return CE_None;
+    }
+
+    return poRawRaster->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, 
+                                  pData, nBufXSize, nBufYSize, 
+                                  eBufType, nPixelSpace, nLineSpace );
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr VRTRawRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                   void * pImage )
+
+{
+    if( poRawRaster == NULL )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "No raw raster band configured on VRTRawRasterBand." );
+        return CE_Failure;
+    }
+
+    return poRawRaster->ReadBlock( nBlockXOff, nBlockYOff, pImage );
+}
+
+/************************************************************************/
+/*                            IWriteBlock()                             */
+/************************************************************************/
+
+CPLErr VRTRawRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
+                                      void * pImage )
+
+{
+    if( poRawRaster == NULL )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "No raw raster band configured on VRTRawRasterBand." );
+        return CE_Failure;
+    }
+
+    return poRawRaster->WriteBlock( nBlockXOff, nBlockYOff, pImage );
+}
+
+/************************************************************************/
+/*                             SetRawLink()                             */
+/************************************************************************/
+
+CPLErr VRTRawRasterBand::SetRawLink( const char *pszFilename, 
+                                     const char *pszVRTPath,
+                                     int bRelativeToVRT, 
+                                     vsi_l_offset nImageOffset, 
+                                     int nPixelOffset, int nLineOffset, 
+                                     const char *pszByteOrder )
+
+{
+    ClearRawLink();
+
+    ((VRTDataset *)poDS)->SetNeedsFlush();
+
+/* -------------------------------------------------------------------- */
+/*      Prepare filename.                                               */
+/* -------------------------------------------------------------------- */
+    char *pszExpandedFilename = NULL;
+
+    if( pszFilename == NULL )
+    {
+        CPLError( CE_Warning, CPLE_AppDefined, 
+                  "Missing <SourceFilename> element in VRTRasterBand." );
+        return CE_Failure;
+    }
+    
+    if( pszVRTPath != NULL && bRelativeToVRT )
+    {
+        pszExpandedFilename = CPLStrdup(
+            CPLProjectRelativeFilename( pszVRTPath, pszFilename ) );
+    }
+    else
+        pszExpandedFilename = CPLStrdup( pszFilename );
+
+/* -------------------------------------------------------------------- */
+/*      Try and open the file.  We always use the large file API.       */
+/* -------------------------------------------------------------------- */
+    FILE *fp = CPLOpenShared( pszExpandedFilename, "rb+", TRUE );
+
+    if( fp == NULL )
+        fp = CPLOpenShared( pszExpandedFilename, "rb", TRUE );
+
+    if( fp == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "Unable to open %s.\n%s", 
+                  pszExpandedFilename,
+                  VSIStrerror( errno ) );
+        
+        CPLFree( pszExpandedFilename );
+        return CE_Failure;
+    }
+
+    CPLFree( pszExpandedFilename );
+
+    pszSourceFilename = CPLStrdup(pszFilename);
+    this->bRelativeToVRT = bRelativeToVRT;
+
+/* -------------------------------------------------------------------- */
+/*      Work out if we are in native mode or not.                       */
+/* -------------------------------------------------------------------- */
+    int bNative = TRUE;
+
+    if( pszByteOrder != NULL )
+    {
+        if( EQUAL(pszByteOrder,"LSB") )
+            bNative = CPL_IS_LSB;
+        else if( EQUAL(pszByteOrder,"MSB") )
+            bNative = !CPL_IS_LSB;
+        else
+        {
+            CPLError( CE_Failure, CPLE_AppDefined, 
+                      "Illegal ByteOrder value '%s', should be LSB or MSB.", 
+                      pszByteOrder );
+            return CE_Failure;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding RawRasterBand.                           */
+/* -------------------------------------------------------------------- */
+    poRawRaster = new RawRasterBand( fp, nImageOffset, nPixelOffset, 
+                                     nLineOffset, GetRasterDataType(), 
+                                     bNative, GetXSize(), GetYSize(), TRUE );
+
+/* -------------------------------------------------------------------- */
+/*      Reset block size to match the raw raster.                       */
+/* -------------------------------------------------------------------- */
+    poRawRaster->GetBlockSize( &nBlockXSize, &nBlockYSize );
+
+    return CE_None;
+}
+
+
+/************************************************************************/
+/*                            ClearRawLink()                            */
+/************************************************************************/
+
+void VRTRawRasterBand::ClearRawLink()
+
+{
+    if( poRawRaster != NULL )
+    {
+        if( poRawRaster->GetFP() != NULL )
+        {
+            CPLCloseShared( poRawRaster->GetFP() );
+        }
+        delete poRawRaster;
+        poRawRaster = NULL;
+    }
+    CPLFree( pszSourceFilename );
+    pszSourceFilename = NULL;
+}
+
+/************************************************************************/
+/*                              XMLInit()                               */
+/************************************************************************/
+
+CPLErr VRTRawRasterBand::XMLInit( CPLXMLNode * psTree, 
+                                      const char *pszVRTPath )
+
+{
+    CPLErr eErr;
+
+    eErr = VRTRasterBand::XMLInit( psTree, pszVRTPath );
+    if( eErr != CE_None )
+        return eErr;
+    
+
+/* -------------------------------------------------------------------- */
+/*      Validate a bit.                                                 */
+/* -------------------------------------------------------------------- */
+    if( psTree == NULL || psTree->eType != CXT_Element
+        || !EQUAL(psTree->pszValue,"VRTRasterBand") 
+        || !EQUAL(CPLGetXMLValue(psTree,"subClass",""),"VRTRawRasterBand") )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Invalid node passed to VRTRawRasterBand::XMLInit()." );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Prepare filename.                                               */
+/* -------------------------------------------------------------------- */
+    int  bRelativeToVRT;
+
+    const char *pszFilename = 
+        CPLGetXMLValue(psTree, "SourceFilename", NULL);
+
+    if( pszFilename == NULL )
+    {
+        CPLError( CE_Warning, CPLE_AppDefined, 
+                  "Missing <SourceFilename> element in VRTRasterBand." );
+        return CE_Failure;
+    }
+
+    bRelativeToVRT = atoi(CPLGetXMLValue(psTree,"SourceFilename.relativeToVRT",
+                                         "1"));
+    
+/* -------------------------------------------------------------------- */
+/*      Collect layout information.                                     */
+/* -------------------------------------------------------------------- */
+    vsi_l_offset nImageOffset;
+    int nPixelOffset, nLineOffset;
+    int nWordDataSize = GDALGetDataTypeSize( GetRasterDataType() ) / 8;
+
+    nImageOffset = atoi(CPLGetXMLValue( psTree, "ImageOffset", "0") );
+
+    if( CPLGetXMLValue( psTree, "PixelOffset", NULL ) == NULL )
+        nPixelOffset = nWordDataSize;
+    else
+        nPixelOffset = atoi(CPLGetXMLValue( psTree, "PixelOffset", "0") );
+    
+    if( CPLGetXMLValue( psTree, "LineOffset", NULL ) == NULL )
+        nLineOffset = nWordDataSize * GetXSize();
+    else
+        nLineOffset = atoi(CPLGetXMLValue( psTree, "LineOffset", "0") );
+
+    const char *pszByteOrder = CPLGetXMLValue( psTree, "ByteOrder", NULL );
+
+/* -------------------------------------------------------------------- */
+/*      Open the file, and setup the raw layer access to the data.      */
+/* -------------------------------------------------------------------- */
+    return SetRawLink( pszFilename, pszVRTPath, bRelativeToVRT, 
+                       nImageOffset, nPixelOffset, nLineOffset, 
+                       pszByteOrder );
+}
+
+/************************************************************************/
+/*                           SerializeToXML()                           */
+/************************************************************************/
+
+CPLXMLNode *VRTRawRasterBand::SerializeToXML( const char *pszVRTPath )
+
+{
+    CPLXMLNode *psTree;
+
+    psTree = VRTRasterBand::SerializeToXML( pszVRTPath );
+
+/* -------------------------------------------------------------------- */
+/*      Set subclass.                                                   */
+/* -------------------------------------------------------------------- */
+    CPLCreateXMLNode( 
+        CPLCreateXMLNode( psTree, CXT_Attribute, "subClass" ), 
+        CXT_Text, "VRTRawRasterBand" );
+
+/* -------------------------------------------------------------------- */
+/*      Setup the filename with relative flag.                          */
+/* -------------------------------------------------------------------- */
+    CPLXMLNode *psNode;
+
+    psNode = 
+        CPLCreateXMLElementAndValue( psTree, "SourceFilename", 
+                                     pszSourceFilename );
+    
+    CPLCreateXMLNode( 
+        CPLCreateXMLNode( psNode, CXT_Attribute, "relativeToVRT" ), 
+        CXT_Text, bRelativeToVRT ? "1" : "0"  );
+
+/* -------------------------------------------------------------------- */
+/*      We can't set the layout if there is no open rawband.            */
+/* -------------------------------------------------------------------- */
+    if( poRawRaster == NULL )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "VRTRawRasterBand::SerializeToXML() fails because poRawRaster is NULL." );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Set other layout information.                                   */
+/* -------------------------------------------------------------------- */
+    CPLCreateXMLElementAndValue( 
+        psTree, "ImageOffset", 
+        CPLSPrintf("%d",(int) poRawRaster->GetImgOffset()) );
+    
+    CPLCreateXMLElementAndValue( 
+        psTree, "PixelOffset", 
+        CPLSPrintf("%d",(int) poRawRaster->GetPixelOffset()) );
+    
+    CPLCreateXMLElementAndValue( 
+        psTree, "LineOffset", 
+        CPLSPrintf("%d",(int) poRawRaster->GetLineOffset()) );
+
+    if( (poRawRaster->GetNativeOrder() && CPL_IS_LSB)
+        || (!poRawRaster->GetNativeOrder() && !CPL_IS_LSB) )
+        CPLCreateXMLElementAndValue( psTree, "ByteOrder", "LSB" );
+    else
+        CPLCreateXMLElementAndValue( psTree, "ByteOrder", "MSB" );
+    
+    return psTree;
+}
diff --git a/Utilities/GDAL/frmts/vrt/vrtsourcedrasterband.cpp b/Utilities/GDAL/frmts/vrt/vrtsourcedrasterband.cpp
new file mode 100644
index 0000000000..ee8ec2b384
--- /dev/null
+++ b/Utilities/GDAL/frmts/vrt/vrtsourcedrasterband.cpp
@@ -0,0 +1,596 @@
+/******************************************************************************
+ * $Id: vrtsourcedrasterband.cpp,v 1.5 2005/10/28 16:59:51 pnagy Exp $
+ *
+ * Project:  Virtual GDAL Datasets
+ * Purpose:  Implementation of VRTSourcedRasterBand
+ * Author:   Frank Warmerdam <warmerdam@pobox.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: vrtsourcedrasterband.cpp,v $
+ * Revision 1.5  2005/10/28 16:59:51  pnagy
+ * Added VRTDerivedBand support
+ *
+ * Revision 1.4  2005/06/28 14:51:05  fwarmerdam
+ * added error reporting if there are no sources
+ *
+ * Revision 1.3  2004/08/25 14:21:00  warmerda
+ * Added support for overviews in sourced band RasterIO() contributed
+ * by Sebasien Grignard.
+ *
+ * Revision 1.2  2004/08/11 18:46:45  warmerda
+ * pass pszVRTPath through serialize methods
+ *
+ * Revision 1.1  2004/07/28 16:56:56  warmerda
+ * New
+ *
+ */
+
+#include "vrtdataset.h"
+#include "cpl_minixml.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: vrtsourcedrasterband.cpp,v 1.5 2005/10/28 16:59:51 pnagy Exp $");
+
+/************************************************************************/
+/* ==================================================================== */
+/*                          VRTSourcedRasterBand                        */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                        VRTSourcedRasterBand()                        */
+/************************************************************************/
+
+VRTSourcedRasterBand::VRTSourcedRasterBand( GDALDataset *poDS, int nBand )
+
+{
+    Initialize( poDS->GetRasterXSize(), poDS->GetRasterYSize() );
+
+    this->poDS = poDS;
+    this->nBand = nBand;
+}
+
+/************************************************************************/
+/*                        VRTSourcedRasterBand()                        */
+/************************************************************************/
+
+VRTSourcedRasterBand::VRTSourcedRasterBand( GDALDataType eType, 
+                                            int nXSize, int nYSize )
+
+{
+    Initialize( nXSize, nYSize );
+
+    eDataType = eType;
+}
+
+/************************************************************************/
+/*                        VRTSourcedRasterBand()                        */
+/************************************************************************/
+
+VRTSourcedRasterBand::VRTSourcedRasterBand( GDALDataset *poDS, int nBand,
+                                            GDALDataType eType, 
+                                            int nXSize, int nYSize )
+
+{
+    Initialize( nXSize, nYSize );
+
+    this->poDS = poDS;
+    this->nBand = nBand;
+
+    eDataType = eType;
+}
+
+/************************************************************************/
+/*                             Initialize()                             */
+/************************************************************************/
+
+void VRTSourcedRasterBand::Initialize( int nXSize, int nYSize )
+
+{
+    VRTRasterBand::Initialize( nXSize, nYSize );
+
+    nSources = 0;
+    papoSources = NULL;
+    bEqualAreas = FALSE;
+}
+
+/************************************************************************/
+/*                       ~VRTSourcedRasterBand()                        */
+/************************************************************************/
+
+VRTSourcedRasterBand::~VRTSourcedRasterBand()
+
+{
+    for( int i = 0; i < nSources; i++ )
+        delete papoSources[i];
+
+    CPLFree( papoSources );
+    nSources = 0;
+}
+
+/************************************************************************/
+/*                             IRasterIO()                              */
+/************************************************************************/
+
+CPLErr VRTSourcedRasterBand::IRasterIO( GDALRWFlag eRWFlag,
+                                 int nXOff, int nYOff, int nXSize, int nYSize,
+                                 void * pData, int nBufXSize, int nBufYSize,
+                                 GDALDataType eBufType,
+                                 int nPixelSpace, int nLineSpace )
+
+{
+    int         iSource;
+    CPLErr      eErr = CE_Failure;
+
+    if( eRWFlag == GF_Write )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Writing through VRTSourcedRasterBand is not supported." );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize the buffer to some background value. Use the         */
+/*      nodata value if available.                                      */
+/* -------------------------------------------------------------------- */
+    if ( nPixelSpace == GDALGetDataTypeSize(eBufType)/8 &&
+         (!bNoDataValueSet || dfNoDataValue == 0) )
+        memset( pData, 0, nBufXSize * nBufYSize * nPixelSpace );
+    else if ( !bEqualAreas || bNoDataValueSet )
+    {
+        double dfWriteValue = 0.0;
+        int    iLine;
+
+        if( bNoDataValueSet )
+            dfWriteValue = dfNoDataValue;
+        
+        for( iLine = 0; iLine < nBufYSize; iLine++ )
+        {
+            GDALCopyWords( &dfWriteValue, GDT_Float64, 0, 
+                           ((GByte *)pData) + nLineSpace * iLine, 
+                           eBufType, nPixelSpace, nBufXSize );
+        }
+    }
+
+
+/* -------------------------------------------------------------------- */
+/*      Do we have overviews that would be appropriate to satisfy       */
+/*      this request?                                                   */
+/* -------------------------------------------------------------------- */
+    if( (nBufXSize < nXSize || nBufYSize < nYSize)
+        && GetOverviewCount() > 0 )
+    {
+        if( OverviewRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, 
+                              pData, nBufXSize, nBufYSize, 
+                              eBufType, nPixelSpace, nLineSpace ) == CE_None )
+            return CE_None;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Overlay each source in turn over top this.                      */
+/* -------------------------------------------------------------------- */
+    for( iSource = 0; iSource < nSources; iSource++ )
+    {
+        eErr = 
+            papoSources[iSource]->RasterIO( nXOff, nYOff, nXSize, nYSize, 
+                                            pData, nBufXSize, nBufYSize, 
+                                            eBufType, nPixelSpace, nLineSpace);
+    }
+    
+    return eErr;
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr VRTSourcedRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                   void * pImage )
+
+{
+    int nPixelSize = GDALGetDataTypeSize(eDataType)/8;
+    int nReadXSize, nReadYSize;
+
+    if( (nBlockXOff+1) * nBlockXSize > GetXSize() )
+        nReadXSize = GetXSize() - nBlockXOff * nBlockXSize;
+    else
+        nReadXSize = nBlockXSize;
+
+    if( (nBlockYOff+1) * nBlockYSize > GetYSize() )
+        nReadYSize = GetYSize() - nBlockYOff * nBlockYSize;
+    else
+        nReadYSize = nBlockYSize;
+
+    return IRasterIO( GF_Read, 
+                      nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, 
+                      nReadXSize, nReadYSize, 
+                      pImage, nReadXSize, nReadYSize, eDataType, 
+                      nPixelSize, nPixelSize * nBlockXSize );
+}
+
+/************************************************************************/
+/*                             AddSource()                              */
+/************************************************************************/
+
+CPLErr VRTSourcedRasterBand::AddSource( VRTSource *poNewSource )
+
+{
+    nSources++;
+
+    papoSources = (VRTSource **) 
+        CPLRealloc(papoSources, sizeof(void*) * nSources);
+    papoSources[nSources-1] = poNewSource;
+
+    ((VRTDataset *)poDS)->SetNeedsFlush();
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                              XMLInit()                               */
+/************************************************************************/
+
+CPLErr VRTSourcedRasterBand::XMLInit( CPLXMLNode * psTree, 
+                                      const char *pszVRTPath )
+
+{
+    CPLErr eErr;
+
+    eErr = VRTRasterBand::XMLInit( psTree, pszVRTPath );
+    if( eErr != CE_None )
+        return eErr;
+    
+/* -------------------------------------------------------------------- */
+/*      Validate a bit.                                                 */
+/* -------------------------------------------------------------------- */
+    if( psTree == NULL || psTree->eType != CXT_Element
+        || (!EQUAL(psTree->pszValue,"VRTSourcedRasterBand") 
+            && !EQUAL(psTree->pszValue,"VRTRasterBand")
+	    && !EQUAL(psTree->pszValue,"VRTDerivedRasterBand")) )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Invalid node passed to VRTSourcedRasterBand::XMLInit()." );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Process sources.                                                */
+/* -------------------------------------------------------------------- */
+    CPLXMLNode  *psChild;
+    VRTDriver *poDriver = (VRTDriver *) GDALGetDriverByName( "VRT" );
+    
+    for( psChild = psTree->psChild; 
+         psChild != NULL && poDriver != NULL; 
+         psChild = psChild->psNext)
+    {
+        VRTSource *poSource;
+        
+        CPLErrorReset();
+        poSource = poDriver->ParseSource( psChild, pszVRTPath );
+        if( poSource != NULL )
+            AddSource( poSource );
+        else if( CPLGetLastErrorType() != CE_None )
+            return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Done.                                                           */
+/* -------------------------------------------------------------------- */
+    if( nSources > 0 )
+        return CE_None;
+    else
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "No valid sources found for band in VRT file:\n%s",
+                  pszVRTPath );
+        return CE_Failure;
+    }
+}
+
+/************************************************************************/
+/*                           SerializeToXML()                           */
+/************************************************************************/
+
+CPLXMLNode *VRTSourcedRasterBand::SerializeToXML( const char *pszVRTPath )
+
+{
+    CPLXMLNode *psTree;
+
+    psTree = VRTRasterBand::SerializeToXML( pszVRTPath );
+
+/* -------------------------------------------------------------------- */
+/*      Process Sources.                                                */
+/* -------------------------------------------------------------------- */
+    for( int iSource = 0; iSource < nSources; iSource++ )
+    {
+        CPLXMLNode      *psXMLSrc;
+
+        psXMLSrc = papoSources[iSource]->SerializeToXML( pszVRTPath );
+        
+        if( psXMLSrc != NULL )
+            CPLAddXMLChild( psTree, psXMLSrc );
+    }
+
+    return psTree;
+}
+
+/************************************************************************/
+/*                          AddSimpleSource()                           */
+/************************************************************************/
+
+CPLErr VRTSourcedRasterBand::AddSimpleSource( GDALRasterBand *poSrcBand, 
+                                       int nSrcXOff, int nSrcYOff, 
+                                       int nSrcXSize, int nSrcYSize, 
+                                       int nDstXOff, int nDstYOff, 
+                                       int nDstXSize, int nDstYSize,
+                                       const char *pszResampling, 
+                                       double dfNoDataValue )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Default source and dest rectangles.                             */
+/* -------------------------------------------------------------------- */
+    if( nSrcYSize == -1 )
+    {
+        nSrcXOff = 0;
+        nSrcYOff = 0;
+        nSrcXSize = poSrcBand->GetXSize();
+        nSrcYSize = poSrcBand->GetYSize();
+    }
+
+    if( nDstYSize == -1 )
+    {
+        nDstXOff = 0;
+        nDstYOff = 0;
+        nDstXSize = nRasterXSize;
+        nDstYSize = nRasterYSize;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create source.                                                  */
+/* -------------------------------------------------------------------- */
+    VRTSimpleSource *poSimpleSource;
+
+    if( pszResampling != NULL && EQUALN(pszResampling,"aver",4) )
+        poSimpleSource = new VRTAveragedSource();
+    else
+    {
+        poSimpleSource = new VRTSimpleSource();
+        if( dfNoDataValue != VRT_NODATA_UNSET )
+            CPLError( 
+                CE_Warning, CPLE_AppDefined, 
+                "NODATA setting not currently supported for nearest\n"
+                "neighbour sampled simple sources on Virtual Datasources." );
+    }
+
+    poSimpleSource->SetSrcBand( poSrcBand );
+    poSimpleSource->SetSrcWindow( nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize );
+    poSimpleSource->SetDstWindow( nDstXOff, nDstYOff, nDstXSize, nDstYSize );
+
+    if( dfNoDataValue != VRT_NODATA_UNSET )
+        poSimpleSource->SetNoDataValue( dfNoDataValue );
+
+/* -------------------------------------------------------------------- */
+/*      Default source and dest rectangles.                             */
+/* -------------------------------------------------------------------- */
+    if ( nSrcXOff == nDstXOff && nSrcYOff == nDstYOff &&
+         nSrcXSize == nDstXSize && nSrcYSize == nRasterYSize )
+        bEqualAreas = TRUE;
+
+/* -------------------------------------------------------------------- */
+/*      If we can get the associated GDALDataset, add a reference to it.*/
+/* -------------------------------------------------------------------- */
+    if( poSrcBand->GetDataset() != NULL )
+        poSrcBand->GetDataset()->Reference();
+
+/* -------------------------------------------------------------------- */
+/*      add to list.                                                    */
+/* -------------------------------------------------------------------- */
+    return AddSource( poSimpleSource );
+}
+
+/************************************************************************/
+/*                          AddComplexSource()                          */
+/************************************************************************/
+
+CPLErr VRTSourcedRasterBand::AddComplexSource( GDALRasterBand *poSrcBand, 
+                                        int nSrcXOff, int nSrcYOff, 
+                                        int nSrcXSize, int nSrcYSize, 
+                                        int nDstXOff, int nDstYOff, 
+                                        int nDstXSize, int nDstYSize,
+                                        double dfScaleOff, 
+                                        double dfScaleRatio,
+                                        double dfNoDataValue )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Default source and dest rectangles.                             */
+/* -------------------------------------------------------------------- */
+    if( nSrcYSize == -1 )
+    {
+        nSrcXOff = 0;
+        nSrcYOff = 0;
+        nSrcXSize = poSrcBand->GetXSize();
+        nSrcYSize = poSrcBand->GetYSize();
+    }
+
+    if( nDstYSize == -1 )
+    {
+        nDstXOff = 0;
+        nDstYOff = 0;
+        nDstXSize = nRasterXSize;
+        nDstYSize = nRasterYSize;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create source.                                                  */
+/* -------------------------------------------------------------------- */
+    VRTComplexSource *poSource;
+
+    poSource = new VRTComplexSource();
+
+    poSource->SetSrcBand( poSrcBand );
+    poSource->SetSrcWindow( nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize );
+    poSource->SetDstWindow( nDstXOff, nDstYOff, nDstXSize, nDstYSize );
+
+/* -------------------------------------------------------------------- */
+/*      Set complex parameters.                                         */
+/* -------------------------------------------------------------------- */
+    if( dfNoDataValue != VRT_NODATA_UNSET )
+        poSource->SetNoDataValue( dfNoDataValue );
+
+    if( dfScaleOff != 0.0 || dfScaleRatio != 1.0 )
+    {
+        poSource->bDoScaling = TRUE;
+        poSource->dfScaleOff = dfScaleOff;
+        poSource->dfScaleRatio = dfScaleRatio;
+          
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If we can get the associated GDALDataset, add a reference to it.*/
+/* -------------------------------------------------------------------- */
+    if( poSrcBand->GetDataset() != NULL )
+        poSrcBand->GetDataset()->Reference();
+
+/* -------------------------------------------------------------------- */
+/*      add to list.                                                    */
+/* -------------------------------------------------------------------- */
+    return AddSource( poSource );
+}
+
+/************************************************************************/
+/*                           AddFuncSource()                            */
+/************************************************************************/
+
+CPLErr VRTSourcedRasterBand::AddFuncSource( VRTImageReadFunc pfnReadFunc, 
+                                     void *pCBData, double dfNoDataValue )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Create source.                                                  */
+/* -------------------------------------------------------------------- */
+    VRTFuncSource *poFuncSource = new VRTFuncSource;
+
+    poFuncSource->fNoDataValue = (float) dfNoDataValue;
+    poFuncSource->pfnReadFunc = pfnReadFunc;
+    poFuncSource->pCBData = pCBData;
+    poFuncSource->eType = GetRasterDataType();
+
+/* -------------------------------------------------------------------- */
+/*      add to list.                                                    */
+/* -------------------------------------------------------------------- */
+    return AddSource( poFuncSource );
+}
+
+/************************************************************************/
+/*                            GetMetadata()                             */
+/************************************************************************/
+
+char **VRTSourcedRasterBand::GetMetadata( const char *pszDomain )
+
+{
+    if( pszDomain != NULL && EQUAL(pszDomain,"vrt_sources") )
+    {
+        char **papszSourceList = NULL;
+
+/* -------------------------------------------------------------------- */
+/*      Process SimpleSources.                                          */
+/* -------------------------------------------------------------------- */
+        for( int iSource = 0; iSource < nSources; iSource++ )
+        {
+            CPLXMLNode      *psXMLSrc;
+            char            *pszXML;
+
+            psXMLSrc = papoSources[iSource]->SerializeToXML( NULL );
+            if( psXMLSrc == NULL )
+                continue;
+
+            pszXML = CPLSerializeXMLTree( psXMLSrc );
+
+            papszSourceList = 
+                CSLSetNameValue( papszSourceList, 
+                                 CPLSPrintf( "source_%d", iSource ), pszXML );
+            CPLFree( pszXML );
+            CPLDestroyXMLNode( psXMLSrc );
+        }
+        
+        return papszSourceList;
+    }
+    else
+        return GDALRasterBand::GetMetadata( pszDomain );
+}
+
+/************************************************************************/
+/*                            SetMetadata()                             */
+/************************************************************************/
+
+CPLErr VRTSourcedRasterBand::SetMetadata( char **papszNewMD, const char *pszDomain )
+
+{
+    if( pszDomain != NULL
+        && (EQUAL(pszDomain,"new_vrt_sources") 
+            || EQUAL(pszDomain,"vrt_sources")) )
+    {
+        VRTDriver *poDriver = (VRTDriver *) GDALGetDriverByName( "VRT" );
+        CPLErr eErr;
+        int    i;
+
+        if( EQUAL(pszDomain,"vrt_sources") )
+        {
+            for( int i = 0; i < nSources; i++ )
+                delete papoSources[i];
+            CPLFree( papoSources );
+            papoSources = NULL;
+            nSources = 0;
+        }
+
+        for( i = 0; i < CSLCount(papszNewMD); i++ )
+        {
+            const char *pszXML = CPLParseNameValue( papszNewMD[i], NULL );
+            CPLXMLNode *psTree = CPLParseXMLString( pszXML );
+            VRTSource *poSource;
+            
+            if( psTree == NULL )
+                return CE_Failure;
+
+            poSource = poDriver->ParseSource( psTree, NULL );
+            CPLDestroyXMLNode( psTree );
+
+            if( poSource != NULL )
+            {
+                eErr = AddSource( poSource );
+                if( eErr != CE_None )
+                    return eErr;
+            }
+            else
+                return CE_Failure;
+        }
+
+        return CE_None;
+    }
+    else
+        return GDALRasterBand::SetMetadata( papszNewMD, pszDomain );
+}
+
diff --git a/Utilities/GDAL/frmts/vrt/vrtsources.cpp b/Utilities/GDAL/frmts/vrt/vrtsources.cpp
new file mode 100644
index 0000000000..d32a2e66e1
--- /dev/null
+++ b/Utilities/GDAL/frmts/vrt/vrtsources.cpp
@@ -0,0 +1,932 @@
+/******************************************************************************
+ * $Id: vrtsources.cpp,v 1.5 2004/08/11 18:46:45 warmerda Exp $
+ *
+ * Project:  Virtual GDAL Datasets
+ * Purpose:  Implementation of VRTSimpleSource, VRTFuncSource and 
+ *           VRTAveragedSource.
+ * Author:   Frank Warmerdam <warmerdam@pobox.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: vrtsources.cpp,v $
+ * Revision 1.5  2004/08/11 18:46:45  warmerda
+ * pass pszVRTPath through serialize methods
+ *
+ * Revision 1.4  2004/03/16 18:36:02  warmerda
+ * Don't use pszVRTPath if it is NULL.
+ *
+ * Revision 1.3  2004/03/16 18:34:35  warmerda
+ * added support for relativeToVRT attribute on SourceFilename
+ *
+ * Revision 1.2  2003/09/11 23:00:04  aamici
+ * add class constructors and destructors where needed in order to
+ * let the mingw/cygwin binutils produce sensible partially linked objet files
+ * with 'ld -r'.
+ *
+ * Revision 1.1  2003/07/17 20:27:18  warmerda
+ * New
+ *
+ */
+
+#include "vrtdataset.h"
+#include "cpl_minixml.h"
+#include "cpl_string.h"
+
+CPL_CVSID("$Id: vrtsources.cpp,v 1.5 2004/08/11 18:46:45 warmerda Exp $");
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             VRTSource                                */
+/* ==================================================================== */
+/************************************************************************/
+
+VRTSource::~VRTSource()
+{
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                          VRTSimpleSource                             */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                          VRTSimpleSource()                           */
+/************************************************************************/
+
+VRTSimpleSource::VRTSimpleSource()
+
+{
+    poRasterBand = NULL;
+}
+
+/************************************************************************/
+/*                          ~VRTSimpleSource()                          */
+/************************************************************************/
+
+VRTSimpleSource::~VRTSimpleSource()
+
+{
+    if( poRasterBand != NULL && poRasterBand->GetDataset() != NULL )
+    {
+        if( poRasterBand->GetDataset()->GetShared() )
+            GDALClose( (GDALDatasetH) poRasterBand->GetDataset() );
+        else
+            poRasterBand->GetDataset()->Dereference();
+    }
+}
+
+/************************************************************************/
+/*                             SetSrcBand()                             */
+/************************************************************************/
+
+void VRTSimpleSource::SetSrcBand( GDALRasterBand *poNewSrcBand )
+
+{
+    poRasterBand = poNewSrcBand;
+}
+
+/************************************************************************/
+/*                            SetSrcWindow()                            */
+/************************************************************************/
+
+void VRTSimpleSource::SetSrcWindow( int nNewXOff, int nNewYOff, 
+                                    int nNewXSize, int nNewYSize )
+
+{
+    nSrcXOff = nNewXOff;
+    nSrcYOff = nNewYOff;
+    nSrcXSize = nNewXSize;
+    nSrcYSize = nNewYSize;
+}
+
+/************************************************************************/
+/*                            SetDstWindow()                            */
+/************************************************************************/
+
+void VRTSimpleSource::SetDstWindow( int nNewXOff, int nNewYOff, 
+                                    int nNewXSize, int nNewYSize )
+
+{
+    nDstXOff = nNewXOff;
+    nDstYOff = nNewYOff;
+    nDstXSize = nNewXSize;
+    nDstYSize = nNewYSize;
+}
+
+/************************************************************************/
+/*                           SetNoDataValue()                           */
+/************************************************************************/
+
+void VRTSimpleSource::SetNoDataValue( double dfNewNoDataValue )
+
+{
+    if( dfNewNoDataValue == VRT_NODATA_UNSET )
+    {
+        bNoDataSet = FALSE;
+        dfNoDataValue = VRT_NODATA_UNSET;
+    }
+    else
+    {
+        bNoDataSet = TRUE;
+        dfNoDataValue = dfNewNoDataValue;
+    }
+}
+
+/************************************************************************/
+/*                           SerializeToXML()                           */
+/************************************************************************/
+
+CPLXMLNode *VRTSimpleSource::SerializeToXML( const char *pszVRTPath )
+
+{
+    CPLXMLNode      *psSrc;
+    int              bRelativeToVRT;
+    const char      *pszRelativePath;
+
+    if( poRasterBand == NULL )
+        return NULL;
+
+    GDALDataset     *poDS = poRasterBand->GetDataset();
+
+    if( poDS == NULL || poRasterBand->GetBand() < 1 )
+        return NULL;
+
+    psSrc = CPLCreateXMLNode( NULL, CXT_Element, "SimpleSource" );
+
+    pszRelativePath = 
+        CPLExtractRelativePath( pszVRTPath, poDS->GetDescription(), 
+                                &bRelativeToVRT );
+    
+    CPLSetXMLValue( psSrc, "SourceFilename", pszRelativePath );
+    
+    CPLCreateXMLNode( 
+        CPLCreateXMLNode( CPLGetXMLNode( psSrc, "SourceFilename" ), 
+                          CXT_Attribute, "relativeToVRT" ), 
+        CXT_Text, bRelativeToVRT ? "1" : "0" );
+
+    CPLSetXMLValue( psSrc, "SourceBand", 
+                    CPLSPrintf("%d",poRasterBand->GetBand()) );
+
+    if( nSrcXOff != -1 
+        || nSrcYOff != -1 
+        || nSrcXSize != -1 
+        || nSrcYSize != -1 )
+    {
+        CPLSetXMLValue( psSrc, "SrcRect.#xOff", 
+                        CPLSPrintf( "%d", nSrcXOff ) );
+        CPLSetXMLValue( psSrc, "SrcRect.#yOff", 
+                        CPLSPrintf( "%d", nSrcYOff ) );
+        CPLSetXMLValue( psSrc, "SrcRect.#xSize", 
+                        CPLSPrintf( "%d", nSrcXSize ) );
+        CPLSetXMLValue( psSrc, "SrcRect.#ySize", 
+                        CPLSPrintf( "%d", nSrcYSize ) );
+    }
+
+    if( nDstXOff != -1 
+        || nDstYOff != -1 
+        || nDstXSize != -1 
+        || nDstYSize != -1 )
+    {
+        CPLSetXMLValue( psSrc, "DstRect.#xOff", CPLSPrintf( "%d", nDstXOff ) );
+        CPLSetXMLValue( psSrc, "DstRect.#yOff", CPLSPrintf( "%d", nDstYOff ) );
+        CPLSetXMLValue( psSrc, "DstRect.#xSize",CPLSPrintf( "%d", nDstXSize ));
+        CPLSetXMLValue( psSrc, "DstRect.#ySize",CPLSPrintf( "%d", nDstYSize ));
+    }
+
+    return psSrc;
+}
+
+/************************************************************************/
+/*                              XMLInit()                               */
+/************************************************************************/
+
+CPLErr VRTSimpleSource::XMLInit( CPLXMLNode *psSrc, const char *pszVRTPath )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Prepare filename.                                               */
+/* -------------------------------------------------------------------- */
+    char *pszSrcDSName = NULL;
+    const char *pszFilename = 
+        CPLGetXMLValue(psSrc, "SourceFilename", NULL);
+
+    if( pszFilename == NULL )
+    {
+        CPLError( CE_Warning, CPLE_AppDefined, 
+                  "Missing <SourceFilename> element in VRTRasterBand." );
+        return CE_Failure;
+    }
+    
+    if( pszVRTPath != NULL
+        && atoi(CPLGetXMLValue( psSrc, "SourceFilename.relativetoVRT", "0")) )
+    {
+        pszSrcDSName = CPLStrdup(
+            CPLProjectRelativeFilename( pszVRTPath, pszFilename ) );
+    }
+    else
+        pszSrcDSName = CPLStrdup( pszFilename );
+        
+/* -------------------------------------------------------------------- */
+/*      Open the file (shared).                                         */
+/* -------------------------------------------------------------------- */
+    GDALDataset *poSrcDS = (GDALDataset *) 
+        GDALOpenShared( pszSrcDSName, GA_ReadOnly );
+    CPLFree( pszSrcDSName );
+    
+    if( poSrcDS == NULL )
+        return CE_Failure;
+
+/* -------------------------------------------------------------------- */
+/*      Get the raster band.                                            */
+/* -------------------------------------------------------------------- */
+    int nSrcBand = atoi(CPLGetXMLValue(psSrc,"SourceBand","1"));
+    
+    poRasterBand = poSrcDS->GetRasterBand(nSrcBand);
+    if( poRasterBand == NULL )
+        return CE_Failure;
+    
+/* -------------------------------------------------------------------- */
+/*      Set characteristics.                                            */
+/* -------------------------------------------------------------------- */
+    nSrcXOff = atoi(CPLGetXMLValue(psSrc,"SrcRect.xOff","-1"));
+    nSrcYOff = atoi(CPLGetXMLValue(psSrc,"SrcRect.yOff","-1"));
+    nSrcXSize = atoi(CPLGetXMLValue(psSrc,"SrcRect.xSize","-1"));
+    nSrcYSize = atoi(CPLGetXMLValue(psSrc,"SrcRect.ySize","-1"));
+    nDstXOff = atoi(CPLGetXMLValue(psSrc,"DstRect.xOff","-1"));
+    nDstYOff = atoi(CPLGetXMLValue(psSrc,"DstRect.yOff","-1"));
+    nDstXSize = atoi(CPLGetXMLValue(psSrc,"DstRect.xSize","-1"));
+    nDstYSize = atoi(CPLGetXMLValue(psSrc,"DstRect.ySize","-1"));
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                              SrcToDst()                              */
+/************************************************************************/
+
+void VRTSimpleSource::SrcToDst( double dfX, double dfY,
+                                double &dfXOut, double &dfYOut )
+
+{
+    dfXOut = ((dfX - nSrcXOff) / nSrcXSize) * nDstXSize + nDstXOff;
+    dfYOut = ((dfY - nSrcYOff) / nSrcYSize) * nDstYSize + nDstYOff;
+}
+
+/************************************************************************/
+/*                              DstToSrc()                              */
+/************************************************************************/
+
+void VRTSimpleSource::DstToSrc( double dfX, double dfY,
+                                double &dfXOut, double &dfYOut )
+
+{
+    dfXOut = ((dfX - nDstXOff) / nDstXSize) * nSrcXSize + nSrcXOff;
+    dfYOut = ((dfY - nDstYOff) / nDstYSize) * nSrcYSize + nSrcYOff;
+}
+
+/************************************************************************/
+/*                          GetSrcDstWindow()                           */
+/************************************************************************/
+
+int 
+VRTSimpleSource::GetSrcDstWindow( int nXOff, int nYOff, int nXSize, int nYSize,
+                                  int nBufXSize, int nBufYSize,
+                                  int *pnReqXOff, int *pnReqYOff,
+                                  int *pnReqXSize, int *pnReqYSize,
+                                  int *pnOutXOff, int *pnOutYOff, 
+                                  int *pnOutXSize, int *pnOutYSize )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Translate requested region in virtual file into the source      */
+/*      band coordinates.                                               */
+/* -------------------------------------------------------------------- */
+    double      dfScaleX = nSrcXSize / (double) nDstXSize;
+    double      dfScaleY = nSrcYSize / (double) nDstYSize;
+
+    *pnReqXOff = (int) floor((nXOff - nDstXOff) * dfScaleX + nSrcXOff);
+    *pnReqYOff = (int) floor((nYOff - nDstYOff) * dfScaleY + nSrcYOff);
+
+    *pnReqXSize = (int) floor(nXSize * dfScaleX + 0.5);
+    *pnReqYSize = (int) floor(nYSize * dfScaleY + 0.5);
+
+/* -------------------------------------------------------------------- */
+/*      This request window corresponds to the whole output buffer.     */
+/* -------------------------------------------------------------------- */
+    *pnOutXOff = 0;
+    *pnOutYOff = 0;
+    *pnOutXSize = nBufXSize;
+    *pnOutYSize = nBufYSize;
+
+/* -------------------------------------------------------------------- */
+/*      Clamp within the bounds of the available source data.           */
+/* -------------------------------------------------------------------- */
+    int bModified = FALSE;
+
+    if( *pnReqXOff < 0 )
+    {
+        *pnReqXSize += *pnReqXOff;
+        *pnReqXOff = 0;
+
+        bModified = TRUE;
+    }
+    
+    if( *pnReqYOff < 0 )
+    {
+        *pnReqYSize += *pnReqYOff;
+        *pnReqYOff = 0;
+        bModified = TRUE;
+    }
+
+    if( *pnReqXSize == 0 )
+        *pnReqXSize = 1;
+    if( *pnReqYSize == 0 )
+        *pnReqYSize = 1;
+
+    if( *pnReqXOff + *pnReqXSize > poRasterBand->GetXSize() )
+    {
+        *pnReqXSize = poRasterBand->GetXSize() - *pnReqXOff;
+        bModified = TRUE;
+    }
+
+    if( *pnReqYOff + *pnReqYSize > poRasterBand->GetYSize() )
+    {
+        *pnReqYSize = poRasterBand->GetYSize() - *pnReqYOff;
+        bModified = TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Don't do anything if the requesting region is completely off    */
+/*      the source image.                                               */
+/* -------------------------------------------------------------------- */
+    if( *pnReqXOff >= poRasterBand->GetXSize()
+        || *pnReqYOff >= poRasterBand->GetYSize()
+        || *pnReqXSize <= 0 || *pnReqYSize <= 0 )
+    {
+        return FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If we haven't had to modify the source rectangle, then the      */
+/*      destination rectangle must be the whole region.                 */
+/* -------------------------------------------------------------------- */
+    if( !bModified )
+        return TRUE;
+
+/* -------------------------------------------------------------------- */
+/*      Now transform this possibly reduced request back into the       */
+/*      destination buffer coordinates in case the output region is     */
+/*      less than the whole buffer.                                     */
+/* -------------------------------------------------------------------- */
+    double dfDstULX, dfDstULY, dfDstLRX, dfDstLRY;
+    double dfScaleWinToBufX, dfScaleWinToBufY;
+
+    SrcToDst( (double) *pnReqXOff, (double) *pnReqYOff, dfDstULX, dfDstULY );
+    SrcToDst( *pnReqXOff + *pnReqXSize, *pnReqYOff + *pnReqYSize, 
+              dfDstLRX, dfDstLRY );
+
+    dfScaleWinToBufX = nBufXSize / (double) nXSize;
+    dfScaleWinToBufY = nBufYSize / (double) nYSize;
+
+    *pnOutXOff = (int) ((dfDstULX - nXOff) * dfScaleWinToBufX+0.001);
+    *pnOutYOff = (int) ((dfDstULY - nYOff) * dfScaleWinToBufY+0.001);
+    *pnOutXSize = (int) ((dfDstLRX - nXOff) * dfScaleWinToBufX+0.001) 
+        - *pnOutXOff;
+    *pnOutYSize = (int) ((dfDstLRY - nYOff) * dfScaleWinToBufY+0.001) 
+        - *pnOutYOff;
+
+    *pnOutXOff = MAX(0,*pnOutXOff);
+    *pnOutYOff = MAX(0,*pnOutYOff);
+    if( *pnOutXOff + *pnOutXSize > nBufXSize )
+        *pnOutXSize = nBufXSize - *pnOutXOff;
+    if( *pnOutYOff + *pnOutYSize > nBufYSize )
+        *pnOutYSize = nBufYSize - *pnOutYOff;
+
+    if( *pnOutXSize < 1 || *pnOutYSize < 1 )
+    {
+        if( nYOff == 26 )						
+        {
+            printf( "adjusted outsize == 0!\n" );
+            printf( "Dst = (%.16g,%.16g,%.16g,%.16g)\n", 
+                    dfDstULX, dfDstULY, dfDstLRX, dfDstLRY );
+            printf( "Out = (%d,%d,%d,%d)\n",
+                    *pnOutXOff, 
+                    *pnOutYOff, 
+                    *pnOutXSize, 
+                    *pnOutYSize );
+        }
+
+        return FALSE;
+    }
+    else
+        return TRUE;
+}
+
+/************************************************************************/
+/*                              RasterIO()                              */
+/************************************************************************/
+
+CPLErr 
+VRTSimpleSource::RasterIO( int nXOff, int nYOff, int nXSize, int nYSize,
+                           void *pData, int nBufXSize, int nBufYSize, 
+                           GDALDataType eBufType, 
+                           int nPixelSpace, int nLineSpace )
+
+{
+    // The window we will actually request from the source raster band.
+    int nReqXOff, nReqYOff, nReqXSize, nReqYSize;
+
+    // The window we will actual set _within_ the pData buffer.
+    int nOutXOff, nOutYOff, nOutXSize, nOutYSize;
+
+    if( !GetSrcDstWindow( nXOff, nYOff, nXSize, nYSize,
+                          nBufXSize, nBufYSize, 
+                          &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
+                          &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize ) )
+    {
+        return CE_None;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Actually perform the IO request.                                */
+/* -------------------------------------------------------------------- */
+    CPLErr eErr;
+
+    eErr = 
+        poRasterBand->RasterIO( GF_Read, 
+                                nReqXOff, nReqYOff, nReqXSize, nReqYSize,
+                                ((unsigned char *) pData) 
+                                + nOutXOff * nPixelSpace
+                                + nOutYOff * nLineSpace, 
+                                nOutXSize, nOutYSize, 
+                                eBufType, nPixelSpace, nLineSpace );
+
+    return eErr;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                         VRTAveragedSource                            */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                         VRTAveragedSource()                          */
+/************************************************************************/
+
+VRTAveragedSource::VRTAveragedSource()
+{
+}
+
+/************************************************************************/
+/*                           SerializeToXML()                           */
+/************************************************************************/
+
+CPLXMLNode *VRTAveragedSource::SerializeToXML( const char *pszVRTPath )
+
+{
+    CPLXMLNode *psSrc = VRTSimpleSource::SerializeToXML( pszVRTPath );
+
+    if( psSrc == NULL )
+        return NULL;
+
+    CPLFree( psSrc->pszValue );
+    psSrc->pszValue = CPLStrdup( "AveragedSource" );
+
+    return psSrc;
+}
+
+/************************************************************************/
+/*                              RasterIO()                              */
+/************************************************************************/
+
+CPLErr 
+VRTAveragedSource::RasterIO( int nXOff, int nYOff, int nXSize, int nYSize,
+                           void *pData, int nBufXSize, int nBufYSize, 
+                           GDALDataType eBufType, 
+                           int nPixelSpace, int nLineSpace )
+
+{
+    // The window we will actually request from the source raster band.
+    int nReqXOff, nReqYOff, nReqXSize, nReqYSize;
+
+    // The window we will actual set _within_ the pData buffer.
+    int nOutXOff, nOutYOff, nOutXSize, nOutYSize;
+
+    if( !GetSrcDstWindow( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, 
+                          &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
+                          &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize ) )
+        return CE_None;
+
+/* -------------------------------------------------------------------- */
+/*      Allocate a temporary buffer to whole the full resolution        */
+/*      data from the area of interest.                                 */
+/* -------------------------------------------------------------------- */
+    float *pafSrc;
+
+    pafSrc = (float *) VSIMalloc(sizeof(float) * nReqXSize * nReqYSize);
+    if( pafSrc == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OutOfMemory, 
+                  "Out of memory allocating working buffer in VRTAveragedSource::RasterIO()." );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Load it.                                                        */
+/* -------------------------------------------------------------------- */
+    CPLErr eErr;
+    
+    eErr = poRasterBand->RasterIO( GF_Read, 
+                                   nReqXOff, nReqYOff, nReqXSize, nReqYSize,
+                                   pafSrc, nReqXSize, nReqYSize, GDT_Float32, 
+                                   0, 0 );
+
+    if( eErr != CE_None )
+    {
+        VSIFree( pafSrc );
+        return eErr;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Do the averaging.                                               */
+/* -------------------------------------------------------------------- */
+    for( int iBufLine = nOutYOff; iBufLine < nOutYOff + nOutYSize; iBufLine++ )
+    {
+        double  dfYDst;
+
+        dfYDst = (iBufLine / (double) nBufYSize) * nYSize + nYOff;
+        
+        for( int iBufPixel = nOutXOff; 
+             iBufPixel < nOutXOff + nOutXSize; 
+             iBufPixel++ )
+        {
+            double dfXDst;
+            double dfXSrcStart, dfXSrcEnd, dfYSrcStart, dfYSrcEnd;
+            int    iXSrcStart, iYSrcStart, iXSrcEnd, iYSrcEnd;
+
+            dfXDst = (iBufPixel / (double) nBufXSize) * nXSize + nXOff;
+
+            // Compute the source image rectangle needed for this pixel.
+            DstToSrc( dfXDst, dfYDst, dfXSrcStart, dfYSrcStart );
+            DstToSrc( dfXDst+1.0, dfYDst+1.0, dfXSrcEnd, dfYSrcEnd );
+
+            // Convert to integers, assuming that the center of the source
+            // pixel must be in our rect to get included.
+            iXSrcStart = (int) floor(dfXSrcStart+0.5);
+            iYSrcStart = (int) floor(dfYSrcStart+0.5);
+            iXSrcEnd = (int) floor(dfXSrcEnd+0.5);
+            iYSrcEnd = (int) floor(dfYSrcEnd+0.5);
+
+            // Transform into the coordinate system of the source *buffer*
+            iXSrcStart -= nReqXOff;
+            iYSrcStart -= nReqYOff;
+            iXSrcEnd -= nReqXOff;
+            iYSrcEnd -= nReqYOff;
+
+            double dfSum = 0.0;
+            int    nPixelCount = 0;
+            
+            for( int iY = iYSrcStart; iY < iYSrcEnd; iY++ )
+            {
+                if( iY < 0 || iY >= nReqYSize )
+                    continue;
+
+                for( int iX = iXSrcStart; iX < iXSrcEnd; iX++ )
+                {
+                    if( iX < 0 || iX >= nReqXSize )
+                        continue;
+
+                    float fSampledValue = pafSrc[iX + iY * nReqXSize];
+
+                    if( bNoDataSet
+                        && ABS(fSampledValue-dfNoDataValue) < 0.0001 )
+                        continue;
+
+                    nPixelCount++;
+                    dfSum += pafSrc[iX + iY * nReqXSize];
+                }
+            }
+
+            if( nPixelCount == 0 )
+                continue;
+
+            // Compute output value.
+            float dfOutputValue = (float) (dfSum / nPixelCount);
+
+            // Put it in the output buffer.
+            GByte *pDstLocation;
+
+            pDstLocation = ((GByte *)pData) 
+                + nPixelSpace * iBufPixel
+                + nLineSpace * iBufLine;
+
+            if( eBufType == GDT_Byte )
+                *pDstLocation = (GByte) MIN(255,MAX(0,dfOutputValue));
+            else
+                GDALCopyWords( &dfOutputValue, GDT_Float32, 4, 
+                               pDstLocation, eBufType, 8, 1 );
+        }
+    }
+
+    VSIFree( pafSrc );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                          VRTComplexSource                            */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                          VRTComplexSource()                          */
+/************************************************************************/
+
+VRTComplexSource::VRTComplexSource()
+
+{
+    bDoScaling = FALSE;
+    dfScaleOff = 0.0;
+    dfScaleRatio = 1.0;
+    
+    bNoDataSet = FALSE;
+    dfNoDataValue = 0.0;
+}
+
+VRTComplexSource::~VRTComplexSource()
+{
+}
+
+/************************************************************************/
+/*                           SerializeToXML()                           */
+/************************************************************************/
+
+CPLXMLNode *VRTComplexSource::SerializeToXML( const char *pszVRTPath )
+
+{
+    CPLXMLNode *psSrc = VRTSimpleSource::SerializeToXML( pszVRTPath );
+
+    if( psSrc == NULL )
+        return NULL;
+
+    CPLFree( psSrc->pszValue );
+    psSrc->pszValue = CPLStrdup( "ComplexSource" );
+
+    if( bNoDataSet )
+    {
+        CPLSetXMLValue( psSrc, "NODATA", 
+                        CPLSPrintf("%g", dfNoDataValue) );
+    }
+        
+    if( bDoScaling )
+    {
+        CPLSetXMLValue( psSrc, "ScaleOffset", 
+                        CPLSPrintf("%g", dfScaleOff) );
+        CPLSetXMLValue( psSrc, "ScaleRatio", 
+                        CPLSPrintf("%g", dfScaleRatio) );
+    }
+
+    return psSrc;
+}
+
+/************************************************************************/
+/*                              XMLInit()                               */
+/************************************************************************/
+
+CPLErr VRTComplexSource::XMLInit( CPLXMLNode *psSrc, const char *pszVRTPath )
+
+{
+    CPLErr eErr;
+
+/* -------------------------------------------------------------------- */
+/*      Do base initialization.                                         */
+/* -------------------------------------------------------------------- */
+    eErr = VRTSimpleSource::XMLInit( psSrc, pszVRTPath );
+    if( eErr != CE_None )
+        return eErr;
+
+/* -------------------------------------------------------------------- */
+/*      Complex parameters.                                             */
+/* -------------------------------------------------------------------- */
+    if( CPLGetXMLValue(psSrc, "ScaleOffset", NULL) != NULL 
+        || CPLGetXMLValue(psSrc, "ScaleRatio", NULL) != NULL )
+    {
+        bDoScaling = TRUE;
+        dfScaleOff = atof(CPLGetXMLValue(psSrc, "ScaleOffset", "0") );
+        dfScaleRatio = atof(CPLGetXMLValue(psSrc, "ScaleRatio", "1") );
+    }
+
+    if( CPLGetXMLValue(psSrc, "NODATA", NULL) != NULL )
+    {
+        bNoDataSet = TRUE;
+        dfNoDataValue = atof(CPLGetXMLValue(psSrc, "NODATA", "0"));
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                              RasterIO()                              */
+/************************************************************************/
+
+CPLErr 
+VRTComplexSource::RasterIO( int nXOff, int nYOff, int nXSize, int nYSize,
+                            void *pData, int nBufXSize, int nBufYSize, 
+                            GDALDataType eBufType, 
+                            int nPixelSpace, int nLineSpace )
+    
+{
+    // The window we will actually request from the source raster band.
+    int nReqXOff, nReqYOff, nReqXSize, nReqYSize;
+
+    // The window we will actual set _within_ the pData buffer.
+    int nOutXOff, nOutYOff, nOutXSize, nOutYSize;
+
+    if( !GetSrcDstWindow( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, 
+                          &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
+                          &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize ) )
+        return CE_None;
+
+/* -------------------------------------------------------------------- */
+/*      Read into a temporary buffer.                                   */
+/* -------------------------------------------------------------------- */
+    float *pafData;
+    CPLErr eErr;
+
+    pafData = (float *) CPLMalloc(nOutXSize*nOutYSize*sizeof(float));
+    eErr = poRasterBand->RasterIO( GF_Read, 
+                                   nReqXOff, nReqYOff, nReqXSize, nReqYSize,
+                                   pafData, nOutXSize, nOutYSize, GDT_Float32,
+                                   sizeof(float), sizeof(float) * nOutXSize );
+    if( eErr != CE_None )
+    {
+        CPLFree( pafData );
+        return eErr;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Selectively copy into output buffer with nodata masking,        */
+/*      and/or scaling.                                                 */
+/* -------------------------------------------------------------------- */
+    int iX, iY;
+
+    for( iY = 0; iY < nOutYSize; iY++ )
+    {
+        for( iX = 0; iX < nOutXSize; iX++ )
+        {
+            float fResult;
+
+            fResult = pafData[iX + iY * nOutXSize];
+            if( bNoDataSet && fResult == dfNoDataValue )
+                continue;
+
+            if( bDoScaling )
+                fResult = (float) (fResult * dfScaleRatio + dfScaleOff);
+
+            GByte *pDstLocation;
+
+            pDstLocation = ((GByte *)pData) 
+                + nPixelSpace * (iX + nOutXOff)
+                + nLineSpace * (iY + nOutYOff);
+
+            if( eBufType == GDT_Byte )
+                *pDstLocation = (GByte) MIN(255,MAX(0,fResult));
+            else
+                GDALCopyWords( &fResult, GDT_Float32, 4, 
+                               pDstLocation, eBufType, 8, 1 );
+                
+        }
+    }
+
+    CPLFree( pafData );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                          VRTFuncSource                               */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                           VRTFuncSource()                            */
+/************************************************************************/
+
+VRTFuncSource::VRTFuncSource()
+
+{
+    pfnReadFunc = NULL;
+    pCBData = NULL;
+    fNoDataValue = (float) VRT_NODATA_UNSET;
+    eType = GDT_Byte;
+}
+
+/************************************************************************/
+/*                           ~VRTFuncSource()                           */
+/************************************************************************/
+
+VRTFuncSource::~VRTFuncSource()
+
+{
+}
+
+/************************************************************************/
+/*                           SerializeToXML()                           */
+/************************************************************************/
+
+CPLXMLNode *VRTFuncSource::SerializeToXML( const char * pszVRTPath )
+
+{
+    return NULL;
+}
+
+/************************************************************************/
+/*                              RasterIO()                              */
+/************************************************************************/
+
+CPLErr 
+VRTFuncSource::RasterIO( int nXOff, int nYOff, int nXSize, int nYSize,
+                         void *pData, int nBufXSize, int nBufYSize, 
+                         GDALDataType eBufType, 
+                         int nPixelSpace, int nLineSpace )
+
+{
+    if( nPixelSpace*8 == GDALGetDataTypeSize( eBufType )
+        && nLineSpace == nPixelSpace * nXSize 
+        && nBufXSize == nXSize && nBufYSize == nYSize 
+        && eBufType == eType )
+    {
+        return pfnReadFunc( pCBData,
+                            nXOff, nYOff, nXSize, nYSize, 
+                            pData );
+    }
+    else
+    {
+        printf( "%d,%d  %d,%d, %d,%d %d,%d %d,%d\n", 
+                nPixelSpace*8, GDALGetDataTypeSize(eBufType),
+                nLineSpace, nPixelSpace * nXSize, 
+                nBufXSize, nXSize, 
+                nBufYSize, nYSize, 
+                (int) eBufType, (int) eType );
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "VRTFuncSource::RasterIO() - Irregular request." );
+        return CE_Failure;
+    }
+}
+
+/************************************************************************/
+/*                        VRTParseCoreSources()                         */
+/************************************************************************/
+
+VRTSource *VRTParseCoreSources( CPLXMLNode *psChild, const char *pszVRTPath )
+
+{
+    if( EQUAL(psChild->pszValue,"AveragedSource") 
+        || (EQUAL(psChild->pszValue,"SimpleSource")
+            && EQUALN(CPLGetXMLValue(psChild, "Resampling", "Nearest"),
+                      "Aver",4)) )
+    {
+        VRTSource * poSource = new VRTAveragedSource();
+        if ( poSource->XMLInit( psChild, pszVRTPath ) == CE_None )
+            return poSource;
+    }
+    else if( EQUAL(psChild->pszValue,"SimpleSource") )
+    {
+        VRTSource * poSource = new VRTSimpleSource();
+        if ( poSource->XMLInit( psChild, pszVRTPath ) == CE_None )
+            return poSource;
+    }
+    else if( EQUAL(psChild->pszValue,"ComplexSource") )
+    {
+        VRTSource * poSource = new VRTComplexSource();
+        if ( poSource->XMLInit( psChild, pszVRTPath ) == CE_None )
+            return poSource;
+    }
+
+    return NULL;
+}
+
diff --git a/Utilities/GDAL/frmts/vrt/vrtwarped.cpp b/Utilities/GDAL/frmts/vrt/vrtwarped.cpp
new file mode 100644
index 0000000000..b5044af117
--- /dev/null
+++ b/Utilities/GDAL/frmts/vrt/vrtwarped.cpp
@@ -0,0 +1,1039 @@
+/******************************************************************************
+ * $Id: vrtwarped.cpp,v 1.13 2005/09/10 15:51:47 fwarmerdam Exp $
+ *
+ * Project:  Virtual GDAL Datasets
+ * Purpose:  Implementation of VRTWarpedRasterBand *and VRTWarpedDataset.
+ * Author:   Frank Warmerdam <warmerdam@pobox.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: vrtwarped.cpp,v $
+ * Revision 1.13  2005/09/10 15:51:47  fwarmerdam
+ * cleanup VRT overviews
+ *
+ * Revision 1.12  2005/08/02 22:21:47  fwarmerdam
+ * fixed a whopper of a memory leak in ProcessBlock
+ *
+ * Revision 1.11  2005/05/23 06:58:46  fwarmerdam
+ * updated to locked blockref api
+ *
+ * Revision 1.10  2005/05/10 04:50:42  fwarmerdam
+ * GDALOvLevelAdjust now global
+ *
+ * Revision 1.9  2005/04/25 22:26:46  fwarmerdam
+ * avoid extra copy if we are loading into the cache block in IReadBlock
+ *
+ * Revision 1.8  2005/04/11 17:36:26  fwarmerdam
+ * make sure we cleanup up transformer(s) too
+ *
+ * Revision 1.7  2005/04/04 15:25:59  fwarmerdam
+ * some functions now CPL_STDCALL
+ *
+ * Revision 1.6  2004/09/29 13:21:08  fwarmerdam
+ * Don't try and use relative include paths.
+ *
+ * Revision 1.5  2004/08/24 20:30:28  warmerda
+ * fixed size passed into GDALCopyWords() - irvine.pix bug
+ *
+ * Revision 1.4  2004/08/13 16:15:08  warmerda
+ * added some docs
+ *
+ * Revision 1.3  2004/08/12 08:24:26  warmerda
+ * added overview support
+ *
+ * Revision 1.2  2004/08/11 20:10:54  warmerda
+ * fixed issue with intializing blocksizes
+ *
+ * Revision 1.1  2004/08/11 18:42:40  warmerda
+ * New
+ *
+ */
+
+#include "vrtdataset.h"
+#include "cpl_minixml.h"
+#include "cpl_string.h"
+#include "gdalwarper.h"
+
+CPL_CVSID("$Id: vrtwarped.cpp,v 1.13 2005/09/10 15:51:47 fwarmerdam Exp $");
+
+/************************************************************************/
+/*                      GDALAutoCreateWarpedVRT()                       */
+/************************************************************************/
+
+/**
+ * Create virtual warped dataset automatically.
+ *
+ * This function will create a warped virtual file representing the 
+ * input image warped into the target coordinate system.  A GenImgProj
+ * transformation is created to accomplish any required GCP/Geotransform
+ * warp and reprojection to the target coordinate system.  The output virtual
+ * dataset will be "northup" in the target coordinate system.   The
+ * GDALSuggestedWarpOutput() function is used to determine the bounds and
+ * resolution of the output virtual file which should be large enough to 
+ * include all the input image 
+ *
+ * Note that the constructed GDALDatasetH will acquire one or more references 
+ * to the passed in hSrcDS.  Reference counting semantics on the source 
+ * dataset should be honoured.  That is, don't just GDALClose() it unless it 
+ * was opened with GDALOpenShared(). 
+ *
+ * The returned dataset will have no associated filename for itself.  If you
+ * want to write the virtual dataset description to a file, use the
+ * GDALSetDescription() function (or SetDescription() method) on the dataset
+ * to assign a filename before it is closed.  
+ *
+ * @param hSrcDS The source dataset. 
+ *
+ * @param pszSrcWKT The coordinate system of the source image.  If NULL, it 
+ * will be read from the source image. 
+ *
+ * @param pszDstWKT The coordinate system to convert to.  If NULL no change 
+ * of coordinate system will take place.  
+ *
+ * @param eResampleAlg One of GRA_NearestNeighbour, GRA_Bilinear, GRA_Cubic or 
+ * GRA_CubicSpline.  Controls the sampling method used. 
+ *
+ * @param dfMaxError Maximum error measured in input pixels that is allowed in 
+ * approximating the transformation (0.0 for exact calculations).
+ *
+ * @param psOptions Additional warp options, normally NULL.
+ *
+ * @return NULL on failure, or a new virtual dataset handle on success.
+ */
+
+GDALDatasetH CPL_STDCALL 
+GDALAutoCreateWarpedVRT( GDALDatasetH hSrcDS, 
+                         const char *pszSrcWKT,
+                         const char *pszDstWKT,
+                         GDALResampleAlg eResampleAlg, 
+                         double dfMaxError, 
+                         const GDALWarpOptions *psOptionsIn )
+    
+{
+    GDALWarpOptions *psWO;
+    int i;
+
+/* -------------------------------------------------------------------- */
+/*      Populate the warp options.                                      */
+/* -------------------------------------------------------------------- */
+    if( psOptionsIn != NULL )
+        psWO = GDALCloneWarpOptions( psOptionsIn );
+    else
+        psWO = GDALCreateWarpOptions();
+
+    psWO->eResampleAlg = eResampleAlg;
+
+    psWO->hSrcDS = hSrcDS;
+
+    psWO->nBandCount = GDALGetRasterCount( hSrcDS );
+    psWO->panSrcBands = (int *) CPLMalloc(sizeof(int) * psWO->nBandCount);
+    psWO->panDstBands = (int *) CPLMalloc(sizeof(int) * psWO->nBandCount);
+
+    for( i = 0; i < psWO->nBandCount; i++ )
+    {
+        psWO->panSrcBands[i] = i+1;
+        psWO->panDstBands[i] = i+1;
+    }
+
+    /* TODO: should fill in no data where available */
+
+/* -------------------------------------------------------------------- */
+/*      Create the transformer.                                         */
+/* -------------------------------------------------------------------- */
+    psWO->pfnTransformer = GDALGenImgProjTransform;
+    psWO->pTransformerArg = 
+        GDALCreateGenImgProjTransformer( psWO->hSrcDS, pszSrcWKT, NULL, NULL,
+                                         TRUE, 1.0, 0 );
+
+/* -------------------------------------------------------------------- */
+/*      Figure out the desired output bounds and resolution.            */
+/* -------------------------------------------------------------------- */
+    double adfDstGeoTransform[6];
+    int    nDstPixels, nDstLines;
+    CPLErr eErr;
+
+    eErr = 
+        GDALSuggestedWarpOutput( hSrcDS, psWO->pfnTransformer, 
+                                 psWO->pTransformerArg, 
+                                 adfDstGeoTransform, &nDstPixels, &nDstLines );
+
+/* -------------------------------------------------------------------- */
+/*      Update the transformer to include an output geotransform        */
+/*      back to pixel/line coordinates.                                 */
+/*                                                                      */
+/* -------------------------------------------------------------------- */
+    GDALSetGenImgProjTransformerDstGeoTransform( 
+        psWO->pTransformerArg, adfDstGeoTransform );
+
+/* -------------------------------------------------------------------- */
+/*      Do we want to apply an approximating transformation?            */
+/* -------------------------------------------------------------------- */
+    if( dfMaxError > 0.0 )
+    {
+        psWO->pTransformerArg = 
+            GDALCreateApproxTransformer( psWO->pfnTransformer, 
+                                         psWO->pTransformerArg, 
+                                         dfMaxError );
+        psWO->pfnTransformer = GDALApproxTransform;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create the VRT file.                                            */
+/* -------------------------------------------------------------------- */
+    GDALDatasetH hDstDS;
+
+    hDstDS = GDALCreateWarpedVRT( hSrcDS, nDstPixels, nDstLines, 
+                                  adfDstGeoTransform, psWO );
+
+    GDALDestroyWarpOptions( psWO );
+
+    if( pszDstWKT != NULL )
+        GDALSetProjection( hDstDS, pszDstWKT );
+    else if( pszSrcWKT != NULL )
+        GDALSetProjection( hDstDS, pszDstWKT );
+    else if( GDALGetGCPCount( hSrcDS ) > 0 )
+        GDALSetProjection( hDstDS, GDALGetGCPProjection( hSrcDS ) );
+    else 
+        GDALSetProjection( hDstDS, GDALGetProjectionRef( hSrcDS ) );
+
+    return hDstDS;
+}
+
+/************************************************************************/
+/*                        GDALCreateWarpedVRT()                         */
+/************************************************************************/
+
+/**
+ * Create virtual warped dataset.
+ *
+ * This function will create a warped virtual file representing the 
+ * input image warped based on a provided transformation.  Output bounds
+ * and resolution are provided explicitly.
+ *
+ * Note that the constructed GDALDatasetH will acquire one or more references 
+ * to the passed in hSrcDS.  Reference counting semantics on the source 
+ * dataset should be honoured.  That is, don't just GDALClose() it unless it 
+ * was opened with GDALOpenShared(). 
+ *
+ * @param hSrcDS The source dataset. 
+ *
+ * @param nOverviewLevels The number of "power of 2" overview levels to be 
+ * built.  If zero, no overview levels will be managed.  
+ *
+ * @param psOptions Additional warp options, normally NULL.
+ *
+ * @return NULL on failure, or a new virtual dataset handle on success.
+ */
+
+GDALDatasetH CPL_STDCALL
+GDALCreateWarpedVRT( GDALDatasetH hSrcDS, 
+                     int nPixels, int nLines, double *padfGeoTransform,
+                     GDALWarpOptions *psOptions )
+    
+{
+/* -------------------------------------------------------------------- */
+/*      Create the VRTDataset and populate it with bands.               */
+/* -------------------------------------------------------------------- */
+    VRTWarpedDataset *poDS = new VRTWarpedDataset( nPixels, nLines );
+    int i;
+
+    psOptions->hDstDS = (GDALDatasetH) poDS;
+
+    poDS->SetGeoTransform( padfGeoTransform );
+
+    for( i = 0; i < psOptions->nBandCount; i++ )
+    {
+        VRTWarpedRasterBand *poBand;
+        GDALRasterBand *poSrcBand = (GDALRasterBand *) 
+            GDALGetRasterBand( hSrcDS, i+1 );
+
+        poDS->AddBand( poSrcBand->GetRasterDataType(), NULL );
+
+        poBand = (VRTWarpedRasterBand *) poDS->GetRasterBand( i+1 );
+        poBand->CopyCommonInfoFrom( poSrcBand );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize the warp on the VRTWarpedDataset.                    */
+/* -------------------------------------------------------------------- */
+    poDS->Initialize( psOptions );
+    
+    return (GDALDatasetH) poDS;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                          VRTWarpedDataset                            */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                          VRTWarpedDataset()                          */
+/************************************************************************/
+
+VRTWarpedDataset::VRTWarpedDataset( int nXSize, int nYSize )
+        : VRTDataset( nXSize, nYSize )
+
+{
+    poWarper = NULL;
+    nBlockXSize = 512;
+    nBlockYSize = 128;
+    eAccess = GA_Update;
+
+    nOverviewCount = 0;
+    papoOverviews = NULL;
+}
+
+/************************************************************************/
+/*                         ~VRTWarpedDataset()                          */
+/************************************************************************/
+
+VRTWarpedDataset::~VRTWarpedDataset()
+
+{
+    FlushCache();
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup overviews.                                              */
+/* -------------------------------------------------------------------- */
+    int iOverview;
+
+    for( iOverview = 0; iOverview < nOverviewCount; iOverview++ )
+    {
+        GDALDatasetH hDS = (GDALDatasetH) papoOverviews[iOverview];
+
+        if( GDALDereferenceDataset( hDS ) < 1 )
+        {
+            GDALReferenceDataset( hDS );
+            GDALClose( hDS );
+        }
+    }
+
+    CPLFree( papoOverviews );
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup warper if one is in effect.                             */
+/* -------------------------------------------------------------------- */
+    if( poWarper != NULL )
+    {
+        const GDALWarpOptions *psWO = poWarper->GetOptions();
+
+/* -------------------------------------------------------------------- */
+/*      We take care to only call GDALClose() on psWO->hSrcDS if the    */
+/*      reference count drops to zero.  This is makes it so that we     */
+/*      can operate reference counting semantics more-or-less           */
+/*      properly even if the dataset isn't open in shared mode,         */
+/*      though we require that the caller also honour the reference     */
+/*      counting semantics even though it isn't a shared dataset.       */
+/* -------------------------------------------------------------------- */
+        if( psWO->hSrcDS != NULL )
+        {
+            if( GDALDereferenceDataset( psWO->hSrcDS ) < 1 )
+            {
+                GDALReferenceDataset( psWO->hSrcDS );
+                GDALClose( psWO->hSrcDS );
+            }
+        }
+
+/* -------------------------------------------------------------------- */
+/*      We are responsible for cleaning up the transformer outselves.   */
+/* -------------------------------------------------------------------- */
+        if( psWO->pTransformerArg != NULL )
+            GDALDestroyTransformer( psWO->pTransformerArg );
+
+        delete poWarper;
+    }
+}
+
+/************************************************************************/
+/*                             Initialize()                             */
+/*                                                                      */
+/*      Initialize a dataset from passed in warp options.               */
+/************************************************************************/
+
+CPLErr VRTWarpedDataset::Initialize( void *psWO )
+
+{
+    if( poWarper != NULL )
+        delete poWarper;
+
+    poWarper = new GDALWarpOperation();
+
+    // The act of initializing this warped dataset with this warp options
+    // will result in our assuming ownership of a reference to the
+    // hSrcDS.
+
+    if( ((GDALWarpOptions *) psWO)->hSrcDS != NULL )
+        GDALReferenceDataset( ((GDALWarpOptions *) psWO)->hSrcDS );
+
+    return poWarper->Initialize( (GDALWarpOptions *) psWO );
+}
+
+/************************************************************************/
+/*                     VRTWarpedOverviewTransform()                     */
+/************************************************************************/
+
+typedef struct {
+    GDALTransformerFunc pfnBaseTransformer;
+    void              *pBaseTransformerArg;
+
+    double            dfXOverviewFactor;
+    double            dfYOverviewFactor;
+} VWOTInfo;
+
+int VRTWarpedOverviewTransform( void *pTransformArg, int bDstToSrc, 
+                                int nPointCount, 
+                                double *padfX, double *padfY, double *padfZ,
+                                int *panSuccess )
+
+{
+    VWOTInfo *psInfo = (VWOTInfo *) pTransformArg;
+    int i, bSuccess;
+
+    if( bDstToSrc )
+    {
+        for( i = 0; i < nPointCount; i++ )
+        {
+            padfX[i] *= psInfo->dfXOverviewFactor;
+            padfY[i] *= psInfo->dfYOverviewFactor;
+        }
+    }
+
+    bSuccess = psInfo->pfnBaseTransformer( psInfo->pBaseTransformerArg, 
+                                           bDstToSrc,
+                                           nPointCount, padfX, padfY, padfZ, 
+                                           panSuccess );
+
+    if( !bDstToSrc )
+    {
+        for( i = 0; i < nPointCount; i++ )
+        {
+            padfX[i] /= psInfo->dfXOverviewFactor;
+            padfY[i] /= psInfo->dfYOverviewFactor;
+        }
+    }
+
+    return bSuccess;
+}
+
+/************************************************************************/
+/*                           BuildOverviews()                           */
+/*                                                                      */
+/*      For overviews, we actually just build a whole new dataset       */
+/*      with an extra layer of transformation on the warper used to     */
+/*      accomplish downsampling by the desired factor.                  */
+/************************************************************************/
+
+CPLErr 
+VRTWarpedDataset::IBuildOverviews( const char *pszResampling, 
+                                   int nOverviews, int *panOverviewList, 
+                                   int nListBands, int *panBandList,
+                                   GDALProgressFunc pfnProgress, 
+                                   void * pProgressData )
+    
+{
+/* -------------------------------------------------------------------- */
+/*      Initial progress result.                                        */
+/* -------------------------------------------------------------------- */
+    if( !pfnProgress( 0.0, NULL, pProgressData ) )
+    {
+        CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Establish which of the overview levels we already have, and     */
+/*      which are new.                                                  */
+/* -------------------------------------------------------------------- */
+    int   i, nNewOverviews, *panNewOverviewList = NULL;
+
+    nNewOverviews = 0;
+    panNewOverviewList = (int *) CPLCalloc(sizeof(int),nOverviews);
+    for( i = 0; i < nOverviews; i++ )
+    {
+        int   j;
+
+        for( j = 0; j < nOverviewCount; j++ )
+        {
+            int    nOvFactor;
+            VRTWarpedDataset *poOverview = papoOverviews[j];
+            
+            nOvFactor = (int) 
+                (0.5+GetRasterXSize() / (double) poOverview->GetRasterXSize());
+
+            if( nOvFactor == panOverviewList[i] 
+                || nOvFactor == GDALOvLevelAdjust( panOverviewList[i], 
+                                                   GetRasterXSize() ) )
+                panOverviewList[i] *= -1;
+        }
+
+        if( panOverviewList[i] > 0 )
+            panNewOverviewList[nNewOverviews++] = panOverviewList[i];
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create each missing overview (we don't need to do anything      */
+/*      to update existing overviews).                                  */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < nNewOverviews; i++ )
+    {
+        int    nOXSize, nOYSize, iBand;
+        VWOTInfo *psInfo;
+        VRTWarpedDataset *poOverviewDS;
+        
+/* -------------------------------------------------------------------- */
+/*      What size should this overview be.                              */
+/* -------------------------------------------------------------------- */
+        nOXSize = (GetRasterXSize() + panNewOverviewList[i] - 1) 
+            / panNewOverviewList[i];
+                                 
+        nOYSize = (GetRasterYSize() + panNewOverviewList[i] - 1) 
+            / panNewOverviewList[i];
+
+/* -------------------------------------------------------------------- */
+/*      Create the overview dataset.                                    */
+/* -------------------------------------------------------------------- */
+        poOverviewDS = new VRTWarpedDataset( nOXSize, nOYSize );
+        
+        for( iBand = 0; iBand < GetRasterCount(); iBand++ )
+        {
+            GDALRasterBand *poOldBand = GetRasterBand(iBand+1);
+            VRTWarpedRasterBand *poNewBand = 
+                new VRTWarpedRasterBand( poOverviewDS, iBand+1, 
+                                         poOldBand->GetRasterDataType() );
+            
+            poNewBand->CopyCommonInfoFrom( poOldBand );
+            poOverviewDS->SetBand( iBand+1, poNewBand );
+        }
+
+        nOverviewCount++;
+        papoOverviews = (VRTWarpedDataset **)
+            CPLRealloc( papoOverviews, sizeof(void*) * nOverviewCount );
+
+        papoOverviews[nOverviewCount-1] = poOverviewDS;
+        
+/* -------------------------------------------------------------------- */
+/*      Prepare update transformation information that will apply       */
+/*      the overview decimation.                                        */
+/* -------------------------------------------------------------------- */
+        GDALWarpOptions *psWO = (GDALWarpOptions *) poWarper->GetOptions();
+        psInfo = (VWOTInfo *) CPLCalloc(sizeof(VWOTInfo),1);
+
+        psInfo->pfnBaseTransformer = psWO->pfnTransformer;
+        psInfo->pBaseTransformerArg = psWO->pTransformerArg;
+
+        psInfo->dfXOverviewFactor = GetRasterXSize() / (double) nOXSize;
+        psInfo->dfYOverviewFactor = GetRasterYSize() / (double) nOYSize;
+
+/* -------------------------------------------------------------------- */
+/*      Initialize the new dataset with adjusted warp options, and      */
+/*      then restore to original condition.                             */
+/* -------------------------------------------------------------------- */
+        psWO->pfnTransformer = VRTWarpedOverviewTransform;
+        psWO->pTransformerArg = psInfo;
+
+        poOverviewDS->Initialize( psWO );
+
+        psWO->pfnTransformer = psInfo->pfnBaseTransformer;
+        psWO->pTransformerArg = psInfo->pBaseTransformerArg;
+    }
+
+    CPLFree( panNewOverviewList );
+
+/* -------------------------------------------------------------------- */
+/*      Progress finished.                                              */
+/* -------------------------------------------------------------------- */
+    pfnProgress( 1.0, NULL, pProgressData );
+
+    SetNeedsFlush();
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                      GDALInitializeWarpedVRT()                       */
+/************************************************************************/
+
+/**
+ * Set warp info on virtual warped dataset.
+ *
+ * Initializes all the warping information for a virtual warped dataset.
+ *
+ * This method is the same as the C++ method VRTWarpedDataset::Initialize().
+ *
+ * @param hDS dataset previously created with the VRT driver, and a 
+ * SUBCLASS of "VRTWarpedDataset".
+ * 
+ * @param psWO the warp options to apply.  Note that ownership of the
+ * transformation information is taken over by the function though everything
+ * else remains the property of the caller. 
+ *
+ * @return CE_None on success or CE_Failure if an error occurs. 
+ */
+
+CPLErr CPL_STDCALL 
+GDALInitializeWarpedVRT( GDALDatasetH hDS, GDALWarpOptions *psWO )
+
+{
+    return ((VRTWarpedDataset *) hDS)->Initialize( psWO );
+}
+
+/************************************************************************/
+/*                              XMLInit()                               */
+/************************************************************************/
+
+CPLErr VRTWarpedDataset::XMLInit( CPLXMLNode *psTree, const char *pszVRTPath )
+
+{
+    CPLErr eErr;
+
+/* -------------------------------------------------------------------- */
+/*      Initialize blocksize before calling sub-init so that the        */
+/*      band initializers can get it from the dataset object when       */
+/*      they are created.                                               */
+/* -------------------------------------------------------------------- */
+    nBlockXSize = atoi(CPLGetXMLValue(psTree,"BlockXSize","512"));
+    nBlockYSize = atoi(CPLGetXMLValue(psTree,"BlockYSize","128"));
+
+/* -------------------------------------------------------------------- */
+/*      Initialize all the general VRT stuff.  This will even           */
+/*      create the VRTWarpedRasterBands and initialize them.            */
+/* -------------------------------------------------------------------- */
+    eErr = VRTDataset::XMLInit( psTree, pszVRTPath );
+
+    if( eErr != CE_None )
+        return eErr;
+
+/* -------------------------------------------------------------------- */
+/*      Find the GDALWarpOptions XML tree.                              */
+/* -------------------------------------------------------------------- */
+    CPLXMLNode *psOptionsTree;
+    psOptionsTree = CPLGetXMLNode( psTree, "GDALWarpOptions" );
+    if( psOptionsTree == NULL )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Count not find required GDALWarpOptions in XML." );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Adjust the SourceDataset in the warp options to take into       */
+/*      account that it is relative to the VRT if appropriate.          */
+/* -------------------------------------------------------------------- */
+    int bRelativeToVRT = 
+        atoi(CPLGetXMLValue(psOptionsTree,
+                            "SourceDataset.relativeToVRT", "0" ));
+
+    const char *pszRelativePath = CPLGetXMLValue(psOptionsTree,
+                                                 "SourceDataset", "" );
+    char *pszAbsolutePath;
+
+    if( bRelativeToVRT )
+        pszAbsolutePath = 
+            CPLStrdup(CPLProjectRelativeFilename( pszVRTPath, 
+                                                  pszRelativePath ) );
+    else
+        pszAbsolutePath = CPLStrdup(pszRelativePath);
+
+    CPLSetXMLValue( psOptionsTree, "SourceDataset", pszAbsolutePath );
+    CPLFree( pszAbsolutePath );
+
+/* -------------------------------------------------------------------- */
+/*      And instantiate the warp options, and corresponding warp        */
+/*      operation.                                                      */
+/* -------------------------------------------------------------------- */
+    GDALWarpOptions *psWO;
+
+    psWO = GDALDeserializeWarpOptions( psOptionsTree );
+    if( psWO == NULL )
+        return CE_Failure;
+
+    this->eAccess = GA_Update;
+    psWO->hDstDS = this;
+
+/* -------------------------------------------------------------------- */
+/*      Instantiate the warp operation.                                 */
+/* -------------------------------------------------------------------- */
+    poWarper = new GDALWarpOperation();
+
+    eErr = poWarper->Initialize( psWO );
+
+    GDALDestroyWarpOptions( psWO );
+    if( eErr != CE_None )
+    {
+        delete poWarper;
+        poWarper = NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Generate overviews, if appropriate.                             */
+/* -------------------------------------------------------------------- */
+    char **papszTokens = CSLTokenizeString( 
+        CPLGetXMLValue( psTree, "OverviewList", "" ));
+    int iOverview;
+
+    for( iOverview = 0; 
+         papszTokens != NULL && papszTokens[iOverview] != NULL;
+         iOverview++ )
+    {
+        int nOvFactor = atoi(papszTokens[iOverview]);
+
+        BuildOverviews( "NEAREST", 1, &nOvFactor, 0, NULL, NULL, NULL );
+    }
+
+    CSLDestroy( papszTokens );
+
+    return eErr;
+}
+
+/************************************************************************/
+/*                           SerializeToXML()                           */
+/************************************************************************/
+
+CPLXMLNode *VRTWarpedDataset::SerializeToXML( const char *pszVRTPath )
+
+{
+    CPLXMLNode *psTree;
+
+    psTree = VRTDataset::SerializeToXML( pszVRTPath );
+
+    if( psTree == NULL )
+        return psTree;
+
+/* -------------------------------------------------------------------- */
+/*      Set subclass.                                                   */
+/* -------------------------------------------------------------------- */
+    CPLCreateXMLNode( 
+        CPLCreateXMLNode( psTree, CXT_Attribute, "subClass" ), 
+        CXT_Text, "VRTWarpedDataset" );
+
+/* -------------------------------------------------------------------- */
+/*      Serialize the block size.                                       */
+/* -------------------------------------------------------------------- */
+    CPLCreateXMLElementAndValue( psTree, "BlockXSize",
+                                 CPLSPrintf( "%d", nBlockXSize ) );
+    CPLCreateXMLElementAndValue( psTree, "BlockYSize",
+                                 CPLSPrintf( "%d", nBlockYSize ) );
+
+/* -------------------------------------------------------------------- */
+/*      Serialize the overview list.                                    */
+/* -------------------------------------------------------------------- */
+    if( nOverviewCount > 0 )
+    {
+        char *pszOverviewList;
+        int iOverview;
+        
+        pszOverviewList = (char *) CPLMalloc(nOverviewCount*8 + 10);
+        pszOverviewList[0] = '\0';
+        for( iOverview = 0; iOverview < nOverviewCount; iOverview++ )
+        {
+            int nOvFactor;
+
+            nOvFactor = (int) 
+                (0.5+GetRasterXSize() 
+                 / (double) papoOverviews[iOverview]->GetRasterXSize());
+
+            sprintf( pszOverviewList + strlen(pszOverviewList), 
+                     "%d ", nOvFactor );
+        }
+
+        CPLCreateXMLElementAndValue( psTree, "OverviewList", pszOverviewList );
+
+        CPLFree( pszOverviewList );
+    }
+
+/* ==================================================================== */
+/*      Serialize the warp options.                                     */
+/* ==================================================================== */
+    CPLXMLNode *psWOTree;
+
+    if( poWarper != NULL )
+    {
+/* -------------------------------------------------------------------- */
+/*      We reset the destination dataset name so it doesn't get         */
+/*      written out in the serialize warp options.                      */
+/* -------------------------------------------------------------------- */
+        char *pszSavedName = CPLStrdup(GetDescription());
+        SetDescription("");
+
+        psWOTree = GDALSerializeWarpOptions( poWarper->GetOptions() );
+        CPLAddXMLChild( psTree, psWOTree );
+
+        SetDescription( pszSavedName );
+        CPLFree( pszSavedName );
+
+/* -------------------------------------------------------------------- */
+/*      We need to consider making the source dataset relative to       */
+/*      the VRT file if possible.  Adjust accordingly.                  */
+/* -------------------------------------------------------------------- */
+        CPLXMLNode *psSDS = CPLGetXMLNode( psWOTree, "SourceDataset" );
+        int bRelativeToVRT;
+        char *pszRelativePath;
+
+        pszRelativePath = 
+            CPLStrdup(
+                CPLExtractRelativePath( pszVRTPath, psSDS->psChild->pszValue, 
+                                        &bRelativeToVRT ) );
+
+        CPLFree( psSDS->psChild->pszValue );
+        psSDS->psChild->pszValue = pszRelativePath;
+
+        CPLCreateXMLNode( 
+            CPLCreateXMLNode( psSDS, CXT_Attribute, "relativeToVRT" ), 
+            CXT_Text, bRelativeToVRT ? "1" : "0" );
+    }
+
+    return psTree;
+}
+
+/************************************************************************/
+/*                            GetBlockSize()                            */
+/************************************************************************/
+
+void VRTWarpedDataset::GetBlockSize( int *pnBlockXSize, int *pnBlockYSize )
+
+{
+    *pnBlockXSize = nBlockXSize;
+    *pnBlockYSize = nBlockYSize;
+}
+
+/************************************************************************/
+/*                            ProcessBlock()                            */
+/*                                                                      */
+/*      Warp a single requested block, and then push each band of       */
+/*      the result into the block cache.                                */
+/************************************************************************/
+
+CPLErr VRTWarpedDataset::ProcessBlock( int iBlockX, int iBlockY )
+
+{
+    if( poWarper == NULL )
+        return CE_Failure;
+
+    const GDALWarpOptions *psWO = poWarper->GetOptions();
+
+/* -------------------------------------------------------------------- */
+/*      Allocate block of memory large enough to hold all the bands     */
+/*      for this block.                                                 */
+/* -------------------------------------------------------------------- */
+    GByte *pabyDstBuffer;
+    int   nDstBufferSize;
+    int   nWordSize = (GDALGetDataTypeSize(psWO->eWorkingDataType) / 8);
+
+    nDstBufferSize = nBlockXSize * nBlockYSize * psWO->nBandCount * nWordSize;
+
+    pabyDstBuffer = (GByte *) VSIMalloc(nDstBufferSize);
+
+    if( pabyDstBuffer == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OutOfMemory,
+                  "Out of memory allocating %d byte buffer in VRTWarpedDataset::ProcessBlock()",
+                  nDstBufferSize );
+        return CE_Failure;
+    }				
+
+    memset( pabyDstBuffer, 0, nDstBufferSize );
+
+/* -------------------------------------------------------------------- */
+/*      Warp into this buffer.                                          */
+/* -------------------------------------------------------------------- */
+    CPLErr eErr;
+
+    eErr = 
+        poWarper->WarpRegionToBuffer( 
+            iBlockX * nBlockXSize, iBlockY * nBlockYSize, 
+            nBlockXSize, nBlockYSize,
+            pabyDstBuffer, psWO->eWorkingDataType );
+
+    if( eErr != CE_None )
+    {
+        VSIFree( pabyDstBuffer );
+        return eErr;
+    }
+                        
+/* -------------------------------------------------------------------- */
+/*      Copy out into cache blocks for each band.                       */
+/* -------------------------------------------------------------------- */
+    int iBand;
+
+    for( iBand = 0; iBand < psWO->nBandCount; iBand++ )
+    {
+        GDALRasterBand *poBand;
+        GDALRasterBlock *poBlock;
+
+        poBand = GetRasterBand(iBand+1);
+        poBlock = poBand->GetLockedBlockRef( iBlockX, iBlockY, TRUE );
+
+        CPLAssert( poBlock != NULL && poBlock->GetDataRef() != NULL );
+
+        GDALCopyWords( pabyDstBuffer + iBand*nBlockXSize*nBlockYSize*nWordSize,
+                       psWO->eWorkingDataType, nWordSize, 
+                       poBlock->GetDataRef(), 
+                       poBlock->GetDataType(), 
+                       GDALGetDataTypeSize(poBlock->GetDataType())/8,
+                       nBlockXSize * nBlockYSize );
+
+        poBlock->DropLock();
+    }
+
+    VSIFree( pabyDstBuffer );
+    
+    return CE_None;
+}
+
+/************************************************************************/
+/*                              AddBand()                               */
+/************************************************************************/
+
+CPLErr VRTWarpedDataset::AddBand( GDALDataType eType, char **papszOptions )
+
+{
+    SetBand( GetRasterCount() + 1,
+             new VRTWarpedRasterBand( this, GetRasterCount() + 1, eType ) );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                        VRTWarpedRasterBand                           */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                        VRTWarpedRasterBand()                         */
+/************************************************************************/
+
+VRTWarpedRasterBand::VRTWarpedRasterBand( GDALDataset *poDS, int nBand,
+                                          GDALDataType eType )
+
+{
+    Initialize( poDS->GetRasterXSize(), poDS->GetRasterYSize() );
+
+    this->poDS = poDS;
+    this->nBand = nBand;
+    this->eAccess = GA_Update;
+
+    ((VRTWarpedDataset *) poDS)->GetBlockSize( &nBlockXSize, 
+                                               &nBlockYSize );
+
+    if( eType != GDT_Unknown )
+        this->eDataType = eType;
+}
+
+/************************************************************************/
+/*                        ~VRTWarpedRasterBand()                        */
+/************************************************************************/
+
+VRTWarpedRasterBand::~VRTWarpedRasterBand()
+
+{
+    FlushCache();
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr VRTWarpedRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                     void * pImage )
+
+{
+    CPLErr eErr;
+    VRTWarpedDataset *poWDS = (VRTWarpedDataset *) poDS;
+    GDALRasterBlock *poBlock;
+
+    poBlock = GetLockedBlockRef( nBlockXOff, nBlockYOff, TRUE );
+
+    eErr = poWDS->ProcessBlock( nBlockXOff, nBlockYOff );
+
+    if( eErr == CE_None && pImage != poBlock->GetDataRef() )
+    {
+        int nDataBytes;
+        nDataBytes = (GDALGetDataTypeSize(poBlock->GetDataType()) / 8)
+            * poBlock->GetXSize() * poBlock->GetYSize();
+        memcpy( pImage, poBlock->GetDataRef(), nDataBytes );
+    }
+
+    poBlock->DropLock();
+
+    return eErr;
+}
+
+/************************************************************************/
+/*                              XMLInit()                               */
+/************************************************************************/
+
+CPLErr VRTWarpedRasterBand::XMLInit( CPLXMLNode * psTree, 
+                                  const char *pszVRTPath )
+
+{
+    return VRTRasterBand::XMLInit( psTree, pszVRTPath );
+}
+
+/************************************************************************/
+/*                           SerializeToXML()                           */
+/************************************************************************/
+
+CPLXMLNode *VRTWarpedRasterBand::SerializeToXML( const char *pszVRTPath )
+
+{
+    CPLXMLNode *psTree = VRTRasterBand::SerializeToXML( pszVRTPath );
+
+/* -------------------------------------------------------------------- */
+/*      Set subclass.                                                   */
+/* -------------------------------------------------------------------- */
+    CPLCreateXMLNode( 
+        CPLCreateXMLNode( psTree, CXT_Attribute, "subClass" ), 
+        CXT_Text, "VRTWarpedRasterBand" );
+
+    return psTree;
+}
+
+/************************************************************************/
+/*                          GetOverviewCount()                          */
+/************************************************************************/
+
+int VRTWarpedRasterBand::GetOverviewCount()
+
+{
+    VRTWarpedDataset *poWDS = (VRTWarpedDataset *) poDS;
+
+    return poWDS->nOverviewCount;
+}
+
+/************************************************************************/
+/*                            GetOverview()                             */
+/************************************************************************/
+
+GDALRasterBand *VRTWarpedRasterBand::GetOverview( int iOverview )
+
+{
+    VRTWarpedDataset *poWDS = (VRTWarpedDataset *) poDS;
+
+    if( iOverview < 0 || iOverview >= poWDS->nOverviewCount )
+        return NULL;
+    else
+        return poWDS->papoOverviews[iOverview]->GetRasterBand( nBand );
+}
+
diff --git a/Utilities/GDAL/frmts/xpm/GNUmakefile b/Utilities/GDAL/frmts/xpm/GNUmakefile
new file mode 100644
index 0000000000..19f2cf3275
--- /dev/null
+++ b/Utilities/GDAL/frmts/xpm/GNUmakefile
@@ -0,0 +1,15 @@
+
+OBJ	=	xpmdataset.o
+
+include ../../GDALmake.opt
+
+XTRAOPTS = -I../mem
+
+CPPFLAGS	:=	$(GDAL_INCLUDE) $(XTRAOPTS) $(CPPFLAGS)
+
+default:	$(OBJ)
+
+clean:
+	rm -f *.o $(O_OBJ)
+
+install-obj:	$(O_OBJ)
diff --git a/Utilities/GDAL/frmts/xpm/makefile.vc b/Utilities/GDAL/frmts/xpm/makefile.vc
new file mode 100644
index 0000000000..23b53de7c1
--- /dev/null
+++ b/Utilities/GDAL/frmts/xpm/makefile.vc
@@ -0,0 +1,15 @@
+
+OBJ	=	xpmdataset.obj
+
+GDAL_ROOT	=	..\..
+
+EXTRAFLAGS	=	-I..\mem
+
+!INCLUDE $(GDAL_ROOT)\nmake.opt
+
+default:	$(OBJ)
+	copy *.obj ..\o
+
+clean:
+	-del *.obj
+
diff --git a/Utilities/GDAL/frmts/xpm/xpmdataset.cpp b/Utilities/GDAL/frmts/xpm/xpmdataset.cpp
new file mode 100644
index 0000000000..02424a0079
--- /dev/null
+++ b/Utilities/GDAL/frmts/xpm/xpmdataset.cpp
@@ -0,0 +1,671 @@
+/******************************************************************************
+ * $Id: xpmdataset.cpp,v 1.13 2006/03/27 17:58:12 fwarmerdam Exp $
+ *
+ * Project:  XPM Driver
+ * Purpose:  Implement GDAL XPM Support
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2002, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ * 
+ * $Log: xpmdataset.cpp,v $
+ * Revision 1.13  2006/03/27 17:58:12  fwarmerdam
+ * Fixed last change.
+ *
+ * Revision 1.12  2006/03/27 17:57:31  fwarmerdam
+ * Added check for static keyword.  Ivan encountered "XPM" in a raw image.
+ *
+ * Revision 1.11  2005/05/05 15:54:49  fwarmerdam
+ * PAM Enabled
+ *
+ * Revision 1.10  2004/11/11 17:07:19  fwarmerdam
+ * Avoid warnings about using char as a subscript.
+ *
+ * Revision 1.9  2003/07/08 21:15:55  warmerda
+ * avoid warnings
+ *
+ * Revision 1.8  2003/04/10 10:24:16  dron
+ * More leaks fixed.
+ *
+ * Revision 1.7  2003/04/09 21:32:02  dron
+ * Memory leak fixed.
+ *
+ * Revision 1.6  2002/11/23 18:54:17  warmerda
+ * added CREATIONDATATYPES metadata for drivers
+ *
+ * Revision 1.5  2002/09/04 06:50:37  warmerda
+ * avoid static driver pointers
+ *
+ * Revision 1.4  2002/06/12 21:12:25  warmerda
+ * update to metadata based driver info
+ *
+ * Revision 1.3  2002/04/16 17:46:47  warmerda
+ * changed path to memdataset.h
+ *
+ * Revision 1.2  2002/04/16 16:12:53  warmerda
+ * Fixed help topic.
+ *
+ * Revision 1.1  2002/04/12 20:13:01  warmerda
+ * New
+ *
+ */
+
+#include "gdal_pam.h"
+#include "cpl_string.h"
+#include "memdataset.h"
+#include "gdal_frmts.h"						      
+
+
+CPL_CVSID("$Id: xpmdataset.cpp,v 1.13 2006/03/27 17:58:12 fwarmerdam Exp $");
+
+static unsigned char *ParseXPM( const char *pszInput,
+                                int *pnXSize, int *pnYSize, 
+                                GDALColorTable **ppoRetTable );
+
+
+/************************************************************************/
+/* ==================================================================== */
+/*				XPMDataset				*/
+/* ==================================================================== */
+/************************************************************************/
+
+class XPMDataset : public GDALPamDataset
+{
+  public:
+                 XPMDataset();
+                 ~XPMDataset();
+
+    static GDALDataset *Open( GDALOpenInfo * );
+};
+
+/************************************************************************/
+/*                            XPMDataset()                            */
+/************************************************************************/
+
+XPMDataset::XPMDataset()
+
+{
+}
+
+/************************************************************************/
+/*                            ~XPMDataset()                             */
+/************************************************************************/
+
+XPMDataset::~XPMDataset()
+
+{
+    FlushCache();
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *XPMDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+/* -------------------------------------------------------------------- */
+/*      First we check to see if the file has the expected header       */
+/*      bytes.  For now we expect the XPM file to start with a line     */
+/*      containing the letters XPM, and to have "static" in the         */
+/*      header.                                                         */
+/* -------------------------------------------------------------------- */
+    if( poOpenInfo->nHeaderBytes < 32 
+        || strstr((const char *) poOpenInfo->pabyHeader,"XPM") == NULL 
+        || strstr((const char *) poOpenInfo->pabyHeader,"static") == NULL )
+        return NULL;
+
+    if( poOpenInfo->eAccess == GA_Update )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "The XPM driver does not support update access to existing"
+                  " files." );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read the whole file into a memory strings.                      */
+/* -------------------------------------------------------------------- */
+    unsigned int nFileSize;
+    char *pszFileContents;
+
+    VSIFSeek( poOpenInfo->fp, 0, SEEK_END );
+    nFileSize = VSIFTell( poOpenInfo->fp );
+    
+    pszFileContents = (char *) VSIMalloc(nFileSize+1);
+    if( pszFileContents == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OutOfMemory, 
+                  "Insufficient memory for loading XPM file %s into memory.", 
+                  poOpenInfo->pszFilename );
+        return NULL;
+    }
+    
+    VSIFSeek( poOpenInfo->fp, 0, SEEK_SET );
+
+    if( VSIFRead( pszFileContents, 1, nFileSize, poOpenInfo->fp ) != nFileSize)
+    {
+        CPLFree( pszFileContents );
+        CPLError( CE_Failure, CPLE_FileIO, 
+                  "Failed to read all %d bytes from file %s.",
+                  nFileSize, poOpenInfo->pszFilename );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Convert into a binary image.                                    */
+/* -------------------------------------------------------------------- */
+    GByte *pabyImage;
+    int   nXSize, nYSize;
+    GDALColorTable *poCT = NULL;
+
+    CPLErrorReset();
+
+    pabyImage = ParseXPM( pszFileContents, &nXSize, &nYSize, &poCT );
+    CPLFree( pszFileContents );
+
+    if( pabyImage == NULL )
+    {
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create a corresponding GDALDataset.                             */
+/* -------------------------------------------------------------------- */
+    XPMDataset 	*poDS;
+
+    poDS = new XPMDataset();
+
+/* -------------------------------------------------------------------- */
+/*      Capture some information from the file that is of interest.     */
+/* -------------------------------------------------------------------- */
+    poDS->nRasterXSize = nXSize;
+    poDS->nRasterYSize = nYSize;
+
+/* -------------------------------------------------------------------- */
+/*      Create band information objects.                                */
+/* -------------------------------------------------------------------- */
+    MEMRasterBand *poBand;
+
+    poBand = new MEMRasterBand( poDS, 1, pabyImage, GDT_Byte, 1, nXSize, 
+                                TRUE );
+    poBand->SetColorTable( poCT );
+    poDS->SetBand( 1, poBand );
+
+    delete poCT;
+
+/* -------------------------------------------------------------------- */
+/*      Initialize any PAM information.                                 */
+/* -------------------------------------------------------------------- */
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML();
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                           XPMCreateCopy()                            */
+/************************************************************************/
+
+static GDALDataset *
+XPMCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, 
+               int bStrict, char ** papszOptions, 
+               GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    int  nBands = poSrcDS->GetRasterCount();
+    int  nXSize = poSrcDS->GetRasterXSize();
+    int  nYSize = poSrcDS->GetRasterYSize();
+    GDALColorTable *poCT;
+
+/* -------------------------------------------------------------------- */
+/*      Some some rudimentary checks                                    */
+/* -------------------------------------------------------------------- */
+    if( nBands != 1 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "XPM driver only supports one band images.\n" );
+
+        return NULL;
+    }
+
+    if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte 
+        && bStrict )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported, 
+                  "XPM driver doesn't support data type %s. "
+                  "Only eight bit bands supported.\n", 
+                  GDALGetDataTypeName( 
+                      poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
+
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If there is no colortable on the source image, create a         */
+/*      greyscale one with 64 levels of grey.                           */
+/* -------------------------------------------------------------------- */
+    GDALRasterBand	*poBand = poSrcDS->GetRasterBand(1);
+    int                 i;
+    GDALColorTable      oGreyTable;
+
+    poCT = poBand->GetColorTable();
+    if( poCT == NULL )
+    {
+        poCT = &oGreyTable;
+
+        for( i = 0; i < 256; i++ )
+        {
+            GDALColorEntry sColor;
+
+            sColor.c1 = (short) i;
+            sColor.c2 = (short) i;
+            sColor.c3 = (short) i;
+            sColor.c4 = 255;
+
+            poCT->SetColorEntry( i, &sColor );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Build list of active colors, and the mapping from pixels to     */
+/*      our active colormap.                                            */
+/* -------------------------------------------------------------------- */
+    const char *pszColorCodes = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-+=[]|:;,.<>?/";
+
+    int  anPixelMapping[256];
+    GDALColorEntry asPixelColor[256];
+    int  nActiveColors = MIN(poCT->GetColorEntryCount(),256);
+
+    // Setup initial colortable and pixel value mapping. 
+    memset( anPixelMapping+0, 0, sizeof(int) * 256 );
+    for( i = 0; i < nActiveColors; i++ )
+    {
+        poCT->GetColorEntryAsRGB( i, asPixelColor + i );
+        anPixelMapping[i] = i;
+    }
+
+/* ==================================================================== */
+/*      Iterate merging colors until we are under our limit (about 85). */
+/* ==================================================================== */
+    while( nActiveColors > (int) strlen(pszColorCodes) )
+    {
+        int nClosestDistance = 768;
+        int iClose1 = -1, iClose2 = -1;
+        int iColor1, iColor2;
+
+        // Find the closest pair of colors. 
+        for( iColor1 = 0; iColor1 < nActiveColors; iColor1++ )
+        {
+            for( iColor2 = iColor1+1; iColor2 < nActiveColors; iColor2++ )
+            {
+                int	nDistance;
+
+                if( asPixelColor[iColor1].c4 < 128 
+                    && asPixelColor[iColor2].c4 < 128 )
+                    nDistance = 0;
+                else
+                    nDistance = 
+                        ABS(asPixelColor[iColor1].c1-asPixelColor[iColor2].c1)
+                      + ABS(asPixelColor[iColor1].c2-asPixelColor[iColor2].c2)
+                      + ABS(asPixelColor[iColor1].c3-asPixelColor[iColor2].c3);
+
+                if( nDistance < nClosestDistance )
+                {
+                    nClosestDistance = nDistance;
+                    iClose1 = iColor1;
+                    iClose2 = iColor2;
+                }
+            }
+
+            if( nClosestDistance < 8 )
+                break;
+        }
+
+        // This should never happen!
+        if( iClose1 == -1 )
+            break;
+
+        // Merge two selected colors - shift icolor2 into icolor1 and
+        // move the last active color into icolor2's slot. 
+        for( i = 0; i < 256; i++ )
+        {
+            if( anPixelMapping[i] == iClose2 )
+                anPixelMapping[i] = iClose1;
+            else if( anPixelMapping[i] == nActiveColors-1 )
+                anPixelMapping[i] = iClose2;
+        }
+
+        asPixelColor[iClose2] = asPixelColor[nActiveColors-1];
+        nActiveColors--;
+    }
+        
+/* ==================================================================== */
+/*      Write the output image.                                         */
+/* ==================================================================== */
+    FILE	*fpPBM;
+
+    fpPBM = VSIFOpen( pszFilename, "wt+" );
+    if( fpPBM == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, 
+                  "Unable to create file `%s'.", 
+                  pszFilename );
+
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Write the header lines.                                         */
+/* -------------------------------------------------------------------- */
+    fprintf( fpPBM, "/* XPM */\n" );
+    fprintf( fpPBM, "static char *%s[] = {\n", 
+             CPLGetBasename( pszFilename ) );
+    fprintf( fpPBM, "/* width height num_colors chars_per_pixel */\n" );
+    fprintf( fpPBM, "\"  %3d   %3d     %3d             1\",\n",
+             nXSize, nYSize, nActiveColors );
+    fprintf( fpPBM, "/* colors */\n" );
+
+/* -------------------------------------------------------------------- */
+/*      Write the color table.                                          */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < nActiveColors; i++ )
+    {
+        if( asPixelColor[i].c4 < 128 )
+            fprintf( fpPBM, "\"%c c None\",\n", pszColorCodes[i] );
+        else
+            fprintf( fpPBM, 
+                     "\"%c c #%02x%02x%02x\",\n",
+                     pszColorCodes[i],
+                     asPixelColor[i].c1, 
+                     asPixelColor[i].c2, 
+                     asPixelColor[i].c3 );
+    }
+
+/* -------------------------------------------------------------------- */
+/*	Dump image.							*/
+/* -------------------------------------------------------------------- */
+    int iLine;
+    GByte 	*pabyScanline;
+
+    pabyScanline = (GByte *) CPLMalloc( nXSize );
+    for( iLine = 0; iLine < nYSize; iLine++ )
+    {
+        poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, 
+                          (void *) pabyScanline, nXSize, 1, GDT_Byte, 0, 0 );
+        
+        fputc( '"', fpPBM );
+        for( int iPixel = 0; iPixel < nXSize; iPixel++ )
+            fputc( pszColorCodes[anPixelMapping[pabyScanline[iPixel]]], 
+                   fpPBM);
+        fprintf( fpPBM, "\",\n" );
+    }
+    
+    CPLFree( pabyScanline );
+
+/* -------------------------------------------------------------------- */
+/*      cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    fprintf( fpPBM, "};\n" );
+    VSIFClose( fpPBM );
+
+/* -------------------------------------------------------------------- */
+/*      Re-open dataset, and copy any auxilary pam information.         */
+/* -------------------------------------------------------------------- */
+    GDALPamDataset *poDS = (GDALPamDataset *) 
+        GDALOpen( pszFilename, GA_ReadOnly );
+
+    if( poDS )
+        poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                          GDALRegister_XPM()                          */
+/************************************************************************/
+
+void GDALRegister_XPM()
+
+{
+    GDALDriver	*poDriver;
+
+    if( GDALGetDriverByName( "XPM" ) == NULL )
+    {
+        poDriver = new GDALDriver();
+        
+        poDriver->SetDescription( "XPM" );
+        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
+                                   "X11 PixMap Format" );
+        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
+                                   "frmt_various.html#XPM" );
+        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "xpm" );
+        poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/x-xpixmap" );
+        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
+                                   "Byte" );
+
+        poDriver->pfnOpen = XPMDataset::Open;
+        poDriver->pfnCreateCopy = XPMCreateCopy;
+
+        GetGDALDriverManager()->RegisterDriver( poDriver );
+    }
+}
+
+/************************************************************************/
+/*                              ParseXPM()                              */
+/************************************************************************/
+
+static unsigned char *
+ParseXPM( const char *pszInput, int *pnXSize, int *pnYSize, 
+          GDALColorTable **ppoRetTable )
+
+{
+/* ==================================================================== */
+/*      Parse input into an array of strings from within the first C    */
+/*      initializer (list os comma separated strings in braces).        */
+/* ==================================================================== */
+    char **papszXPMList = NULL;
+    const char *pszNext = pszInput;
+    int  i;
+
+    // Skip till after open brace.
+    while( *pszNext != '\0' && *pszNext != '{' ) 
+        pszNext++;
+
+    if( *pszNext == '\0' )
+        return NULL;
+
+    pszNext++;
+
+    // Read lines till close brace.
+    
+    while( *pszNext != '\0' && *pszNext != '}' )
+    {
+        // skip whole comment. 
+        if( EQUALN(pszNext,"/*",2) )
+        {
+            pszNext += 2;
+            while( *pszNext != '\0' && !EQUALN(pszNext,"*/",2) )
+                pszNext++;
+        }
+
+        // reading string constants
+        else if( *pszNext == '"' )
+        {
+            char   *pszLine;
+
+            pszNext++;
+            i = 0;
+
+            while( pszNext[i] != '\0' && pszNext[i] != '"' )
+                i++;
+
+            pszLine = (char *) CPLMalloc(i+1);
+            strncpy( pszLine, pszNext, i );
+            pszLine[i] = '\0';
+
+            papszXPMList = CSLAddString( papszXPMList, pszLine );
+            CPLFree( pszLine );
+            pszNext = pszNext + i + 1;
+        }
+
+        // just ignore everything else (whitespace, commas, newlines, etc).
+        else
+            pszNext++;
+    }
+
+    if( CSLCount(papszXPMList) < 3 || *pszNext != '}' )
+    {
+        CSLDestroy( papszXPMList );
+        return NULL;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Get the image information.                                      */
+/* -------------------------------------------------------------------- */
+    int nColorCount, nCharsPerPixel;
+
+    if( sscanf( papszXPMList[0], "%d %d %d %d", 
+                pnXSize, pnYSize, &nColorCount, &nCharsPerPixel ) != 4 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Image definition (%s) not well formed.",
+                  papszXPMList[0] );
+        CSLDestroy( papszXPMList );
+        return NULL;
+    }
+
+    if( nCharsPerPixel != 1 )
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Only one character per pixel XPM images supported by GDAL at this time." );
+        CSLDestroy( papszXPMList );
+        return NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Parse out colors.                                               */
+/* -------------------------------------------------------------------- */
+    int iColor;
+    int anCharLookup[256];
+    GDALColorTable oCTable;
+
+    for( i = 0; i < 256; i++ ) 
+        anCharLookup[i] = -1;
+
+    for( iColor = 0; iColor < nColorCount; iColor++ )
+    {
+        char **papszTokens = CSLTokenizeString( papszXPMList[iColor+1]+1 );
+        GDALColorEntry sColor;
+        int            nRed, nGreen, nBlue;
+
+        if( CSLCount(papszTokens) != 2 || !EQUAL(papszTokens[0],"c") )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined, 
+                      "Ill formed color definition (%s) in XPM header.", 
+                      papszXPMList[iColor+1] );
+            CSLDestroy( papszXPMList );
+            CSLDestroy( papszTokens );
+            return NULL;
+        }
+
+        anCharLookup[(int)papszXPMList[iColor+1][0]] = iColor;
+        
+        if( EQUAL(papszTokens[1],"None") )
+        {
+            sColor.c1 = 0;
+            sColor.c2 = 0;
+            sColor.c3 = 0;
+            sColor.c4 = 0;
+        }
+        else if( sscanf( papszTokens[1], "#%02x%02x%02x", 
+                         &nRed, &nGreen, &nBlue ) != 3 )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined, 
+                      "Ill formed color definition (%s) in XPM header.", 
+                      papszXPMList[iColor+1] );
+            CSLDestroy( papszXPMList );
+            CSLDestroy( papszTokens );
+            return NULL;
+        }
+        else
+        {
+            sColor.c1 = (short) nRed;
+            sColor.c2 = (short) nGreen;
+            sColor.c3 = (short) nBlue;
+            sColor.c4 = 255;
+        }
+
+        oCTable.SetColorEntry( iColor, &sColor );
+
+        CSLDestroy( papszTokens );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Prepare image buffer.                                           */
+/* -------------------------------------------------------------------- */
+    GByte *pabyImage;
+
+    pabyImage = (GByte *) VSIMalloc(*pnXSize * *pnYSize);
+    if( pabyImage == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OutOfMemory, 
+                  "Insufficient memory for %dx%d XPM image buffer.", 
+                  *pnXSize, *pnYSize );
+        CSLDestroy( papszXPMList );
+        return NULL;
+    }
+
+    memset( pabyImage, 0, *pnXSize * *pnYSize );
+
+/* -------------------------------------------------------------------- */
+/*      Parse image.                                                    */
+/* -------------------------------------------------------------------- */
+    for( int iLine = 0; iLine < *pnYSize; iLine++ )
+    {
+        const char *pszInLine = papszXPMList[iLine + nColorCount + 1];
+
+        if( pszInLine == NULL )
+        {
+            CPLFree( pabyImage );
+            CSLDestroy( papszXPMList );
+            CPLError( CE_Failure, CPLE_AppDefined, 
+                      "Insufficient imagery lines in XPM image." );
+            return NULL;
+        }
+
+        for( int iPixel = 0; 
+             pszInLine[iPixel] != '\0' && iPixel < *pnXSize; 
+             iPixel++ )
+        {
+            int nPixelValue = anCharLookup[(int)pszInLine[iPixel]];
+            if( nPixelValue != -1 )
+                pabyImage[iLine * *pnXSize + iPixel] = (GByte) nPixelValue;
+        }
+    }
+
+    CSLDestroy( papszXPMList );
+
+    *ppoRetTable = oCTable.Clone();
+
+    return pabyImage;
+}
-- 
GitLab