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=<dir>" 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_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_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_ndoubles, >->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,>if->gt_version)) return 0; + scan(message,aux); + if (sscanf(message,FMT_REV,>if->gt_rev_major, + >if->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 = ®isteredCODECS; (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'> </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> +<?xml version="1.0"?> +<Jp2Profile version="1.0"> + <Header> + <name>Default</name> + <description>LizardTech preferred settings (20051216)</description> + </Header> + <Codestream> + <layers> + 8 + </layers> + <levels> + 99 + </levels> + <tileSize> + 0 0 + </tileSize> + <progressionOrder> + RPCL + </progressionOrder> + <codeblockSize> + 64 64 + </codeblockSize> + <pltMarkers> + true + </pltMarkers> + <wavelet97> + false + </wavelet97> + <precinctSize> + 256 256 + </precinctSize> + </Codestream> +</Jp2Profile> + +</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 “use_root_folder” 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™ 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> <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> <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> <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["NAD27 / UTM zone 11N",GEOGCS["NAD27",DATUM["North_American_Datum_1927",SPHEROID["Clarke 1866",6378206.4,294.9786982139006,AUTHORITY["EPSG","7008"]],AUTHORITY["EPSG","6267"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["EPSG","4267"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-117],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","26711"]]</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